MishkaCmsCore.Runtime.PageBuilder.Tree.Serializer (Mishka CMS Core v0.0.1)

Copy Markdown View Source

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.

Summary

Functions

Formats generated HEEx with the LiveView formatter.

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

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

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

Functions

format(source)

@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(tree)

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

serialize_node(tree, id)

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

to_heex(tree)

@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.