# `Noizu.Tessera.Hologram.Adapter`
[🔗](https://github.com/the-robot-lives/tessera/blob/v0.2.0/lib/noizu/tessera/hologram/adapter.ex#L1)

Generates the Hologram component (the "adapter", CONTRACT.md §3/§5) inside a
module that did `use Noizu.Tessera.Component`.

The generated component, in the same module as the spec:

  * declares a Hologram `prop` for every `attr` and every `on_<event>`
    binding prop — put directly by the DSL macros during module-body
    expansion, so they are part of Hologram's `@__props__` accumulator
    before Hologram's `__before_compile__` runs (hooks run in registration
    order). Json/regular `prop` entries are declared by the author's own
    `prop` DSL line, which delegates to Hologram's `prop` macro, with
    tessera opts `json:`/`default:` riding along;
  * renders the component tag with kebab-case attributes, booleans by
    presence (`nil`/`false` values make Hologram drop the attribute),
    `tsr-props` JSON (camelCase keys, omitted when no json prop is set),
    the `data-tsr-on` bridge map built from the binding props
    (`:action` ⇒ `@page`; `{:action, target: "layout" | cid}`), `data-cy`,
    and passes slot content through with `<slot />`;
  * the `js_import` of the component `.mjs` by ABSOLUTE path (built from the
    component module's `__DIR__`) binding `register` is emitted as raw
    `@__js_imports__`/`@external_resource` attributes at `__using__` time
    (same expansion as Hologram's own `js_import/2` macro produces), and a
    generated `init/3` calls it via `Hologram.JS.call/2` so the bundler
    keeps it (CONTRACT.md §5).

# `available?`

```elixir
@spec available?() :: boolean()
```

True when Hologram is compiled into the path (adapter generation is gated on this).

# `body_ast`

```elixir
@spec body_ast(Noizu.Tessera.Spec.t(), String.t()) :: Macro.t()
```

The quoted AST of the generated component body: the `init/2` + `init/3`
pair (+ the `__tessera_init__/2` and `__tessera_init__/3` hooks, all
defoverridable), the template, the bridge helpers and the
`__tessera_spec__/0` + `__tessera_manifest__/0` readers. Spliced from
`__before_compile__` (the spec must be known). `caller_dir` is the
directory of the component module's file; the `.mjs` import path is built
from it and must be absolute (CONTRACT.md §5).

# `event_pairs`

```elixir
@spec event_pairs(Noizu.Tessera.Spec.t()) :: [
  {Noizu.Tessera.Naming.snake(), Noizu.Tessera.Naming.kebab()}
]
```

`[{on_persona_select, "persona-select"}, ...]` — binding-prop atom to event
name pairs, in spec order. Drives the generated `data-tsr-on` helper.

# `has_top_level_register?`

```elixir
@spec has_top_level_register?(String.t()) :: boolean()
```

True when `source` contains a top-level `register()` call statement.

The match is anchored per line on purpose: it must only fire on a call
STATEMENT standing alone on its own line, never on an inline call
(`foo(); register()`) or inside another expression — only a statement the
bundle actually evaluates as a top-level statement provides the
load-bearing registration. Leading whitespace (indentation) is tolerated
so formatted sources (`mix format`, nested modules) still match.

# `hologram_prop_entries`

```elixir
@spec hologram_prop_entries(Noizu.Tessera.Spec.t()) :: [{atom(), atom(), keyword()}]
```

Hologram `prop` declaration entries the DSL must put into `@__props__`
(Hologram's own `{name, type, opts}` shape): one per attr and one
`on_<event>` binding prop per event. Put during module-body expansion —
NOT from `__before_compile__` — so Hologram's `__before_compile__` (hooks
run in registration order) sees the complete list.

# `js_import_asts`

```elixir
@spec js_import_asts(String.t() | nil) :: [Macro.t()]
```

The raw AST equivalent of Hologram's `js_import(:register, from: path)` —
the `@__js_imports__` + `@external_resource` attribute puts. Emitted at
`__using__` time so the entries are in the accumulator before any
`__before_compile__` runs.

# `json_prop_pairs`

```elixir
@spec json_prop_pairs(%{props: [map()]}) :: [
  {Noizu.Tessera.Naming.snake(), Noizu.Tessera.Naming.camel()}
]
```

`[{tag_list, "tagList"}, ...]` — json prop atom to camelCase key pairs, in
spec order. Drives the generated `tsr-props` helper.

# `mjs_path`

```elixir
@spec mjs_path(String.t(), String.t()) :: String.t()
```

The absolute `.mjs` path for a spec module file + colocated js filename.

# `template_markup`

```elixir
@spec template_markup(Noizu.Tessera.Spec.t()) :: String.t()
```

The `~HOLO` markup rendered by the generated component (CONTRACT.md §3).

# `warn_missing_top_level_register`

```elixir
@spec warn_missing_top_level_register(String.t()) :: :ok
```

Compile-time sanity check: the component's `.mjs` must carry a TOP-LEVEL
`register()` call. It is load-bearing — stateless SSR never calls
`init/3`, so the element gets defined only when the page bundle evaluates
the `.mjs` and its last statement registers it. Missing it means the
component renders server-side but never upgrades on the client. Warns
(never raises) so an unusual packaging layout cannot break the host build.

# `write_contract_next_to_mjs`

```elixir
@spec write_contract_next_to_mjs(Noizu.Tessera.Spec.t()) :: :ok
```

Writes the generated contract module next to the component's `.mjs`
(colocated with the spec module) when `spec.js` is set and the content
changed. Warns (never raises) when the write fails, so a read-only source
tree cannot break the host build.

---

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