Packages
surface
0.11.0
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/surface/macro_component.ex
defmodule Surface.MacroComponent do
@moduledoc """
A low-level component which is responsible for translating its own
content at compile time.
"""
alias Surface.IOHelper
alias Surface.AST
@doc """
This function is called to expand a macro component into a
set of Surface.AST nodes.
"""
@callback expand(
attributes :: [AST.Attribute.t()],
content :: binary(),
meta :: AST.Meta.t()
) :: AST.t() | [AST.t()]
defmacro __using__(_) do
quote do
use Surface.BaseComponent, type: unquote(__MODULE__)
use Surface.API, include: [:prop, :slot]
@behaviour unquote(__MODULE__)
end
end
@doc """
Evaluates the values of the static properties of a macro component.
Usually called inside `translate/2` in order to retrieve the
properties' values at compile-time.
"""
def eval_static_props!(component, attributes, caller) do
for %AST.Attribute{name: name} = attr <- attributes,
prop = component.__get_prop__(name),
prop.opts[:static],
into: %{} do
eval_value(attr, prop, caller)
end
end
defp eval_value(%AST.Attribute{name: name, value: %AST.Literal{value: value}}, _prop, _caller)
when is_list(value) do
{name, to_string(value)}
end
defp eval_value(%AST.Attribute{name: name, value: %AST.Literal{value: value}}, _prop, _caller) do
{name, value}
end
defp eval_value(%AST.Attribute{value: value_ast}, prop, caller) do
%AST.AttributeExpr{original: value, value: expr, meta: %{line: line, file: file}} = value_ast
env = %Macro.Env{caller | line: line}
{evaluated_value, _} =
try do
Code.eval_quoted(expr, [], env)
rescue
exception ->
%exception_mod{} = exception
message = """
could not evaluate expression {#{value}}. Reason:
(#{inspect(exception_mod)}) #{Exception.message(exception)}
"""
error = %CompileError{line: line, file: file, description: message}
reraise(error, __STACKTRACE__)
end
if valid_value?(prop.type, evaluated_value) do
{prop.name, evaluated_value}
else
message = invalid_value_error(prop.name, prop.type, evaluated_value, value)
IOHelper.compile_error(message, file, line)
end
end
defp valid_value?(:string, value) when not is_binary(value) do
false
end
defp valid_value?(:boolean, value) when not is_boolean(value) do
false
end
defp valid_value?(:keyword, value) do
Keyword.keyword?(value)
end
defp valid_value?(_, _value) do
true
end
defp invalid_value_error(prop_name, prop_type, value, expr) do
"""
invalid value for property "#{prop_name}"
Expected a #{prop_type} while evaluating {#{String.trim(expr)}}, got: #{inspect(value)}
Hint: static properties of macro components can only accept static values like module attributes,
literals or compile-time expressions. Runtime variables and expressions, including component
assigns, cannot be evaluated as they are not available during compilation.
"""
end
end