# `MishkaCmsCore.Runtime.Resources.Validations.StatefulPlacement`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/resources/__validations/stateful_placement.ex#L5)

Refuses markup that places a live component somewhere a live component cannot live.

A stateful component compiles to a `Phoenix.LiveComponent`, and calling one produces a
`%Phoenix.LiveView.Component{}` — a token that only a LiveView's diff knows how to turn into
HTML. Three of the four things this runtime compiles are rendered somewhere no such diff runs, and
each failure is worse than an error message:

  * **An error page** is served by a CONTROLLER. `MishkaCmsWeb.ErrorHTML.render/2` calls
    `page_module.render(conn, status)` and flattens the result, so the token raises — inside that
    function's own rescue, which falls back to Phoenix's plain default. The site's custom error
    page is replaced for every visitor, with a warning in the log and nothing on the screen.
  * **A layout** wraps error pages as well as pages, so it inherits that.
  * **Another component's template** is rendered by the builder's canvas through
    `CanvasLive.contain/3`, which forces iodata on purpose so a component's raise costs the
    component and not the author's unsaved session. A live component reached from inside another
    component's markup builds the token anyway and blows up the PARENT's containment — a red chip
    on a component the author did not touch.

## Where it is enforced

At the action boundary, on the three resources that have somewhere to put markup, so the builder,
the Gervaz form, the JSON:API, a seed, a kit installer and a version restore all meet the same
rule. A page is the one surface that is a LiveView from end to end, so a page is not checked.

The rule has two ends and both are held. Markup that names a component already live is refused
when the markup is written; a component that becomes live while something already names it is
refused when IT is written — created live, switched to live, renamed or moved to another site
while live, or unarchived while live — with the documents that name it in the message. An archived
document is one of those documents: unarchiving it writes none of its markup, so nothing would
read that markup then.

## Every field that is compiled with the live `component/1` in reach

  * a layout — `template` and `helpers[].code`
  * an error page — each `error_pages[].template`, each `error_pages[].layout.template`,
    `root_layout_template` and `helpers[].code`
  * a component — `template`, `body`, `helpers[].code`, and in `extra` the `prelude` and each
    `clauses[].template` and `clauses[].body`

## How a component is found in markup

By reading each field in the language it is compiled in, never by a pattern over the source.

HEEx is parsed. `MishkaCmsCore.Runtime.PageBuilder.Tree.parse/1` is the same reader the editor
uses, so a `component_name` inside a comment or a code sample is not a placement and a name built
at runtime — `component_name={@which}` — is not a literal this can resolve. The template's own
Elixir is read as well, because `{component(%{component_name: "x"})}` reaches the same function.
Markup that will not parse is left alone: `Validations.TemplateCompiles` is what refuses that, and
two messages for one mistake is worse than one.

Elixir — a helper, a body, a prelude — is quoted, and a `component` call with a literal name is a
placement, as is anything its `~H` sigils place.

`root_layout_template` is classic EEx, compiled by `Phoenix.HTML.Engine` rather than the tag
engine, so a `<.component>` tag in it is TEXT that reaches the visitor as written and calls
nothing. What reaches the live function from there is an expression — `<%= component(…) %>` —
and that is what is read.

## Only what this write changed

A record whose stored markup predates this validation stays editable. Refusing every other field on
it would lock the author out of the one screen the markup gets fixed in.

---

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