arizona_render (arizona v0.4.0)
View SourceWalks compiled templates and produces final rendered output.
Scalar values are escaped, and dynamic attributes rendered, through the
template's render backend (an arizona_renderer module carried in the backend
field) -- so the output format (HTML, the native JSON wire, terminal ANSI) is the
backend's concern, not this module's. undefined backend on a hand-constructed
map means a plain-value-only template that never invokes a backend callback;
templates from the parse transform always carry one.
Two flavors of rendering:
- Initial render (
render/1,render/2) -- evaluates dynamics througharizona_eval, builds an output iolist by zipping statics with values, and returns a snapshot the differ can later use to compute updates. - SSR render (
render_to_iolist/1,render_to_iolist/2) -- direct output for the very first server response, no snapshot needed because no client is connected yet.
The zip_* and fingerprint_payload/1 functions are called by the differ
(arizona_diff) and live process (arizona_live) to materialize wire
payloads when sending updates over WebSocket.
Snapshot shape
render/1,2 and the SSR helpers all build maps of the form:
#{s := [binary()],
d := [{az(), term()}],
deps => [#{key() => true}],
diff => false, %% propagated from az-nodiff templates
view_id => binary(),
backend => module()} %% render backend, propagated from the templateAz-nodiff safety
Templates compiled with az-nodiff carry diff => false and dynamics with
Az = undefined. The diff engine never evaluates such a template's own
dynamics once rendered (a root short-circuits; a slot keeps its first render
while it holds the same az-nodiff element), so undefined Az values never reach
op-code targets.
Summary
Functions
The layout layer currently rendering in this process, or undefined outside
one. The flag spans exactly one layer's own dynamics (see render_layout/3).
True when a fingerprint wire payload was built in this process since the last
drain, clearing the note. arizona_live asks this after a diff to skip the
dedup_fps/2 walk outright for the dominant all-scalar frame -- a frame with
no ~"f" payload has nothing to strip and no fingerprint to record. Every
producer of such a payload (zip_item/2, zip_list_fp/2, zip_stream_fp/3,
fingerprint_payload/1; the render_fp_val/2 constructors run only inside
them) sets the note, so a missed strip is impossible; the failure direction is
a STALE note (a payload built outside a drain bracket, e.g. at mount), which
merely walks one frame unnecessarily.
Builds the wire-format fingerprint payload for a snapshot.
Initial render of a template into HTML and a fresh snapshot.
Initial render that also threads view state through nested stateful children.
Initial render without the document: the snapshot and child views of
render/2, minus the HTML.
SSR render: produces HTML iolist with no snapshot, used for the first server response before any client is attached.
SSR render for a stateful handler (embedded-component style).
Like render_view_to_iolist/2, paired with the document's media type: the
outermost rendered template's backend answers it (content_type/0), so a route
serving a ?native view answers with JSON and an HTML one with HTML.
render_view/2 with the page's template passed through TmplFun between its
render/1 and the walk: the one mount sequence for both. render_view/2 passes
none (the template as it is); arizona_utf8:render_view/2 (dev mode) passes a wrap that checks
every value the walk evaluates, so the page still mounts once.
SSR render for a route-level page, returning the page alone (static
generation; the HTTP path uses render_view/2, which also names its media type).
Resolves an id-or-template to a binary id.
The document render/2 would have returned for Snapshot: the same walk over
the same values, so the bytes are identical.
Interleaves the static binary segments with their corresponding rendered dynamic values to produce the final HTML iolist.
Renders a single each-item snapshot.
Renders a list of item snapshots against a template as a fingerprinted wire
payload -- a ~"t"/~"f"/~"s"/~"d" map, ~"d" being one dynamics list
per item.
Renders a snapshot to a wire-format fingerprint payload (if f present)
or to a plain HTML binary.
Renders a stream's visible items (looked up from Items by Order keys)
against a template, producing the same fingerprinted wire payload as
zip_list_fp/2 -- and, for the same reason, only that shape.
Types
-nominal dynamic() :: each_list() | each_stream() | arizona_template:snapshot() | term().
-nominal each_list() :: #{t := 0, items := [[{arizona_template:az(), term(), map()}]], template := arizona_template:template()}.
-nominal each_stream() :: #{t := 0, items := #{term() => [{arizona_template:az(), term(), map()}]}, order := [term()], template := arizona_template:template()}.
-nominal static() :: binary().
Functions
The layout layer currently rendering in this process, or undefined outside
one. The flag spans exactly one layer's own dynamics (see render_layout/3).
-spec drain_fp_note() -> boolean().
True when a fingerprint wire payload was built in this process since the last
drain, clearing the note. arizona_live asks this after a diff to skip the
dedup_fps/2 walk outright for the dominant all-scalar frame -- a frame with
no ~"f" payload has nothing to strip and no fingerprint to record. Every
producer of such a payload (zip_item/2, zip_list_fp/2, zip_stream_fp/3,
fingerprint_payload/1; the render_fp_val/2 constructors run only inside
them) sets the note, so a missed strip is impossible; the failure direction is
a STALE note (a payload built outside a drain bracket, e.g. at mount), which
merely walks one frame unnecessarily.
Builds the wire-format fingerprint payload for a snapshot.
Snapshots with an f field become a #{~"f", ~"s", ~"d"} map;
plain snapshots fall back to a HTML binary.
-spec render(Template) -> {HTML, Snapshot} when Template :: arizona_template:template(), HTML :: iolist(), Snapshot :: arizona_template:snapshot().
Initial render of a template into HTML and a fresh snapshot.
Evaluates every dynamic, zips the result into the static skeleton, and
returns {HTML, Snapshot} so the live process can later diff against the
snapshot.
-spec render(Template, Views) -> {HTML, Snapshot, Views1} when Template :: arizona_template:template(), Views :: map(), HTML :: iolist(), Snapshot :: arizona_template:snapshot(), Views1 :: map().
Initial render that also threads view state through nested stateful children.
Returns {HTML, Snapshot, NewViews} -- NewViews accumulates the snapshots
of any child views encountered during evaluation.
-spec render_snapshot(Template, Views) -> {Snapshot, Views1} when Template :: arizona_template:template(), Views :: map(), Snapshot :: arizona_template:snapshot(), Views1 :: map().
Initial render without the document: the snapshot and child views of
render/2, minus the HTML.
A live mount ships the fingerprint payload (fingerprint_payload/1) whenever the
root carries a fingerprint, which is always for a compiled template, so the HTML
render/2 builds alongside would be thrown away. snapshot_html/1 builds it
from the snapshot for the rare root that has none.
-spec render_to_iolist(Template) -> iolist() when Template :: arizona_template:template().
SSR render: produces HTML iolist with no snapshot, used for the first server response before any client is attached.
-spec render_to_iolist(Handler, Opts) -> iolist() when Handler :: module(), Opts :: arizona_live:route_opts().
SSR render for a stateful handler (embedded-component style).
Mounts via mount/1, renders, optionally wraps in a layout. Used
by tests that SSR stateful components in isolation; production
HTTP SSR goes through render_view/2. on_mount is a
route-level concept and is intentionally not honored here.
-spec render_view(Handler, Opts) -> {ContentType, iolist()} when Handler :: module(), Opts :: arizona_live:route_opts(), ContentType :: binary().
Like render_view_to_iolist/2, paired with the document's media type: the
outermost rendered template's backend answers it (content_type/0), so a route
serving a ?native view answers with JSON and an HTML one with HTML.
-spec render_view(Handler, Opts, TmplFun) -> {ContentType, iolist()} when Handler :: module(), Opts :: arizona_live:route_opts(), TmplFun :: none | fun((arizona_template:template()) -> arizona_template:template()), ContentType :: binary().
render_view/2 with the page's template passed through TmplFun between its
render/1 and the walk: the one mount sequence for both. render_view/2 passes
none (the template as it is); arizona_utf8:render_view/2 (dev mode) passes a wrap that checks
every value the walk evaluates, so the page still mounts once.
-spec render_view_to_iolist(Handler, Opts) -> iolist() when Handler :: module(), Opts :: arizona_live:route_opts().
SSR render for a route-level page, returning the page alone (static
generation; the HTTP path uses render_view/2, which also names its media type).
Applies the on_mount chain, mounts the handler, renders, and optionally
wraps the page output in a layout module. Request data is supplied as
bindings by the caller (via arizona_middleware:extract/1 middlewares).
Use render_to_iolist/2 for embedded-component SSR (no on_mount).
-spec resolve_id(Input) -> binary() when Input :: binary() | arizona_template:template().
Resolves an id-or-template to a binary id.
If the input is already a binary, returns it unchanged. If it is a template, SSR-renders it and converts the iolist to a binary.
-spec snapshot_html(Snapshot) -> iolist() when Snapshot :: arizona_template:snapshot().
The document render/2 would have returned for Snapshot: the same walk over
the same values, so the bytes are identical.
Interleaves the static binary segments with their corresponding rendered dynamic values to produce the final HTML iolist.
Handles plain values, nested templates, and each containers (lists and
streams) recursively.
-spec zip_item(Template, Dynamics) -> map() | binary() when Template :: map(), Dynamics :: [{arizona_template:az(), term(), map()}].
Renders a single each-item snapshot.
If the template has a fingerprint, returns a wire-format map keyed by
~"f"/~"s"/~"d". Otherwise returns a plain HTML binary.
-spec zip_list_fp(Template, Items) -> map() when Template :: arizona_template:each_template(), Items :: [[{arizona_template:az(), term(), map()}]].
Renders a list of item snapshots against a template as a fingerprinted wire
payload -- a ~"t"/~"f"/~"s"/~"d" map, ~"d" being one dynamics list
per item.
A arizona_template:each_template/0 declares f and t required and the
parse transform is its only producer, so both are always present. There is
deliberately no binary fallback for a template missing them: the four
callers in arizona_diff put the result straight into [?OP_TEXT, Az, Payload]
and the client decides text-vs-markup purely by payload type (isHtml = typeof op[2] !== 'string'). A map is an object and gets parsed as HTML; a
binary is a string on the wire and would be written into a TEXT NODE, so the
whole list would show up as visibly escaped markup with nothing raising
anywhere. A malformed template fails loudly at the match instead.
Renders a snapshot to a wire-format fingerprint payload (if f present)
or to a plain HTML binary.
-spec zip_stream_fp(Template, Items, Order) -> map() when Template :: arizona_template:each_template(), Items :: #{term() => [{arizona_template:az(), term(), map()}]}, Order :: [term()].
Renders a stream's visible items (looked up from Items by Order keys)
against a template, producing the same fingerprinted wire payload as
zip_list_fp/2 -- and, for the same reason, only that shape.