arizona_render (arizona v0.4.0)

View Source

Walks 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 through arizona_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 template

Az-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

dynamic()

-nominal dynamic() :: each_list() | each_stream() | arizona_template:snapshot() | term().

each_list()

-nominal each_list() ::
             #{t := 0,
               items := [[{arizona_template:az(), term(), map()}]],
               template := arizona_template:template()}.

each_stream()

-nominal each_stream() ::
             #{t := 0,
               items := #{term() => [{arizona_template:az(), term(), map()}]},
               order := [term()],
               template := arizona_template:template()}.

layout()

-nominal layout() :: {module(), atom()}.

static()

-nominal static() :: binary().

Functions

current_layout()

-spec current_layout() -> {module(), atom()} | undefined.

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

drain_fp_note()

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

fingerprint_payload(Snapshot)

-spec fingerprint_payload(Snapshot) -> map() | binary() when Snapshot :: map().

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.

format_error(Reason, Stacktrace)

-spec format_error(Reason, Stacktrace) -> ErrorInfo
                      when
                          Reason :: term(), Stacktrace :: [tuple()], ErrorInfo :: #{general := iolist()}.

render(Template)

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

render(Template, Views)

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

render_snapshot(Template, Views)

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

render_to_iolist(Template)

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

render_to_iolist(Handler, Opts)

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

render_view(Handler, Opts)

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

render_view(Handler, Opts, TmplFun)

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

render_view_to_iolist(Handler, Opts)

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

resolve_id(Input)

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

snapshot_html(Snapshot)

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

zip(Statics, Dynamics)

-spec zip(Statics, Dynamics) -> iolist() when Statics :: [static()], Dynamics :: [dynamic()].

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.

zip_item(Template, Dynamics)

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

zip_list_fp(Template, Items)

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

zip_or_fp(Snapshot)

-spec zip_or_fp(Snapshot) -> map() | binary() when Snapshot :: map().

Renders a snapshot to a wire-format fingerprint payload (if f present) or to a plain HTML binary.

zip_stream_fp(Template, Items, Order)

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