Permalink
Cannot retrieve contributors at this time
defmodule Mix.Tasks.Compile.Elm do | |
@config_defaults [ | |
src_dir: "web", | |
main_module: "Main.elm", | |
compiler_arguments: ["--output", "elm.js"], | |
destination_dir: "priv/static", | |
assets_to_stage: ["elm.js"] | |
] | |
@moduledoc """ | |
Performs compilation and staging of Elm assets. | |
Requires an elm-lang installation, see: https://guide.elm-lang.org/install.html | |
The elm compiler task accepts the following options: | |
- `src_dir`: Location of the Elm assets to be compiled. Default: `#{@config_defaults[:src_dir]}` | |
- `main_module`: The name of the main Elm module. Default: `#{@config_defaults[:main_module]}` | |
- `compiler_arguments`: Arguments passed to the Elm compiler. Default: `#{@config_defaults[:compiler_arguments]}` | |
- `destination_dir`: Location where the compiled Elm assets will be placed. Default: `#{@config_defaults[:destination_dir]}` | |
- `assets_to_stage`: List of files and dirs to move to the `destination_dir` after the elm compiler finishes. Default: `[#{Enum.join(@config_defaults[:assets_to_stage], ", ")}]` | |
## Example: | |
def project do | |
[app: :my_app, | |
version: "1.0.0", | |
elixir: "~> 1.7", | |
elm: [ | |
src_dir: "src", | |
destination_dir: "public", | |
compiler_arguments: ["--output", "main.js", "--debug"], | |
assets_to_stage: ["index.html", "main.js", "styles/", "resources/"] | |
], | |
deps: deps()] | |
end | |
""" | |
@shortdoc "Compiles Elm source files" | |
@type step_result :: :ok | {:error, [String.t()]} | |
use Mix.Task.Compiler | |
@spec run([String.t()]) :: step_result | |
def run(_args) do | |
unless Mix.env() == :prod do | |
Mix.shell().info("Compiling Elm assets") | |
with :ok <- verify_elm_install(), | |
{:ok, elm_config} <- project_elm_config(), | |
:ok <- verify_source_dir(elm_config), | |
:ok <- verify_main_elm_file(elm_config), | |
:ok <- compile_elm_assets(elm_config), | |
:ok <- ensure_staging_area(elm_config), | |
:ok <- move_compiled_assets_to_staging_area(elm_config) do | |
Mix.shell().info("Elm assets compiled and moved to #{elm_config[:destination_dir]}.") | |
:ok | |
else | |
{:error, reason} -> | |
{:error, [reason]} | |
end | |
end | |
end | |
@spec verify_elm_install :: step_result | |
defp verify_elm_install do | |
case System.find_executable("elm") do | |
nil -> | |
{:error, "\"elm\" not found in the path. An elm-lang installation is required."} | |
_ -> | |
:ok | |
end | |
end | |
@spec project_elm_config :: step_result | |
defp project_elm_config do | |
case Mix.Project.config()[:elm] do | |
nil -> | |
{:ok, @config_defaults} | |
config -> | |
{:ok, merge_config(config)} | |
end | |
end | |
@spec merge_config(Keyword.t()) :: step_result | |
def merge_config(config) do | |
Keyword.merge(config, @config_defaults, fn _key, supplied_value, default_value -> | |
unless supplied_value == nil do | |
supplied_value | |
else | |
default_value | |
end | |
end) | |
end | |
@spec verify_source_dir(Keyword.t()) :: step_result | |
defp verify_source_dir(config) do | |
if File.exists?(config[:src_dir]) do | |
:ok | |
else | |
{:error, "Expected source dir: #{config[:src_dir]} not found."} | |
end | |
end | |
@spec verify_main_elm_file(Keyword.t()) :: step_result | |
defp verify_main_elm_file(config) do | |
main_elm_module = Path.join(config[:src_dir], config[:main_module]) | |
if File.exists?(main_elm_module) do | |
:ok | |
else | |
{:error, "Expected Elm source file: #{main_elm_module} not found."} | |
end | |
end | |
@spec compile_elm_assets(Keyword.t()) :: step_result | |
defp compile_elm_assets(config) do | |
source_dir = config[:src_dir] | |
main_elm_module = config[:main_module] | |
exec = "elm" | |
args = ["make"] ++ [main_elm_module] ++ config[:compiler_arguments] | |
status = cmd(exec, args, source_dir, []) | |
if status == 0 do | |
:ok | |
else | |
{:error, "Could not compile with \"#{exec}\" (exit status: #{status})."} | |
end | |
end | |
@spec cmd(String.t(), [String.t()], Path.t(), Keyword.t()) :: step_result | |
defp cmd(exec, args, cwd, env) do | |
opts = [ | |
cd: cwd, | |
env: env | |
] | |
{_, status} = System.cmd(exec, args, opts) | |
status | |
end | |
@spec ensure_staging_area(Keyword.t()) :: step_result | |
defp ensure_staging_area(config) do | |
destination_dir = config[:destination_dir] | |
case File.mkdir_p(destination_dir) do | |
:ok -> | |
:ok | |
{:error, error_code} -> | |
{:error, "Could not create #{config[:destination_dir]}. Posix error-code: #{error_code}."} | |
end | |
end | |
@spec move_compiled_assets_to_staging_area(Keyword.t()) :: step_result | |
defp move_compiled_assets_to_staging_area(config) do | |
destination_area = config[:destination_dir] | |
source_area = config[:src_dir] | |
Enum.each(config[:assets_to_stage], fn filename -> | |
source = Path.join(source_area, filename) | |
case File.dir?(source) do | |
true -> | |
File.cp_r(source, Path.join(destination_area, filename)) | |
false -> | |
File.cp(source, Path.join(destination_area, filename)) | |
end | |
end) | |
:ok | |
end | |
end |