lib/live_view_native/stylesheets.ex

defmodule LiveViewNative.Stylesheets do
  @moduledoc """
  Provides runtime support for LiveView Native's stylesheet system.
  """

  @doc """
  Returns a map containing all class names for templates within the given
  LiveView, LiveComponent or Phoenix Components module. `tree_key` can be
  passed for retrieving a specific class tree; this should only used for
  modules which have multiple tree shaking passes applied at compile-time
  (for example, layout modules), otherwise `:default` should be passed.
  """
  def get_class_tree(class_tree_module, tree_key \\ :default, opts \\ []) do
    expand = Keyword.get(opts, :expand, true)

    case apply(class_tree_module, :class_tree, [tree_key]) do
      result when expand ->
        expand_class_tree(result)

      result ->
        result
    end
  end

  @doc """
  Returns the internal class tree module for the given LiveView, LiveComponent
  or Phoenix Components module.
  """
  def get_class_tree_module(module) do
    Module.safe_concat([LiveViewNative, Internal, ClassTree, module])
  end

  @doc """
  Compiles all stylesheets for the given class tree and returns them
  as a single map.
  """
  def reduce_stylesheets(%{contents: %{class_names: class_names}}, stylesheet_modules) do
    stylesheet_modules
    |> Enum.reduce(%{}, fn stylesheet_module, acc ->
      compiled_stylesheet = apply(stylesheet_module, :compile_ast, [class_names])

      Map.merge(acc, compiled_stylesheet)
    end)
  end

  ###

  defp expand_class_tree(%{branches: branches} = class_tree) do
    branches
    |> Enum.reject(&(&1 in class_tree.expanded_branches))
    |> Enum.reduce(class_tree, fn branch, %{contents: %{} = acc_contents} = acc ->
      branch_class_tree =
        branch
        |> get_class_tree(:default)
        |> expand_class_tree()

      acc_class_names = acc_contents.class_names ++ branch_class_tree.contents.class_names

      acc_class_mappings =
        Map.merge(acc_contents.class_mappings, branch_class_tree.contents.class_mappings)

      %{
        acc
        | branches: acc.branches,
          contents: %{
            acc_contents
            | class_names: acc_class_names,
              class_mappings: acc_class_mappings
          },
          expanded_branches: class_tree.expanded_branches ++ [branch]
      }
    end)
  end
end