@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.
@@ -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
- let actualCompilerContext;
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, i) => {
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
- const lines = [];
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, i) => processNode(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, i) => {
2996
- if (caseNode.condition?.length) caseNode.condition.forEach((conditionValue) => {
2997
- lines.push(` case ${conditionValue}:`);
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, i) => this._processNode(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.27",
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.27",
20
- "@xaendar/types": "0.7.27",
19
+ "@xaendar/common": "0.7.29",
20
+ "@xaendar/types": "0.7.29",
21
21
  "typescript": "^6.0.3"
22
22
  }
23
23
  }