Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 670x 670x 670x 670x 670x 35x 670x 670x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 407x 108x 108x | /**
* @file program.js
* @description The shape of a compiled render program, and the vocabulary the
* compiler and the runtime agree on.
*
* ## Why a program at all
*
* The previous rendering contract between the compiler and the runtime was a
* single string: the compiler handed over a template with `{{ }}` still in it,
* and the runtime worked out what that meant on every update. Working it out
* meant interpolating the whole template into HTML, handing that HTML to
* `DOMParser`, and diffing the resulting tree against the live DOM — so the
* cost of changing one value was proportional to the size of the template, and
* the runtime had to carry a template engine to pay it.
*
* None of that work depends on runtime values. Which text nodes are dynamic,
* which attributes are bound, which elements carry an event handler, and which
* subtrees can never change are all properties of the template, and the
* compiler already has the template. A render program is the compiler writing
* those answers down once, so the runtime can stop rediscovering them.
*
* ## The two halves
*
* A program is a **skeleton**, a list of **ops**, and a list of **blocks**.
*
* The skeleton is the template with every dynamic part removed: interpolations
* become comment markers, bound attributes are dropped. It is valid HTML that
* is parsed exactly once per component *class* and then cloned per instance,
* so a 2000-node template is parsed once for the life of the page rather than
* once per update.
*
* An op says what to do and where. `{ k: 'text', t: 3, x: 'count' }` reads
* "evaluate `count`, write it to text marker 3". Each op becomes one reactive
* effect at mount, so a write to `count` wakes that op and nothing else.
*
* ## Addressing, and why markers rather than paths
*
* The compiler cannot number nodes by walking its own AST and assume the
* browser will produce the same tree: HTML parsing inserts implied elements
* (`<tbody>`), relocates misplaced content, and closes tags the author left
* open. A path computed from the compiler's tree can therefore address a
* different node than the one the compiler meant.
*
* So the compiler emits *markers* — `data-axb="n"` on a dynamic element,
* `<!--axt:n-->` where dynamic text goes — and the runtime resolves them
* against the tree the browser actually built. That resolution happens once per
* component class ({@link module:lib/core/renderer/program/CompiledTemplate}),
* is cached as index paths, and every later instance resolves by path with no
* search.
*
* ## What the compiler refuses to compile
*
* A program is emitted only when every construct in the template is one the
* program runtime implements. Anything else and the component keeps the string
* renderer, with the reason recorded. Partial compilation is not offered: a
* template that is half compiled and half diffed has two sources of truth for
* the same DOM, and they will disagree.
*
* That fallback is visible rather than silent — `avenx build` reports which
* components did not compile and why, which is the same house rule Atlas
* follows when its analysis is incomplete.
*
* The refusal reasons themselves live with the layer that decides them, in
* {@link module:lib/compiler/ir/nodes}. They used to live here, next to a
* second template compiler that read already-rewritten markup; that compiler
* has been replaced by the IR and its lowering pass, so this module is now the
* format and the vocabulary and nothing else.
* @module lib/compiler/render/program
*/
/**
* The program format version.
*
* A bundle carries programs produced by the compiler that built it, and the
* runtime is concatenated into that same bundle, so the two can never be
* mismatched in a deployed application. The version exists for the case that
* can happen: a stored or transported program (a trace fixture, a cached
* build) read back by a newer runtime. A runtime that does not recognise the
* version falls back to the string renderer rather than guessing.
* @type {number}
*/
export const PROGRAM_VERSION = 2;
/**
* Op kinds a program may contain.
*
* Kept as short string constants rather than numbers: a program is JSON in a
* bundle a developer may well end up reading, and `'text'` costs three bytes
* more than `0` while costing nothing to understand.
* @enum {string}
*/
export const OpKind = {
/** Write an expression's value into a text marker. */
TEXT: 'text',
/** Write an expression's value into a text marker without escaping. */
RAW: 'raw',
/** Set an attribute to an expression's value. */
ATTR: 'attr',
/** Set an attribute from a mix of literal parts and expressions. */
ATTR_PARTS: 'attrp',
/** Set or remove a boolean attribute from an expression's truthiness. */
BOOL: 'bool',
/** Bind a DOM event handler declared in the template. */
EVENT: 'event',
/** Toggle `display` from an expression's truthiness (`data-ax-show`). */
SHOW: 'show',
/** Apply a string or object class binding (`data-ax-class`). */
CLASS: 'class',
/** Replace an element's inner HTML (`data-ax-html`). */
HTML: 'html',
/** Evaluate a prop for a child component mounted at this element. */
PROP: 'prop',
/** Set or update an inline style from a string or object (`data-ax-style`). */
STYLE: 'style',
/** Render the first arm whose test is truthy, at a text anchor. */
IF: 'if',
/** Render one copy of a block per list element, at a text anchor. */
FOR: 'for',
/** Render transcluded content, or a fallback block, at a text anchor. */
SLOT: 'slot',
/** Render a block once its trigger fires, at a text anchor. */
DEFER: 'defer',
};
/**
* Op kinds that own one or more blocks and therefore a DOM range.
*
* Everything else writes to a single node the skeleton already contains. A
* range op inserts and removes nodes, so it needs an anchor and a teardown
* path, and the runtime dispatches on this rather than re-listing the kinds.
* @type {Set<string>}
*/
export const RANGE_OPS = new Set([OpKind.IF, OpKind.FOR, OpKind.SLOT, OpKind.DEFER]);
/**
* Whether a value looks like a render program this runtime can execute.
* @param {any} value - The candidate.
* @returns {boolean} True when it is a program of a known version.
*/
export function isProgram(value) {
return !!(
value &&
typeof value === 'object' &&
value.v === PROGRAM_VERSION &&
typeof value.html === 'string' &&
Array.isArray(value.ops)
);
}
/**
* Returns a program's blocks, tolerating a program that declares none.
*
* A template with no control flow lowers to a root block and nothing else, and
* omitting the empty array keeps that program byte-identical to what the
* previous format produced for the same template.
* @param {object} program - A render program.
* @returns {object[]} The blocks.
*/
export function programBlocks(program) {
return Array.isArray(program && program.blocks) ? program.blocks : [];
}
|