defmodule Hologram.Compiler.Helpers do
alias Hologram.Compiler.{Binder, Transformer}
alias Hologram.Compiler.IR.{FunctionDefinitionVariants, ModuleDefinition}
alias Hologram.Typespecs, as: T
def aggregate_bindings(params) do
Enum.with_index(params)
|> Enum.reduce([], &aggregate_bindings_from_param/2)
|> Enum.sort()
end
defp aggregate_bindings_from_param({param, idx}, acc) do
Binder.bind(param)
|> Enum.reduce(acc, &maybe_add_binding(&1, &2, idx))
end
def aggregate_function_def_variants(function_defs) do
Enum.reduce(function_defs, %{}, fn fd, acc ->
if Map.has_key?(acc, fd.name) do
Map.put(acc, fd.name, acc[fd.name] ++ [fd])
else
Map.put(acc, fd.name, [fd])
end
end)
|> Enum.map(fn {name, variants} ->
{name, %FunctionDefinitionVariants{name: name, variants: variants}}
end)
|> Enum.into(%{})
end
@doc """
Returns the corresponding class name which can be used in JavaScript.
## Examples
iex> Helpers.class_name(Abc.Bcd)
"Elixir_Abc_Bcd"
"""
@spec class_name(module()) :: String.t()
def class_name(module) do
[:"Elixir" | Module.split(module)]
|> Enum.join("_")
end
def fetch_block_body(ast) do
case ast do
{:__block__, _, body} ->
body
_ ->
[ast]
end
end
@spec get_components(T.module_definitions_map()) :: list(%ModuleDefinition{})
def get_components(module_defs_map) do
module_defs_map
|> Enum.filter(fn {_, module_def} -> module_def.component? end)
|> Enum.map(fn {_, module_def} -> module_def end)
end
@spec get_pages(T.module_definitions_map()) :: list(%ModuleDefinition{})
def get_pages(module_defs_map) do
module_defs_map
|> Enum.filter(fn {_, module_def} -> module_def.page? end)
|> Enum.map(fn {_, module_def} -> module_def end)
end
defp maybe_add_binding(binding, acc, idx) do
name = List.last(binding).name
if Keyword.has_key?(acc, name) do
acc
else
Keyword.put(acc, name, {idx, binding})
end
end
@doc """
Returns the corresponding Elixir module.
## Examples
iex> Helpers.module([:Abc, :Bcd])
Elixir.Abc.Bcd
"""
@spec module(T.module_name_segments()) :: module()
def module(module_segs) do
[:"Elixir" | module_segs]
|> Enum.join(".")
|> String.to_existing_atom()
end
@doc """
Returns the corresponding module name (without the "Elixir" segment at the beginning).
## Examples
iex> Helpers.module_name(Abc.Bcd)
"Abc.Bcd"
"""
@spec module_name(module()) :: String.t()
def module_name(module) do
Module.split(module)
|> Enum.join(".")
end
@doc """
Returns the corresponding module segments (without the "Elixir" segment at the beginning).
## Examples
iex> Helpers.module_name_segments(Abc.Bcd)
[:Abc, :Bcd]
"""
@spec module_name_segments(module() | String.T) :: T.module_name_segments()
def module_name_segments(module_name) when is_binary(module_name) do
Module.split("Elixir.#{module_name}")
|> Enum.map(&String.to_atom/1)
end
def module_name_segments(module) do
Module.split(module)
|> Enum.map(&String.to_atom/1)
end
def transform_params(params, context) do
if(params, do: params, else: [])
|> Enum.map(&Transformer.transform(&1, context))
end
@doc """
Returns true if the first module has a "use" directive for the second module.
## Examples
iex> user_module = %ModuleDefinition{module: Hologram.Compiler.Parser, ...}
iex> Helpers.uses_module?(user_module, Hologram.Commons.Parser)
true
"""
@spec uses_module?(%ModuleDefinition{}, module()) :: boolean()
def uses_module?(user_module_def, used_module) do
Enum.any?(user_module_def.uses, &(&1.module == used_module))
end
end