Current section
Files
Jump to
Current section
Files
lib/mix/tasks/css.parser.ex
defmodule Mix.Tasks.Cssex.Parser do
use Mix.Task
require Logger
@moduledoc """
Task to parse cssex files into css files.
You can use two types of flags, `--e` (and additionally `--a`) or `--c`.
For the --e flag you use any number of
`--e path/to_cssex/file.cssex=path/to_css/output.css`
And if you want those paths to be relative to some application you can pass it with `--a`
`--a myapp_web`
arguments to specify each entry and its output file, or a single path to the cssex where the output file will be in the same directory, with the same file name but the extension cssex replaced by css
`--e path/to_cssex/file.cssex`
The `--c` flag is used to indicate an entry in the config of the application, in order to read the entry points from there
`--c myapp_web`
So it would look something like:
```
mix cssex.parser --e path/to_cssex/file.cssex=path/to_css/output.css
```
"""
@doc """
Run the parser with `mix cssex.parser`.
Required arguments:
```
--e /source/path.cssex=/final/path.css
--e /source/path.cssex
--e source/path.cssex=final/path.css --a yourapp_web
--e source/path.cssex --a yourapp_web
```
Or
```
--c yourapp_web
```
Where `yourapp_web` specifies a config under the key `CSSEx`, with a key of `:entry_points` composed of tuple pairs of source & destination files.
"""
def run([]),
do:
error(
"either specify the file paths with --e cssex/file.cssex=output/file.css, and/or the --a application flag or a module from where to load a CSSEx config, with --c myapp_web",
64
)
def run(["--c", app_string]) do
app = String.to_atom(app_string)
dir = Application.app_dir(app)
env = Application.get_env(app, CSSEx)
case not is_nil(env) && CSSEx.make_config(env, dir) do
%CSSEx{entry_points: [_ | _]} = config ->
do_run(config)
_ ->
error(
"loading default entry points for app: #{app}. The retrieved config was: #{env} - where instead it was expected a keyword list with an :entry_points entry specifying at least one file",
1
)
end
end
def run(args) do
{options, _, _} = OptionParser.parse(args, strict: [e: :keep, a: :string])
{module, opts} = Keyword.pop_first(options, :a, false)
eps =
Enum.reduce(opts, [], fn {_, paths}, acc ->
case String.split(paths, "=", trim: true) do
[from, to] ->
[{from, to} | acc]
[from] ->
[{from, String.replace(from, ".cssex", ".css")} | acc]
_ ->
error("invalid paths #{paths}", 64)
end
end)
dir =
case module do
false ->
nil
_ ->
String.to_atom(module)
|> Application.app_dir()
end
case length(eps) > 0 do
true ->
CSSEx.make_config([entry_points: eps], dir)
|> do_run()
false ->
error("no paths given", 64)
end
end
defp do_run(%CSSEx{entry_points: eps}) do
cwd = File.cwd!()
tasks =
for {path, final} <- eps do
expanded_base = CSSEx.assemble_path(path, cwd)
expanded_final = CSSEx.assemble_path(final, cwd)
Task.async(fn ->
result =
CSSEx.Parser.parse_file(
nil,
Path.dirname(expanded_base),
Path.basename(expanded_base),
expanded_final
)
{result, expanded_base, expanded_final}
end)
end
processed = Task.yield_many(tasks, 60_000)
Enum.each(processed, fn
{_, {:ok, {{:ok, %{valid?: true}, _}, base, final}}} ->
ok(base, final)
{_, {:ok, {{:error, %{error: error}}, _, _}}} ->
error(error)
error ->
error(error)
end)
case Enum.all?(processed, fn
{_, {task_res, {{processed_res, _, _}, _, _}}} ->
task_res == :ok and processed_res == :ok
_ ->
false
end) do
true -> :ok
_ -> exit({:shutdown, 1})
end
end
defp error(msg, code) do
error(msg)
exit({:shutdown, code})
end
defp error(msg), do: Logger.error("ERROR :: #{inspect(msg)}")
defp ok(base, file), do: Logger.info("PROCESSED :: #{base} into #{inspect(file)}")
end