# `MishkaCmsCore.Runtime.Compilers.ComponentCompiler`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/compilers/component_compiler.ex#L5)

Runtime compiler for Phoenix Components with dynamic content compilation.

This module provides functionality to dynamically compile Phoenix Components at runtime
from configuration data. It generates complete Phoenix Component modules with proper
attribute definitions, slot definitions, helper functions, and HEEx template compilation.

## Features

- Dynamic component module generation from configuration
- Full Phoenix Component attribute and slot support
- HEEx template compilation with proper context
- Default value handling for all attribute types including @rest
- Helper function integration
- Component metadata and access control information
- Support for struct-based attributes
- Proper Phoenix LiveView integration
- Total `_, …` catch-all appended to narrowed variant dispatchers (`color_variant`, `size_class`, …)
  so a component renders a bare class instead of crashing when a page calls a variant/color the
  install narrowed away

## Component Structure

Generated components include:
- `__component_information__/0` - Returns component metadata
- `__component_information__/1` - Returns specific metadata by key
- Main component function (named after the component title)
- `update_many/1` and `render/1`, for a stateful component only
- Helper functions (if defined)
- Proper attr/slot declarations for Phoenix Component compliance

## Usage

    params = %{
      title: "alert_box",
      component_module: MyApp.Components.AlertBox,
      template: ~S'''
      <div class={"alert alert-#{@type} #{@class}"} role="alert">
        <h4 :if={@title}><%= @title %></h4>
        <%= render_slot(@inner_block) %>
        <button :if={@closable} class="ml-auto" phx-click={@on_close}>×</button>
      </div>
      ''',
      attrs: [
        %{name: "type", type: "string", opts: [default: "info", values: ["info", "warning", "error", "success"]]},
        %{name: "title", type: "string", opts: [default: nil]},
        %{name: "closable", type: "boolean", opts: [default: false]},
        %{name: "on_close", type: "string", opts: [default: nil]},
        %{name: "class", type: "string", opts: [default: ""]}
      ],
      slots: [
        %{name: "inner_block", opts: [required: true, doc: "Main content of the alert"]}
      ],
      helpers: [
        %{
          name: "format_message",
          args: "message, type",
          code: ~S"""
          case type do
            "error" -> "⚠️ " <> message
            "success" -> "✅ " <> message
            _ -> message
          end
          """
        }
      ]
    }

    ast = ComponentCompiler.build_module(params)
    MishkaCmsCore.Runtime.Compilers.ModuleCompiler.compile_module(ast)

# `build_attributes`

Build component attributes.

Generates Phoenix Component attribute declarations from the component configuration.
Creates proper `attr/3` calls with type validation and options handling.

## Parameters

- `{params, functions_ast}` - Tuple containing params and accumulated AST functions

## Returns

Returns updated tuple with attribute setup and individual attribute declarations added.
Each attribute is properly validated and converted to Phoenix Component format with:
- Proper type conversion (including struct types)
- Option filtering to valid Phoenix Component options
- Default value handling

## Supported Options

Valid attribute options that are preserved:
- `:required` - Whether the attribute is required
- `:default` - Default value for the attribute
- `:examples` - Example values
- `:values` - Allowed values list
- `:doc` - Attribute documentation
- `:include` - Global attributes to include
- `:exclude` - Global attributes to exclude

# `build_component_function`

Build the main component function with HEEx template compilation and default values.

## Parameters

- `{params, functions_ast}` - Tuple containing params and accumulated AST functions

## Returns

Returns updated tuple with the main component function added.

## Generated Functions

- `component_name(assigns)` - Main component function named after the title

It:
1. Merges default values into assigns
2. Executes any body code (if present)
3. Compiles and renders the HEEx template
4. Returns the rendered output

## Default Value Handling

Default values are extracted from attribute definitions and merged into assigns
before rendering, ensuring all attributes have proper defaults including @rest
for global attributes.

# `build_component_information`

Build component information including metadata.

Creates AST for the `__component_information__` functions that provide access to component
metadata, private information, ACL permissions, and extra data. These functions are used
by the runtime system to introspect component properties.

## Parameters

- `params` - Atomized component parameters map

## Returns

Returns a tuple `{params, functions_ast}` where `functions_ast` contains the AST for:
- `__component_information__/0` - Returns complete metadata map
- `__component_information__/1` - Returns metadata by atom key or key path
- `__component_information__/1` - Returns metadata by string dot-notation path

## Metadata Structure

The metadata includes:
- `:component` - Public component info (name, timestamp, attrs, slots)
- `:private` - Private runtime info (id, priority, module, site details)
- `:acl` - Access control permissions
- `:extra` - Additional custom metadata

# `build_event_fallback`

Closes a stateful component's `handle_event/3` with a clause that answers anything left over.

## Why it is a stage of its own, run last

A component's `handle_event/3` runs in whatever LiveView renders it, so an event with no matching
clause is an unmatched function call inside a visitor's whole page rather than inside one widget.
This makes that a no-op.

It has to be emitted AFTER the kit's own clauses, and that is the entire reason this is a separate
stage: Elixir takes the first clause that matches, so a catch-all emitted with the rest of the
module shape — which is built before `build_helpers/1` runs — swallows every handler the kit
declared. Measured as a counter that rendered, took a click, and did not move.

Stateless components get nothing: they have no event channel to answer on.

# `build_helpers`

Build helper functions for the component.

Processes helper function definitions and creates corresponding function AST.
Helper functions extend component functionality with custom Elixir code.

## Parameters

- `{params, functions_ast}` - Tuple containing params and accumulated AST functions

## Returns

Returns updated tuple with helper functions added to the AST.

## Helper Function Structure

Each helper function definition should contain:
- `:name` - Function name as string
- `:args` - Function arguments as Elixir code string
- `:code` - Function body as Elixir code string

## Example Helper Definition

    %{
      name: "format_date",
      args: "date, format \\ :short",
      code: "Calendar.strftime(date, format)"
    }

This generates a helper function that can be called within the component template.

# `build_module`

Main function to build component modules from component data.

Takes component configuration parameters and returns a complete AST for a Phoenix Component module.
The function orchestrates the entire compilation process through a pipeline of transformations.

## Parameters

- `params` - Map containing component configuration with keys:
  - `:title` - Component name (required)
  - `:component_module` - Target module name (required)
  - `:template` - HEEx template string (optional)
  - `:body` - Elixir code to execute before rendering (optional)
  - `:attrs` - List of attribute definitions (optional)
  - `:slots` - List of slot definitions (optional)
  - `:helpers` - List of helper function definitions (optional)
  - `:site`, `:site_id`, `:host` - Site context information
  - `:permissions` - Access control permissions
  - `:extra` - Additional metadata

## Returns

Returns the complete AST for the component module that can be compiled using
`MishkaCmsCore.Runtime.Compilers.ModuleCompiler.compile_module/1`.

## Example

    params = %{
      title: "card",
      component_module: MyApp.Components.Card,
      template: ~S(<div class="card {@class}"><%= @content %></div>),
      attrs: [
        %{name: "class", type: "string", opts: [default: ""]},
        %{name: "content", type: "string", opts: [required: true]}
      ]
    }

    ast = build_module(params)

# `build_module_attributes`

Build module-level attributes (e.g. `@indicator_positions [...]`).

Reads `params[:module_attributes]` — a list of `%{name, value, when}` maps.
Emits `Module.put_attribute(__MODULE__, :name, value)` calls inside the
module body so the resulting attribute is referenceable by subsequent
`attr/3` `values:` lists.

When-gating is performed by the Importer before this point — the compiler
always emits whatever is in the list.

# `build_slots`

Build component slots.

Generates Phoenix Component slot declarations from the component configuration.
Creates proper `slot/3` calls with nested attribute support for slot parameters.

## Parameters

- `{params, functions_ast}` - Tuple containing params and accumulated AST functions

## Returns

Returns updated tuple with slot setup and individual slot declarations added.
Each slot includes:
- Proper slot declaration with options
- Nested attribute definitions for slot parameters
- Option filtering to valid Phoenix Component slot options

## Supported Slot Options

Valid slot options that are preserved:
- `:required` - Whether the slot is required
- `:validate_attrs` - Whether to validate slot attributes
- `:doc` - Slot documentation

Slot attributes support the same options as regular component attributes.

# `check_root`

```elixir
@spec check_root(module()) :: :ok | {:error, String.t()}
```

Renders a freshly compiled stateful component once, and says whether its root will survive a
visitor.

## The failure this turns into a compile failure

`Phoenix.LiveView.Diff.component_to_rendered/3` raises

    Stateful components must have a single static HTML tag at the root

and it raises inside a VISITOR's LiveView, where nothing contains it — the canvas has
`contain/3`, a published page has nothing. Worse, the compile succeeded, so
`MishkaCmsCore.Runtime.States.CompileFailureState` holds no entry and the next request does not
short-circuit to a placeholder either: the page simply dies, again.

`%Phoenix.LiveView.Rendered{}.root` is `false` for two sibling tags, for leading text, for `{@x}`
alone and — the one nobody expects — for `<div :if={@x}>`, because a conditional root is not a
static one. None of those is unusual markup, and none of them is visible as a problem on the
canvas: `LiveViewHelpers.static_component/1` calls `render/1` as a plain function, which never
consults `root`.

## Why it is a render and not a read of the AST

Because `root` is computed by the engine, not stated by the author. Reproducing that judgement
here would be a copy of library internals pinned to a version — the same argument that kept a
hand-written iodata walker out of the canvas.

## What it supplies

Each declared attr's default, a sentinel for one with none, `[]` for every slot, and an `id`,
because a component asked for an attribute it declares required would raise for a reason that has
nothing to do with its root. Wrapped in a rescue: this runs at compile time, where a rescue is
permitted, and a component that raises for its own reasons is reported as failing to render rather
than as failing this check.

## What it cannot see

The shape of the root, and nothing else. HEEx compiles every `{…}` into a closure the diff calls
later, so a fault inside one is not raised by `render/1` at all — which is the same reason the
builder's canvas needs `contain/3` — and a raise in the eager `body` is already rescued by
`guarded_body/2`, on purpose. The rescue here is a belt for what neither of those lets through.

Answers `:ok` for a stateless component, which has no such rule.

# `create_module_ast`

Create the final module AST.

Assembles all component parts into a complete Phoenix Component module AST.
The generated module includes all necessary imports and uses for proper Phoenix Component operation.

## Parameters

- `{params, functions_ast}` - Tuple containing params and all accumulated function ASTs

## Returns

Returns the complete module AST ready for compilation via `ModuleCompiler.compile_module/1`.

## Generated Module Structure

The final module includes:
- `defmodule` declaration with the target module name
- Required Phoenix Component imports and uses
- LiveView integration via the configured `:web_module`
- Runtime component helper imports
- All accumulated function definitions (info, attrs, slots, helpers, main function)

The module is fully self-contained and ready for runtime compilation and loading.

# `make_env`

Create environment for template compilation.

Builds a compilation environment with necessary imports for HEEx template processing.
Ensures that Phoenix HTML, Component, and LiveView helpers are available during template compilation.

## Returns

Returns a `Macro.Env` struct with all required imports defined for proper template compilation.

## Imported Modules

- `Phoenix.HTML` - HTML generation helpers
- `Phoenix.Component` - Component helpers and sigil_H

Failed imports are logged but don't stop the compilation process, ensuring robustness
even when some modules aren't available.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
