# `MishkaChelekom.CmsBundleExporter`
[🔗](https://github.com/mishka-group/mishka_chelekom/blob/v0.0.9/lib/mishka_chelekom/cms_bundle_exporter.ex#L1)

Converts a Mishka Chelekom `.exs` + `.eex` source pair into the
**final** `Runtime.Component` create-params shape consumed by the
MishkaCMS UI-kit installer (schema `mishka.ui_kit.bundle.v3`).

This module owns every kit-specific decision. The MishkaCMS-side
installer is a trivial loader: `Jason.decode!` + `Ash.bulk_create`.
No re-narrowing, no rewriting, no slug-guessing happens at install
time. Whatever this module emits is what lands in the database.

## Pipeline

    1. `read_exs/1`               eval `.exs`, get name/args/scripts
    2. `render_maximal/2`         EEx-eval with all-options assigns
    3. `parse_ast/1`              `Code.string_to_quoted!`
    4. `walk/1`                   collect public defs, private defps,
                                  attr/slot decls bound to each def,
                                  module attributes, prelude (alias/
                                  import/use) with Tier-1 dedup
    5. `resolve_aliased_attrs/2`  map `attr :x, JS, ...` → struct type
                                  with full module path
    6. `drop_unrepresentable_defaults/1` strip struct/map/call AST
                                  defaults that can't survive JSON
    7. `rewrite_sibling_refs/3`   HEEx tokenizer pass on every public
                                  fn's `~H""` content; rewrite sibling
                                  tags to `<.component component_name=
                                  "<kit>-<X-slug>" .../>`
    8. `slug_names/2`             apply lowercase + hyphen rewrite to
                                  every emitted name
    9. `emit_components/3`        one component-params per public def

# `component_params`

```elixir
@type component_params() :: map()
```

# `js_hook`

```elixir
@type js_hook() :: %{required(String.t()) =&gt; term()}
```

# `convert`

```elixir
@spec convert(String.t(), String.t(), String.t(), String.t(), keyword()) ::
  {:ok, %{components: [component_params()], scripts: [map()]}}
  | {:error, term()}
```

Convert one `.exs` + `.eex` pair into an ordered list of v3
component-params (one entry per public function in the source) plus
the script entries from the `.exs`'s `scripts:` field.

## Options

  * `:base64` (boolean) — when `true`, encode `template`/`body` strings
    as `"base64:" <> Base.encode64(...)`. Default `false`.
  * `:extra_siblings` (`MapSet<String.t()>`) — public-function names
    from OTHER `.exs` files in the same kit. Templates that reference
    them (e.g. `tabs.eex` calling `<.scroll_area/>`) get rewritten to
    the runtime-helper form just like same-file siblings. Default
    empty MapSet.

# `list_public_defs`

```elixir
@spec list_public_defs(String.t(), String.t()) ::
  {:ok, [String.t()]} | {:error, term()}
```

Lightweight pre-pass: returns just the public-function names from a
`.exs+.eex` pair. Used by the Mix task to harvest the kit-wide set
before invoking `convert/5` per file (so cross-file `<.X/>` refs
rewrite correctly).

---

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