# `MishkaCmsCore.Runtime.PageBuilder.Tree.Serializer`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/page_builder/tree/serializer.ex#L5)

Turns a `MishkaCmsCore.Runtime.PageBuilder.Tree` back into a HEEx template — the other half of the
round trip `MishkaCmsCore.Runtime.PageBuilder.Tree.parse/1` opens.

This is what the editor saves. The React editor's serializer only understood palette-dragged
elements, so saving a *loaded* page rewrote every original node as `<div class="element-...">`;
that whole failure mode is gone because there is only one node shape to emit.

## Two steps, deliberately separate

`serialize/1` is total: it walks the tree and emits source, never raising and never consulting a
parser. `format/1` then runs `Phoenix.LiveView.HTMLFormatter` over that source and returns
`{:ok, binary} | {:error, reason}`.

Keeping them apart is what lets the save path be safe without a `rescue` around the whole thing:
serialize, re-parse to prove the output is readable, and only then format. A template that fails
either check is refused with a message instead of being written over a working page.

ADR-11: formatting on save is an explicit, documented normalization. A hand-written template will
come back formatted the first time it is saved through the editor, and the round-trip tests assert
that the second save is a fixed point.

## Attribute fidelity

`name="literal"`, `name={expression}`, a bare `disabled`, and a root `{@rest}` are four different
things and all four survive. The old JSON encoding collapsed the middle two into the string
`"{expr}"` and recovered them with a `binary_part` guess, which is why `viewBox` and friends kept
getting mangled.

# `format`

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

Formats generated HEEx with the LiveView formatter.

Returns `{:error, reason}` when the formatter cannot read the source. That only happens if we
emitted something invalid, so the save path treats it as a refusal rather than writing the
template anyway.

# `serialize`

```elixir
@spec serialize(MishkaCmsCore.Runtime.PageBuilder.Tree.t()) :: String.t()
```

Emits the tree as HEEx source, unformatted. Total — an unreachable node or an unknown kind is
skipped rather than raising.

# `serialize_node`

```elixir
@spec serialize_node(MishkaCmsCore.Runtime.PageBuilder.Tree.t(), String.t()) ::
  String.t()
```

Emits one node and its descendants as HEEx source — what the code drawer shows for a selection.

# `to_heex`

```elixir
@spec to_heex(MishkaCmsCore.Runtime.PageBuilder.Tree.t()) ::
  {:ok, String.t()} | {:error, term()}
```

The full save-time pipeline: serialize, prove the output re-parses, then format.

Returns `{:error, reason}` if the emitted source cannot be read back, which is the guarantee that
a save never replaces a working template with one the compiler would reject.

---

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