@xaendar/compiler 0.4.6 → 0.4.10

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.
@@ -2222,10 +2222,11 @@ function processElement(node, nodeName, parentNode, context) {
2222
2222
  `const ${nodeName} = document.createElement("${node.tagName}");`,
2223
2223
  ...node.attributes?.map((attr) => {
2224
2224
  const value = attr.value;
2225
- return typeof value === "string" ? `${nodeName}.setAttribute('${attr.name}', ${value});` : `effect(() => ${nodeName}.setAttribute('${attr.name}', ${resolveExpression(value.expression, context)}));`;
2225
+ return typeof value === "string" ? `${nodeName}.setAttribute('${attr.name}', ${value});` : `unwatchFns.push(effect(() => ${nodeName}.setAttribute('${attr.name}', ${resolveExpression(value.expression, context)})));`;
2226
2226
  }) || [],
2227
2227
  ...node.events?.map((event) => `${nodeName}.addEventListener("${event.name}", ($event) => this.${event.handler});`) || [],
2228
2228
  `${parentNode}.appendChild(${nodeName});`,
2229
+ `unwatchFns.push(() => ${parentNode}.removeChild(${nodeName}));`,
2229
2230
  ...node.children.map((child, i) => processNode(child, i.toString(), nodeName, childrenContext)).flat()
2230
2231
  ];
2231
2232
  }
@@ -2263,6 +2264,8 @@ function processElement(node, nodeName, parentNode, context) {
2263
2264
  * @returns Array of generated code lines.
2264
2265
  */
2265
2266
  function processFor(node, nodeName, parentNode, parentContext) {
2267
+ const mainBlock = new Array();
2268
+ const functionsToProcess = /* @__PURE__ */ new Map();
2266
2269
  const iterableSource = node.iterableSource;
2267
2270
  const iterableExpr = parentContext.getIdentifier(iterableSource) ?? `this.${iterableSource}`;
2268
2271
  const itemsName = getTextIdentifier(parentNode, nodeName, "items");
@@ -2280,13 +2283,23 @@ function processFor(node, nodeName, parentNode, parentContext) {
2280
2283
  evenName,
2281
2284
  oddName
2282
2285
  ], parentContext);
2283
- return [
2284
- `const ${itemsName} = ${iterableExpr};`,
2285
- `for (let ${counterName} = 0; ${counterName} < ${itemsName}.length; ${counterName}++) {`,
2286
- ...indent(`const ${node.itemAlias} = ${itemsName}[${counterName}];`, `const ${indexName} = ${counterName};`, `const ${firstName} = ${counterName} === 0;`, `const ${lastName} = ${counterName} === ${itemsName}.length - 1;`, `const ${evenName} = ${counterName} % 2 === 0;`, `const ${oddName} = ${counterName} % 2 !== 0;`),
2287
- ...node.children.flatMap((child, i) => indent(...processNode(child, `${nodeName}_${i}`, parentNode, forContext))),
2288
- "}"
2289
- ];
2286
+ functionsToProcess.set(`for_${nodeName}`, {
2287
+ code: [
2288
+ `const ${node.itemAlias} = ${itemsName}[${counterName}];`,
2289
+ `const ${indexName} = ${counterName};`,
2290
+ `const ${firstName} = ${counterName} === 0;`,
2291
+ `const ${lastName} = ${counterName} === ${itemsName}.length - 1;`,
2292
+ `const ${evenName} = ${counterName} % 2 === 0;`,
2293
+ `const ${oddName} = !${evenName};`,
2294
+ ...node.children.flatMap((child, i) => indent(...processNode(child, `${nodeName}_${i}`, parentNode, forContext)))
2295
+ ],
2296
+ args: [itemsName, counterName]
2297
+ });
2298
+ mainBlock.push("(() => {", ...indent("let localUnwatchFns = [];", "const unwatch = () => {", ...indent("localUnwatchFns?.forEach(fn => fn());", "localUnwatchFns = [];", "unwatchFns = unwatchFns.filter(fn => !localUnwatchFns.includes(fn));"), "};", "unwatchFns.push(", ...indent("effect(() => {", ...indent("unwatch();", `const ${itemsName} = ${iterableExpr};`, "Signal.subtle.untrack(() => {", ...indent(`for (let ${counterName} = 0; ${counterName} < ${itemsName}.length; ${counterName}++) {`, ...indent(`localUnwatchFns.push(...this.for_${nodeName}(${counterName}));`, "unwatchFns.push(...localUnwatchFns);"), "}"), "});"), "})"), ");"), "})();");
2299
+ return {
2300
+ mainBlock,
2301
+ fns: functionsToProcess
2302
+ };
2290
2303
  }
2291
2304
  /**
2292
2305
  * Resolves the name that should be used in generated code for a given
@@ -2317,27 +2330,39 @@ function resolveImplicit(node, implicit) {
2317
2330
  */
2318
2331
  function processIf(node, nodeName, parentNode, context) {
2319
2332
  const ifContext = new Context([], context);
2320
- const code = [
2321
- `if (${resolveExpression(node.conditionNode, context)}) {`,
2322
- ...processConsequent(node, nodeName, parentNode, ifContext),
2323
- "}"
2324
- ];
2333
+ const functionsToProcess = /* @__PURE__ */ new Map();
2334
+ const mainBlock = new Array();
2335
+ const ifKey = `if_${nodeName}`;
2336
+ mainBlock.push(`if (${resolveExpression(node.conditionNode, context)}) {`, ...indent(`checkAndUpdateState(0, this.${ifKey}.bind(this));`), "}");
2337
+ functionsToProcess.set(ifKey, processConsequent(node, nodeName, parentNode, ifContext));
2325
2338
  let alt = node.alternate;
2339
+ let index = 0;
2326
2340
  while (alt?.type === ASTNodeType.ElseIf) {
2327
2341
  const elseIfContext = new Context([], context);
2328
- code[code.length - 1] += ` else if (${resolveExpression(alt.conditionNode, context)}) {`;
2329
- code.push(...processConsequent(alt, nodeName, parentNode, elseIfContext), "}");
2342
+ const keyElseIf = `elseIf_${nodeName}_${index}`;
2343
+ mainBlock[mainBlock.length - 1] += ` else if (${resolveExpression(alt.conditionNode, context)}) {`;
2344
+ mainBlock.push(...indent(`checkAndUpdateState(${++index}, this.${keyElseIf}.bind(this));`), "}");
2345
+ functionsToProcess.set(keyElseIf, processConsequent(alt, nodeName, parentNode, elseIfContext));
2330
2346
  alt = alt.alternate;
2331
2347
  }
2332
2348
  if (alt) {
2333
2349
  const elseContext = new Context([], context);
2334
- code[code.length - 1] += " else {";
2335
- code.push(...processConsequent(alt, nodeName, parentNode, elseContext), "}");
2350
+ const keyElse = `else_${nodeName}`;
2351
+ mainBlock[mainBlock.length - 1] += " else {";
2352
+ mainBlock.push(...indent(`checkAndUpdateState(${++index}, this.${keyElse}.bind(this));`), "}");
2353
+ functionsToProcess.set(keyElse, processConsequent(alt, nodeName, parentNode, elseContext));
2336
2354
  }
2337
- return code;
2355
+ return {
2356
+ mainBlock: [
2357
+ "(() => {",
2358
+ ...indent("let state;", "let localUnwatchFns = []", "const checkAndUpdateState = (newState, fn) => {", ...indent("if (state === newState) {", ...indent("return;"), "}", "state = newState;", "unwatch();", "localUnwatchFns = Signal.subtle.untrack(fn);", "unwatchFns.push(...localUnwatchFns);"), "};", "const unwatch = () => {", ...indent("localUnwatchFns?.forEach(fn => fn());", "unwatchFns = unwatchFns.filter(fn => !localUnwatchFns.includes(fn));", "localUnwatchFns = [];"), "}", "unwatchFns.push(", ...indent("effect(() => {", ...indent(...mainBlock), "})"), ");"),
2359
+ "})();"
2360
+ ],
2361
+ fns: functionsToProcess
2362
+ };
2338
2363
  }
2339
2364
  function processConsequent(node, nodeName, parentNode, context) {
2340
- return node.consequent.map((child, i) => indent(...processNode(child, `${nodeName}_${i}`, parentNode, context))).flat();
2365
+ return node.consequent.map((child, i) => processNode(child, `${nodeName}_${i}`, parentNode, context)).flat();
2341
2366
  }
2342
2367
  //#endregion
2343
2368
  //#region ../packages/compiler/src/render-generator/states/process-switch.state.ts
@@ -2353,11 +2378,24 @@ function processConsequent(node, nodeName, parentNode, context) {
2353
2378
  * @returns Array of generated code lines.
2354
2379
  */
2355
2380
  function processSwitch(node, nodeName, parentNode, context) {
2356
- return [
2357
- `switch (${resolveExpression(node.expression, context)}) {`,
2358
- ...node.cases.map((caseNode) => [...indent(...!caseNode.condition ? ["default: {"] : caseNode.condition.map((cond, i, arr) => `case ${cond}:${i === arr.length - 1 ? " {" : ""}`), ...caseNode.children.map((child, i) => indent(...processNode(child, `${nodeName}_${i}_${i}`, parentNode, new Context([], context)))).flat(), `${indent("break;")}`, `}`)]).flat(),
2359
- "}"
2360
- ];
2381
+ const mainBlock = new Array();
2382
+ const functionsToProcess = /* @__PURE__ */ new Map();
2383
+ mainBlock.push(`switch (${resolveExpression(node.expression, context)}) {`);
2384
+ node.cases.forEach((caseNode, i) => {
2385
+ const caseContext = new Context([], context);
2386
+ const caseName = caseNode.condition ? `case_${nodeName}_${i}` : `default_${nodeName}`;
2387
+ functionsToProcess.set(caseName, caseNode.children.map((child, i) => processNode(child, `${nodeName}_${i}_${i}`, parentNode, caseContext)).flat());
2388
+ mainBlock.push(...indent(...!caseNode.condition ? ["default: {"] : caseNode.condition.map((cond, i, arr) => `case ${cond}:${i === arr.length - 1 ? " {" : ""}`), ...indent(`localUnwatchFns = Signal.subtle.untrack(this.${caseName}.bind(this));`, "unwatchFns.push(...localUnwatchFns);", "break;"), "}"));
2389
+ });
2390
+ mainBlock.push("}");
2391
+ return {
2392
+ mainBlock: [
2393
+ "(() => {",
2394
+ ...indent("let localUnwatchFns = []", "const checkAndUpdateState = (newState, fn) => {", ...indent("unwatch();", "localUnwatchFns = Signal.subtle.untrack(fn);", "unwatchFns.push(...localUnwatchFns);"), "};", "const unwatch = () => {", ...indent("localUnwatchFns?.forEach(fn => fn());", "unwatchFns = unwatchFns.filter(fn => !localUnwatchFns.includes(fn));", "localUnwatchFns = [];"), "}", "unwatchFns.push(", ...indent("effect(() => {", ...indent("unwatch();", ...mainBlock), "})"), ");"),
2395
+ "})();"
2396
+ ],
2397
+ fns: functionsToProcess
2398
+ };
2361
2399
  }
2362
2400
  //#endregion
2363
2401
  //#region ../packages/compiler/src/render-generator/states/process-text-and-interpolation.state.ts
@@ -2377,7 +2415,7 @@ function processTextAndInterpolation(node, nodeName, parentNode, context) {
2377
2415
  return [
2378
2416
  `const ${nodeName} = document.createTextNode(${textValue});`,
2379
2417
  `${parentNode}.appendChild(${nodeName});`,
2380
- `effect(() => ${nodeName}.textContent = ${textValue});`
2418
+ `unwatchFns.push(effect(() => ${nodeName}.textContent = ${textValue}));`
2381
2419
  ];
2382
2420
  }
2383
2421
  //#endregion
@@ -2392,14 +2430,13 @@ var nodeToProcess = /* @__PURE__ */ new Map();
2392
2430
  function generateRenderFunction(ast, cssVariableName) {
2393
2431
  nodeToProcess.clear();
2394
2432
  const context = new Context();
2395
- const renderFunctions = [
2396
- "_render() {",
2397
- ...indent(...[cssVariableName ? `this._root.adoptedStyleSheets = [${cssVariableName}];` : void 0].filter((line) => !!line), ...ast.map((node, i) => [...processNode(node, i.toString(), ROOT_NODE, context)]).flat()),
2398
- "}"
2399
- ];
2433
+ const renderFunctions = ["_render() {"];
2434
+ if (cssVariableName) renderFunctions.push(` this._root.adoptedStyleSheets = [${cssVariableName}];`);
2435
+ renderFunctions.push(" let unwatchFns = [];", ...indent(...ast.map((node, i) => [...processNode(node, i.toString(), ROOT_NODE, context)]).flat()), " return unwatchFns;", "}");
2400
2436
  while (nodeToProcess.size > 0) {
2401
- const [key, fn] = nodeToProcess.entries().next().value;
2402
- renderFunctions.push(`${key} {`, ...indent(...fn()), "}");
2437
+ const [key, fnData] = nodeToProcess.entries().next().value;
2438
+ if ("args" in fnData) renderFunctions.push(`${key}(${fnData.args.join(", ")}) {`, " let unwatchFns = [];", ...indent(...fnData.fn(...fnData.args)), " return unwatchFns;", "}");
2439
+ else renderFunctions.push(`${key}() {`, " let unwatchFns = [];", ...indent(...fnData.fn()), " return unwatchFns;", "}");
2403
2440
  nodeToProcess.delete(key);
2404
2441
  }
2405
2442
  return renderFunctions.join("\n");
@@ -2415,17 +2452,20 @@ function processNode(node, nodeName, parentNode, context) {
2415
2452
  case ASTNodeType.Interpolation: return processTextAndInterpolation(node, getTextIdentifier(parentNode, nodeName), parentNode, context);
2416
2453
  case ASTNodeType.Element: return processElement(node, getElementIdentifier(node, parentNode, nodeName), parentNode, context);
2417
2454
  case ASTNodeType.If:
2418
- const keyIf = `if_${nodeName}()`;
2419
- nodeToProcess.set(keyIf, () => processIf(node, nodeName, parentNode, context));
2420
- return [`this.${keyIf};`];
2455
+ const conditionalBlockData = processIf(node, nodeName, parentNode, context);
2456
+ conditionalBlockData.fns.forEach((fnBody, key) => nodeToProcess.set(key, { fn: () => fnBody }));
2457
+ return conditionalBlockData.mainBlock;
2421
2458
  case ASTNodeType.For:
2422
- const keyFor = `for_${nodeName}()`;
2423
- nodeToProcess.set(keyFor, () => processFor(node, nodeName, parentNode, context));
2424
- return [`this.${keyFor};`];
2459
+ const forBlockData = processFor(node, nodeName, parentNode, context);
2460
+ forBlockData.fns.forEach((fnBody, key) => nodeToProcess.set(key, {
2461
+ fn: () => fnBody.code,
2462
+ args: fnBody.args
2463
+ }));
2464
+ return forBlockData.mainBlock;
2425
2465
  case ASTNodeType.Switch:
2426
- const keySwitch = `switch_${nodeName}()`;
2427
- nodeToProcess.set(keySwitch, () => processSwitch(node, nodeName, parentNode, context));
2428
- return [`this.${keySwitch};`];
2466
+ const switchBlockData = processSwitch(node, nodeName, parentNode, context);
2467
+ switchBlockData.fns.forEach((fnBody, key) => nodeToProcess.set(key, { fn: () => fnBody }));
2468
+ return switchBlockData.mainBlock;
2429
2469
  case ASTNodeType.ConstDeclaration: return processConstDeclaration(node, nodeName, parentNode, context);
2430
2470
  default: return [];
2431
2471
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xaendar/compiler",
3
- "version": "0.4.6",
3
+ "version": "0.4.10",
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.4.6",
20
- "@xaendar/types": "0.4.6",
19
+ "@xaendar/common": "0.4.10",
20
+ "@xaendar/types": "0.4.10",
21
21
  "typescript": "^6.0.3"
22
22
  }
23
23
  }