@xaendar/compiler 0.7.26 → 0.7.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/xaendar-compiler.es.js +44 -84
- package/package.json +3 -3
|
@@ -342,45 +342,12 @@ var GLOBAL_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
|
342
342
|
"Window"
|
|
343
343
|
]);
|
|
344
344
|
var ROOT_NODE = "root";
|
|
345
|
-
/**
|
|
346
|
-
* Resolves references to component properties inside a template expression.
|
|
347
|
-
*
|
|
348
|
-
* Identifiers that are not found in the active scope chain and are not
|
|
349
|
-
* well-known globals are prefixed with `this.` so they resolve against
|
|
350
|
-
* the component instance at runtime. Identifiers found in the scope chain
|
|
351
|
-
* are resolved either as a bare local reference (if declared directly in
|
|
352
|
-
* the current generated function's own scope) or via a runtime
|
|
353
|
-
* `parentContext.get(...)` traversal (if inherited from an enclosing
|
|
354
|
-
* scope) — and a trailing `()` is appended in either case if the
|
|
355
|
-
* identifier was declared as a signal, so the generated code always
|
|
356
|
-
* correctly unwraps it.
|
|
357
|
-
*
|
|
358
|
-
* The original formatting of the expression — parentheses, spacing,
|
|
359
|
-
* operator tokens, member access dots — is preserved verbatim by delegating
|
|
360
|
-
* to `node.getText()` for any subtree that contains no resolvable identifiers.
|
|
361
|
-
*
|
|
362
|
-
* @param expression - Either a raw identifier string or a validated
|
|
363
|
-
* `Expression` node produced by `validateExpression`.
|
|
364
|
-
* @param compilerContext - The active template scope context.
|
|
365
|
-
* @returns The resolved expression as a JavaScript string ready for codegen.
|
|
366
|
-
*
|
|
367
|
-
* @example
|
|
368
|
-
* // Simple identifier
|
|
369
|
-
* resolveExpression('items', context) // → 'this.items'
|
|
370
|
-
*
|
|
371
|
-
* @example
|
|
372
|
-
* // Signal identifier inherited from an ancestor scope (e.g. `$index` in a
|
|
373
|
-
* // nested @for body)
|
|
374
|
-
* resolveExpression('$index', context) // → "parentContext.get('$index')()"
|
|
375
|
-
*
|
|
376
|
-
* @example
|
|
377
|
-
* // Complex expression — formatting preserved
|
|
378
|
-
* resolveExpression(node, context)
|
|
379
|
-
* // typeof id !== 'boolean' || pippo instanceof HTMLElement
|
|
380
|
-
* // → typeof this.id !== 'boolean' || this.pippo instanceof HTMLElement
|
|
381
|
-
*/
|
|
382
345
|
function resolveExpression(expression, compilerContext, options) {
|
|
383
|
-
|
|
346
|
+
let actualCompilerContext;
|
|
347
|
+
let actualOptions;
|
|
348
|
+
if (compilerContext instanceof CompilerContext) actualCompilerContext = compilerContext;
|
|
349
|
+
else actualOptions = compilerContext;
|
|
350
|
+
return emitNode(expression, expression, actualCompilerContext, mapDefaultOptions(actualOptions));
|
|
384
351
|
}
|
|
385
352
|
/**
|
|
386
353
|
* Emits the resolved text for a node.
|
|
@@ -395,8 +362,10 @@ function resolveExpression(expression, compilerContext, options) {
|
|
|
395
362
|
function emitNode(node, parent, compilerContext, options) {
|
|
396
363
|
if (isIdentifier(node) && needsResolution(node, parent)) {
|
|
397
364
|
const text = node.text;
|
|
398
|
-
if (compilerContext
|
|
399
|
-
|
|
365
|
+
if (compilerContext?.hasUnresolvableIdentifier(text) || options.skipResolution) return text;
|
|
366
|
+
if (options.resolver) return `${options.resolver}.${text}`;
|
|
367
|
+
if (compilerContext) return resolveIdentifierAccess(text, compilerContext);
|
|
368
|
+
return text;
|
|
400
369
|
}
|
|
401
370
|
if (!containsResolvableIdentifier(node, parent)) return node.getText();
|
|
402
371
|
const sourceText = node.getSourceFile().text;
|
|
@@ -427,13 +396,13 @@ function emitNode(node, parent, compilerContext, options) {
|
|
|
427
396
|
* @param compilerContext - The active template scope context.
|
|
428
397
|
* @returns The generated code expression that yields the identifier's value.
|
|
429
398
|
*/
|
|
430
|
-
function resolveIdentifierAccess(text, compilerContext
|
|
399
|
+
function resolveIdentifierAccess(text, compilerContext) {
|
|
431
400
|
if (compilerContext.hasIdentifier(text)) {
|
|
432
401
|
const kind = compilerContext.getIdentifierKind(text);
|
|
433
402
|
const access = `context.get('${text}')`;
|
|
434
403
|
return kind === "signal" ? `${access}()` : access;
|
|
435
404
|
}
|
|
436
|
-
return
|
|
405
|
+
return text;
|
|
437
406
|
}
|
|
438
407
|
/**
|
|
439
408
|
* Returns true if the subtree rooted at `node` contains at least one
|
|
@@ -2909,18 +2878,17 @@ var Parser = class {
|
|
|
2909
2878
|
* `TypeChecker` for why) — attribute expressions and event calls are
|
|
2910
2879
|
* emitted as bare statements, validated in place.
|
|
2911
2880
|
*/
|
|
2912
|
-
function typeCheckElement(node,
|
|
2881
|
+
function typeCheckElement(node, processNode, context) {
|
|
2913
2882
|
const lines = [];
|
|
2914
2883
|
node.attributes.forEach(({ value }) => {
|
|
2915
|
-
if (typeof value !== "string") lines.push(`${resolveExpression(value.expression,
|
|
2884
|
+
if (typeof value !== "string") lines.push(`${resolveExpression(value.expression, { resolver: "root" })};`);
|
|
2916
2885
|
});
|
|
2917
|
-
context.addUnresolvableIdentifier("$event");
|
|
2918
2886
|
node.events.forEach(({ handler, parameters }) => {
|
|
2919
|
-
const args = parameters.map((parameter) => resolveExpression(parameter,
|
|
2887
|
+
const args = parameters.map((parameter) => resolveExpression(parameter, { resolver: "root" })).join(", ");
|
|
2920
2888
|
lines.push(`root.${handler}(${args});`);
|
|
2921
2889
|
});
|
|
2922
2890
|
node.children.forEach((child, i) => {
|
|
2923
|
-
lines.push(...processNode(child, context
|
|
2891
|
+
lines.push(...processNode(child, context));
|
|
2924
2892
|
});
|
|
2925
2893
|
return lines;
|
|
2926
2894
|
}
|
|
@@ -2936,31 +2904,31 @@ function typeCheckElement(node, context, index, processNode) {
|
|
|
2936
2904
|
* exactly like it would for a loop written by hand — with no synthetic
|
|
2937
2905
|
* function boundary needed.
|
|
2938
2906
|
*/
|
|
2939
|
-
function typeCheckFor(node,
|
|
2940
|
-
const
|
|
2907
|
+
function typeCheckFor(node, processNode, context) {
|
|
2908
|
+
const forContext = new CompilerContext([], context);
|
|
2941
2909
|
const indexName = resolveImplicit(node, "$index");
|
|
2942
2910
|
const firstName = resolveImplicit(node, "$first");
|
|
2943
2911
|
const lastName = resolveImplicit(node, "$last");
|
|
2944
2912
|
const evenName = resolveImplicit(node, "$even");
|
|
2945
2913
|
const oddName = resolveImplicit(node, "$odd");
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
]
|
|
2914
|
+
[
|
|
2915
|
+
indexName,
|
|
2916
|
+
firstName,
|
|
2917
|
+
lastName,
|
|
2918
|
+
evenName,
|
|
2919
|
+
oddName,
|
|
2920
|
+
node.itemAlias
|
|
2921
|
+
].forEach((identifier) => forContext.addUnresolvableIdentifier(identifier));
|
|
2954
2922
|
const lines = [];
|
|
2955
|
-
lines.push(`for (const ${node.itemAlias} of
|
|
2923
|
+
lines.push(`for (const ${node.itemAlias} of root.${node.iterableSource}) {`);
|
|
2956
2924
|
lines.push(...indent([
|
|
2957
2925
|
`let ${indexName}!: number;`,
|
|
2958
2926
|
`let ${firstName}!: boolean;`,
|
|
2959
2927
|
`let ${lastName}!: boolean;`,
|
|
2960
2928
|
`let ${evenName}!: boolean;`,
|
|
2961
2929
|
`let ${oddName}!: boolean;`,
|
|
2962
|
-
`${resolveExpression(node.trackExpression,
|
|
2963
|
-
...node.children.flatMap((child
|
|
2930
|
+
`${resolveExpression(node.trackExpression, { skipResolution: true })};`,
|
|
2931
|
+
...node.children.flatMap((child) => processNode(child, context))
|
|
2964
2932
|
]));
|
|
2965
2933
|
lines.push("}");
|
|
2966
2934
|
return lines;
|
|
@@ -2993,26 +2961,23 @@ function resolveImplicit(node, implicit) {
|
|
|
2993
2961
|
* `else if`, TS already knows the first condition was false) — something
|
|
2994
2962
|
* flat sibling functions could never express.
|
|
2995
2963
|
*/
|
|
2996
|
-
function typeCheckIf(node,
|
|
2964
|
+
function typeCheckIf(node, processNode, context) {
|
|
2997
2965
|
const lines = [];
|
|
2998
|
-
const condition = resolveExpression(node.conditionNode,
|
|
2999
|
-
const ifContext = new CompilerContext([], context);
|
|
2966
|
+
const condition = resolveExpression(node.conditionNode, { resolver: "root" });
|
|
3000
2967
|
lines.push(`if (${condition}) {`);
|
|
3001
|
-
lines.push(...indent(node.children.flatMap((child
|
|
2968
|
+
lines.push(...indent(node.children.flatMap((child) => processNode(child, context))));
|
|
3002
2969
|
lines.push("}");
|
|
3003
2970
|
let alt = node.alternate;
|
|
3004
2971
|
while (alt?.type === ASTNodeType.ElseIf) {
|
|
3005
|
-
const elseIfCondition = resolveExpression(alt.conditionNode,
|
|
3006
|
-
const elseIfContext = new CompilerContext([], context);
|
|
2972
|
+
const elseIfCondition = resolveExpression(alt.conditionNode, { resolver: "root" });
|
|
3007
2973
|
lines.push(`else if (${elseIfCondition}) {`);
|
|
3008
|
-
lines.push(...indent(alt.children.flatMap((child
|
|
2974
|
+
lines.push(...indent(alt.children.flatMap((child) => processNode(child, context))));
|
|
3009
2975
|
lines.push("}");
|
|
3010
2976
|
alt = alt.alternate;
|
|
3011
2977
|
}
|
|
3012
2978
|
if (alt) {
|
|
3013
|
-
const elseContext = new CompilerContext([], context);
|
|
3014
2979
|
lines.push("else {");
|
|
3015
|
-
lines.push(...indent(alt.children.flatMap((child
|
|
2980
|
+
lines.push(...indent(alt.children.flatMap((child) => processNode(child, context))));
|
|
3016
2981
|
lines.push("}");
|
|
3017
2982
|
}
|
|
3018
2983
|
return lines;
|
|
@@ -3034,15 +2999,11 @@ function typeCheckIf(node, context, index, processNode) {
|
|
|
3034
2999
|
* emitted as stacked `case` labels sharing that same body, matching real
|
|
3035
3000
|
* JS/TS fallthrough syntax directly.
|
|
3036
3001
|
*/
|
|
3037
|
-
function typeCheckSwitch(node,
|
|
3038
|
-
const lines = [`switch (${resolveExpression(node.expression,
|
|
3039
|
-
node.children.forEach((caseNode
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
lines.push(` case ${conditionValue}:`);
|
|
3043
|
-
});
|
|
3044
|
-
else lines.push(" default:");
|
|
3045
|
-
lines.push(...indent(indent([...caseNode.children.flatMap((child, ci) => processNode(child, caseContext, ci.toString())), "break;"])));
|
|
3002
|
+
function typeCheckSwitch(node, processNode, context) {
|
|
3003
|
+
const lines = [`switch (${resolveExpression(node.expression, { resolver: "root" })}) {`];
|
|
3004
|
+
node.children.forEach((caseNode) => {
|
|
3005
|
+
caseNode.condition?.length ? caseNode.condition.forEach((conditionValue) => lines.push(` case ${conditionValue}:`)) : lines.push(" default:");
|
|
3006
|
+
lines.push(...indent(indent([...caseNode.children.flatMap((child) => processNode(child, context)), "break;"])));
|
|
3046
3007
|
});
|
|
3047
3008
|
lines.push("}");
|
|
3048
3009
|
return lines;
|
|
@@ -3056,8 +3017,8 @@ function typeCheckSwitch(node, context, index, processNode) {
|
|
|
3056
3017
|
* interpolation's expression is emitted as a bare statement — enough for
|
|
3057
3018
|
* TS to validate it, with no name needing to be bound to the result.
|
|
3058
3019
|
*/
|
|
3059
|
-
function typeCheckTextAndInterpolation(node,
|
|
3060
|
-
return node.type === ASTNodeType.Interpolation ? [`${resolveExpression(node.expression,
|
|
3020
|
+
function typeCheckTextAndInterpolation(node, _processNode, _context) {
|
|
3021
|
+
return node.type === ASTNodeType.Interpolation ? [`${resolveExpression(node.expression, { resolver: "root" })};`] : [];
|
|
3061
3022
|
}
|
|
3062
3023
|
//#endregion
|
|
3063
3024
|
//#region ../packages/compiler/src/type-checker/type-checker.ts
|
|
@@ -3107,8 +3068,7 @@ var TypeChecker = class {
|
|
|
3107
3068
|
* relevant if the consuming project has `noUnusedLocals` enabled.
|
|
3108
3069
|
*/
|
|
3109
3070
|
generate() {
|
|
3110
|
-
const
|
|
3111
|
-
const body = this._ast.flatMap((node, i) => this._processNode(node, context, i.toString()));
|
|
3071
|
+
const body = this._ast.flatMap((node) => this._processNode(node));
|
|
3112
3072
|
return [
|
|
3113
3073
|
"function typeCheck() {",
|
|
3114
3074
|
...indent(body.some((line) => line.includes("$event")) ? ["let $event!: Event;", ...body] : body),
|
|
@@ -3120,10 +3080,10 @@ var TypeChecker = class {
|
|
|
3120
3080
|
* back down as `processNode` so state functions can recurse into their
|
|
3121
3081
|
* own children inline.
|
|
3122
3082
|
*/
|
|
3123
|
-
_processNode = (node, context
|
|
3083
|
+
_processNode = (node, context) => {
|
|
3124
3084
|
const state = this._states[node.type];
|
|
3125
3085
|
if (!state) throw new Error(`[Type Checker] No transition function for token type ${ASTNodeType[node.type]}`);
|
|
3126
|
-
return state(node,
|
|
3086
|
+
return state(node, this._processNode, context);
|
|
3127
3087
|
};
|
|
3128
3088
|
};
|
|
3129
3089
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xaendar/compiler",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.28",
|
|
4
4
|
"description": "A library for transpiling Xaendar Templates into JavaScript code",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@xaendar/common": "0.7.
|
|
20
|
-
"@xaendar/types": "0.7.
|
|
19
|
+
"@xaendar/common": "0.7.28",
|
|
20
|
+
"@xaendar/types": "0.7.28",
|
|
21
21
|
"typescript": "^6.0.3"
|
|
22
22
|
}
|
|
23
23
|
}
|