@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.
@@ -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
- return emitNode(expression, expression, compilerContext, mapDefaultOptions(options));
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.hasUnresolvableIdentifier(text) || options.skipResolution) return text;
399
- return resolveIdentifierAccess(text, compilerContext, options.resolver);
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, resolver) {
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 `${resolver}.${text}`;
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, context, index, processNode) {
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, context, { resolver: "root" })};`);
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, context, { resolver: "root" })).join(", ");
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, i.toString()));
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, context, index, processNode) {
2940
- const iterableExpr = context.hasIdentifier(node.iterableSource) ? node.iterableSource : `root.${node.iterableSource}`;
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
- const forContext = new CompilerContext([
2947
- node.itemAlias,
2948
- [indexName, "signal"],
2949
- [firstName, "signal"],
2950
- [lastName, "signal"],
2951
- [evenName, "signal"],
2952
- [oddName, "signal"]
2953
- ], context);
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 ${iterableExpr}) {`);
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, forContext, { skipResolution: true })};`,
2963
- ...node.children.flatMap((child, i) => processNode(child, forContext, i.toString()))
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, context, index, processNode) {
2964
+ function typeCheckIf(node, processNode, context) {
2997
2965
  const lines = [];
2998
- const condition = resolveExpression(node.conditionNode, context, { resolver: "root" });
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, i) => processNode(child, ifContext, i.toString()))));
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, context, { resolver: "root" });
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, i) => processNode(child, elseIfContext, i.toString()))));
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, i) => processNode(child, elseContext, i.toString()))));
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, context, index, processNode) {
3038
- const lines = [`switch (${resolveExpression(node.expression, context, { resolver: "root" })}) {`];
3039
- node.children.forEach((caseNode, i) => {
3040
- const caseContext = new CompilerContext([], context);
3041
- if (caseNode.condition?.length) caseNode.condition.forEach((conditionValue) => {
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, context, index, processNode) {
3060
- return node.type === ASTNodeType.Interpolation ? [`${resolveExpression(node.expression, context, { resolver: "root" })};`] : [];
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 context = new CompilerContext();
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, index) => {
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, context, index, this._processNode);
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.26",
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.26",
20
- "@xaendar/types": "0.7.26",
19
+ "@xaendar/common": "0.7.28",
20
+ "@xaendar/types": "0.7.28",
21
21
  "typescript": "^6.0.3"
22
22
  }
23
23
  }