Builds the template IR from template source.
This runs on the template as the developer wrote it -- after declarations and
imports are removed and scoped-CSS classes are applied, but before any
directive rewriting. That ordering is the whole point: reading <@for item in items> directly is what lets the compiler record that it is a loop over
items binding item, instead of rewriting it into a <template> element
and asking the runtime to work that out again.
What it refuses
A construct the IR does not model yet aborts the build of that template with a IRRefusal naming the construct. The caller keeps the legacy path for that component and the build reports it. There is no partial IR, for the same reason there is no partial render program: two descriptions of one template disagree eventually, and the disagreement is unattributable.
- Source:
Members
(inner, constant) DIRECTIVE_BINDINGS :Map:.<string:, string:>
Directive attributes that become bindings rather than markup.
Type:
- Source:
(inner, constant) UNMODELLED_ATTRS :Array:.<Array:.<string:>>
Attributes whose presence means the element needs machinery the IR does not model, mapped to the reason reported.
Type:
- Source:
(inner, constant) UNMODELLED_TAGS :Map:.<string:, string:>
Directive tags the IR does not model yet, mapped to the reason reported.
Type:
- Source:
Methods
(static) buildTemplateIR(template, optionsopt) → {Object}
Builds the IR for a template.
Parameters:
- Source:
Returns:
The root fragment, or the reason the template could not be represented.
- Type
- Object
(static) parseForHeader(header) → {Object}
Parses a <@for> header into its parts.
Two binding forms, both pre-existing and both preserved exactly:
item in listbinds each element toitem.[a, b] in pairsdestructures each element -- which is an array -- intoaandb. It does not mean(item, index), however much it looks like the JavaScript it resembles.
Either form also binds index implicitly, which is how the loop index has
always been reached. Both facts are load-bearing for existing applications,
so they are read out of the previous implementation rather than redesigned
here; changing either belongs in its own change, with its own migration.
Parameters:
| Name | Type | Description |
|---|---|---|
header |
string | The raw header text. |
- Source:
Throws:
-
When the header is not a loop header.
- Type
- IRRefusal
Returns:
The parts.
- Type
- Object
(static) splitInterpolations(source) → {Array:.<{expr:: (string:|null:), raw:: boolean:, value:: string:}>}
Splits text into literal and expression segments, in order.
Parameters:
| Name | Type | Description |
|---|---|---|
source |
string | The source text. |
- Source:
Returns:
The segments.
(inner) assertHeaderNotTruncated(node, test)
Rejects a conditional header that the tag scan cut in half.
<@if count > 3> cannot be read unambiguously: the > that means "greater
than" and the > that means "end of tag" are the same character, and nothing
in the surrounding text distinguishes them. Bracket depth rescues
<@for x in xs.filter(a => a.n > 1)> because the comparison is inside a call;
a bare comparison has nothing to hide behind.
So the compiler does not guess. A header that was truncated leaves the rest
of the expression as the first text node of the branch body -- " 3>" above
-- and that shape is what this detects. The author gets the parenthesised
form, which the scanner reads correctly, rather than a condition that
silently tests the wrong thing.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
object | The parsed directive node. |
test |
string | The header text as scanned. |
- Source:
Throws:
-
When the header was cut at a comparison.
- Type
- IRRefusal
(inner) buildAttributes(node) → {Object}
Builds an element's bindings and events from its attribute map.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
object | The parsed HTML node. |
- Source:
Throws:
-
When an attribute needs machinery the IR does not model.
- Type
- IRRefusal
Returns:
The element parts.
- Type
- Object
(inner) buildConditional(siblings, index, buildChildren) → {Object}
Collects an <@if> chain starting at index, consuming its continuations.
Parameters:
| Name | Type | Description |
|---|---|---|
siblings |
Array:.<object:> | The sibling list being walked. |
index |
number | Index of the |
buildChildren |
function | Recursive child builder. |
- Source:
Returns:
The conditional node and the index to resume at.
- Type
- Object
(inner) buildDefer(node, when, buildChildren) → {object}
Builds a deferred block from a <@defer> element.
<@placeholder> is a sibling of the deferred content rather than a wrapper
around it, so it is separated out here the same way <@empty> is separated
from a loop body.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
object | The parsed |
when |
string | The resolved trigger. |
buildChildren |
function | Recursive child builder. |
- Source:
Returns:
The defer node.
- Type
- object
(inner) buildIteration(node, buildChildren) → {object}
Builds an iteration node from a <@for> element.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
object | The parsed |
buildChildren |
function | Recursive child builder. |
- Source:
Returns:
The iteration node.
- Type
- object
(inner) buildProps(node) → {Array:.<object:>}
Builds the props of a child component from its attribute map.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
object | The parsed HTML node. |
- Source:
Returns:
Prop descriptors.
(inner) findTopLevelWord(source, word) → {number}
Finds the offset of a top-level occurrence of a keyword in an expression.
"Top level" means outside quotes and outside every bracket pair, so the in
of <@for k in Object.keys(map)> is found and the in of an x in y
written inside a call's arguments is not.
Parameters:
| Name | Type | Description |
|---|---|---|
source |
string | The text to scan. |
word |
string | The keyword to find. |
- Source:
Returns:
The offset, or -1.
- Type
- number
(inner) hasInterpolation(value) → {boolean}
Whether a string contains a template interpolation.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
string | The text to test. |
- Source:
Returns:
True when it contains {{ }} or {{{ }}}.
- Type
- boolean
(inner) isComponentTag(tag) → {boolean}
Whether a tag name refers to a child component rather than an element.
Parameters:
| Name | Type | Description |
|---|---|---|
tag |
string | The tag name as written. |
- Source:
Returns:
True for a PascalCase reference.
- Type
- boolean
(inner) parseEventName(name) → {Object}
Parses an event attribute name into its event and modifiers.
Parameters:
| Name | Type | Description |
|---|---|---|
name |
string | The attribute name, including the leading |
- Source:
Returns:
The parsed parts.
- Type
- Object
(inner) splitListAndKey(header) → {Object}
Strips a trailing key="..." clause from a <@for> header.
Parameters:
| Name | Type | Description |
|---|---|---|
header |
string | The header text after the list expression. |
- Source:
Returns:
The list source and the key source.
- Type
- Object
(inner) unwrapDirectiveValue(value) → {string}
Reads a directive attribute's expression, with or without {{ }} around it.
Both spellings are documented and both appear in the wild:
<p data-ax-show="isOpen">
<p data-ax-style="{{ { fontWeight: bold ? '700' : '400' } }}">
The braces are how an author writes an object literal without it looking
like an attribute value, so they are stripped here rather than handed to the
expression parser, which would read {{ as the start of a block.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
string | The attribute value as written. |
- Source:
Returns:
The expression source.
- Type
- string