Packages
surface
0.9.3
0.12.3
0.12.2
0.12.1
0.12.0
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.1
0.1.0
0.1.0-alpha.2
0.1.0-alpha.1
0.1.0-alpha.0
A component based library for Phoenix LiveView
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/compile/surface.ex
defmodule Mix.Tasks.Compile.Surface do
@moduledoc """
Generate CSS and JS/TS assets for components.
## Setup
Update `mix.exs`, adding the `:surface` compiler to the list of compilers:
```elixir
def project do
[
...,
compilers: [:phoenix] ++ Mix.compilers() ++ [:surface]
]
end
```
## Configuration (optional)
The Surface compiler provides some options for custom configuration in your `config/dev.exs`.
### Options
* `hooks_output_dir` - defines the folder where the compiler generates the JS hooks files.
Default is `./assets/js/_hooks/`.
* `css_output_file` - defines the css file where the compiler generates the code.
Default is `./assets/css/_components.css`.
### Example
config :surface, :compiler,
hooks_output_dir: "assets/js/surface",
css_output_file: "assets/css/surface.css"
"""
use Mix.Task
@recursive true
alias Mix.Task.Compiler.Diagnostic
@switches [
return_errors: :boolean,
warnings_as_errors: :boolean
]
@doc false
def run(args) do
# Do nothing if it's a dependency. We only have to run it once for the main project
if "--from-mix-deps-compile" in args do
{:noop, []}
else
{compile_opts, _argv, _err} = OptionParser.parse(args, switches: @switches)
opts = Application.get_env(:surface, :compiler, [])
asset_opts = Keyword.take(opts, [:hooks_output_dir, :css_output_file])
asset_components = Surface.components()
project_components = Surface.components(only_current_project: true)
[
Mix.Tasks.Compile.Surface.ValidateComponents.validate(project_components),
Mix.Tasks.Compile.Surface.AssetGenerator.run(asset_components, asset_opts)
]
|> List.flatten()
|> handle_diagnostics(compile_opts)
end
end
@doc false
def handle_diagnostics(diagnostics, compile_opts) do
case diagnostics do
[] ->
{:noop, []}
diagnostics ->
if !compile_opts[:return_errors], do: print_diagnostics(diagnostics)
status = status(compile_opts[:warnings_as_errors], diagnostics)
{status, diagnostics}
end
end
defp print_diagnostics(diagnostics) do
for %Diagnostic{message: message, severity: severity, file: file, position: position} <- diagnostics do
print_diagnostic(message, severity, file, position)
end
end
defp print_diagnostic(message, :warning, file, line) do
# Use IO.warn(message, file: ..., line: ...) on Elixir v1.14+
rel_file = file |> Path.relative_to_cwd() |> to_charlist()
IO.warn(message, [{nil, :__FILE__, 1, [file: rel_file, line: line]}])
end
defp print_diagnostic(message, :error, file, line) do
error = IO.ANSI.format([:red, "error: "])
stacktrace =
" #{file}" <>
if(line, do: ":#{line}", else: "")
IO.puts(:stderr, [error, message, ?\n, stacktrace])
end
defp status(warnings_as_errors, diagnostics) do
cond do
Enum.any?(diagnostics, &(&1.severity == :error)) -> :error
warnings_as_errors && Enum.any?(diagnostics, &(&1.severity == :warning)) -> :error
true -> :ok
end
end
end