Turns a parsed template expression into JavaScript source.
What moved, and why
Avenx used to ship every template expression, computed value and handler to
the browser as source text, and evaluate it by walking an AST there. That
put a parser, an interpreter and a scope walker in every production bundle,
and made the cost of reading count a recursive dispatch rather than a
property access.
The parse is a pure function of the source, and the compiler already has the source. So the parse happens here, once, at build time, and what reaches the browser is a closure the engine itself compiled:
count * 2 → ($s) => axGet($s, "count") * 2
item.qty → ($s) => axRead(axGet($s, "item"), "qty", false)
items.filter(i => !i.done)
→ ($s) => axCall(axRead(axGet($s,"items"), "filter", false),
axGet($s,"items"), [(i) => !axRead(i,"done",false)],
"items.filter")
The security boundary is unchanged
Every gate the interpreter applied is still applied, in the same place and on
the same terms — it is simply called rather than interpreted. A member read
emits module:lib/core/expression/ops.readMember with the key already
resolved, so x['const'+'ructor'] and x.constructor still arrive at one
check as one string. A call emits callFunction. A free identifier emits
readIdentifier, which resolves scope-first and routes an allowed global
through the tracer's substitution point.
Two checks get stronger by moving: naming a restricted global and writing a forbidden static key are now build errors with a source location, rather than runtime refusals a developer only sees when the branch executes.
No eval, by construction
The emitted text is written into the component module the bundler links, so
the engine compiles it exactly as it compiles the rest of the bundle. Nothing
here or downstream calls eval or new Function, which is what makes "an
Avenx page needs no 'unsafe-eval'" a property of the pipeline rather than a
claim about it.
Arrow parameters stop being a runtime concept
The interpreter carried a Frame chain so a lambda parameter would not
resolve against component state. A compiled arrow's parameters are real
JavaScript parameters, so the engine's own scoping does that work: this
module only has to know which names are lexically bound so it emits the bare
name instead of a scope read.
- Source:
Classes
Members
(static, constant) RUNTIME_BINDINGS :Object:.<string:, string:>
The local names the emitted code uses for the runtime primitives.
Short, prefixed, and declared in one place so the module emitter and the
generator cannot drift. $s is the scope; everything else is imported from
the runtime.
Type:
(static, constant) RUNTIME_IMPORT_NAMES :Array:.<string:>
The named runtime imports a module containing compiled expressions needs.
Type:
(inner, constant) DIRECT_BINARY :Set:.<string:>
Binary operators that map straight onto JavaScript's own.
in is absent deliberately: the interpreter coerced the right-hand side with
Object(right), and emitting a bare in would throw where the interpreter
returned false.
Type:
(inner, constant) DIRECT_UNARY :Set:.<string:>
Unary operators that map straight onto JavaScript's own.
Type:
Methods
(static) compileExpressionToSource(source) → {string}
Compiles one expression to a scope-taking arrow function.
Parameters:
| Name | Type | Description |
|---|---|---|
source |
string | The expression source. |
Throws:
-
When the expression cannot be compiled.
- Type
- ExpressionCodegenError
Returns:
JavaScript source for ($s) => value.
- Type
- string
(static) compileStatementsToSource(source) → {string}
Compiles a run of expression statements to a scope-taking arrow function.
Used for inline event handlers and any action body that is a sequence of
expressions rather than real statement syntax. The value is discarded, which
is what the interpreter did for a Program.
Parameters:
| Name | Type | Description |
|---|---|---|
source |
string | The statement source. |
Throws:
-
When the source cannot be compiled.
- Type
- ExpressionCodegenError
Returns:
JavaScript source for ($s) => { … }.
- Type
- string
(static) runtimeImportStatement(base, specifieropt, opsSpecifieropt) → {string}
The import statement a module holding a compiled class body needs.
One function rather than a literal repeated at each framing site. There are
three -- the CLI's module emitter, the Vite plugin's wrapper, and the tests
that frame a bare class body themselves -- and when the generator started
emitting calls to the expression primitives, every site that still wrote the
import by hand produced a module that threw axGet is not defined at its
first render. Deriving the line from the same list the generator emits
against makes that class of drift impossible.
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
base |
string | The base class name to import ( |
|
specifier |
string |
<optional> |
Where the base class comes from. |
opsSpecifier |
string |
<optional> |
Where the primitives come from, when that is somewhere else. A caller importing the base class straight from its own module -- a test does -- still has to reach the runtime index for these. |
Returns:
The import declaration, or two.
- Type
- string
(static) tryCompileExpression(source) → {Object|Object}
Compiles an expression, returning null rather than throwing.
Parameters:
| Name | Type | Description |
|---|---|---|
source |
string | The expression source. |
Returns:
The generated source, or the reason.
- Type
- Object | Object
(static) tryCompileStatements(source) → {Object|Object}
Compiles a statement run, returning null rather than throwing.
Parameters:
| Name | Type | Description |
|---|---|---|
source |
string | The statement source. |
Returns:
The generated source, or the reason.
- Type
- Object | Object
(inner) describe(node) → {string}
Describes a node for a runtime error message, matching the interpreter.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
object | The node. |
Returns:
A short human-readable description.
- Type
- string
(inner) emit(node, bound, source) → {string}
Generates JavaScript for one AST node.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
object | The AST node. |
bound |
Bound | null | Names bound by enclosing arrow parameters. |
source |
string | The original expression, for error messages. |
Returns:
JavaScript source for the node's value.
- Type
- string
(inner) emitArguments(args, bound, source) → {string}
Emits an argument list, including spreads.
Parameters:
| Name | Type | Description |
|---|---|---|
args |
Array:.<object:> | Argument nodes. |
bound |
Bound | null | Enclosing bindings. |
source |
string | The original expression. |
Returns:
An array literal of the arguments.
- Type
- string
(inner) emitAssignment(node, bound, source) → {string}
Emits an assignment, routing writes through the guarded primitives.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
object | The Assignment node. |
bound |
Bound | null | Enclosing bindings. |
source |
string | The original expression. |
Returns:
JavaScript source for the assignment.
- Type
- string
(inner) emitCall(node, bound, source) → {string}
Emits a call, preserving the receiver for a method call.
The receiver has to be evaluated once and used twice — as the object of the
member read and as this — so a method call binds it to a temporary rather
than emitting the object expression twice, which would run its side effects
twice.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
object | The Call node. |
bound |
Bound | null | Enclosing bindings. |
source |
string | The original expression. |
Returns:
JavaScript source for the call.
- Type
- string
(inner) emitLiteral(value) → {string}
Emits a literal value.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
any | The literal's value. |
Returns:
The literal source.
- Type
- string
(inner) emitUpdate(node, bound, source) → {string}
Emits ++ / --, preserving prefix and postfix value semantics.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
object | The Update node. |
bound |
Bound | null | Enclosing bindings. |
source |
string | The original expression. |
Returns:
JavaScript source for the update.
- Type
- string
(inner) quote(value) → {string}
Quotes a string as a JavaScript literal.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
any | The value to quote. |
Returns:
The literal source.
- Type
- string