@xaendar/compiler 0.7.27 → 0.7.29
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 +73 -36
- package/package.json +3 -3
|
@@ -342,12 +342,45 @@ 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
|
+
*/
|
|
345
382
|
function resolveExpression(expression, compilerContext, options) {
|
|
346
|
-
|
|
347
|
-
let actualOptions;
|
|
348
|
-
if (compilerContext instanceof CompilerContext) actualCompilerContext = compilerContext;
|
|
349
|
-
else actualOptions = compilerContext;
|
|
350
|
-
return emitNode(expression, expression, actualCompilerContext, mapDefaultOptions(actualOptions));
|
|
383
|
+
return emitNode(expression, expression, compilerContext, mapDefaultOptions(options));
|
|
351
384
|
}
|
|
352
385
|
/**
|
|
353
386
|
* Emits the resolved text for a node.
|
|
@@ -2878,18 +2911,16 @@ var Parser = class {
|
|
|
2878
2911
|
* `TypeChecker` for why) — attribute expressions and event calls are
|
|
2879
2912
|
* emitted as bare statements, validated in place.
|
|
2880
2913
|
*/
|
|
2881
|
-
function typeCheckElement(node, processNode) {
|
|
2882
|
-
const lines =
|
|
2914
|
+
function typeCheckElement(node, processNode, context) {
|
|
2915
|
+
const lines = new Array();
|
|
2883
2916
|
node.attributes.forEach(({ value }) => {
|
|
2884
|
-
if (typeof value !== "string") lines.push(`${resolveExpression(value.expression, { resolver: "root" })};`);
|
|
2917
|
+
if (typeof value !== "string") lines.push(`${resolveExpression(value.expression, context, { resolver: "root" })};`);
|
|
2885
2918
|
});
|
|
2886
2919
|
node.events.forEach(({ handler, parameters }) => {
|
|
2887
|
-
const args = parameters.map((parameter) => resolveExpression(parameter, { resolver: "root" })).join(", ");
|
|
2920
|
+
const args = parameters.map((parameter) => resolveExpression(parameter, context, { resolver: "root" })).join(", ");
|
|
2888
2921
|
lines.push(`root.${handler}(${args});`);
|
|
2889
2922
|
});
|
|
2890
|
-
node.children.forEach((child
|
|
2891
|
-
lines.push(...processNode(child));
|
|
2892
|
-
});
|
|
2923
|
+
node.children.forEach((child) => lines.push(...processNode(child, context)));
|
|
2893
2924
|
return lines;
|
|
2894
2925
|
}
|
|
2895
2926
|
//#endregion
|
|
@@ -2904,13 +2935,22 @@ function typeCheckElement(node, processNode) {
|
|
|
2904
2935
|
* exactly like it would for a loop written by hand — with no synthetic
|
|
2905
2936
|
* function boundary needed.
|
|
2906
2937
|
*/
|
|
2907
|
-
function typeCheckFor(node, processNode) {
|
|
2938
|
+
function typeCheckFor(node, processNode, context) {
|
|
2939
|
+
const forContext = new CompilerContext([], context);
|
|
2908
2940
|
const indexName = resolveImplicit(node, "$index");
|
|
2909
2941
|
const firstName = resolveImplicit(node, "$first");
|
|
2910
2942
|
const lastName = resolveImplicit(node, "$last");
|
|
2911
2943
|
const evenName = resolveImplicit(node, "$even");
|
|
2912
2944
|
const oddName = resolveImplicit(node, "$odd");
|
|
2913
|
-
|
|
2945
|
+
[
|
|
2946
|
+
indexName,
|
|
2947
|
+
firstName,
|
|
2948
|
+
lastName,
|
|
2949
|
+
evenName,
|
|
2950
|
+
oddName,
|
|
2951
|
+
node.itemAlias
|
|
2952
|
+
].forEach((identifier) => forContext.addUnresolvableIdentifier(identifier));
|
|
2953
|
+
const lines = new Array();
|
|
2914
2954
|
lines.push(`for (const ${node.itemAlias} of root.${node.iterableSource}) {`);
|
|
2915
2955
|
lines.push(...indent([
|
|
2916
2956
|
`let ${indexName}!: number;`,
|
|
@@ -2918,8 +2958,8 @@ function typeCheckFor(node, processNode) {
|
|
|
2918
2958
|
`let ${lastName}!: boolean;`,
|
|
2919
2959
|
`let ${evenName}!: boolean;`,
|
|
2920
2960
|
`let ${oddName}!: boolean;`,
|
|
2921
|
-
`${resolveExpression(node.trackExpression, { skipResolution: true })};`,
|
|
2922
|
-
...node.children.flatMap((child
|
|
2961
|
+
`${resolveExpression(node.trackExpression, context, { skipResolution: true })};`,
|
|
2962
|
+
...node.children.flatMap((child) => processNode(child, forContext))
|
|
2923
2963
|
]));
|
|
2924
2964
|
lines.push("}");
|
|
2925
2965
|
return lines;
|
|
@@ -2952,23 +2992,23 @@ function resolveImplicit(node, implicit) {
|
|
|
2952
2992
|
* `else if`, TS already knows the first condition was false) — something
|
|
2953
2993
|
* flat sibling functions could never express.
|
|
2954
2994
|
*/
|
|
2955
|
-
function typeCheckIf(node, processNode) {
|
|
2956
|
-
const lines =
|
|
2957
|
-
const condition = resolveExpression(node.conditionNode, { resolver: "root" });
|
|
2995
|
+
function typeCheckIf(node, processNode, context) {
|
|
2996
|
+
const lines = new Array();
|
|
2997
|
+
const condition = resolveExpression(node.conditionNode, context, { resolver: "root" });
|
|
2958
2998
|
lines.push(`if (${condition}) {`);
|
|
2959
|
-
lines.push(...indent(node.children.flatMap((child) => processNode(child))));
|
|
2999
|
+
lines.push(...indent(node.children.flatMap((child) => processNode(child, context))));
|
|
2960
3000
|
lines.push("}");
|
|
2961
3001
|
let alt = node.alternate;
|
|
2962
3002
|
while (alt?.type === ASTNodeType.ElseIf) {
|
|
2963
|
-
const elseIfCondition = resolveExpression(alt.conditionNode, { resolver: "root" });
|
|
3003
|
+
const elseIfCondition = resolveExpression(alt.conditionNode, context, { resolver: "root" });
|
|
2964
3004
|
lines.push(`else if (${elseIfCondition}) {`);
|
|
2965
|
-
lines.push(...indent(alt.children.flatMap((child) => processNode(child))));
|
|
3005
|
+
lines.push(...indent(alt.children.flatMap((child) => processNode(child, context))));
|
|
2966
3006
|
lines.push("}");
|
|
2967
3007
|
alt = alt.alternate;
|
|
2968
3008
|
}
|
|
2969
3009
|
if (alt) {
|
|
2970
3010
|
lines.push("else {");
|
|
2971
|
-
lines.push(...indent(alt.children.flatMap((child) => processNode(child))));
|
|
3011
|
+
lines.push(...indent(alt.children.flatMap((child) => processNode(child, context))));
|
|
2972
3012
|
lines.push("}");
|
|
2973
3013
|
}
|
|
2974
3014
|
return lines;
|
|
@@ -2990,14 +3030,11 @@ function typeCheckIf(node, processNode) {
|
|
|
2990
3030
|
* emitted as stacked `case` labels sharing that same body, matching real
|
|
2991
3031
|
* JS/TS fallthrough syntax directly.
|
|
2992
3032
|
*/
|
|
2993
|
-
function typeCheckSwitch(node, processNode) {
|
|
2994
|
-
const lines = [`switch (${resolveExpression(node.expression, { resolver: "root" })}) {`];
|
|
2995
|
-
node.children.forEach((caseNode
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
});
|
|
2999
|
-
else lines.push(" default:");
|
|
3000
|
-
lines.push(...indent(indent([...caseNode.children.flatMap((child) => processNode(child)), "break;"])));
|
|
3033
|
+
function typeCheckSwitch(node, processNode, context) {
|
|
3034
|
+
const lines = [`switch (${resolveExpression(node.expression, context, { resolver: "root" })}) {`];
|
|
3035
|
+
node.children.forEach((caseNode) => {
|
|
3036
|
+
caseNode.condition?.length ? caseNode.condition.forEach((conditionValue) => lines.push(` case ${conditionValue}:`)) : lines.push(" default:");
|
|
3037
|
+
lines.push(...indent(indent([...caseNode.children.flatMap((child) => processNode(child, context)), "break;"])));
|
|
3001
3038
|
});
|
|
3002
3039
|
lines.push("}");
|
|
3003
3040
|
return lines;
|
|
@@ -3011,8 +3048,8 @@ function typeCheckSwitch(node, processNode) {
|
|
|
3011
3048
|
* interpolation's expression is emitted as a bare statement — enough for
|
|
3012
3049
|
* TS to validate it, with no name needing to be bound to the result.
|
|
3013
3050
|
*/
|
|
3014
|
-
function typeCheckTextAndInterpolation(node, _processNode) {
|
|
3015
|
-
return node.type === ASTNodeType.Interpolation ? [`${resolveExpression(node.expression, { resolver: "root" })};`] : [];
|
|
3051
|
+
function typeCheckTextAndInterpolation(node, _processNode, context) {
|
|
3052
|
+
return node.type === ASTNodeType.Interpolation ? [`${resolveExpression(node.expression, context, { resolver: "root" })};`] : [];
|
|
3016
3053
|
}
|
|
3017
3054
|
//#endregion
|
|
3018
3055
|
//#region ../packages/compiler/src/type-checker/type-checker.ts
|
|
@@ -3062,7 +3099,7 @@ var TypeChecker = class {
|
|
|
3062
3099
|
* relevant if the consuming project has `noUnusedLocals` enabled.
|
|
3063
3100
|
*/
|
|
3064
3101
|
generate() {
|
|
3065
|
-
const body = this._ast.flatMap((node
|
|
3102
|
+
const body = this._ast.flatMap((node) => this._processNode(node));
|
|
3066
3103
|
return [
|
|
3067
3104
|
"function typeCheck() {",
|
|
3068
3105
|
...indent(body.some((line) => line.includes("$event")) ? ["let $event!: Event;", ...body] : body),
|
|
@@ -3074,10 +3111,10 @@ var TypeChecker = class {
|
|
|
3074
3111
|
* back down as `processNode` so state functions can recurse into their
|
|
3075
3112
|
* own children inline.
|
|
3076
3113
|
*/
|
|
3077
|
-
_processNode = (node) => {
|
|
3114
|
+
_processNode = (node, context) => {
|
|
3078
3115
|
const state = this._states[node.type];
|
|
3079
3116
|
if (!state) throw new Error(`[Type Checker] No transition function for token type ${ASTNodeType[node.type]}`);
|
|
3080
|
-
return state(node, this._processNode);
|
|
3117
|
+
return state(node, this._processNode, context);
|
|
3081
3118
|
};
|
|
3082
3119
|
};
|
|
3083
3120
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xaendar/compiler",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.29",
|
|
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.29",
|
|
20
|
+
"@xaendar/types": "0.7.29",
|
|
21
21
|
"typescript": "^6.0.3"
|
|
22
22
|
}
|
|
23
23
|
}
|