Every Elixir expression a HEEx template contains, found by tokenising it.
Why it exists
Because the questions worth asking about a template — does it read this assign, does it call that
helper — are questions about its EXPRESSIONS, and a pattern over the source cannot tell an
expression from the text around it. @flash in a comment, in a string, in a <pre> showing
somebody a code sample: a pattern finds all three, and a tokeniser finds none of them.
What comes back is source, one string per expression, for Code.string_to_quoted/1 to turn into
something walkable — see MishkaCmsCore.Runtime.PageBuilder.CodeReads.
iex> MishkaCmsCore.Runtime.PageBuilder.Markup.expressions(~S|<div id={@id}>{@flash}</div>|)
["@id", "@flash"]Tokens, not the tree
EEx.tokenize/1 and Phoenix.LiveView.TagEngine.Tokenizer directly, rather than the tree
MishkaCmsCore.Runtime.PageBuilder.LiveView.HtmlTokenizer builds on top of them. Nesting is a
question this module does not ask, and asking it fails on templates this one can answer for: a
fragment like <div class={paint(@tone)}> has no closing tag, so the tree is an error — while the
attribute was tokenised perfectly well on the way to finding that out.
What it does with a template it cannot read
Answers [], which every caller has to read as "nothing found" rather than "nothing there".
component_arguments/1 is the exception: its caller decides what a component can reach from the
answer, so an unreadable template there is :dynamic rather than an empty list.
Summary
Functions
Every expression a template HANDS TO a <.component …/> tag — the values a runtime component can
be given, as source.
Every Elixir expression in a template: attribute values, interpolations and EEx tags.
Functions
Every expression a template HANDS TO a <.component …/> tag — the values a runtime component can
be given, as source.
A literal attribute is not among them. component_name="card" is a string written in the
template, so it can carry nothing the template does not already say itself.
:dynamic when what a tag hands over cannot be read off the template at all: a tag that spreads a
map built while the page renders, or a template that cannot be tokenised. A caller must read that
as "anything could have been handed over", never as "nothing was" — which is the opposite of what
expressions/1 promises for the same unreadable template, and the reason the two answers have
different shapes.
iex> MishkaCmsCore.Runtime.PageBuilder.Markup.component_arguments(~S|<.component component_name="card" bag={assigns} />|)
{:ok, ["assigns"]}
iex> MishkaCmsCore.Runtime.PageBuilder.Markup.component_arguments(~S|<.component {@attrs} />|)
:dynamic
Every Elixir expression in a template: attribute values, interpolations and EEx tags.