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

Runtime compiler for Phoenix Layout modules with dynamic content compilation.

This module provides comprehensive functionality to dynamically compile Phoenix Layout modules
at runtime from configuration data. It generates complete layout modules with proper template
compilation, metadata management, helper functions, and resource handling for CMS layouts.

## Features

- **Dynamic layout module generation** from configuration parameters
- **HEEx template compilation** with Phoenix LiveView integration
- **Layout metadata and information** functions for runtime introspection
- **Helper function integration** for custom layout logic
- **Resource management** including CSS, JavaScript, and meta tags
- **SEO optimization** with customizable meta tag support
- **Multi-site layout support** with site-specific configurations
- **Access control integration** with permission management

## Layout Module Structure

Generated layout modules include:
- `__layout_information__/0` - Returns complete layout metadata
- `__layout_information__/1` - Returns specific metadata by key or path
- `render/1` - Renders layout with assigns
- `render/2` - Renders layout with assigns and inner content
- Helper functions (if defined in configuration)
- Proper Phoenix LiveView and MishkaCMS integration

## Basic Usage

The primary entry point is `build_module/1` which takes layout configuration
and returns a complete AST ready for compilation:

    params = %{
      title: "Main Layout",
      layout_module: MyApp.Layouts.Main,
      template: ~S'''
      <!DOCTYPE html>
      <html>
        <head>
          <title><%= @title %></title>
          <%= for meta <- @seo.meta_tags do %>
            <meta name={meta.name} content={meta.content}>
          <% end %>
        </head>
        <body>
          <header>Site Header</header>
          <main><%= @inner_content %></main>
          <footer>Site Footer</footer>
        </body>
      </html>
      ''',
      site: "main_site",
      site_id: 1
    }

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

## SEO and Resource Management

Layouts provide built-in SEO and resource management:

    # SEO configuration
    seo_tags: [
      %{name: "description", content: "Page description"},
      %{name: "keywords", content: "keyword1, keyword2"},
      %{property: "og:title", content: "Social Media Title"},
      %{property: "og:image", content: "https://example.com/image.png"}
    ]

    # Resource links
    resource_links: [
      %{rel: "stylesheet", href: "/assets/theme.css", type: "text/css"},
      %{rel: "preconnect", href: "https://fonts.googleapis.com"},
      %{rel: "dns-prefetch", href: "//cdn.example.com"}
    ]

## Helper Functions in Layouts

Helper functions provide custom logic within layouts:

    helpers: [
      %{
        name: "render_breadcrumbs",
        args: "breadcrumbs",
        code: ~S'''
        breadcrumbs
        |> Enum.with_index()
        |> Enum.map(fn {crumb, index} ->
          if index == length(breadcrumbs) - 1 do
            ~H"<span class='current'><%= crumb.title %></span>"
          else
            ~H"<a href={crumb.path}><%= crumb.title %></a>"
          end
        end)
        |> Enum.intersperse(~H" > ")
        '''
      }
    ]

## Error Handling

The compiler handles various error conditions gracefully:

- **Missing template** - Uses empty template with warning
- **Invalid helper code** - Excludes problematic helpers with logging
- **Missing required fields** - Uses sensible defaults where possible
- **Template compilation errors** - Provides detailed error information

## Performance Considerations

- **Template compilation** is performed once during module compilation
- **Helper functions** are compiled as regular module functions
- **Metadata access** uses compiled functions for optimal performance
- **Resource links** are pre-processed and escaped during compilation

## Security Features

- **Template sanitization** through Phoenix's HEEx engine
- **Access control** integration with permission checks
- **XSS prevention** through proper escaping
- **Safe helper execution** with proper error boundaries

See individual function documentation for detailed implementation examples.

# `build_helpers`

Builds helper functions for custom layout logic.

Processes helper function definitions from the layout configuration and compiles
them into the layout module. Helper functions provide custom logic that can be
called from within layout templates.

## Parameters

- `{params, functions_ast}` - Tuple containing:
  - `params` - Layout configuration including `:helpers` list
  - `functions_ast` - Existing function ASTs from previous pipeline stages

## Returns

Returns updated tuple `{params, functions_ast}` with helper functions added.

## Helper Function Format

Each helper function should be defined as a map with:
- `:name` - Function name as string (required)
- `:args` - Function arguments as string (required)
- `:code` - Function body as Elixir code string (required)

## Examples

    helpers: [
      %{
        name: "format_date",
        args: "date",
        code: ~S'''
        case date do
          %Date{} -> Date.to_string(date)
          %DateTime{} -> DateTime.to_date(date) |> Date.to_string()
          _ -> "Invalid date"
        end
        '''
      },
      %{
        name: "render_navigation",
        args: "items, current_path",
        code: ~S'''
        for item <- items || [] do
          active_class = if item.path == current_path, do: "active", else: ""
          ~H"""
          <a href={item.path} class={"nav-link " <> active_class}>
            <%= item.title %>
          </a>
          """
        end
        '''
      },
      %{
        name: "calculate_reading_time",
        args: "content",
        code: ~S'''
        word_count = String.split(content || "") |> length()
        reading_time = max(1, div(word_count, 200))
        "#{reading_time} min read"
        '''
      }
    ]

## Template Usage

Helper functions can be called directly from layout templates:

    template = ~S'''
    <!DOCTYPE html>
    <html>
      <head>
        <title><%= @title %></title>
      </head>
      <body>
        <nav>
          <%= render_navigation(@nav_items, @current_path) %>
        </nav>
        <main>
          <time><%= format_date(@published_at) %></time>
          <p>Reading time: <%= calculate_reading_time(@content) %></p>
          <%= @inner_content %>
        </main>
      </body>
    </html>
    '''

## Function Compilation

Helper functions are compiled as regular module functions with:
- **Full Elixir syntax support** - Use any valid Elixir code
- **Access to imports** - Phoenix.Component, HEEx, etc.
- **Pattern matching** - Multiple function heads supported
- **Guard clauses** - When conditions supported
- **Private scope** - Helper functions are public within the layout module

## Advanced Helper Examples

    helpers: [
      # Multi-clause function with pattern matching
      %{
        name: "format_currency",
        args: "amount, currency \\ "USD"",
        code: ~S'''
        case currency do
          "USD" -> "$#{:erlang.float_to_binary(amount / 100, decimals: 2)}"
          "EUR" -> "€#{:erlang.float_to_binary(amount / 100, decimals: 2)}"
          _ -> "#{amount}"
        end
        '''
      },

      # Helper with HEEx components
      %{
        name: "render_breadcrumbs",
        args: "crumbs",
        code: ~S'''
        crumbs
        |> Enum.with_index()
        |> Enum.map(fn {crumb, index} ->
          if index == length(crumbs) - 1 do
            ~H"<span class='breadcrumb-current'><%= crumb.title %></span>"
          else
            ~H"<a href={crumb.url} class='breadcrumb-link'><%= crumb.title %></a>"
          end
        end)
        |> Enum.intersperse(~H" &gt; ")
        '''
      }
    ]

## Error Handling

- **Syntax errors** in helper code will prevent module compilation
- **Invalid arguments** will cause compilation failures with detailed messages
- **Missing dependencies** in helper code will be reported during compilation
- **Runtime errors** in helpers will be caught and logged during template rendering

## Performance Considerations

- Helper functions are compiled once during module compilation
- No runtime overhead for function calls within templates
- Complex helpers should be optimized for template rendering performance
- Consider caching expensive computations within helper implementations

This function is called as the third stage in the compilation pipeline.

# `build_layout_information`

Builds layout information and metadata functions.

Creates AST for the `__layout_information__` functions that provide comprehensive
access to layout metadata, private information, ACL permissions, and extra data.
These functions are essential for runtime introspection and layout management.

## Parameters

- `params` - Atomized layout configuration map containing all layout data

## Returns

Returns a tuple `{params, functions_ast}` where:
- `params` - Original parameters (passed through for pipeline)
- `functions_ast` - List containing the layout information function ASTs

## Generated Functions

The generated module will contain these information functions:

### `__layout_information__/0`
Returns complete layout metadata as a structured map with sections:
- `:layout` - Core layout data (title, path, content, resources, SEO)
- `:private` - Private metadata (IDs, module info, site data, whether the layout reads the
  signed-in user — see `MishkaCmsCore.Runtime.Compilers.UserClosure`)
- `:acl` - Access control permissions
- `:extra` - Additional custom metadata

### `__layout_information__/1` (atom key)
Returns specific top-level section by atom key.

### `__layout_information__/1` (list of keys)
Returns nested data using a list of keys for deep access.

### `__layout_information__/1` (dot-notation string)
Returns nested data using dot-notation string paths like "private.site_id".

## Layout Information Structure

    %{
      layout: %{
        title: "Layout Title",
        path: "/layouts/example",
        timestamp: ~N[2024-01-15 10:30:00],
        content: "layout content",
        resource_links: [%{rel: "stylesheet", href: "/css/main.css"}],
        seo: %{meta_tags: [%{name: "description", content: "..."}]}
      },
      private: %{
        id: 123,
        layout_module: MyApp.Layouts.Example,
        priority: 0,
        site: "main_site",
        site_id: 1,
        host: "example.com",
        format: :heex,
        reads_user: false
      },
      acl: %{permissions: ["layout_view", "layout_edit"]},
      extra: %{theme: "modern", version: "1.0"}
    }

## Examples

    # After compilation, access layout information:
    info = MyLayout.__layout_information__()
    layout_data = MyLayout.__layout_information__(:layout)
    site_id = MyLayout.__layout_information__("private.site_id")
    permissions = MyLayout.__layout_information__([:acl, :permissions])

## Error Handling

The string-based accessor includes error handling for invalid paths,
returning `nil` when the path cannot be resolved or atoms don't exist.

This function is typically called as the first stage in the compilation pipeline.

# `build_module`

Builds a complete layout module AST from configuration parameters.

This is the primary entry point for the layout compilation process. It orchestrates
the entire compilation pipeline to transform layout configuration into a complete
Phoenix module AST ready for compilation.

## Parameters

- `params` - Map containing layout configuration with keys:
  - `:title` - Layout title/name (required)
  - `:layout_module` - Target module name (required)
  - `:template` - HEEx template string (required)
  - `:path` - Layout path/URL (optional)
  - `:helpers` - List of helper function definitions (optional)
  - `:resource_links` - List of CSS/JS resource links (optional)
  - `:seo_tags` - List of SEO meta tags (optional)
  - `:site`, `:site_id`, `:host` - Site identification (optional)
  - `:permissions` - Access control permissions (optional)
  - `:extra` - Additional metadata (optional)
  - `:priority` - Layout priority for ordering (optional)
  - `:format` - Template format (:heex, default)

## Returns

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

## Examples

    # Basic layout
    params = %{
      title: "Simple Layout",
      layout_module: MyApp.Layouts.Simple,
      template: ~S'''
      <html>
        <body><%= @inner_content %></body>
      </html>
      '''
    }

    ast = build_module(params)

    # Complex layout with helpers and resources
    params = %{
      title: "Admin Layout",
      layout_module: MyApp.Layouts.Admin,
      template: admin_template,
      helpers: [
        %{name: "admin_nav", args: "user", code: "render_admin_navigation(user)"}
      ],
      resource_links: [
        %{rel: "stylesheet", href: "/admin/admin.css"}
      ],
      permissions: ["admin_access"]
    }

    ast = build_module(params)

## Compilation Pipeline

The function processes layout data through these stages:
1. **Information building** - Creates metadata and information functions
2. **Render functions** - Compiles template and creates render functions
3. **Helper functions** - Processes and compiles helper functions
4. **Module creation** - Assembles final module AST with imports

Each stage adds specific functionality to the final compiled module.

# `build_render_functions`

Builds render functions for layout template rendering.

Compiles the HEEx template and creates render functions that handle different
rendering scenarios. The functions automatically merge layout metadata with
provided assigns and handle inner content injection.

## Parameters

- `{params, functions_ast}` - Tuple containing:
  - `params` - Layout configuration with template and metadata
  - `functions_ast` - Existing function ASTs from previous pipeline stages

## Returns

Returns updated tuple `{params, functions_ast}` with render functions added.

## Generated Functions

### `render/1` - Basic rendering with assigns
Renders the layout with provided assigns, automatically merging layout
metadata into the assigns map.

    MyLayout.render(%{
      title: "Page Title",
      custom_data: "value"
    })

### `render/2` - Rendering with inner content
Renders the layout with assigns and injects inner content. The inner content
is available in the template as `@inner_content`.

    MyLayout.render(%{
      title: "Page Title"
    }, "<div>Page content here</div>")

## Template Compilation

Templates are compiled using Phoenix's HEEx engine with:
- **XSS protection** through automatic escaping
- **LiveView integration** for dynamic updates
- **Component support** for MishkaCMS runtime components
- **Proper error handling** with detailed compilation feedback

## Layout Data Merging

Both render functions automatically merge layout metadata into assigns:

    # Layout data is merged first, then user assigns override
    final_assigns = Map.merge(layout_metadata, user_assigns)

Available layout data includes:
- `@title` - Layout title
- `@path` - Layout path
- `@resource_links` - CSS/JS resource links
- `@seo` - SEO metadata with meta_tags
- `@content` - Layout content
- Plus any custom data from the layout configuration

## Template Variables

Templates have access to:
- **All assigns** - User-provided and layout metadata
- **Inner content** - `@inner_content` (render/2 only)
- **Helper functions** - Custom layout helper functions
- **MishkaCMS components** - Runtime compiled components

## Examples

    # Template with layout features
    template = ~S'''
    <!DOCTYPE html>
    <html>
      <head>
        <title><%= @title %></title>
        <%= for link <- @resource_links do %>
          <link rel={link.rel} href={link.href}>
        <% end %>
        <%= for meta <- @seo.meta_tags do %>
          <meta name={meta.name} content={meta.content}>
        <% end %>
      </head>
      <body>
        <header><%= render_header() %></header>
        <main><%= @inner_content %></main>
        <footer><%= @copyright_text %></footer>
      </body>
    </html>
    '''

    # Usage after compilation
    html = MyLayout.render(%{
      title: "My Page",
      copyright_text: "© 2024"
    }, "<p>Welcome to my site!</p>")

## Error Handling

Template compilation errors are caught and reported with:
- **File context** - Using generated file names for debugging
- **Line numbers** - Accurate error positioning
- **Syntax highlighting** - Clear error messages

Invalid template syntax will prevent module compilation with detailed feedback.

This function is called as the second stage in the compilation pipeline.

# `create_module_ast`

Creates the final module AST with all components assembled.

This is the final stage of the compilation pipeline that assembles all generated
function ASTs into a complete module definition with proper imports and structure.

## Parameters

- `{params, functions_ast}` - Tuple containing:
  - `params` - Layout configuration with `:layout_module` target
  - `functions_ast` - All compiled function ASTs from previous stages

## Returns

Returns the complete module AST ready for compilation.

## Generated Module Structure

The final module includes:

```elixir
defmodule TargetModule do
  require Logger
  use MishkaCmsWeb, :live_view  # whatever :web_module names
  import MishkaCmsCore.Runtime.LiveViewHelpers, only: [component: 1, component: 2]

  # Layout information functions
  def __layout_information__() do ... end
  def __layout_information__(key) when is_atom(key) do ... end
  def __layout_information__(keys) when is_list(keys) do ... end
  def __layout_information__(key) when is_binary(key) do ... end

  # Render functions
  def render(assigns) when is_map(assigns) do ... end
  def render(assigns, inner_content) when is_map(assigns) do ... end

  # Helper functions (if defined)
  def helper_function_name(args) do ... end
end
```

## Module Dependencies

The generated module automatically includes:

- **Logger** - For debugging and error reporting
- **The configured `:web_module`** - Phoenix LiveView functionality
- **MishkaCmsCore.Runtime.LiveViewHelpers** - Runtime component access

These imports enable the layout to:
- Use LiveView features like `~H` sigil
- Access Phoenix.Component functionality
- Call runtime-compiled MishkaCMS components
- Log errors and debug information

## Example Generated Module

    defmodule MyApp.Layouts.Main do
      require Logger
      use MishkaCmsWeb, :live_view  # whatever :web_module names
      import MishkaCmsCore.Runtime.LiveViewHelpers, only: [component: 1, component: 2]

      def __layout_information__() do
        %{
          layout: %{title: "Main Layout", path: "/layouts/main"},
          private: %{site: "main", format: :heex},
          acl: %{permissions: []},
          extra: nil
        }
      end

      def render(assigns) when is_map(assigns) do
        # Compiled template code here
      end

      def format_title(title, site_name) do
        # Helper function code here
      end
    end

## Integration Points

The module structure ensures compatibility with:
- **Phoenix LiveView** - Standard LiveView module pattern
- **MishkaCMS Runtime** - Component and layout system integration
- **Phoenix Components** - Full component library access
- **Elixir OTP** - Standard module loading and hot code swapping

## Compilation Process

After this function returns the AST:

    ast = create_module_ast({params, functions_ast})
    {:ok, module} = MishkaCmsCore.Runtime.Compilers.ModuleCompiler.compile_module(ast)

The module is then available for use throughout the application.

This is the final stage in the compilation pipeline and produces the
complete module ready for runtime compilation and usage.

# `make_env`

Creates compilation environment for template processing.

Builds a macro environment with proper imports and context for HEEx template
compilation. This environment determines what modules and functions are
available within compiled templates.

## Returns

Returns a `Macro.Env` struct configured for layout template compilation.

## Environment Configuration

The environment includes:
- **Base Environment** - Standard Elixir compilation environment
- **Dynamic Imports** - Configurable module imports for templates
- **Error Handling** - Graceful handling of missing import modules

## Current Import Strategy

Currently, the imports list is empty (`[]`) but the infrastructure supports
dynamic import addition. Future enhancements can include:

    # Example of potential imports
    imports = [
      Phoenix.Component,
      Phoenix.HTML,
      MyApp.HelperModules.DateHelpers,
      MyApp.HelperModules.TextHelpers
    ]

## Import Processing

Each import is processed with:
1. **Module availability check** - Verifies module is loaded
2. **Safe import definition** - Uses `Macro.Env.define_import/3`
3. **Error logging** - Warns about failed imports without stopping compilation
4. **Graceful degradation** - Continues compilation even if some imports fail

## Error Handling

Import failures are handled gracefully:
- **Module not loaded** - Logs warning and continues
- **Import definition error** - Logs error details and continues
- **Compilation continues** - Failed imports don't prevent template compilation

## Usage in Template Compilation

This environment is passed to `Phoenix.LiveView.TagEngine.compile/2` to provide
proper context for template compilation:

    env = make_env()
    opts = [caller: env, ...]
    Phoenix.LiveView.TagEngine.compile(template, opts)

## Performance Considerations

- Environment creation is lightweight
- Import processing is done once during compilation
- Failed imports have minimal performance impact
- Environment is reused across template compilation calls

---

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