lib/moon/convert/module.ex

defmodule Moon.Convert.Module do
  @moduledoc "Functions for converting AST surface -> live_view"

  require Logger

  def translate_render(%{component_type: Surface.LiveComponent}, ast), do: ast

  def translate_render(
        %{component_type: Surface.Component, short: short},
        {:def, m1, [{:render, m2, attrs} | children]}
      ) do
    {:def, m1, [{:"#{Macro.underscore(short)}", m2, attrs} | children]}
  end

  def translate_prop(_, {:prop, meta, [{name, _, nil}, :event]}, doc),
    do: {:attr, meta, [name, Event, translate_prop_options([], doc)]}

  # translate {:prop, [line: 21], [{:step, [line: 21], nil}, :integer, [default: 1]]}
  # to {:attr, [line: 38], [:id, :string, [required: true]]}
  def translate_prop(_, {macro_name, meta, [{name, _, nil}, type]}, doc)
      when macro_name in [:prop, :data],
      do: {:attr, meta, [name, translate_prop_type(type), translate_prop_options([], doc)]}

  def translate_prop(_, {macro_name, meta, [{name, _, nil}, type, options]}, doc)
      when macro_name in [:prop, :data],
      do:
        {:attr, meta,
         [
           name,
           translate_prop_type(type),
           translate_prop_options(options, doc)
         ]}

  def translate_prop_type(type)
      when type in [:boolean, :integer, :float, :string, :atom, :list, :map, :global],
      do: type

  def translate_prop_type(_), do: :any

  def translate_prop_options(options, doc) do
    (options
     |> Enum.map(fn
       {:values!, v} ->
         {:values, v}

       {:from_context, _v} ->
         Logger.warning("Attribute is taken from_context")
         dbg(options)
         nil

       {k, v} when k in [:required, :default, :values, :examples] ->
         {k, v}
     end)
     |> Enum.filter(&(!!&1))) ++
      ((doc && [doc: doc]) || []) ++
      (((Keyword.has_key?(options, :default) || Keyword.has_key?(options, :required)) && []) ||
         [default: nil])
  end

  def translate_slot(_, {:slot, meta, [{:default, _, nil}]}, doc),
    do: {:slot, meta, [:inner_block, (doc && [doc: doc]) || []]}

  def translate_slot(_, {:slot, meta, [{name, _, nil}]}, doc),
    do: {:slot, meta, [name, (doc && [doc: doc]) || []]}

  def translate_slot(_, {:slot, meta, [{:default, _, nil}, options]}, doc),
    do: {:slot, meta, [:inner_block, options ++ ((doc && [doc: doc]) || [])]}

  def translate_slot(_, {:slot, meta, [{name, _, nil}, options]}, doc),
    do: {:slot, meta, [name, options ++ ((doc && [doc: doc]) || [])]}

  def translate_doc(_, _res = {:@, _, [{:doc, _, [text]}]}) do
    text
  end

  def translate_moduledoc(%{component_type: Surface.LiveComponent}, res), do: res

  def translate_moduledoc(
        %{component_type: Surface.Component},
        {:@, m1, [{:moduledoc, m2, [text]}]}
      ),
      do: (text && {:@, m1, [{:doc, m2, [text]}]}) || :skip

  def translate_use_defmodule(
        %{component_type: Surface.Component},
        {:use, _, _}
      ) do
    :skip
  end

  def translate_use_defmodule(
        %{config: config},
        {type, m1, [{:__aliases__, m2, alias} | other]}
      )
      when type in [:use, :defmodule] do
    {type, m1, [{:__aliases__, m2, config[:"#{type}_translates"].(alias)} | other]}
  end
end