defmodule ElixirScript.CLI do
@moduledoc false
@app_version Mix.Project.config()[:version]
@switches [
output: :string, elixir: :boolean,
help: :boolean, full_build: :boolean, version: :boolean,
watch: :boolean, format: :string, js_module: [:string, :keep], remove_unused: :boolean
]
@aliases [
o: :output, e: :elixir, h: :help, v: :version, f: :format
]
def main(argv) do
argv
|> parse_args
|> process
end
def parse_args(args) do
{ options, input, errors } = OptionParser.parse(args, switches: @switches, aliases: @aliases)
cond do
length(errors) > 0 ->
:help
Keyword.get(options, :help, false) ->
:help
Keyword.get(options, :version, false) ->
:version
length(input) == 0 ->
:help
true ->
{ input, options }
end
end
def help_message() do
"""
usage: elixirscript [options]
path to elixir files or
the elixir code string if passed the -e flag
options:
--js-module [:] A js module used in your code. ex: React:react
Multiple can be defined
-f --format [format] module format of output. options: es (default), common, umd
-o --output [path] places output at the given path.
Can be a directory or filename.
-e --elixir read input as elixir code string
--remove-unused Removes unused modules from output
--full-build informs the compiler to do a full build instead of an incremental one
-v --version the current version number
-h --help this message
"""
end
def process(:help) do
IO.write help_message
end
def process(:version) do
IO.write @app_version
end
def process({ input, options }) do
if options_contains_unknown_values(options) do
process(:help)
else
do_process(input, options)
end
end
def do_process(input, options) do
{watch, options} = Keyword.pop(options, :watch, false)
js_modules = Keyword.get_values(options, :js_module)
|> build_js_modules
compile_opts = %{
include_path: true,
core_path: Keyword.get(options, :core_path, "Elixir.Bootstrap"),
full_build: Keyword.get(options, :full_build, false),
output: Keyword.get(options, :output, :stdout),
format: String.to_atom(Keyword.get(options, :format, "es")),
js_modules: js_modules,
remove_unused: Keyword.get(options, :remove_unused, false)
}
case options[:elixir] do
true ->
ElixirScript.compile(input, compile_opts)
_ ->
input = handle_input(input)
ElixirScript.compile_path(input, compile_opts)
if watch do
ElixirScript.Watcher.start_link(input, compile_opts)
:timer.sleep :infinity
end
end
end
defp options_contains_unknown_values(options) do
Enum.any?(options, fn({key, _value}) ->
if key in Keyword.keys(@switches) do
false
else
true
end
end)
end
defp handle_input(input) do
input = input
|> Enum.map(fn(x) -> String.split(x, [" ", ","], trim: true) end)
|> List.flatten
end
defp build_js_modules(values) do
values
|> Enum.map(fn x ->
[identifier, path] = String.split(x,":", trim: true)
{ format_identifier(identifier), format_path(path) }
end)
end
defp format_identifier(id) do
id
|> String.split(".")
|> Module.concat
end
defp format_path(path) do
path
|> String.replace("\"", "")
|> String.replace("`", "")
|> String.replace("'", "")
end
end