├── .gitignore ├── LICENSE ├── README.md ├── config └── config.exs ├── lib └── mix │ └── tasks │ └── phx.gen.elm.ex ├── mix.exs ├── mix.lock └── priv ├── static └── elm-logo.png └── templates └── phx.gen.elm ├── assets ├── elm-package.json ├── elm │ ├── Main.elm │ ├── State.elm │ ├── Types.elm │ └── View.elm └── js │ └── elm-embed.js ├── controllers └── elm_controller.ex ├── templates └── elm │ └── index.html.eex ├── test └── elm │ ├── Main.elm │ ├── Sample.elm │ └── elm-package.json └── views └── elm_view.ex /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | elm-stuff 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Andrew MacMurray 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phoenix Elm Scaffold 2 | 3 | Mix Task to generate an elm app inside a Phoenix (1.3) app with the necessary scaffolding 4 | 5 | ## Installation 6 | 7 | Install the package by adding to `deps` and run `mix deps.get` 8 | 9 | ```elixir 10 | def deps do 11 | [{:phoenix_elm_scaffold, "~> 0.2.6"}] 12 | end 13 | ``` 14 | 15 | ## Usage 16 | 17 | To generate the scaffolding for an elm app, make sure you have [elm](https://guide.elm-lang.org/install.html) installed and run: 18 | 19 | ```sh 20 | > mix phx.gen.elm 21 | ``` 22 | 23 | this will generate the following files and run install commands: 24 | 25 | ``` 26 | ├── assets 27 | │   ├── elm 28 | │   │   ├── Main.elm 29 | │   │   ├── State.elm 30 | │   │   ├── Types.elm 31 | │   │   └── View.elm 32 | │   ├── elm-package.json 33 | │   ├── js 34 | │   │   └── elm-embed.js 35 | ├── lib 36 | │   └── your_app 37 | │   └── web 38 | │   ├── controllers 39 | │   │   ├── elm_controller.ex 40 | │   ├── templates 41 | │   │   ├── elm 42 | │   │   │   └── index.html.eex 43 | │   └── views 44 | │      └── elm_view.ex 45 | └── test 46 | └── elm 47 |    ├── Main.elm 48 |    ├── Sample.elm 49 |    └── elm-package.json 50 | ``` 51 | 52 | 53 | after the command has finished follow the next steps: 54 | 55 | 1: add the following to the `plugins` section of your `brunch-config.js` 56 | 57 | ```js 58 | elmBrunch: { 59 | elmFolder: ".", // Set to path where elm-package.json is located, defaults to project root 60 | mainModules: ['elm/Main.elm'], 61 | outputFile: 'elm.js', 62 | outputFolder: '../assets/js', 63 | makeParameters: ['--debug'] // optional debugger for development 64 | } 65 | ``` 66 | 67 | 2: add `elm` to the `watched` array in your `brunch-config.js` 68 | You may also want to add `/elm\\.js/` to the babel ignore pattern to speed up compilation 69 | 70 | ```js 71 | babel: { 72 | ignore: [/vendor/, /elm\.js/] 73 | } 74 | ``` 75 | 76 | 77 | 3: in your `app.js` file add the following 78 | 79 | ```js 80 | import ElmApp from './elm.js' 81 | import elmEmbed from './elm-embed.js' 82 | 83 | elmEmbed.init(ElmApp) 84 | ``` 85 | 86 | 87 | 4: and finally in your `router.ex` file add 88 | 89 | ```elixir 90 | get "/path-to-elm-app", ElmController, :index 91 | ``` 92 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | -------------------------------------------------------------------------------- /lib/mix/tasks/phx.gen.elm.ex: -------------------------------------------------------------------------------- 1 | defmodule Mix.Tasks.Phx.Gen.Elm do 2 | use Mix.Task 3 | 4 | @shortdoc "Generates an elm app inside a Phoenix (1.3) app with the necessary scaffolding" 5 | @instructions """ 6 | 1. add the following to the `plugins` section of your `brunch-config.js` 7 | 8 | ```js 9 | elmBrunch: { 10 | elmFolder: '.', 11 | mainModules: ['elm/Main.elm'], 12 | outputFile: 'elm.js', 13 | outputFolder: '../assets/js', 14 | makeParameters: ['--debug'] // optional debugger for development 15 | } 16 | ``` 17 | 18 | 2. add `elm` to the `watched` array in your `brunch-config.js` 19 | You may also want to add `/elm\\.js/` to the babel ignore pattern to speed up compilation 20 | 21 | ```js 22 | babel: { 23 | ignore: [/vendor/, /elm\.js/] 24 | } 25 | ``` 26 | 27 | 28 | 3. in your `app.js` file add the following 29 | 30 | ```js 31 | import ElmApp from './elm.js' 32 | import elmEmbed from './elm-embed.js' 33 | 34 | elmEmbed.init(ElmApp) 35 | ``` 36 | 37 | 4. and finally in your `router.ex` file add 38 | 39 | ```elixir 40 | get "/path-to-elm-app", ElmController, :index 41 | ``` 42 | """ 43 | 44 | @moduledoc """ 45 | Generates an elm app inside a Phoenix (1.3) app with the necessary scaffolding 46 | 47 | adds: 48 | - elm files (`Main`, `Types`, `State`, `View`) 49 | - an embed script 50 | - `elm-package.json` 51 | - A phoenix `controller`, `view` and `template` 52 | - an `elm-test` setup 53 | 54 | to run the generator: 55 | 56 | ```sh 57 | > mix phx.gen.elm 58 | ``` 59 | then follow post install instructions: 60 | 61 | #{@instructions} 62 | 63 | """ 64 | 65 | @src "priv/templates/phx.gen.elm" 66 | 67 | def run(_argv) do 68 | copy_phoenix_files() 69 | copy_elm_files() 70 | install_node_modules() 71 | post_install_instructions() 72 | update_time_created() 73 | end 74 | 75 | defp post_install_instructions do 76 | instructions = """ 77 | 78 | 🎉 ✨ Your elm app is almost ready to go! ✨ 🎉 79 | 80 | #{@instructions} 81 | 82 | """ 83 | 84 | Mix.shell.info(instructions) 85 | end 86 | 87 | 88 | defp copy_phoenix_files do 89 | templates = [ 90 | {:eex, "views/elm_view.ex", web_dir("views/elm_view.ex")}, 91 | {:eex, "controllers/elm_controller.ex", web_dir("controllers/elm_controller.ex")}, 92 | {:text, "templates/elm/index.html.eex", web_dir("templates/elm/index.html.eex")} 93 | ] 94 | 95 | Mix.shell.info("adding phoenix files 🕊 🔥") 96 | copy_files(templates) 97 | end 98 | 99 | defp update_time_created do 100 | [ 101 | web_dir("views/elm_view.ex"), 102 | web_dir("controllers/elm_controller.ex") 103 | ] 104 | |> Enum.map(&File.touch!(&1)) 105 | end 106 | 107 | defp web_dir(path) do 108 | Mix.Phoenix.context_app() 109 | |> Mix.Phoenix.web_path() 110 | |> Path.join(path) 111 | end 112 | 113 | defp copy_files(templates) do 114 | Mix.Phoenix.copy_from( 115 | [:phoenix_elm_scaffold], @src, [app_name: app_module_name()], templates 116 | ) 117 | end 118 | 119 | defp copy_elm_files do 120 | files = [ 121 | "assets/elm/Main.elm", 122 | "assets/elm/State.elm", 123 | "assets/elm/View.elm", 124 | "assets/elm/Types.elm", 125 | "assets/js/elm-embed.js", 126 | "assets/elm-package.json", 127 | "test/elm/Main.elm", 128 | "test/elm/Sample.elm", 129 | "test/elm/elm-package.json" 130 | ] 131 | |> Enum.map(&text_file/1) 132 | 133 | Mix.shell.info("adding elm files 🌳") 134 | copy_files(files) 135 | end 136 | 137 | defp text_file(path) do 138 | {:text, path, path} 139 | end 140 | 141 | defp app_module_name do 142 | Mix.Phoenix.context_app() 143 | |> Atom.to_string 144 | |> String.split("_") 145 | |> Enum.map(&String.capitalize/1) 146 | |> Enum.join("") 147 | end 148 | 149 | defp install_node_modules do 150 | deps = [ 151 | "elm" 152 | ] 153 | 154 | dev_deps = [ 155 | "elm-brunch", 156 | "elm-test" 157 | ] 158 | 159 | change_dir = "cd assets" 160 | pre_install = "npm install" 161 | node_install_deps = "npm install -S " <> Enum.join(deps, " ") 162 | node_install_dev_deps = "npm install -D " <> Enum.join(dev_deps, " ") 163 | 164 | # TODO: make these not depend on a global version of elm 165 | elm_install = "elm-package install -y" 166 | elm_compile = "elm-make elm/Main.elm --output=js/elm.js" 167 | 168 | all_cmds = [ 169 | change_dir, 170 | pre_install, 171 | node_install_deps, 172 | node_install_dev_deps, 173 | elm_install, 174 | elm_compile 175 | ] 176 | 177 | cmd = Enum.join(all_cmds, " && ") 178 | 179 | Mix.shell.info("installing node modules for elm-app ⬇️") 180 | Mix.shell.info(cmd) 181 | status = Mix.shell.cmd(cmd, stderr_to_stdout: true) 182 | case status do 183 | 0 -> :ok 184 | _ -> raise "Error installing node modules: #{status}" 185 | end 186 | end 187 | end 188 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule ElmScaffold.Mixfile do 2 | use Mix.Project 3 | 4 | @github_url "https://github.com/andrewMacmurray/phoenix-elm-scaffold" 5 | 6 | def project do 7 | [app: :phoenix_elm_scaffold, 8 | name: "Phoenix Elm Scaffold", 9 | version: "0.2.6", 10 | elixir: "~> 1.4", 11 | build_embedded: Mix.env == :prod, 12 | start_permanent: Mix.env == :prod, 13 | deps: deps(), 14 | description: description(), 15 | package: package(), 16 | docs: docs(), 17 | source_url: @github_url, 18 | homepage_url: @github_url] 19 | end 20 | 21 | def application do 22 | [extra_applications: [:logger]] 23 | end 24 | 25 | defp deps do 26 | [{:phoenix, "~> 1.3.0"}, 27 | {:ex_doc, "~> 0.14", only: :dev}] 28 | end 29 | 30 | defp description do 31 | """ 32 | A mix task to generate scaffolding for an elm app inside a Phoenix (1.3) app 33 | """ 34 | end 35 | 36 | defp package do 37 | [ 38 | maintainers: ["Andrew MacMurray"], 39 | licenses: ["BSD3"], 40 | links: %{"GitHub" => @github_url}, 41 | source_url: @github_url 42 | ] 43 | end 44 | 45 | defp docs do 46 | [ 47 | logo: "priv/static/elm-logo.png", 48 | extras: ["README.md"] 49 | ] 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{"earmark": {:hex, :earmark, "1.2.2", "f718159d6b65068e8daeef709ccddae5f7fdc770707d82e7d126f584cd925b74", [:mix], []}, 2 | "ex_doc": {:hex, :ex_doc, "0.15.1", "d5f9d588fd802152516fccfdb96d6073753f77314fcfee892b15b6724ca0d596", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, optional: false]}]}, 3 | "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []}, 4 | "phoenix": {:hex, :phoenix, "1.3.0", "1c01124caa1b4a7af46f2050ff11b267baa3edb441b45dbf243e979cd4c5891b", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, optional: false]}]}, 5 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [:mix], []}, 6 | "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]}, 7 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []}} 8 | -------------------------------------------------------------------------------- /priv/static/elm-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewMacmurray/phoenix-elm-scaffold/005826bf157826bd2d15d9b00d9a2e74fbe8238f/priv/static/elm-logo.png -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/assets/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "elm" 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.1.1 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0" 13 | }, 14 | "elm-version": "0.18.0 <= v < 0.19.0" 15 | } 16 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/assets/elm/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import Html exposing (program) 4 | import Types exposing (Model, Msg) 5 | import State exposing (init, subscriptions, update) 6 | import View exposing (view) 7 | 8 | 9 | main : Program Never Model Msg 10 | main = 11 | program 12 | { init = init 13 | , update = update 14 | , view = view 15 | , subscriptions = subscriptions 16 | } 17 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/assets/elm/State.elm: -------------------------------------------------------------------------------- 1 | module State exposing (..) 2 | 3 | import Types exposing (..) 4 | 5 | 6 | init : ( Model, Cmd Msg ) 7 | init = 8 | {} ! [] 9 | 10 | 11 | update : Msg -> Model -> ( Model, Cmd Msg ) 12 | update msg model = 13 | case msg of 14 | NoOp -> 15 | model ! [] 16 | 17 | 18 | subscriptions : Model -> Sub Msg 19 | subscriptions model = 20 | Sub.none 21 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/assets/elm/Types.elm: -------------------------------------------------------------------------------- 1 | module Types exposing (..) 2 | 3 | 4 | type alias Model = 5 | {} 6 | 7 | 8 | type Msg 9 | = NoOp 10 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/assets/elm/View.elm: -------------------------------------------------------------------------------- 1 | module View exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.Attributes exposing (..) 5 | import Types exposing (..) 6 | 7 | 8 | view : Model -> Html Msg 9 | view model = 10 | div [ style styles ] [ text "your app goes here" ] 11 | 12 | 13 | styles : List ( String, String ) 14 | styles = 15 | [ ( "text-align", "center" ) 16 | , ( "color", "#0192C8" ) 17 | , ( "margin-top", "2em" ) 18 | , ( "font-family", "helvetica" ) 19 | ] 20 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/assets/js/elm-embed.js: -------------------------------------------------------------------------------- 1 | function init (Elm) { 2 | var node = document.getElementById('elm-app') 3 | var app = Elm.Main.embed(node) 4 | } 5 | 6 | module.exports = { init } 7 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/controllers/elm_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule <%= app_name %>Web.ElmController do 2 | use <%= app_name %>Web, :controller 3 | 4 | def index(conn, _params) do 5 | render conn, "index.html" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/templates/elm/index.html.eex: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/test/elm/Main.elm: -------------------------------------------------------------------------------- 1 | port module Main exposing (..) 2 | 3 | import Json.Encode exposing (Value) 4 | import Sample 5 | import Test exposing (..) 6 | import Test.Runner.Node exposing (TestProgram, run) 7 | 8 | 9 | allTests : Test 10 | allTests = 11 | describe "all tests" 12 | [ Sample.all 13 | ] 14 | 15 | 16 | main : TestProgram 17 | main = 18 | run emit allTests 19 | 20 | 21 | port emit : ( String, Value ) -> Cmd msg 22 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/test/elm/Sample.elm: -------------------------------------------------------------------------------- 1 | module Sample exposing (..) 2 | 3 | import Test exposing (..) 4 | import Expect 5 | 6 | 7 | all : Test 8 | all = 9 | describe "sample test" 10 | [ test "assert the truth" <| 11 | \() -> 12 | (1 + 1) |> Expect.equal 2 13 | ] 14 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/test/elm/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "Elm Tests", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD-3-Clause", 6 | "source-directories": [ 7 | ".", 8 | "../assets/elm" 9 | ], 10 | "exposed-modules": [], 11 | "dependencies": { 12 | "elm-community/json-extra": "2.0.0 <= v < 3.0.0", 13 | "elm-lang/html": "2.0.0 <= v < 3.0.0", 14 | "mgold/elm-random-pcg": "4.0.2 <= v < 5.0.0", 15 | "elm-lang/core": "5.0.0 <= v < 6.0.0", 16 | "elm-community/elm-test": "3.0.0 <= v < 4.0.0", 17 | "rtfeldman/node-test-runner": "3.0.0 <= v < 4.0.0" 18 | }, 19 | "elm-version": "0.18.0 <= v < 0.19.0" 20 | } 21 | -------------------------------------------------------------------------------- /priv/templates/phx.gen.elm/views/elm_view.ex: -------------------------------------------------------------------------------- 1 | defmodule <%= app_name %>Web.ElmView do 2 | use <%= app_name %>Web, :view 3 | end 4 | --------------------------------------------------------------------------------