@xaendar/compiler 0.7.25 → 0.7.27
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 +33 -79
- 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) {
|
|
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
|
|
2891
|
+
lines.push(...processNode(child));
|
|
2924
2892
|
});
|
|
2925
2893
|
return lines;
|
|
2926
2894
|
}
|
|
@@ -2936,31 +2904,22 @@ 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 iterableExpr = context.hasIdentifier(node.iterableSource) ? node.iterableSource : `root.${node.iterableSource}`;
|
|
2907
|
+
function typeCheckFor(node, processNode) {
|
|
2941
2908
|
const indexName = resolveImplicit(node, "$index");
|
|
2942
2909
|
const firstName = resolveImplicit(node, "$first");
|
|
2943
2910
|
const lastName = resolveImplicit(node, "$last");
|
|
2944
2911
|
const evenName = resolveImplicit(node, "$even");
|
|
2945
2912
|
const oddName = resolveImplicit(node, "$odd");
|
|
2946
|
-
const forContext = new CompilerContext([
|
|
2947
|
-
node.itemAlias,
|
|
2948
|
-
[indexName, "signal"],
|
|
2949
|
-
[firstName, "signal"],
|
|
2950
|
-
[lastName, "signal"],
|
|
2951
|
-
[evenName, "signal"],
|
|
2952
|
-
[oddName, "signal"]
|
|
2953
|
-
], context);
|
|
2954
2913
|
const lines = [];
|
|
2955
|
-
lines.push(`for (const ${node.itemAlias} of
|
|
2914
|
+
lines.push(`for (const ${node.itemAlias} of root.${node.iterableSource}) {`);
|
|
2956
2915
|
lines.push(...indent([
|
|
2957
2916
|
`let ${indexName}!: number;`,
|
|
2958
2917
|
`let ${firstName}!: boolean;`,
|
|
2959
2918
|
`let ${lastName}!: boolean;`,
|
|
2960
2919
|
`let ${evenName}!: boolean;`,
|
|
2961
2920
|
`let ${oddName}!: boolean;`,
|
|
2962
|
-
`${resolveExpression(node.trackExpression,
|
|
2963
|
-
...node.children.flatMap((child, i) => processNode(child
|
|
2921
|
+
`${resolveExpression(node.trackExpression, { skipResolution: true })};`,
|
|
2922
|
+
...node.children.flatMap((child, i) => processNode(child))
|
|
2964
2923
|
]));
|
|
2965
2924
|
lines.push("}");
|
|
2966
2925
|
return lines;
|
|
@@ -2993,26 +2952,23 @@ function resolveImplicit(node, implicit) {
|
|
|
2993
2952
|
* `else if`, TS already knows the first condition was false) — something
|
|
2994
2953
|
* flat sibling functions could never express.
|
|
2995
2954
|
*/
|
|
2996
|
-
function typeCheckIf(node,
|
|
2955
|
+
function typeCheckIf(node, processNode) {
|
|
2997
2956
|
const lines = [];
|
|
2998
|
-
const condition = resolveExpression(node.conditionNode,
|
|
2999
|
-
const ifContext = new CompilerContext([], context);
|
|
2957
|
+
const condition = resolveExpression(node.conditionNode, { resolver: "root" });
|
|
3000
2958
|
lines.push(`if (${condition}) {`);
|
|
3001
|
-
lines.push(...indent(node.children.flatMap((child
|
|
2959
|
+
lines.push(...indent(node.children.flatMap((child) => processNode(child))));
|
|
3002
2960
|
lines.push("}");
|
|
3003
2961
|
let alt = node.alternate;
|
|
3004
2962
|
while (alt?.type === ASTNodeType.ElseIf) {
|
|
3005
|
-
const elseIfCondition = resolveExpression(alt.conditionNode,
|
|
3006
|
-
const elseIfContext = new CompilerContext([], context);
|
|
2963
|
+
const elseIfCondition = resolveExpression(alt.conditionNode, { resolver: "root" });
|
|
3007
2964
|
lines.push(`else if (${elseIfCondition}) {`);
|
|
3008
|
-
lines.push(...indent(alt.children.flatMap((child
|
|
2965
|
+
lines.push(...indent(alt.children.flatMap((child) => processNode(child))));
|
|
3009
2966
|
lines.push("}");
|
|
3010
2967
|
alt = alt.alternate;
|
|
3011
2968
|
}
|
|
3012
2969
|
if (alt) {
|
|
3013
|
-
const elseContext = new CompilerContext([], context);
|
|
3014
2970
|
lines.push("else {");
|
|
3015
|
-
lines.push(...indent(alt.children.flatMap((child
|
|
2971
|
+
lines.push(...indent(alt.children.flatMap((child) => processNode(child))));
|
|
3016
2972
|
lines.push("}");
|
|
3017
2973
|
}
|
|
3018
2974
|
return lines;
|
|
@@ -3034,15 +2990,14 @@ function typeCheckIf(node, context, index, processNode) {
|
|
|
3034
2990
|
* emitted as stacked `case` labels sharing that same body, matching real
|
|
3035
2991
|
* JS/TS fallthrough syntax directly.
|
|
3036
2992
|
*/
|
|
3037
|
-
function typeCheckSwitch(node,
|
|
3038
|
-
const lines = [`switch (${resolveExpression(node.expression,
|
|
2993
|
+
function typeCheckSwitch(node, processNode) {
|
|
2994
|
+
const lines = [`switch (${resolveExpression(node.expression, { resolver: "root" })}) {`];
|
|
3039
2995
|
node.children.forEach((caseNode, i) => {
|
|
3040
|
-
const caseContext = new CompilerContext([], context);
|
|
3041
2996
|
if (caseNode.condition?.length) caseNode.condition.forEach((conditionValue) => {
|
|
3042
2997
|
lines.push(` case ${conditionValue}:`);
|
|
3043
2998
|
});
|
|
3044
2999
|
else lines.push(" default:");
|
|
3045
|
-
lines.push(...indent(indent([...caseNode.children.flatMap((child
|
|
3000
|
+
lines.push(...indent(indent([...caseNode.children.flatMap((child) => processNode(child)), "break;"])));
|
|
3046
3001
|
});
|
|
3047
3002
|
lines.push("}");
|
|
3048
3003
|
return lines;
|
|
@@ -3056,8 +3011,8 @@ function typeCheckSwitch(node, context, index, processNode) {
|
|
|
3056
3011
|
* interpolation's expression is emitted as a bare statement — enough for
|
|
3057
3012
|
* TS to validate it, with no name needing to be bound to the result.
|
|
3058
3013
|
*/
|
|
3059
|
-
function typeCheckTextAndInterpolation(node,
|
|
3060
|
-
return node.type === ASTNodeType.Interpolation ? [`${resolveExpression(node.expression,
|
|
3014
|
+
function typeCheckTextAndInterpolation(node, _processNode) {
|
|
3015
|
+
return node.type === ASTNodeType.Interpolation ? [`${resolveExpression(node.expression, { resolver: "root" })};`] : [];
|
|
3061
3016
|
}
|
|
3062
3017
|
//#endregion
|
|
3063
3018
|
//#region ../packages/compiler/src/type-checker/type-checker.ts
|
|
@@ -3107,8 +3062,7 @@ var TypeChecker = class {
|
|
|
3107
3062
|
* relevant if the consuming project has `noUnusedLocals` enabled.
|
|
3108
3063
|
*/
|
|
3109
3064
|
generate() {
|
|
3110
|
-
const
|
|
3111
|
-
const body = this._ast.flatMap((node, i) => this._processNode(node, context, i.toString()));
|
|
3065
|
+
const body = this._ast.flatMap((node, i) => this._processNode(node));
|
|
3112
3066
|
return [
|
|
3113
3067
|
"function typeCheck() {",
|
|
3114
3068
|
...indent(body.some((line) => line.includes("$event")) ? ["let $event!: Event;", ...body] : body),
|
|
@@ -3120,10 +3074,10 @@ var TypeChecker = class {
|
|
|
3120
3074
|
* back down as `processNode` so state functions can recurse into their
|
|
3121
3075
|
* own children inline.
|
|
3122
3076
|
*/
|
|
3123
|
-
_processNode = (node
|
|
3077
|
+
_processNode = (node) => {
|
|
3124
3078
|
const state = this._states[node.type];
|
|
3125
3079
|
if (!state) throw new Error(`[Type Checker] No transition function for token type ${ASTNodeType[node.type]}`);
|
|
3126
|
-
return state(node,
|
|
3080
|
+
return state(node, this._processNode);
|
|
3127
3081
|
};
|
|
3128
3082
|
};
|
|
3129
3083
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xaendar/compiler",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.27",
|
|
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.27",
|
|
20
|
+
"@xaendar/types": "0.7.27",
|
|
21
21
|
"typescript": "^6.0.3"
|
|
22
22
|
}
|
|
23
23
|
}
|