Lavash.Transformers.ValidateTemplate (Lavash v0.4.0-rc.5)

Copy Markdown View Source

Compile-time validation of template-level references against the DSL.

Where Lavash.Transformers.ValidateDsl checks entity-internal references (rx body deps, action reads: lists, set targets, calc deps), this transformer checks template references — things the user writes in HEEx that resolve to DSL-declared names at runtime.

What's validated

  • phx-click="action_name" (and phx-submit, phx-change, phx-blur, phx-focus, phx-keydown, phx-keyup, phx-window-keydown, phx-window-keyup) on plain HTML tags must refer to a declared action :name on the module (or an auto-generated setter from state :name, setter: true / optimistic: true).

  • phx-value-<key>="..." attributes on the same node as a phx-event must correspond to a declared params [...] entry on the action. So <button phx-click="bump_by" phx-value-amount="5"> is valid only if :bump_by was declared as action :bump_by, [:amount] do ... end. Catches typo'd value keys whose runtime symptom is "the action body receives a nil param and crashes inside String.to_integer(nil)."

  • @name assign references anywhere an Elixir expression appears in HEEx ({@field}, :if={@open}, class={@theme}, <%= @count %>, etc.). Every reference must resolve to one of:

    • a declared state field (including from: :assigns ones)
    • a Phoenix.Component attr or slot declared on the module
    • a Phoenix-injected assign (@flash, @socket, @live_action, @uploads, @streams, @myself, @inner_block, @__changed__, @__given__)

    Catches typos like {@flsh[:info]} whose runtime symptom is silent nil — or a KeyError deep inside a render that doesn't point at the template. Bare variable references (e.g. item inside :for={item <- @items}) are ignored: they're Elixir locals, not assigns, and the compiler catches undefined ones itself.

What's not validated

  • Dynamic event values like phx-click={@some_var} or phx-click={JS.dispatch(...)} — the AST is an expression, not a static string. We can't know at compile time whether the runtime value resolves to a server event name or a JS command. When the phx-event itself is dynamic, the phx-value-* check is also skipped (we don't know which action to check against).

  • Events on component nodes like <.my_component phx-click="...">. The attribute binds to a prop, not an event handler on the host module. Component-level events are validated when the component itself compiles.

  • Modules using def render(assigns) without template do. No token pipeline runs, no extraction, no validation.

Ordering

Runs after AnalyzeTemplate (which collects template event references into :lavash_template_phx_events) and before the Compile{Component,LiveView} transformers.

Summary

Functions

after?(arg1)

Callback implementation for Spark.Dsl.Transformer.after?/1.

after_compile?()

Callback implementation for Spark.Dsl.Transformer.after_compile?/0.

before?(arg1)

Callback implementation for Spark.Dsl.Transformer.before?/1.

transform(dsl_state)

Callback implementation for Spark.Dsl.Transformer.transform/1.