Module: lib/compiler/parser/htmlTree

The template tree parser and serializer.

parser/tokenizer.js answers "where does this tag begin and end in the source". This module answers the next question: what tree do those tags describe. It was previously private to ComponentParser, which was fine while the compiler had exactly one consumer for it. The render compiler (lib/compiler/render/) is a second, and a second copy of an HTML parser is how two halves of one compiler come to disagree about what a template says.

It is deliberately not a spec-compliant HTML parser. Avenx templates are authored rather than scraped, and by the time a template reaches here the declaration tags are gone and the directives have been rewritten into ordinary elements. What it guarantees is that serializeHTML(parseHTML(x)) round-trips a template the compiler itself produced.

Source:

Classes

HTMLNode

Members

(static, constant) DEFAULT_VOID_TAGS :Array:.<string:>

The set of HTML tags that are void (self-closing / no children) by default.

Type:
  • Array:.<string:>
Source:

(inner, constant) IF_CHAIN_TAGS :Set:.<string:>

The arms of a conditional chain.

They are parsed as siblings rather than as a nest, which is what lets </@if> terminate the chain and what lets a consumer read the arms as an ordered list instead of unwinding a ladder.

Type:
  • Set:.<string:>
Source:

(inner, constant) IF_CONTINUATION_TAGS :Set:.<string:>

The arms that continue a chain, and therefore end the arm before them.

Type:
  • Set:.<string:>
Source:

Methods

(static) buildVoidTagsSet(customVoidTagsopt) → {Set:.<string:>}

Builds the effective set of void tags for a parse or serialize pass.

Parameters:
Name Type Attributes Description
customVoidTags Array:.<string:> <optional>

Additional void tag names (lowercase).

Source:
Returns:

The effective set.

Type
Set:.<string:>

(static) getLineAndColumn(source, offset) → {Object}

Calculates 1-based line and column numbers for a character offset in a source string.

Parameters:
Name Type Description
source string

The original source code string.

offset number

The zero-based character index.

Source:
Returns:
Type
Object

(static) parseAttributes(attrStr) → {Object:.<string:, string:>}

Parses an attribute string into a key-value object.

Handles three attribute forms:

  • Quoted values: name="value" or name='value'. A backslash-escaped quote (\" or \') inside the value is preserved verbatim rather than ending the value early, so expressions containing an apostrophe or a quote character (e.g. @click='say(\'hi\')') parse correctly instead of being split into several bogus attributes.
  • Unquoted values: name=value, read up to the next whitespace or >.
  • Valueless boolean attributes: disabled, mapped to the string 'true' (matching the attr="true" / attr="false" convention the runtime's boolean-attribute handling already expects, rather than null).
Parameters:
Name Type Description
attrStr string
Source:
Returns:
Type
Object:.<string:, string:>

(static) parseHTML(html, customVoidTagsopt) → {Array:.<HTMLNode:>}

Parses an HTML string into a tree of HTMLNode elements with positional metadata.

Parameters:
Name Type Attributes Description
html string
customVoidTags Array:.<string:> <optional>
Source:
Returns:
Type
Array:.<HTMLNode:>

(static) scanTagEnd(html, start) → {number}

Finds the offset of the > that ends the tag opening at start.

Quoting is honoured for every tag, because a > inside title="a > b" has never been the end of a tag. Bracket depth is honoured only for @-prefixed directive tags, and that exception is the point of this function.

A directive header carries an expression rather than attributes:

<@for row in rows.filter(r => r.score > 90)>

Scanning for the first unquoted > ends that tag at r.score , which is why the previous implementation truncated the list expression to rows.filter(r = and reported it as a malformed template expression. The expression is not malformed; the scan was. Inside (, [ or { a > is a comparison or an arrow, never a tag end, so the scan tracks depth and only accepts a > at depth zero.

The exception is deliberately not extended to ordinary elements. <div data-x=a(b>c)> is not markup anyone writes, and widening the rule would change how existing templates parse for no gain.

Parameters:
Name Type Description
html string

The full template source.

start number

Offset of the < that opens the tag.

Source:
Returns:

Offset of the closing >, or -1 when the tag is unterminated.

Type
number

(static) serializeHTML(nodes, customVoidTagsopt) → {string}

Serializes an HTMLNode tree back to an HTML string.

Parameters:
Name Type Attributes Description
nodes Array:.<HTMLNode:>
customVoidTags Array:.<string:> <optional>

Additional project-specific void tag names (lowercase), loaded from avenx.config.json. Should match what was passed to parseHTML for the same template so a custom void tag round-trips consistently.

Source:
Returns:
Type
string