Lowers the template IR to a render program.
What a program is now
A skeleton, a list of ops, and a list of blocks. A block has the same
shape as the root -- skeleton, ops, marker counts -- and control flow refers
to blocks by index. <@if> becomes one op naming its arms; <@for> becomes
one op naming its body block, its list expression and its key.
That is the whole difference from the previous design, and it is what lets a
list compile at all. Before, a loop body had no representation: the compiler
rewrote it into a <template data-ax-for> element and the runtime parsed the
body's markup again on every pass. A block is that body, parsed once and
cloned per item, with its own ops addressing its own nodes.
Expressions are indices, not source
An op carries x: 3, not x: "cart.total". The sources are interned into an
array this function returns beside the program, the caller compiles them
positionally into the component's expression table, and the array itself
never reaches the bundle.
The previous design keyed the compiled table by the expression's source text, so every expression shipped twice -- once as a key and once as compiled code -- and the contract between compiler and runtime was string identity, which nothing could check. An index is checkable, smaller, and means a program is self-contained: the runtime never needs the template source to work out what a binding meant.
Scope inside a block
A loop body reads names the enclosing component does not declare. The op
records the binding names (as, ix); the runtime derives a scope holding
them and evaluates the block's expressions against it. The compiler does not
rewrite item.qty into anything else, because the name is what the author
wrote and what every diagnostic, Atlas edge and trace entry has to keep
calling it.
- Source:
Classes
Members
(inner, constant) ELEMENT_MARKER :string
Attribute the runtime resolves bound elements by, before it is stripped.
Type:
- string
- Source:
(inner, constant) RESERVED_TRIGGERS :Set:.<string:>
<@defer when="..."> values that name a subscription rather than a condition.
Read from the previous implementation rather than redesigned: timer(1500)
and 1500ms are both documented and both in use. Anything else compiles as
an expression, so <@defer when="rows.length"> works without new syntax.
Type:
- Source:
Methods
(static) lowerToProgram(ir, optionsopt) → {Object}
Lowers an IR tree to a render program.
Parameters:
| Name | Type | Attributes | Description | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
ir |
object | The root fragment from module:lib/compiler/ir/build. |
|||||||||
options |
object |
<optional> |
Lowering options. Properties
|
- Source:
Returns:
The program plus the sources its indices refer to, or the refusal.
- Type
- Object
(inner) isPurelyStatic(nodes) → {boolean}
Whether a subtree contains nothing that needs an op.
The static mark is applied by an earlier pass, and a mark that turns out to wrap control flow is a disagreement between two passes rather than something to render around. Checking here means the disagreement costs an ordinary compile of that subtree instead of refusing the whole template.
Parameters:
| Name | Type | Description |
|---|---|---|
nodes |
Array:.<object:> | IR nodes. |
- Source:
Returns:
True when every node is literal markup.
- Type
- boolean
(inner) isSubscriptionTrigger(when) → {boolean}
Whether a trigger names a subscription rather than a reactive condition.
Parameters:
| Name | Type | Description |
|---|---|---|
when |
string | The trigger as written. |
- Source:
Returns:
True when the runtime subscribes rather than evaluates.
- Type
- boolean
(inner) literalPropExpression(value) → {string}
Renders a literal prop value as the expression source the mounter expects.
The component mounter evaluates data-props-* as an expression, so a literal
has to arrive quoted -- except for the four literals that mean themselves.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
string | The attribute value as written. |
- Source:
Returns:
An expression source.
- Type
- string