@barefootjs/jsx 0.33.6 → 0.35.0

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.
Files changed (111) hide show
  1. package/dist/adapters/parsed-expr-emitter.d.ts +17 -0
  2. package/dist/adapters/parsed-expr-emitter.d.ts.map +1 -1
  3. package/dist/analyzer.d.ts.map +1 -1
  4. package/dist/expression-parser.d.ts +17 -4
  5. package/dist/expression-parser.d.ts.map +1 -1
  6. package/dist/index.d.ts +4 -3
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +445 -282
  9. package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
  10. package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts.map +1 -1
  11. package/dist/ir-to-client-js/control-flow/plan/build-component-loop.d.ts.map +1 -1
  12. package/dist/ir-to-client-js/control-flow/plan/build-composite-loop.d.ts.map +1 -1
  13. package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts +11 -0
  14. package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts.map +1 -1
  15. package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
  16. package/dist/ir-to-client-js/control-flow/plan/build-plain-row.d.ts +52 -0
  17. package/dist/ir-to-client-js/control-flow/plan/build-plain-row.d.ts.map +1 -0
  18. package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts +2 -0
  19. package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts.map +1 -1
  20. package/dist/ir-to-client-js/control-flow/plan/lazy-conditional.d.ts +6 -7
  21. package/dist/ir-to-client-js/control-flow/plan/lazy-conditional.d.ts.map +1 -1
  22. package/dist/ir-to-client-js/control-flow/plan/lazy-preamble.d.ts +5 -7
  23. package/dist/ir-to-client-js/control-flow/plan/lazy-preamble.d.ts.map +1 -1
  24. package/dist/ir-to-client-js/control-flow/plan/lazy-row-eligibility.d.ts.map +1 -1
  25. package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +10 -6
  26. package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
  27. package/dist/ir-to-client-js/control-flow/shared.d.ts +11 -9
  28. package/dist/ir-to-client-js/control-flow/shared.d.ts.map +1 -1
  29. package/dist/ir-to-client-js/control-flow/stringify/composite-loop.d.ts.map +1 -1
  30. package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts.map +1 -1
  31. package/dist/ir-to-client-js/csr-substitute.d.ts +16 -0
  32. package/dist/ir-to-client-js/csr-substitute.d.ts.map +1 -1
  33. package/dist/ir-to-client-js/emit-reactive.d.ts.map +1 -1
  34. package/dist/ir-to-client-js/emit-registration.d.ts.map +1 -1
  35. package/dist/ir-to-client-js/html-template.d.ts +2 -2
  36. package/dist/ir-to-client-js/html-template.d.ts.map +1 -1
  37. package/dist/ir-to-client-js/imports.d.ts +2 -2
  38. package/dist/ir-to-client-js/imports.d.ts.map +1 -1
  39. package/dist/ir-to-client-js/index.d.ts.map +1 -1
  40. package/dist/ir-to-client-js/markup-slots.d.ts +21 -0
  41. package/dist/ir-to-client-js/markup-slots.d.ts.map +1 -0
  42. package/dist/ir-to-client-js/safe-html.d.ts +158 -0
  43. package/dist/ir-to-client-js/safe-html.d.ts.map +1 -0
  44. package/dist/ir-to-client-js/utils.d.ts +34 -1
  45. package/dist/ir-to-client-js/utils.d.ts.map +1 -1
  46. package/dist/jsx-to-ir.d.ts.map +1 -1
  47. package/dist/lowering-registry.d.ts +12 -0
  48. package/dist/lowering-registry.d.ts.map +1 -1
  49. package/dist/prop-rewrite.d.ts +8 -3
  50. package/dist/prop-rewrite.d.ts.map +1 -1
  51. package/dist/props-binding.d.ts +22 -1
  52. package/dist/props-binding.d.ts.map +1 -1
  53. package/dist/ssr-defaults.d.ts.map +1 -1
  54. package/dist/types.d.ts +5 -10
  55. package/dist/types.d.ts.map +1 -1
  56. package/package.json +2 -2
  57. package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +10 -12
  58. package/src/__tests__/build-plain-row-consolidation.test.ts +120 -0
  59. package/src/__tests__/client-template-escape-soundness.test.ts +538 -0
  60. package/src/__tests__/csr-template-loop-shadowing.test.ts +4 -2
  61. package/src/__tests__/flatmap-segments.test.ts +1 -1
  62. package/src/__tests__/lazy-conditional.test.ts +52 -12
  63. package/src/__tests__/lazy-preamble.test.ts +11 -11
  64. package/src/__tests__/lazy-row-eligibility.test.ts +8 -3
  65. package/src/__tests__/loop-child-reactive-attr-const-shadow.test.ts +25 -3
  66. package/src/__tests__/loop-item-conditional-codegen.test.ts +4 -2
  67. package/src/__tests__/lowering-value-position.test.ts +232 -0
  68. package/src/__tests__/map-arbitrary-body.test.ts +1 -1
  69. package/src/__tests__/nested-loop-index-param.test.ts +14 -3
  70. package/src/__tests__/nested-loop-reactive-attrs.test.ts +4 -2
  71. package/src/__tests__/reactive-attrs-in-map.test.ts +13 -10
  72. package/src/__tests__/rewrite-destructured-props.test.ts +37 -0
  73. package/src/__tests__/safe-html-splice-door.test.ts +61 -0
  74. package/src/__tests__/ssr-defaults.test.ts +59 -0
  75. package/src/adapters/parsed-expr-emitter.ts +43 -0
  76. package/src/analyzer.ts +4 -0
  77. package/src/expression-parser.ts +63 -29
  78. package/src/index.ts +10 -3
  79. package/src/ir-to-client-js/collect-elements.ts +18 -2
  80. package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +20 -28
  81. package/src/ir-to-client-js/control-flow/plan/build-component-loop.ts +9 -7
  82. package/src/ir-to-client-js/control-flow/plan/build-composite-loop.ts +10 -6
  83. package/src/ir-to-client-js/control-flow/plan/build-inner-loop.ts +8 -8
  84. package/src/ir-to-client-js/control-flow/plan/build-lazy-row.ts +18 -2
  85. package/src/ir-to-client-js/control-flow/plan/build-loop-child-arm.ts +3 -3
  86. package/src/ir-to-client-js/control-flow/plan/build-loop.ts +35 -35
  87. package/src/ir-to-client-js/control-flow/plan/build-plain-row.ts +110 -0
  88. package/src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts +5 -2
  89. package/src/ir-to-client-js/control-flow/plan/lazy-conditional.ts +11 -10
  90. package/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts +11 -13
  91. package/src/ir-to-client-js/control-flow/plan/lazy-row-eligibility.ts +13 -3
  92. package/src/ir-to-client-js/control-flow/plan/loop.ts +10 -6
  93. package/src/ir-to-client-js/control-flow/shared.ts +40 -17
  94. package/src/ir-to-client-js/control-flow/stringify/composite-loop.ts +2 -1
  95. package/src/ir-to-client-js/control-flow/stringify/lazy-row.ts +10 -0
  96. package/src/ir-to-client-js/csr-substitute.ts +29 -0
  97. package/src/ir-to-client-js/emit-reactive.ts +4 -1
  98. package/src/ir-to-client-js/emit-registration.ts +2 -4
  99. package/src/ir-to-client-js/html-template.ts +58 -244
  100. package/src/ir-to-client-js/imports.ts +3 -0
  101. package/src/ir-to-client-js/index.ts +4 -4
  102. package/src/ir-to-client-js/markup-slots.ts +25 -0
  103. package/src/ir-to-client-js/safe-html.ts +243 -0
  104. package/src/ir-to-client-js/utils.ts +58 -10
  105. package/src/jsx-to-ir.ts +115 -20
  106. package/src/lowering-registry.ts +21 -0
  107. package/src/prop-rewrite.ts +8 -4
  108. package/src/props-binding.ts +37 -1
  109. package/src/ssr-defaults.ts +45 -0
  110. package/src/types.ts +5 -10
  111. package/src/__tests__/text-slot-escaping.test.ts +0 -65
package/dist/index.js CHANGED
@@ -184,6 +184,56 @@ import ts10 from "typescript";
184
184
 
185
185
  // src/expression-parser.ts
186
186
  import ts2 from "typescript";
187
+
188
+ // src/lowering-registry.ts
189
+ var plugins = [];
190
+ function registerLoweringPlugin(plugin) {
191
+ const existing = plugins.findIndex((p) => p.name === plugin.name);
192
+ if (existing >= 0)
193
+ plugins[existing] = plugin;
194
+ else
195
+ plugins.push(plugin);
196
+ }
197
+ function getLoweringPlugins() {
198
+ return [...plugins];
199
+ }
200
+ function prepareLoweringMatchers(metadata) {
201
+ const matchers = [];
202
+ for (const plugin of plugins) {
203
+ const matcher = plugin.prepare(metadata);
204
+ if (matcher)
205
+ matchers.push(matcher);
206
+ }
207
+ return matchers;
208
+ }
209
+ function matchLoweringCall(callee, args, metadata) {
210
+ for (const matcher of prepareLoweringMatchers(metadata)) {
211
+ const node = matcher(callee, args);
212
+ if (node)
213
+ return node;
214
+ }
215
+ return null;
216
+ }
217
+ function loweringNodeChildren(node) {
218
+ if (node.kind === "helper-call")
219
+ return [...node.args];
220
+ const children = [node.base];
221
+ for (const t of node.triples) {
222
+ if (t.guard)
223
+ children.push(t.guard);
224
+ children.push(t.value);
225
+ }
226
+ return children;
227
+ }
228
+ function isValidHelperId(helper) {
229
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(helper);
230
+ }
231
+ function __resetLoweringPluginsForTest(next = []) {
232
+ plugins.length = 0;
233
+ plugins.push(...next);
234
+ }
235
+
236
+ // src/expression-parser.ts
187
237
  var PARSED_EXPR_KINDS = [
188
238
  "identifier",
189
239
  "literal",
@@ -526,7 +576,7 @@ function convertNode(node, raw) {
526
576
  }
527
577
  if (n === undefined || Number.isNaN(n)) {
528
578
  const parsedDepth = convertNode(depthNode, raw);
529
- if (checkSupport(parsedDepth, "rendered").supported) {
579
+ if (checkSupport(parsedDepth, "rendered", []).supported) {
530
580
  depthExpr = parsedDepth;
531
581
  flatDepth = 1;
532
582
  } else {
@@ -1304,13 +1354,13 @@ function getUnaryOperatorString(op) {
1304
1354
  return "unknown";
1305
1355
  }
1306
1356
  }
1307
- function isSupported(expr) {
1308
- return checkSupport(expr, "rendered");
1357
+ function isSupported(expr, opts) {
1358
+ return checkSupport(expr, "rendered", opts?.loweringMatchers ?? []);
1309
1359
  }
1310
- function isSupportedValue(expr) {
1311
- return checkSupport(expr, "value");
1360
+ function isSupportedValue(expr, opts) {
1361
+ return checkSupport(expr, "value", opts?.loweringMatchers ?? []);
1312
1362
  }
1313
- function checkSupport(expr, pos) {
1363
+ function checkSupport(expr, pos, matchers) {
1314
1364
  switch (expr.kind) {
1315
1365
  case "unsupported":
1316
1366
  return { supported: false, reason: expr.reason };
@@ -1319,7 +1369,7 @@ function checkSupport(expr, pos) {
1319
1369
  return { supported: false, reason: "Unsupported syntax: ObjectLiteralExpression" };
1320
1370
  }
1321
1371
  for (const prop of expr.properties) {
1322
- const propSupport = checkSupport(prop.kind === "spread" ? prop.expr : prop.value, pos);
1372
+ const propSupport = checkSupport(prop.kind === "spread" ? prop.expr : prop.value, pos, matchers);
1323
1373
  if (!propSupport.supported)
1324
1374
  return propSupport;
1325
1375
  }
@@ -1334,7 +1384,7 @@ function checkSupport(expr, pos) {
1334
1384
  return { supported: false, reason: "Standalone arrow functions / regex literals are not supported" };
1335
1385
  case "array-literal": {
1336
1386
  for (const el of expr.elements) {
1337
- const elSupport = checkSupport(el, pos);
1387
+ const elSupport = checkSupport(el, pos, matchers);
1338
1388
  if (!elSupport.supported)
1339
1389
  return elSupport;
1340
1390
  }
@@ -1347,28 +1397,39 @@ function checkSupport(expr, pos) {
1347
1397
  reason: `String.prototype.${expr.method} supports only a string pattern + string replacement (the regex form is deferred); use a string pattern or wrap the expression in /* @client */`
1348
1398
  };
1349
1399
  }
1350
- const objSupport = checkSupport(expr.object, pos);
1400
+ const objSupport = checkSupport(expr.object, pos, matchers);
1351
1401
  if (!objSupport.supported)
1352
1402
  return objSupport;
1353
1403
  for (const arg of expr.args) {
1354
- const argSupport = checkSupport(arg, pos);
1404
+ const argSupport = checkSupport(arg, pos, matchers);
1355
1405
  if (!argSupport.supported)
1356
1406
  return argSupport;
1357
1407
  }
1358
1408
  if (expr.method === "flat" && expr.depthExpr) {
1359
- const depthSupport = checkSupport(expr.depthExpr, pos);
1409
+ const depthSupport = checkSupport(expr.depthExpr, pos, matchers);
1360
1410
  if (!depthSupport.supported)
1361
1411
  return depthSupport;
1362
1412
  }
1363
1413
  return { supported: true, level: "L2" };
1364
1414
  }
1365
1415
  case "call": {
1416
+ for (const matcher of matchers) {
1417
+ const node = matcher(expr.callee, expr.args);
1418
+ if (!node)
1419
+ continue;
1420
+ for (const child of loweringNodeChildren(node)) {
1421
+ const childSupport = checkSupport(child, pos, matchers);
1422
+ if (!childSupport.supported)
1423
+ return childSupport;
1424
+ }
1425
+ return { supported: true, level: "L2" };
1426
+ }
1366
1427
  const cb = asCallbackMethodCall(expr);
1367
1428
  if (cb) {
1368
- const objSupport = checkSupport(cb.object, pos);
1429
+ const objSupport = checkSupport(cb.object, pos, matchers);
1369
1430
  if (!objSupport.supported)
1370
1431
  return objSupport;
1371
- const bodySupport = checkSupport(cb.arrow.body, pos);
1432
+ const bodySupport = checkSupport(cb.arrow.body, pos, matchers);
1372
1433
  if (!bodySupport.supported) {
1373
1434
  return {
1374
1435
  supported: false,
@@ -1377,13 +1438,13 @@ function checkSupport(expr, pos) {
1377
1438
  };
1378
1439
  }
1379
1440
  for (const rest of cb.args) {
1380
- const restSupport = checkSupport(rest, pos);
1441
+ const restSupport = checkSupport(rest, pos, matchers);
1381
1442
  if (!restSupport.supported)
1382
1443
  return restSupport;
1383
1444
  }
1384
1445
  return { supported: true, level: "L5" };
1385
1446
  }
1386
- const calleeSupport = checkSupport(expr.callee, pos);
1447
+ const calleeSupport = checkSupport(expr.callee, pos, matchers);
1387
1448
  if (!calleeSupport.supported) {
1388
1449
  return calleeSupport;
1389
1450
  }
@@ -1402,7 +1463,7 @@ function checkSupport(expr, pos) {
1402
1463
  return { supported: true, level: "L1" };
1403
1464
  }
1404
1465
  for (const arg of expr.args) {
1405
- const argSupport = checkSupport(arg, pos);
1466
+ const argSupport = checkSupport(arg, pos, matchers);
1406
1467
  if (!argSupport.supported) {
1407
1468
  return argSupport;
1408
1469
  }
@@ -1410,7 +1471,7 @@ function checkSupport(expr, pos) {
1410
1471
  return { supported: true, level: "L2" };
1411
1472
  }
1412
1473
  case "member": {
1413
- const objSupport = checkSupport(expr.object, pos);
1474
+ const objSupport = checkSupport(expr.object, pos, matchers);
1414
1475
  if (!objSupport.supported) {
1415
1476
  return objSupport;
1416
1477
  }
@@ -1420,19 +1481,19 @@ function checkSupport(expr, pos) {
1420
1481
  return { supported: true, level: "L2" };
1421
1482
  }
1422
1483
  case "index-access": {
1423
- const objSupport = checkSupport(expr.object, pos);
1484
+ const objSupport = checkSupport(expr.object, pos, matchers);
1424
1485
  if (!objSupport.supported)
1425
1486
  return objSupport;
1426
- const indexSupport = checkSupport(expr.index, pos);
1487
+ const indexSupport = checkSupport(expr.index, pos, matchers);
1427
1488
  if (!indexSupport.supported)
1428
1489
  return indexSupport;
1429
1490
  return { supported: true, level: "L2" };
1430
1491
  }
1431
1492
  case "binary": {
1432
- const leftSupport = checkSupport(expr.left, pos);
1493
+ const leftSupport = checkSupport(expr.left, pos, matchers);
1433
1494
  if (!leftSupport.supported)
1434
1495
  return leftSupport;
1435
- const rightSupport = checkSupport(expr.right, pos);
1496
+ const rightSupport = checkSupport(expr.right, pos, matchers);
1436
1497
  if (!rightSupport.supported)
1437
1498
  return rightSupport;
1438
1499
  if (["===", "==", "!==", "!=", ">", "<", ">=", "<="].includes(expr.op)) {
@@ -1444,7 +1505,7 @@ function checkSupport(expr, pos) {
1444
1505
  return { supported: false, reason: `Unknown operator: ${expr.op}` };
1445
1506
  }
1446
1507
  case "unary": {
1447
- const argSupport = checkSupport(expr.argument, pos);
1508
+ const argSupport = checkSupport(expr.argument, pos, matchers);
1448
1509
  if (!argSupport.supported)
1449
1510
  return argSupport;
1450
1511
  if (expr.op === "!") {
@@ -1456,25 +1517,25 @@ function checkSupport(expr, pos) {
1456
1517
  return { supported: false, reason: `Unsupported unary operator: ${expr.op}` };
1457
1518
  }
1458
1519
  case "logical": {
1459
- const leftSupport = checkSupport(expr.left, pos);
1520
+ const leftSupport = checkSupport(expr.left, pos, matchers);
1460
1521
  if (!leftSupport.supported)
1461
1522
  return leftSupport;
1462
1523
  if (expr.op === "??" && expr.right.kind === "object-literal" && expr.right.properties.length === 0) {
1463
1524
  return { supported: true, level: "L4" };
1464
1525
  }
1465
- const rightSupport = checkSupport(expr.right, pos);
1526
+ const rightSupport = checkSupport(expr.right, pos, matchers);
1466
1527
  if (!rightSupport.supported)
1467
1528
  return rightSupport;
1468
1529
  return { supported: true, level: "L4" };
1469
1530
  }
1470
1531
  case "conditional": {
1471
- const testSupport = checkSupport(expr.test, pos);
1532
+ const testSupport = checkSupport(expr.test, pos, matchers);
1472
1533
  if (!testSupport.supported)
1473
1534
  return testSupport;
1474
- const consSupport = checkSupport(expr.consequent, pos);
1535
+ const consSupport = checkSupport(expr.consequent, pos, matchers);
1475
1536
  if (!consSupport.supported)
1476
1537
  return consSupport;
1477
- const altSupport = checkSupport(expr.alternate, pos);
1538
+ const altSupport = checkSupport(expr.alternate, pos, matchers);
1478
1539
  if (!altSupport.supported)
1479
1540
  return altSupport;
1480
1541
  return { supported: true, level: "L4" };
@@ -1482,7 +1543,7 @@ function checkSupport(expr, pos) {
1482
1543
  case "template-literal": {
1483
1544
  for (const part of expr.parts) {
1484
1545
  if (part.type === "expression") {
1485
- const partSupport = checkSupport(part.expr, pos);
1546
+ const partSupport = checkSupport(part.expr, pos, matchers);
1486
1547
  if (!partSupport.supported)
1487
1548
  return partSupport;
1488
1549
  }
@@ -2628,12 +2689,22 @@ function renderLoopBindingAccess(b, base) {
2628
2689
  }
2629
2690
  return parent;
2630
2691
  }
2631
- function wrapLoopParamAsAccessor(expr, paramName, bindings) {
2692
+ function wrapLoopParamAsAccessor(expr, paramName, bindings, indexParam) {
2693
+ let result;
2632
2694
  if (bindings && bindings.length > 0) {
2633
- return rewriteLoopBindingRefs(expr, bindings, "__bfItem()");
2695
+ result = rewriteLoopBindingRefs(expr, bindings, "__bfItem()");
2696
+ } else {
2697
+ const re = new RegExp(`${ID_BOUNDARY_BEFORE}${escapeIdentifierForRegex(paramName)}(?!\\s*\\()(?!-)${ID_BOUNDARY_AFTER}`, "gu");
2698
+ result = replaceInExprContexts(expr, re, () => `${paramName}()`);
2634
2699
  }
2635
- const re = new RegExp(`${ID_BOUNDARY_BEFORE}${escapeIdentifierForRegex(paramName)}(?!\\s*\\()(?!-)${ID_BOUNDARY_AFTER}`, "gu");
2636
- return replaceInExprContexts(expr, re, () => `${paramName}()`);
2700
+ if (indexParam && indexParam !== paramName) {
2701
+ result = wrapIndexParamAsAccessor(result, indexParam);
2702
+ }
2703
+ return result;
2704
+ }
2705
+ function wrapIndexParamAsAccessor(expr, indexParam) {
2706
+ const re = new RegExp(`${ID_BOUNDARY_BEFORE}${escapeIdentifierForRegex(indexParam)}(?!\\s*\\()(?!-)${ID_BOUNDARY_AFTER}`, "gu");
2707
+ return replaceInExprContexts(expr, re, () => `${indexParam}()`);
2637
2708
  }
2638
2709
  function rewriteLoopBindingRefs(expr, bindings, accessor) {
2639
2710
  const byName = new Map;
@@ -2680,7 +2751,7 @@ function wrapExprWithLoopParams(expr, loopParams) {
2680
2751
  let result = expr;
2681
2752
  for (const p of loopParams) {
2682
2753
  const spec = typeof p === "string" ? { param: p } : p;
2683
- result = wrapLoopParamAsAccessor(result, spec.param, spec.bindings);
2754
+ result = wrapLoopParamAsAccessor(result, spec.param, spec.bindings, spec.index);
2684
2755
  }
2685
2756
  return result;
2686
2757
  }
@@ -2967,6 +3038,20 @@ function buildPropAliasMap(params) {
2967
3038
  }
2968
3039
  return map;
2969
3040
  }
3041
+ function resolveBodyDestructuredPropAliases(localConstants, propsObjectName) {
3042
+ const aliases = new Map;
3043
+ if (propsObjectName === null)
3044
+ return aliases;
3045
+ for (const c of localConstants) {
3046
+ if (c.isModule)
3047
+ continue;
3048
+ const m = c.parsed;
3049
+ if (m?.kind === "member" && !m.computed && m.object.kind === "identifier" && m.object.name === propsObjectName) {
3050
+ aliases.set(c.name, m.property);
3051
+ }
3052
+ }
3053
+ return aliases;
3054
+ }
2970
3055
  function boundPropLocalNames(b) {
2971
3056
  if (b.propsObjectName !== null)
2972
3057
  return EMPTY_SET;
@@ -3199,6 +3284,18 @@ function resolveGetterAliases(localConstants, isGetter) {
3199
3284
  }
3200
3285
  return aliases;
3201
3286
  }
3287
+ function collectAliasableGetterNames(signals, memos) {
3288
+ const getterNames = new Set;
3289
+ for (const sig of signals) {
3290
+ if (sig.getter && !sig.isModule && !sig.envReader)
3291
+ getterNames.add(sig.getter);
3292
+ }
3293
+ for (const memo of memos) {
3294
+ if (!memo.isModule)
3295
+ getterNames.add(memo.name);
3296
+ }
3297
+ return getterNames;
3298
+ }
3202
3299
  function buildSignalMemoEnv(signals, memos, propsObjectName, localConstants = []) {
3203
3300
  const substitutions = new Map;
3204
3301
  for (const s of signals) {
@@ -3378,6 +3475,65 @@ class BindingScope {
3378
3475
  }
3379
3476
  }
3380
3477
 
3478
+ // src/ir-to-client-js/safe-html.ts
3479
+ function safeHtml(expr) {
3480
+ return expr;
3481
+ }
3482
+ function interp(span) {
3483
+ return `\${${span}}`;
3484
+ }
3485
+ function escapedText(expr) {
3486
+ return safeHtml(`escapeText(${expr})`);
3487
+ }
3488
+ function escapedTextOrMarkup(expr) {
3489
+ return safeHtml(`escapeTextOrMarkup(${expr})`);
3490
+ }
3491
+ function branchSlotValue(expr, slotsVar) {
3492
+ return safeHtml(`__bfSlot(${expr}, ${slotsVar})`);
3493
+ }
3494
+ function childrenMarkup(expr) {
3495
+ return safeHtml(`markupOrEmpty(${expr})`);
3496
+ }
3497
+ function joinedMarkup(expr) {
3498
+ return safeHtml(`Array.isArray(${expr}) ? ${expr}.join('') : (${expr} ?? '')`);
3499
+ }
3500
+ function renderChildCall(registryName, propsExpr, tailArgs) {
3501
+ return safeHtml(`renderChild('${registryName}', ${propsExpr}${tailArgs})`);
3502
+ }
3503
+ function dangerousInnerHtml(expr) {
3504
+ return safeHtml(`((${expr}) ?? {}).__html ?? ''`);
3505
+ }
3506
+ function conditionalMarkup(condition, whenTrue, whenFalse) {
3507
+ return safeHtml(`${condition} ? \`${whenTrue}\` : \`${whenFalse}\``);
3508
+ }
3509
+ function mappedRowsMarkup(arrayExpr, method, params, body) {
3510
+ return safeHtml(`${arrayExpr}.${method}(${params} => ${body}).join('')`);
3511
+ }
3512
+ var EMPTY_MARKUP = safeHtml("''");
3513
+ function isChildrenPassthroughExpr(expr) {
3514
+ return /^([A-Za-z_$][\w$]*\.)?children$/.test(expr.trim());
3515
+ }
3516
+ function spliceChildValue(node, valueExpr, cx) {
3517
+ if (node.joinArrayChild)
3518
+ return joinedMarkup(valueExpr);
3519
+ if (cx.branchSlotsVar)
3520
+ return branchSlotValue(valueExpr, cx.branchSlotsVar);
3521
+ if (node.slotId) {
3522
+ return cx.markupSlotIds?.has(node.slotId) ? escapedTextOrMarkup(valueExpr) : escapedText(valueExpr);
3523
+ }
3524
+ const resolved = valueExpr.trim().replace(/^\(+|\)+$/g, "");
3525
+ if (isChildrenPassthroughExpr(node.expr) || isChildrenPassthroughExpr(resolved)) {
3526
+ return childrenMarkup(valueExpr);
3527
+ }
3528
+ return escapedText(valueExpr);
3529
+ }
3530
+
3531
+ // src/ir-to-client-js/markup-slots.ts
3532
+ var DYNAMIC_ELEMENT_WRITER_KIND = "markup";
3533
+ function markupSlotIdsOf(ctx) {
3534
+ return new Set(ctx.dynamicElements.map((e) => e.slotId));
3535
+ }
3536
+
3381
3537
  // src/ir-to-client-js/html-template.ts
3382
3538
  function createStringProtector() {
3383
3539
  const strings = [];
@@ -3547,22 +3703,11 @@ function templateAttrExpr(attrName, valExpr, presenceOrUndefined) {
3547
3703
  function escapeAttrValueExpr(valExpr) {
3548
3704
  return `escapeAttr(${valExpr})`;
3549
3705
  }
3550
- function escapeTextSlotExpr(innerExpr, isMarkup = false) {
3551
- return `${isMarkup ? "escapeTextOrMarkup" : "escapeText"}(${innerExpr})`;
3552
- }
3553
- function isChildrenPassthroughExpr(expr) {
3554
- return /^([A-Za-z_$][\w$]*\.)?children$/.test(expr.trim());
3555
- }
3556
- function bareSpliceExpr(node, valueExpr) {
3557
- const resolved = valueExpr.trim().replace(/^\(+|\)+$/g, "");
3558
- const isChildren = isChildrenPassthroughExpr(node.expr) || isChildrenPassthroughExpr(resolved);
3559
- return !node.joinArrayChild && isChildren ? `markupOrEmpty(${valueExpr})` : valueExpr;
3560
- }
3561
3706
  function dangerouslyHtmlChildren(attrs, toExpr) {
3562
3707
  const attr = attrs.find((a) => a.name === "dangerouslySetInnerHTML");
3563
3708
  if (!attr || attr.value.kind !== "expression")
3564
3709
  return null;
3565
- return `\${((${toExpr(attr.value)}) ?? {}).__html ?? ''}`;
3710
+ return interp(dangerousInnerHtml(toExpr(attr.value)));
3566
3711
  }
3567
3712
  function transformKeyValue(value, transformExpr) {
3568
3713
  switch (value.kind) {
@@ -3678,7 +3823,7 @@ function buildSpreadAttrsMergeCall(args) {
3678
3823
  return `\${spreadAttrs({${objMembers.join(", ")}})}`;
3679
3824
  }
3680
3825
  function itemAnchorTemplate(keyExpr) {
3681
- return `<!--${loopItemMarker("${" + keyExpr + "}")}-->`;
3826
+ return `<!--${loopItemMarker("${escapeCommentText(" + keyExpr + ")}")}-->`;
3682
3827
  }
3683
3828
  function renderPreamble(preamble, opts) {
3684
3829
  let out = "";
@@ -3687,9 +3832,9 @@ function renderPreamble(preamble, opts) {
3687
3832
  const text = opts.textVariant === "template" ? seg.templateText ?? seg.text : seg.text;
3688
3833
  out += opts.transformJs ? opts.transformJs(text) : text;
3689
3834
  } else if (opts.rawLeaf) {
3690
- out += opts.renderLeaf(escapeLeafTextExpressions(seg.ir));
3835
+ out += opts.renderLeaf(seg.ir);
3691
3836
  } else {
3692
- out += "`" + opts.renderLeaf(escapeLeafTextExpressions(seg.ir)) + "`";
3837
+ out += "`" + opts.renderLeaf(seg.ir) + "`";
3693
3838
  }
3694
3839
  }
3695
3840
  return out;
@@ -3734,36 +3879,12 @@ function renderFlatMapProjectionClientBody(inner, restSpreadNames) {
3734
3879
  const chained = applyLoopChain(inner);
3735
3880
  const params = inner.index ? `(${inner.param}, ${inner.index})` : `(${inner.param})`;
3736
3881
  const key = inner.key ? `(${inner.key})` : "undefined";
3737
- const html = inner.children.map((c) => irToHtmlTemplate(escapeLeafTextExpressions(c), restSpreadNames, 1, undefined, undefined)).join("");
3882
+ const html = inner.children.map((c) => irToHtmlTemplate(c, restSpreadNames, 1, undefined, undefined)).join("");
3738
3883
  return `${chained}.map(${params} => ({ k: ${key}, h: \`${html}\` }))`;
3739
3884
  }
3740
- function escapeLeafTextExpressions(ir) {
3741
- switch (ir.type) {
3742
- case "element":
3743
- return { ...ir, children: ir.children.map(escapeLeafTextExpressions) };
3744
- case "fragment":
3745
- return { ...ir, children: ir.children.map(escapeLeafTextExpressions) };
3746
- case "expression": {
3747
- if (ir.expr === "null" || ir.expr === "undefined")
3748
- return ir;
3749
- if (ir.slotId || ir.expr.trimStart().startsWith("escapeText("))
3750
- return ir;
3751
- return { ...ir, expr: `escapeText((${ir.expr}))`, templateExpr: ir.templateExpr ? `escapeText((${ir.templateExpr}))` : ir.templateExpr };
3752
- }
3753
- case "conditional":
3754
- return {
3755
- ...ir,
3756
- whenTrue: escapeLeafTextExpressions(ir.whenTrue),
3757
- whenFalse: ir.whenFalse ? escapeLeafTextExpressions(ir.whenFalse) : ir.whenFalse
3758
- };
3759
- default:
3760
- return ir;
3761
- }
3762
- }
3763
3885
  function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, branchSlotsVar, inHoistedChildren = false) {
3764
3886
  const recurse = (n) => irToHtmlTemplate(n, restSpreadNames, loopDepth, loopParams, branchSlotsVar, inHoistedChildren);
3765
3887
  const wrapExpr = (expr) => wrapExprWithLoopParams(expr, loopParams);
3766
- const wrapInterpolation = (expr) => branchSlotsVar ? `__bfSlot(${expr}, ${branchSlotsVar})` : expr;
3767
3888
  switch (node.type) {
3768
3889
  case "element": {
3769
3890
  const mergeCtx = {
@@ -3809,25 +3930,17 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
3809
3930
  case "expression": {
3810
3931
  if (node.expr === "null" || node.expr === "undefined")
3811
3932
  return "";
3812
- const escapeForClient = (e) => node.escapeInClientTemplate ? `escapeText(${e})` : e;
3813
- if (node.markerless) {
3814
- const bare = escapeForClient(wrapInterpolation(wrapExpr(node.expr)));
3815
- return `\${${bare}}`;
3816
- }
3817
- const inner = escapeForClient(wrapInterpolation(wrapExpr(node.expr)));
3818
- const valueExpr = node.joinArrayChild ? `Array.isArray(${inner}) ? ${inner}.join('') : (${inner} ?? '')` : inner;
3819
- if (node.slotId) {
3820
- const slotted = branchSlotsVar || node.joinArrayChild ? valueExpr : escapeTextSlotExpr(valueExpr);
3821
- return `<!--bf:${node.slotId}-->\${${slotted}}<!--/-->`;
3822
- }
3823
- return `\${${bareSpliceExpr(node, valueExpr)}}`;
3933
+ const hole = interp(spliceChildValue(node, wrapExpr(node.expr), { branchSlotsVar }));
3934
+ if (node.markerless)
3935
+ return hole;
3936
+ return node.slotId ? `<!--bf:${node.slotId}-->${hole}<!--/-->` : hole;
3824
3937
  }
3825
3938
  case "conditional": {
3826
3939
  const trueBranch = recurse(node.whenTrue);
3827
3940
  const falseBranch = recurse(node.whenFalse);
3828
3941
  const trueHtml = node.slotId ? addCondAttrToTemplate(trueBranch, node.slotId) : trueBranch;
3829
3942
  const falseHtml = node.slotId ? addCondAttrToTemplate(falseBranch, node.slotId) : falseBranch;
3830
- return `\${${wrapExpr(node.condition)} ? \`${trueHtml}\` : \`${falseHtml}\`}`;
3943
+ return interp(conditionalMarkup(wrapExpr(node.condition), trueHtml, falseHtml));
3831
3944
  }
3832
3945
  case "fragment":
3833
3946
  return node.children.map(recurse).join("");
@@ -3864,7 +3977,7 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
3864
3977
  const propsExpr = propsEntries.length > 0 ? `{${propsEntries.join(", ")}}` : "{}";
3865
3978
  const keyProp = node.props.find((p) => p.name === "key");
3866
3979
  const keyArg = keyProp ? `, ${attrValueToString(keyProp.value) ?? "undefined"}` : "";
3867
- return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${renderChildScopeArgs(node, keyArg)})}`;
3980
+ return interp(renderChildCall(nameForRegistryRef(node.name), propsExpr, renderChildScopeArgs(node, keyArg)));
3868
3981
  }
3869
3982
  case "loop": {
3870
3983
  const innerRecurse = (n) => irToHtmlTemplate(n, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar);
@@ -3882,12 +3995,12 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
3882
3995
  const body = renderPreamble(node.flatMapCallback, {
3883
3996
  renderLeaf: (ir) => irToHtmlTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar)
3884
3997
  });
3885
- mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
3998
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, "flatMap", node.flatMapCallback.params, body));
3886
3999
  } else if (node.preamble) {
3887
4000
  const preamble = renderPreamble(node.preamble, { textVariant: "client", renderLeaf: (ir) => irToHtmlTemplate(ir, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar) });
3888
- mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => { ${preamble} return \`${childTemplate}\` }).join('')}`;
4001
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, iterMethod, callbackParam, `{ ${preamble} return \`${childTemplate}\` }`));
3889
4002
  } else {
3890
- mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => \`${childTemplate}\`).join('')}`;
4003
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, iterMethod, callbackParam, `\`${childTemplate}\``));
3891
4004
  }
3892
4005
  return `<!--${loopStartMarker(node.markerId)}-->${mapExpr}<!--${loopEndMarker(node.markerId)}-->`;
3893
4006
  }
@@ -4146,20 +4259,15 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
4146
4259
  case "expression": {
4147
4260
  if (node.expr === "null" || node.expr === "undefined")
4148
4261
  return "";
4149
- const wrapped = wrapExpr(node.expr);
4150
- const value = node.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : wrapped;
4151
- if (node.slotId) {
4152
- return `<!--bf:${node.slotId}-->\${${node.joinArrayChild ? value : escapeTextSlotExpr(wrapped)}}<!--/-->`;
4153
- }
4154
- const spliced = bareSpliceExpr(node, value);
4155
- return `\${${node.escapeInClientTemplate ? `escapeText(${spliced})` : spliced}}`;
4262
+ const hole = interp(spliceChildValue(node, wrapExpr(node.expr), {}));
4263
+ return node.slotId ? `<!--bf:${node.slotId}-->${hole}<!--/-->` : hole;
4156
4264
  }
4157
4265
  case "conditional": {
4158
4266
  const trueBranch = recurse(node.whenTrue);
4159
4267
  const falseBranch = recurse(node.whenFalse);
4160
4268
  const trueHtml = node.slotId ? addCondAttrToTemplate(trueBranch, node.slotId) : trueBranch;
4161
4269
  const falseHtml = node.slotId ? addCondAttrToTemplate(falseBranch, node.slotId) : falseBranch;
4162
- return `\${${wrapExpr(node.condition)} ? \`${trueHtml}\` : \`${falseHtml}\`}`;
4270
+ return interp(conditionalMarkup(wrapExpr(node.condition), trueHtml, falseHtml));
4163
4271
  }
4164
4272
  case "fragment":
4165
4273
  return node.children.map(recurse).join("");
@@ -4183,12 +4291,12 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
4183
4291
  const body = renderPreamble(node.flatMapCallback, {
4184
4292
  renderLeaf: (ir) => irToPlaceholderTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams)
4185
4293
  });
4186
- mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
4294
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, "flatMap", node.flatMapCallback.params, body));
4187
4295
  } else if (node.preamble) {
4188
4296
  const preamble = renderPreamble(node.preamble, { textVariant: "client", renderLeaf: (ir) => irToPlaceholderTemplate(ir, restSpreadNames, loopDepth + 1, loopParams) });
4189
- mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => { ${preamble} return \`${childTemplate}\` }).join('')}`;
4297
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, iterMethod, callbackParam, `{ ${preamble} return \`${childTemplate}\` }`));
4190
4298
  } else {
4191
- mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => \`${childTemplate}\`).join('')}`;
4299
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, iterMethod, callbackParam, `\`${childTemplate}\``));
4192
4300
  }
4193
4301
  return `<!--${loopStartMarker(node.markerId)}-->${mapExpr}<!--${loopEndMarker(node.markerId)}-->`;
4194
4302
  }
@@ -4390,13 +4498,8 @@ function irToComponentTemplateWithOpts(node, opts) {
4390
4498
  return "";
4391
4499
  return `<!--bf:${node.slotId}--><!--/-->`;
4392
4500
  }
4393
- const wrapped = transformExpr(node.expr, node.templateExpr);
4394
- const value = node.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : wrapped;
4395
- if (node.slotId) {
4396
- const isMarkup = opts.markupSlotIds?.has(node.slotId) ?? false;
4397
- return `<!--bf:${node.slotId}-->\${${node.joinArrayChild ? value : escapeTextSlotExpr(wrapped, isMarkup)}}<!--/-->`;
4398
- }
4399
- return `\${${bareSpliceExpr(node, value)}}`;
4501
+ const hole = interp(spliceChildValue(node, transformExpr(node.expr, node.templateExpr), { markupSlotIds: opts.markupSlotIds }));
4502
+ return node.slotId ? `<!--bf:${node.slotId}-->${hole}<!--/-->` : hole;
4400
4503
  }
4401
4504
  case "conditional": {
4402
4505
  if (node.clientOnly && node.slotId) {
@@ -4406,7 +4509,7 @@ function irToComponentTemplateWithOpts(node, opts) {
4406
4509
  const falseBranch = recurse(node.whenFalse);
4407
4510
  const trueHtml = node.slotId ? addCondAttrToTemplate(trueBranch, node.slotId) : trueBranch;
4408
4511
  const falseHtml = node.slotId ? addCondAttrToTemplate(falseBranch, node.slotId) : falseBranch;
4409
- return `\${${transformExpr(node.condition, node.templateCondition)} ? \`${trueHtml}\` : \`${falseHtml}\`}`;
4512
+ return interp(conditionalMarkup(transformExpr(node.condition, node.templateCondition), trueHtml, falseHtml));
4410
4513
  }
4411
4514
  case "fragment":
4412
4515
  return node.children.map(recurse).join("");
@@ -4444,7 +4547,7 @@ function irToComponentTemplateWithOpts(node, opts) {
4444
4547
  const propsExpr = propsEntries.length > 0 ? `{${propsEntries.join(", ")}}` : "{}";
4445
4548
  const keyProp = node.props.find((p) => p.name === "key");
4446
4549
  const keyArg = keyProp ? `, ${transformKeyValue(keyProp.value, transformExpr)}` : "";
4447
- return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${keyArg})}`;
4550
+ return interp(renderChildCall(nameForRegistryRef(node.name), propsExpr, keyArg));
4448
4551
  }
4449
4552
  case "loop": {
4450
4553
  const innerOpts = { ...opts, loopDepth: loopDepth + 1 };
@@ -4454,7 +4557,7 @@ function irToComponentTemplateWithOpts(node, opts) {
4454
4557
  case "if-statement": {
4455
4558
  const consequent = recurse(node.consequent);
4456
4559
  const alternate = node.alternate ? recurse(node.alternate) : "";
4457
- return `\${${transformExpr(node.condition, node.templateCondition)} ? \`${consequent}\` : \`${alternate}\`}`;
4560
+ return interp(conditionalMarkup(transformExpr(node.condition, node.templateCondition), consequent, alternate));
4458
4561
  }
4459
4562
  case "provider":
4460
4563
  case "async":
@@ -4542,7 +4645,7 @@ function generateCsrTemplate(node, inlinableConstants, ctx, restSpreadNames, pro
4542
4645
  }
4543
4646
  }
4544
4647
  const effectiveUnsafeLocalNames = mergeCsrNullUnsafe(ctx, unsafeLocalNames);
4545
- const markupSlotIds = new Set(ctx.dynamicElements.map((e) => e.slotId));
4648
+ const markupSlotIds = markupSlotIdsOf(ctx);
4546
4649
  return generateCsrTemplateWithOpts(node, { inlinableConstants, restSpreadNames, propsObjectName, csrEnv, unsafeLocalNames: effectiveUnsafeLocalNames, deferredChildSlots, loopDepth: -1, markupSlotIds, restPropsName: ctx.restPropsName });
4547
4650
  }
4548
4651
  function mergeCsrNullUnsafe(ctx, unsafeLocalNames) {
@@ -4741,13 +4844,8 @@ function generateCsrTemplateWithOpts(node, opts) {
4741
4844
  }
4742
4845
  {
4743
4846
  const transformed = transformExpr(node.expr, node.templateExpr);
4744
- const expr = transformed === UNSAFE_TEMPLATE_EXPR ? "''" : transformed;
4745
- const value = node.joinArrayChild ? `Array.isArray(${expr}) ? ${expr}.join('') : (${expr} ?? '')` : expr;
4746
- if (node.slotId) {
4747
- const isMarkup = opts.markupSlotIds?.has(node.slotId) ?? false;
4748
- return `<!--bf:${node.slotId}-->\${${node.joinArrayChild ? value : escapeTextSlotExpr(expr, isMarkup)}}<!--/-->`;
4749
- }
4750
- return `\${${bareSpliceExpr(node, value)}}`;
4847
+ const hole = interp(transformed === UNSAFE_TEMPLATE_EXPR ? EMPTY_MARKUP : spliceChildValue(node, transformed, { markupSlotIds: opts.markupSlotIds }));
4848
+ return node.slotId ? `<!--bf:${node.slotId}-->${hole}<!--/-->` : hole;
4751
4849
  }
4752
4850
  case "conditional": {
4753
4851
  if (node.clientOnly && node.slotId) {
@@ -4757,7 +4855,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4757
4855
  const falseBranch = recurse(node.whenFalse);
4758
4856
  const trueHtml = node.slotId ? addCondAttrToTemplate(trueBranch, node.slotId) : trueBranch;
4759
4857
  const falseHtml = node.slotId ? addCondAttrToTemplate(falseBranch, node.slotId) : falseBranch;
4760
- return `\${${transformExpr(node.condition, node.templateCondition)} ? \`${trueHtml}\` : \`${falseHtml}\`}`;
4858
+ return interp(conditionalMarkup(transformExpr(node.condition, node.templateCondition), trueHtml, falseHtml));
4761
4859
  }
4762
4860
  case "fragment":
4763
4861
  return node.children.map(recurse).join("");
@@ -4806,7 +4904,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4806
4904
  const propsExpr = propsEntries.length > 0 ? `{${propsEntries.join(", ")}}` : "{}";
4807
4905
  const keyProp = node.props.find((p) => p.name === "key");
4808
4906
  const keyArg = keyProp ? `, ${transformKeyValue(keyProp.value, transformExpr)}` : "";
4809
- return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${renderChildScopeArgs(node, keyArg)})}`;
4907
+ return interp(renderChildCall(nameForRegistryRef(node.name), propsExpr, renderChildScopeArgs(node, keyArg)));
4810
4908
  }
4811
4909
  case "loop": {
4812
4910
  const childScope = (opts.scope ?? BindingScope.EMPTY).enterLoopRow(node);
@@ -4839,19 +4937,19 @@ function generateCsrTemplateWithOpts(node, opts) {
4839
4937
  transformJs: (t) => rewritePropsObjectRef(t, propsObjectName ?? null, restPropsName ?? null, { enclosingScope: childScope }),
4840
4938
  renderLeaf: (ir) => recurseInLoopBody(stripLeafKeyAttr(ir))
4841
4939
  });
4842
- mapExpr = `\${${iterArrayExpr}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
4940
+ mapExpr = interp(mappedRowsMarkup(iterArrayExpr, "flatMap", node.flatMapCallback.params, body));
4843
4941
  } else if (node.preamble) {
4844
4942
  const preamble = renderPreamble(node.preamble, { textVariant: "template", transformJs: (t) => rewritePropsObjectRef(t, propsObjectName ?? null, restPropsName ?? null, { enclosingScope: childScope }), renderLeaf: (ir) => recurseInLoopBody(ir) });
4845
- mapExpr = `\${${iterArrayExpr}.${iterMethod}(${callbackParam} => { ${preamble} return \`${childTemplate}\` }).join('')}`;
4943
+ mapExpr = interp(mappedRowsMarkup(iterArrayExpr, iterMethod, callbackParam, `{ ${preamble} return \`${childTemplate}\` }`));
4846
4944
  } else {
4847
- mapExpr = `\${${iterArrayExpr}.${iterMethod}(${callbackParam} => \`${childTemplate}\`).join('')}`;
4945
+ mapExpr = interp(mappedRowsMarkup(iterArrayExpr, iterMethod, callbackParam, `\`${childTemplate}\``));
4848
4946
  }
4849
4947
  return `<!--${loopStartMarker(node.markerId)}-->${mapExpr}<!--${loopEndMarker(node.markerId)}-->`;
4850
4948
  }
4851
4949
  case "if-statement": {
4852
4950
  const consequent = recurse(node.consequent);
4853
4951
  const alternate = node.alternate ? recurse(node.alternate) : "";
4854
- return `\${${transformExpr(node.condition, node.templateCondition)} ? \`${consequent}\` : \`${alternate}\`}`;
4952
+ return interp(conditionalMarkup(transformExpr(node.condition, node.templateCondition), consequent, alternate));
4855
4953
  }
4856
4954
  case "provider":
4857
4955
  case "async":
@@ -4952,8 +5050,6 @@ function collectAstPropRefs(node, propNames, out) {
4952
5050
  walkWithScope(node, (n, parent, shadowed) => {
4953
5051
  if (shadowed || !propNames.has(n.text))
4954
5052
  return;
4955
- if (parent && ts7.isShorthandPropertyAssignment(parent) && parent.name === n)
4956
- return;
4957
5053
  if (isNonValuePosition(n, parent))
4958
5054
  return;
4959
5055
  out.add(n.text);
@@ -7179,6 +7275,7 @@ var CLIENT_EXPORTS = new Set([
7179
7275
  "isSSRPortal",
7180
7276
  "findSiblingSlot",
7181
7277
  "cleanupPortalPlaceholder",
7278
+ "trackPosition",
7182
7279
  "createSearchParams",
7183
7280
  "queryHref",
7184
7281
  "formatDate",
@@ -8498,7 +8595,8 @@ var BROWSER_ONLY_CLIENT_APIS = new Set([
8498
8595
  "createPortal",
8499
8596
  "isSSRPortal",
8500
8597
  "findSiblingSlot",
8501
- "cleanupPortalPlaceholder"
8598
+ "cleanupPortalPlaceholder",
8599
+ "trackPosition"
8502
8600
  ]);
8503
8601
  function importsBrowserOnlyClientApi(ctx) {
8504
8602
  for (const imp of ctx.imports) {
@@ -10734,9 +10832,8 @@ function collectBranchLocalPropRefsViaSubstitution(node, ctx) {
10734
10832
  function visit2(n, parent) {
10735
10833
  if (ts14.isIdentifier(n) && propDepsMap.has(n.text)) {
10736
10834
  const isObjectKey = parent && ts14.isPropertyAssignment(parent) && parent.name === n;
10737
- const isShorthand = parent && ts14.isShorthandPropertyAssignment(parent) && parent.name === n;
10738
10835
  const isAccessName = parent && ts14.isPropertyAccessExpression(parent) && parent.name === n;
10739
- if (!isObjectKey && !isShorthand && !isAccessName) {
10836
+ if (!isObjectKey && !isAccessName) {
10740
10837
  const deps = propDepsMap.get(n.text);
10741
10838
  if (deps && deps.size > 0) {
10742
10839
  if (!acc)
@@ -11214,8 +11311,7 @@ function lowerFormControlValueSsr(tagName, attrs, children) {
11214
11311
  children.push({
11215
11312
  type: "expression",
11216
11313
  expr,
11217
- templateExpr: `escapeText(${templateExpr ?? expr})`,
11218
- escapeInClientTemplate: true,
11314
+ templateExpr,
11219
11315
  typeInfo: null,
11220
11316
  reactive: false,
11221
11317
  slotId: null,
@@ -11226,26 +11322,76 @@ function lowerFormControlValueSsr(tagName, attrs, children) {
11226
11322
  }
11227
11323
  const selectedForLiteral = (optValue) => AttrValueOf.expression(`(${expr}) === ${JSON.stringify(optValue)}`, templateExpr !== undefined ? { templateExpr: `(${templateExpr}) === ${JSON.stringify(optValue)}` } : undefined);
11228
11324
  const selectedForExpr = (optExpr, optTemplateExpr) => AttrValueOf.expression(`(${expr}) === (${optExpr})`, templateExpr !== undefined || optTemplateExpr !== undefined ? { templateExpr: `(${templateExpr ?? expr}) === (${optTemplateExpr ?? optExpr})` } : undefined);
11325
+ const matchConditions = [];
11326
+ let optionSetIsDynamic = false;
11229
11327
  const distribute = (nodes) => {
11230
11328
  for (const n of nodes) {
11329
+ if (n.type === "text")
11330
+ continue;
11231
11331
  if (n.type === "element" && n.tag === "option") {
11232
- if (n.attrs.some((a) => a.name === "selected"))
11332
+ if (n.attrs.some((a) => a.name === "selected")) {
11333
+ optionSetIsDynamic = true;
11233
11334
  continue;
11335
+ }
11234
11336
  const optValue = n.attrs.find((a) => a.name === "value");
11235
- if (!optValue)
11337
+ if (!optValue) {
11338
+ optionSetIsDynamic = true;
11236
11339
  continue;
11340
+ }
11237
11341
  if (optValue.value.kind === "literal") {
11238
- n.attrs.push({ name: "selected", value: selectedForLiteral(optValue.value.value), loc: n.loc });
11342
+ const selected = selectedForLiteral(optValue.value.value);
11343
+ n.attrs.push({ name: "selected", value: selected, loc: n.loc });
11344
+ matchConditions.push(selected);
11239
11345
  } else if (optValue.value.kind === "expression") {
11240
11346
  const selected = selectedForExpr(optValue.value.expr, optValue.value.templateExpr);
11241
11347
  n.attrs.push({ name: "selected", value: selected, loc: n.loc });
11348
+ matchConditions.push(selected);
11349
+ } else {
11350
+ optionSetIsDynamic = true;
11242
11351
  }
11243
- } else if (n.type === "fragment" || n.type === "loop" || n.type === "element" && n.tag === "optgroup") {
11352
+ } else if (n.type === "fragment" || n.type === "element" && n.tag === "optgroup") {
11244
11353
  distribute(n.children);
11354
+ } else if (n.type === "loop") {
11355
+ optionSetIsDynamic = true;
11356
+ distribute(n.children);
11357
+ } else {
11358
+ optionSetIsDynamic = true;
11245
11359
  }
11246
11360
  }
11247
11361
  };
11248
11362
  distribute(children);
11363
+ if (optionSetIsDynamic || matchConditions.length === 0)
11364
+ return;
11365
+ if (isMultiSelection(attrs))
11366
+ return;
11367
+ const orExpr = matchConditions.map((c) => `(${c.expr})`).join(" || ");
11368
+ const orTemplateExpr = matchConditions.some((c) => c.templateExpr !== undefined) ? matchConditions.map((c) => `(${c.templateExpr ?? c.expr})`).join(" || ") : undefined;
11369
+ children.unshift({
11370
+ type: "element",
11371
+ tag: "option",
11372
+ attrs: [
11373
+ { name: "value", value: AttrValueOf.literal(""), loc: valueAttr.loc },
11374
+ { name: "disabled", value: AttrValueOf.booleanAttr(), loc: valueAttr.loc },
11375
+ { name: "hidden", value: AttrValueOf.booleanAttr(), loc: valueAttr.loc },
11376
+ {
11377
+ name: "selected",
11378
+ value: AttrValueOf.expression(`!(${orExpr})`, orTemplateExpr !== undefined ? { templateExpr: `!(${orTemplateExpr})` } : undefined),
11379
+ loc: valueAttr.loc
11380
+ }
11381
+ ],
11382
+ events: [],
11383
+ ref: null,
11384
+ children: [],
11385
+ slotId: null,
11386
+ needsScope: false,
11387
+ loc: valueAttr.loc
11388
+ });
11389
+ }
11390
+ function isMultiSelection(attrs) {
11391
+ if (attrs.some((a) => a.name === "multiple"))
11392
+ return true;
11393
+ const sizeAttr = attrs.find((a) => a.name === "size");
11394
+ return sizeAttr?.value.kind === "literal" && Number(sizeAttr.value.value) > 1;
11249
11395
  }
11250
11396
  function transformHtmlElement(node, ctx, tagName) {
11251
11397
  const { attrs, events, ref } = processAttributes(node.openingElement.attributes, ctx);
@@ -15608,7 +15754,7 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx, options)
15608
15754
  },
15609
15755
  loop: ({ node: n, scope, descend }) => {
15610
15756
  const emitDepth = fixedDepth ?? scope.depth + 1;
15611
- const loopParamsForTemplate = outerLoopParam ? [outerLoopParam, { param: n.param, bindings: n.paramBindings }] : undefined;
15757
+ const loopParamsForTemplate = outerLoopParam ? [outerLoopParam, { param: n.param, bindings: n.paramBindings, index: n.index }] : undefined;
15612
15758
  const template = n.children.map((c) => irToPlaceholderTemplate(c, undefined, emitDepth, loopParamsForTemplate)).join("");
15613
15759
  const refsOuter = outerLoopParam ? identifierPattern(outerLoopParam).test(n.array) : false;
15614
15760
  const bindings = emptyLoopChildBindings();
@@ -16205,7 +16351,7 @@ function collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loop
16205
16351
  const readsPreamble = preambleNames !== undefined && preambleNames.size > 0 && anyNameIn(expanded.freeIds ?? extractFreeIdentifiersFromText(expanded.expr), preambleNames);
16206
16352
  if (!readsPreamble && classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind === "none")
16207
16353
  return;
16208
- const loopParamsForCond = loopParam ? [{ param: loopParam, bindings: loopParamBindings }] : undefined;
16354
+ const loopParamsForCond = loopParam ? [{ param: loopParam, bindings: loopParamBindings, index: loopIndex }] : undefined;
16209
16355
  const whenTrueHtml = irToHtmlTemplate(n.whenTrue, undefined, 0, loopParamsForCond, "__slots");
16210
16356
  const whenFalseHtml = irToHtmlTemplate(n.whenFalse, undefined, 0, loopParamsForCond, "__slots");
16211
16357
  conditionals.push({
@@ -16789,6 +16935,7 @@ var RUNTIME_IMPORT_CANDIDATES = [
16789
16935
  "escapeAttr",
16790
16936
  "escapeText",
16791
16937
  "escapeTextOrNode",
16938
+ "escapeCommentText",
16792
16939
  "bfMarkup",
16793
16940
  "escapeTextOrMarkup",
16794
16941
  "markupOrEmpty",
@@ -17001,45 +17148,6 @@ function collectComponentNames(node) {
17001
17148
 
17002
17149
  // src/relocate.ts
17003
17150
  import ts18 from "typescript";
17004
-
17005
- // src/lowering-registry.ts
17006
- var plugins = [];
17007
- function registerLoweringPlugin(plugin) {
17008
- const existing = plugins.findIndex((p) => p.name === plugin.name);
17009
- if (existing >= 0)
17010
- plugins[existing] = plugin;
17011
- else
17012
- plugins.push(plugin);
17013
- }
17014
- function getLoweringPlugins() {
17015
- return [...plugins];
17016
- }
17017
- function prepareLoweringMatchers(metadata) {
17018
- const matchers = [];
17019
- for (const plugin of plugins) {
17020
- const matcher = plugin.prepare(metadata);
17021
- if (matcher)
17022
- matchers.push(matcher);
17023
- }
17024
- return matchers;
17025
- }
17026
- function matchLoweringCall(callee, args, metadata) {
17027
- for (const matcher of prepareLoweringMatchers(metadata)) {
17028
- const node = matcher(callee, args);
17029
- if (node)
17030
- return node;
17031
- }
17032
- return null;
17033
- }
17034
- function isValidHelperId(helper) {
17035
- return /^[A-Za-z_][A-Za-z0-9_]*$/.test(helper);
17036
- }
17037
- function __resetLoweringPluginsForTest(next = []) {
17038
- plugins.length = 0;
17039
- plugins.push(...next);
17040
- }
17041
-
17042
- // src/relocate.ts
17043
17151
  function classify(name, env) {
17044
17152
  return env.bindings.get(name) ?? "global";
17045
17153
  }
@@ -17887,7 +17995,7 @@ function emitRegistrationAndHydration(lines, ctx, _ir, graph, inlinability) {
17887
17995
  const isCommentScope = isFragmentRoot || _ir.root.type === "component";
17888
17996
  const defParts = [`init: init${name}`];
17889
17997
  if (canGenerateStaticTemplate(_ir.root, propNamesForStaticCheck, inlinableConstants, unsafeLocalNames)) {
17890
- const markupSlotIds = new Set(ctx.dynamicElements.map((e) => e.slotId));
17998
+ const markupSlotIds = markupSlotIdsOf(ctx);
17891
17999
  const templateHtml = irToComponentTemplate(_ir.root, inlinableConstants, restSpreadNames, ctx.propsObjectName, markupSlotIds, ctx.restPropsName);
17892
18000
  if (templateHtml) {
17893
18001
  defParts.push(buildTemplateDefPart(ctx, templateHtml));
@@ -18912,12 +19020,12 @@ function nestedLoopIndexAlias(inner, syntheticIndexVar, paramHead, comps, events
18912
19020
  return null;
18913
19021
  return `const ${index} = ${syntheticIndexVar}`;
18914
19022
  }
18915
- function buildChildRefBindings(refs, loopParam, loopParamBindings) {
19023
+ function buildChildRefBindings(refs, loopParam, loopParamBindings, loopIndex) {
18916
19024
  if (refs.length === 0)
18917
19025
  return [];
18918
19026
  return refs.map((r) => ({
18919
19027
  childSlotId: r.childSlotId,
18920
- callback: wrapLoopParamAsAccessor(r.callback, loopParam, loopParamBindings)
19028
+ callback: wrapLoopParamAsAccessor(r.callback, loopParam, loopParamBindings, loopIndex)
18921
19029
  }));
18922
19030
  }
18923
19031
  function buildStaticChildRefBindings(refs) {
@@ -18950,17 +19058,17 @@ function destructureLoopParam(param, paramBindings) {
18950
19058
  }
18951
19059
  return { head: param, unwrap: "" };
18952
19060
  }
18953
- function buildPreambleRegionPlans(regions, loopParam, loopParamBindings) {
19061
+ function buildPreambleRegionPlans(regions, loopParam, loopParamBindings, loopIndex) {
18954
19062
  if (!regions || regions.length === 0)
18955
19063
  return [];
18956
19064
  return regions.map((r) => {
18957
- const wrapped = wrapLoopParamAsAccessor(r.expr, loopParam, loopParamBindings);
18958
- const valueExpr = r.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : `escapeText(${wrapped})`;
19065
+ const wrapped = wrapLoopParamAsAccessor(r.expr, loopParam, loopParamBindings, loopIndex);
19066
+ const valueExpr = spliceChildValue({ expr: r.expr, slotId: r.slotId, joinArrayChild: r.joinArrayChild }, wrapped, {});
18959
19067
  return { slotId: r.slotId, valueExpr };
18960
19068
  });
18961
19069
  }
18962
- function buildComponentPropsExpr2(comp, loopParam, loopParamBindings) {
18963
- const wrap = loopParam ? (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings) : (expr) => expr;
19070
+ function buildComponentPropsExpr2(comp, loopParam, loopParamBindings, loopIndex) {
19071
+ const wrap = loopParam ? (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings, loopIndex) : (expr) => expr;
18964
19072
  const entries = comp.props.map((p) => {
18965
19073
  if (p.isEventHandler) {
18966
19074
  const handlerExpr = attrValueToString(p.value) ?? "undefined";
@@ -19004,8 +19112,8 @@ function buildDepthLevels(innerLoops, nestedComps, childEvents) {
19004
19112
  loopInfo: loop
19005
19113
  }));
19006
19114
  }
19007
- function emitEventSetup(ls, indent, elVar, ev, loopParam, loopParamBindings, bodyIsMultiRoot = false) {
19008
- const handler = loopParam ? wrapLoopParamAsAccessor(ev.handler, loopParam, loopParamBindings) : ev.handler;
19115
+ function emitEventSetup(ls, indent, elVar, ev, loopParam, loopParamBindings, bodyIsMultiRoot = false, loopIndex) {
19116
+ const handler = loopParam ? wrapLoopParamAsAccessor(ev.handler, loopParam, loopParamBindings, loopIndex) : ev.handler;
19009
19117
  emitListenerBlock(ls, indent, elVar, ev.childSlotId, "__e", ev.eventName, handler, "dom", bodyIsMultiRoot);
19010
19118
  }
19011
19119
  function buildCompSelector(comp) {
@@ -19017,15 +19125,15 @@ function isTextOnlyConditional(node) {
19017
19125
  const checkNode = (n) => n.type === "text" || n.type === "expression" || n.type === "conditional" && isTextOnlyConditional(n);
19018
19126
  return checkNode(node.whenTrue) && checkNode(node.whenFalse);
19019
19127
  }
19020
- function emitComponentAndEventSetup(ls, indent, elVar, comps, events, loopParam, loopParamBindings, bodyIsMultiRoot = false) {
19021
- const wrap = loopParam ? (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings) : (expr) => expr;
19128
+ function emitComponentAndEventSetup(ls, indent, elVar, comps, events, loopParam, loopParamBindings, bodyIsMultiRoot = false, loopIndex) {
19129
+ const wrap = loopParam ? (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings, loopIndex) : (expr) => expr;
19022
19130
  const upsertFn = bodyIsMultiRoot ? "upsertChildItem" : "upsertChild";
19023
19131
  for (const comp of comps) {
19024
- const propsExpr = buildComponentPropsExpr2(comp, loopParam, loopParamBindings);
19132
+ const propsExpr = buildComponentPropsExpr2(comp, loopParam, loopParamBindings, loopIndex);
19025
19133
  const isTextOnly = comp.children?.length ? comp.children.every((c) => c.type === "expression" || c.type === "text" || isTextOnlyConditional(c)) : false;
19026
19134
  const rawChildrenExpr = isTextOnly ? irChildrenToJsExpr(comp.children) : null;
19027
19135
  const childrenFreeIds = isTextOnly && comp.children ? irChildrenFreeIds(comp.children) : undefined;
19028
- const childrenRefsLoop = loopParam != null && rawChildrenExpr != null && childrenFreeIds != null && exprRefsLoopBinding(childrenFreeIds, { param: loopParam, paramBindings: loopParamBindings });
19136
+ const childrenRefsLoop = loopParam != null && rawChildrenExpr != null && childrenFreeIds != null && (exprRefsLoopBinding(childrenFreeIds, { param: loopParam, paramBindings: loopParamBindings }) || !!loopIndex && childrenFreeIds.has(loopIndex));
19029
19137
  const slotIdLit = comp.slotId ? `'${comp.slotId}'` : "null";
19030
19138
  const keyProp = comp.props.find((p) => p.name === "key");
19031
19139
  const keyArg = keyProp ? `, ${wrap(attrValueToString(keyProp.value) ?? "undefined")}` : ", undefined";
@@ -19038,7 +19146,7 @@ function emitComponentAndEventSetup(ls, indent, elVar, comps, events, loopParam,
19038
19146
  }
19039
19147
  }
19040
19148
  for (const ev of events) {
19041
- emitEventSetup(ls, indent, elVar, ev, loopParam, loopParamBindings, bodyIsMultiRoot);
19149
+ emitEventSetup(ls, indent, elVar, ev, loopParam, loopParamBindings, bodyIsMultiRoot, loopIndex);
19042
19150
  }
19043
19151
  }
19044
19152
 
@@ -19419,12 +19527,12 @@ function buildBranchInnerLoopsPlan(args) {
19419
19527
  const inner = innerLoops[i];
19420
19528
  if (!inner.refsOuterParam || !inner.template)
19421
19529
  continue;
19422
- const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings);
19423
- const wrapBoth = (expr) => wrapLoopParamAsAccessor(wrapOuter(expr), inner.param, inner.paramBindings);
19530
+ const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings, inner.index);
19531
+ const wrapBoth = (expr) => wrapLoopParamAsAccessor(wrapOuter(expr), inner.param, inner.paramBindings, inner.index);
19424
19532
  const csl = inner.containerSlotId;
19425
19533
  const containerExpr = csl ? `(${scopeVar}.querySelector('[bf="${csl}"]') ?? ${scopeVar}.querySelector(\`[${BF_HOST2}="\${__scopeId}"][${BF_AT2}="${csl}"]\`) ?? ${scopeVar})` : `findCondContainer(${scopeVar}, '${condSlotId}')`;
19426
19534
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(inner.param, inner.paramBindings);
19427
- const wrappedKey = inner.key ? wrapLoopParamAsAccessor(inner.key, inner.param, inner.paramBindings) : null;
19535
+ const wrappedKey = inner.key ? wrapLoopParamAsAccessor(inner.key, inner.param, inner.paramBindings, inner.index) : null;
19428
19536
  const wrapIRNode = (node) => {
19429
19537
  if (node.type === "component") {
19430
19538
  return {
@@ -19586,8 +19694,8 @@ function buildLoopChildArmPlan(args) {
19586
19694
 
19587
19695
  // src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts
19588
19696
  function buildReactiveEffectsPlan(args) {
19589
- const { attrs, texts, conditionals, loopParam, loopParamBindings, profileComponentName } = args;
19590
- const wrap = (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings);
19697
+ const { attrs, texts, conditionals, loopParam, loopParamBindings, loopIndex, profileComponentName } = args;
19698
+ const wrap = (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings, loopIndex);
19591
19699
  const attrsBySlot = new Map;
19592
19700
  for (const attr of attrs) {
19593
19701
  let bucket = attrsBySlot.get(attr.childSlotId);
@@ -19671,6 +19779,7 @@ function buildLoopReactiveEffectsPlan(elem, profileComponentName) {
19671
19779
  conditionals: elem.bindings.conditionals,
19672
19780
  loopParam: elem.param,
19673
19781
  loopParamBindings: elem.paramBindings,
19782
+ loopIndex: elem.index,
19674
19783
  profileComponentName
19675
19784
  });
19676
19785
  }
@@ -19749,10 +19858,10 @@ function buildInnerLoopsPlan(args) {
19749
19858
  return plan;
19750
19859
  }
19751
19860
  function buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, outerLoopParamBindings) {
19752
- const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings);
19753
- const wrapBoth = (expr) => wrapLoopParamAsAccessor(wrapOuter(expr), inner.param, inner.paramBindings);
19861
+ const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings, inner.index);
19862
+ const wrapBoth = (expr) => wrapLoopParamAsAccessor(wrapOuter(expr), inner.param, inner.paramBindings, inner.index);
19754
19863
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(inner.param, inner.paramBindings);
19755
- const wrappedKey = inner.key ? wrapLoopParamAsAccessor(inner.key, inner.param, inner.paramBindings) : null;
19864
+ const wrappedKey = inner.key ? wrapLoopParamAsAccessor(inner.key, inner.param, inner.paramBindings, inner.index) : null;
19756
19865
  const wrapIRNode = (node) => {
19757
19866
  if (node.type === "component") {
19758
19867
  return {
@@ -19783,11 +19892,11 @@ function buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, o
19783
19892
  }));
19784
19893
  const reactiveTexts = inner.bindings.reactiveTexts.map((text) => ({
19785
19894
  slotId: text.slotId,
19786
- wrappedExpression: wrapLoopParamAsAccessor(wrapOuter(text.expression), inner.param, inner.paramBindings),
19895
+ wrappedExpression: wrapLoopParamAsAccessor(wrapOuter(text.expression), inner.param, inner.paramBindings, inner.index),
19787
19896
  insideConditional: !!text.insideConditional
19788
19897
  }));
19789
19898
  const reactiveAttrs = inner.bindings.reactiveAttrs.map((attr) => {
19790
- const wrapped = wrapLoopParamAsAccessor(wrapOuter(attr.expression), inner.param, inner.paramBindings);
19899
+ const wrapped = wrapLoopParamAsAccessor(wrapOuter(attr.expression), inner.param, inner.paramBindings, inner.index);
19791
19900
  return {
19792
19901
  slotId: attr.childSlotId,
19793
19902
  attrName: attr.attrName,
@@ -19804,14 +19913,14 @@ function buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, o
19804
19913
  if (inner.preamble) {
19805
19914
  const leafLoopParams = outerLoopParam ? [
19806
19915
  { param: outerLoopParam, bindings: outerLoopParamBindings },
19807
- { param: inner.param, bindings: inner.paramBindings }
19808
- ] : [{ param: inner.param, bindings: inner.paramBindings }];
19916
+ { param: inner.param, bindings: inner.paramBindings, index: inner.index }
19917
+ ] : [{ param: inner.param, bindings: inner.paramBindings, index: inner.index }];
19809
19918
  preludeStatements.push(renderPreamble(inner.preamble, {
19810
19919
  transformJs: (t) => wrapInner(wrapOuter(t)),
19811
19920
  renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, leafLoopParams, undefined)
19812
19921
  }));
19813
19922
  }
19814
- const childRefs = buildChildRefBindings(inner.bindings.refs, inner.param, inner.paramBindings);
19923
+ const childRefs = buildChildRefBindings(inner.bindings.refs, inner.param, inner.paramBindings, inner.index);
19815
19924
  const conditionals = buildLoopChildConditionalsPlan({
19816
19925
  conditionals: inner.bindings.conditionals,
19817
19926
  scopeVar: `__innerEl${uidSuffix}`,
@@ -19860,7 +19969,7 @@ function buildTopLevelCompositePlan(elem, profileComponentName) {
19860
19969
  const nestedComps = elem.nestedComponents;
19861
19970
  const depthLevels = buildDepthLevels(elem.innerLoops ?? [], nestedComps, elem.bindings.events);
19862
19971
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
19863
- const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings);
19972
+ const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings, elem.index);
19864
19973
  const outerCompsByDepth = nestedComps.filter((c) => !c.loopDepth || c.loopDepth === 0);
19865
19974
  return {
19866
19975
  kind: "composite",
@@ -19874,12 +19983,12 @@ function buildTopLevelCompositePlan(elem, profileComponentName) {
19874
19983
  indexParam: elem.index || "__idx",
19875
19984
  mapPreambleWrapped: elem.preamble ? renderPreamble(elem.preamble, {
19876
19985
  transformJs: wrap,
19877
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined)
19986
+ renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings, index: elem.index }], undefined)
19878
19987
  }) : "",
19879
19988
  template: elem.template,
19880
19989
  outerComps: filterCondCompsOut(outerCompsByDepth, elem.bindings.conditionals),
19881
19990
  outerEvents: elem.bindings.events.filter((ev) => ev.nestedLoops.length === 0),
19882
- childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
19991
+ childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings, elem.index),
19883
19992
  innerLoops: buildInnerLoopsPlan({
19884
19993
  levels: depthLevels,
19885
19994
  parentElVar: "__el",
@@ -19888,12 +19997,14 @@ function buildTopLevelCompositePlan(elem, profileComponentName) {
19888
19997
  }),
19889
19998
  loopParam: elem.param,
19890
19999
  loopParamBindings: elem.paramBindings,
20000
+ loopIndex: elem.index,
19891
20001
  reactiveEffects: hasReactive(elem) ? buildReactiveEffectsPlan({
19892
20002
  attrs: elem.bindings.reactiveAttrs,
19893
20003
  texts: elem.bindings.reactiveTexts,
19894
20004
  conditionals: elem.bindings.conditionals,
19895
20005
  loopParam: elem.param,
19896
20006
  loopParamBindings: elem.paramBindings,
20007
+ loopIndex: elem.index,
19897
20008
  profileComponentName
19898
20009
  }) : null,
19899
20010
  branchClearChildren: false,
@@ -19910,7 +20021,7 @@ function buildBranchCompositePlan(loop, cv, profileComponentName) {
19910
20021
  const childEvents = loop.bindings.events;
19911
20022
  const depthLevels = buildDepthLevels(innerLoops, nestedComps, childEvents);
19912
20023
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(loop.param, loop.paramBindings);
19913
- const wrap = (expr) => wrapLoopParamAsAccessor(expr, loop.param, loop.paramBindings);
20024
+ const wrap = (expr) => wrapLoopParamAsAccessor(expr, loop.param, loop.paramBindings, loop.index);
19914
20025
  const outerCompsByDepth = nestedComps.filter((c) => !c.loopDepth || c.loopDepth === 0);
19915
20026
  return {
19916
20027
  kind: "composite",
@@ -19924,12 +20035,12 @@ function buildBranchCompositePlan(loop, cv, profileComponentName) {
19924
20035
  indexParam: loop.index || "__idx",
19925
20036
  mapPreambleWrapped: loop.preamble ? renderPreamble(loop.preamble, {
19926
20037
  transformJs: wrap,
19927
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings }], undefined)
20038
+ renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings, index: loop.index }], undefined)
19928
20039
  }) : "",
19929
20040
  template: loop.template,
19930
20041
  outerComps: filterCondCompsOut(outerCompsByDepth, loop.bindings.conditionals),
19931
20042
  outerEvents: childEvents.filter((ev) => ev.nestedLoops.length === 0),
19932
- childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings),
20043
+ childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings, loop.index),
19933
20044
  innerLoops: buildInnerLoopsPlan({
19934
20045
  levels: depthLevels,
19935
20046
  parentElVar: "__el",
@@ -19938,12 +20049,14 @@ function buildBranchCompositePlan(loop, cv, profileComponentName) {
19938
20049
  }),
19939
20050
  loopParam: loop.param,
19940
20051
  loopParamBindings: loop.paramBindings,
20052
+ loopIndex: loop.index,
19941
20053
  reactiveEffects: hasReactiveBranch(loop) ? buildReactiveEffectsPlan({
19942
20054
  attrs: loop.bindings.reactiveAttrs,
19943
20055
  texts: loop.bindings.reactiveTexts,
19944
20056
  conditionals: loop.bindings.conditionals,
19945
20057
  loopParam: loop.param,
19946
20058
  loopParamBindings: loop.paramBindings,
20059
+ loopIndex: loop.index,
19947
20060
  profileComponentName
19948
20061
  }) : null,
19949
20062
  branchClearChildren: true,
@@ -20078,7 +20191,7 @@ function wiringOn(branch) {
20078
20191
  found.push("reactive text");
20079
20192
  return found;
20080
20193
  }
20081
- function analyzeLazyConditional(cond, indexParam, arms) {
20194
+ function analyzeLazyConditional(cond, arms) {
20082
20195
  for (const [label, branch] of [["true", cond.whenTrue], ["false", cond.whenFalse]]) {
20083
20196
  const wiring = wiringOn(branch);
20084
20197
  if (wiring.length > 0) {
@@ -20099,9 +20212,6 @@ function analyzeLazyConditional(cond, indexParam, arms) {
20099
20212
  if (!cond.conditionFreeIdentifiers) {
20100
20213
  return NO(`conditional on slot ${cond.slotId}: condition has no analyzable identifier set`);
20101
20214
  }
20102
- if (cond.conditionFreeIdentifiers.has(indexParam)) {
20103
- return NO(`conditional on slot ${cond.slotId}: condition reads the loop index parameter '${indexParam}'`);
20104
- }
20105
20215
  return {
20106
20216
  lazySafe: true,
20107
20217
  facts: {
@@ -20120,7 +20230,7 @@ var NO_PREAMBLE = {
20120
20230
  facts: { declaredNames: new Set, freeNames: new Set }
20121
20231
  };
20122
20232
  var NO2 = (reason) => ({ lazySafe: false, reason });
20123
- function analyzeLazyPreamble(preamble, indexParam, primableNames) {
20233
+ function analyzeLazyPreamble(preamble, primableNames) {
20124
20234
  if (!preamble)
20125
20235
  return NO_PREAMBLE;
20126
20236
  if (preamble.builderNames.length > 0) {
@@ -20159,9 +20269,6 @@ function analyzeLazyPreamble(preamble, indexParam, primableNames) {
20159
20269
  }
20160
20270
  }
20161
20271
  const readNames = extractFreeIdentifiersFromStatementText(text);
20162
- if (readNames.has(indexParam) && !declaredNames.has(indexParam)) {
20163
- return NO2(`map-callback preamble reads the loop index parameter '${indexParam}'`);
20164
- }
20165
20272
  const freeNames = new Set(readNames);
20166
20273
  for (const declared of declaredNames)
20167
20274
  freeNames.delete(declared);
@@ -20311,9 +20418,6 @@ function lazyRowEligibility(args) {
20311
20418
  if (shape.hasParamUnwrap)
20312
20419
  return NO3("destructured loop param without param bindings");
20313
20420
  for (const b of bindings) {
20314
- if (b.referencesIndex) {
20315
- return NO3(`binding on slot ${b.slotId} references the loop index parameter`);
20316
- }
20317
20421
  if (b.opaqueOuterNames.includes(UNKNOWN_IDENTIFIERS)) {
20318
20422
  return NO3(`binding on slot ${b.slotId} has no analyzable identifier set`);
20319
20423
  }
@@ -20398,6 +20502,7 @@ function classifyLazyBinding(args) {
20398
20502
  }
20399
20503
  if (name === indexParam) {
20400
20504
  referencesIndex = true;
20505
+ readsItem = true;
20401
20506
  return;
20402
20507
  }
20403
20508
  if (INERT_BINDING_GLOBALS.has(name))
@@ -20447,12 +20552,12 @@ function decideLazyRow(args) {
20447
20552
  for (const b of loop.paramBindings ?? [])
20448
20553
  rowLocalNames.add(b.name);
20449
20554
  const primableNames = new Set([...scope.signals.keys(), ...scope.memos]);
20450
- const preambleAnalysis = analyzeLazyPreamble(loop.preamble, args.indexParam, primableNames);
20555
+ const preambleAnalysis = analyzeLazyPreamble(loop.preamble, primableNames);
20451
20556
  const rawConditionals = loop.bindings.conditionals ?? [];
20452
20557
  const condFacts = [];
20453
20558
  let conditionalRefusal = null;
20454
20559
  for (const cond of rawConditionals) {
20455
- const verdict = analyzeLazyConditional(cond, args.indexParam, {
20560
+ const verdict = analyzeLazyConditional(cond, {
20456
20561
  whenTrueHtml: addCondAttrToTemplate(wrap(cond.whenTrueHtml), cond.slotId),
20457
20562
  whenFalseHtml: addCondAttrToTemplate(wrap(cond.whenFalseHtml), cond.slotId)
20458
20563
  });
@@ -20594,7 +20699,8 @@ function decideLazyRow(args) {
20594
20699
  preambleStatements: args.mapPreambleWrapped,
20595
20700
  itemNeedsPreamble: [...attrs, ...texts, ...conditionals].some((b) => b.readsItem && b.readsPreamble),
20596
20701
  outerNeedsPreamble: [...attrs, ...texts, ...conditionals].some((b) => b.readsOuter && b.readsPreamble),
20597
- conditionals
20702
+ conditionals,
20703
+ readsIndex: classified.some((c) => c.referencesIndex)
20598
20704
  },
20599
20705
  decision
20600
20706
  };
@@ -20645,6 +20751,43 @@ function loopSourceIdentifiers(loop, arrayExpr) {
20645
20751
  return names;
20646
20752
  }
20647
20753
 
20754
+ // src/ir-to-client-js/control-flow/plan/build-plain-row.ts
20755
+ function buildPlainRowCore(inputs) {
20756
+ const { loop, arrayExpr, callSite, flatMapLeafItem, anchored, scope } = inputs;
20757
+ const wrapItem = (expr) => wrapLoopParamAsAccessor(expr, loop.param, loop.paramBindings);
20758
+ const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(loop.param, loop.paramBindings);
20759
+ const indexParam = loop.index || "__idx";
20760
+ const mapPreambleWrapped = loop.preamble ? renderPreamble(loop.preamble, {
20761
+ transformJs: wrapItem,
20762
+ renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings }], undefined)
20763
+ }) : "";
20764
+ const preambleRegions = buildPreambleRegionPlans(loop.preambleRegions, loop.param, loop.paramBindings, loop.index);
20765
+ const lazyRow = buildLazyRowPlan({
20766
+ loop,
20767
+ arrayExpr,
20768
+ indexParam,
20769
+ paramUnwrap,
20770
+ mapPreambleWrapped,
20771
+ preambleRegionCount: preambleRegions.length,
20772
+ callSite,
20773
+ flatMapLeafItem,
20774
+ anchored,
20775
+ scope
20776
+ }) ?? undefined;
20777
+ const mapPreambleWrappedFinal = !lazyRow && loop.index ? wrapIndexParamAsAccessor(mapPreambleWrapped, loop.index) : mapPreambleWrapped;
20778
+ const templateFinal = !lazyRow && loop.index ? wrapIndexParamAsAccessor(loop.template, loop.index) : loop.template;
20779
+ return {
20780
+ indexParam,
20781
+ paramHead,
20782
+ paramUnwrap,
20783
+ preambleRegions,
20784
+ lazyRow,
20785
+ mapPreambleWrapped: mapPreambleWrappedFinal,
20786
+ template: templateFinal,
20787
+ wrapItem
20788
+ };
20789
+ }
20790
+
20648
20791
  // src/ir-to-client-js/control-flow/plan/build-branch-loop.ts
20649
20792
  function buildBranchLoopPlan(loop, profileComponentName, lazyScope) {
20650
20793
  const containerSlotId = loop.containerSlotId;
@@ -20659,16 +20802,18 @@ function buildBranchLoopPlan(loop, profileComponentName, lazyScope) {
20659
20802
  };
20660
20803
  return composite;
20661
20804
  }
20662
- const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(loop.param, loop.paramBindings);
20663
20805
  const hasReactiveEffects = loop.bindings.reactiveAttrs.length > 0 || loop.bindings.reactiveTexts.length > 0 || loop.bindings.conditionals.length > 0;
20664
20806
  const fm = loop.flatMapClient;
20665
20807
  const arrayExpr = fm ? `(${buildChainedArrayExpr(loop)}).flatMap(${fm.params} => ${fm.body})` : buildChainedArrayExpr(loop);
20666
- const indexParam = loop.index || "__idx";
20667
- const mapPreambleWrapped = loop.preamble ? renderPreamble(loop.preamble, {
20668
- transformJs: (t) => wrapLoopParamAsAccessor(t, loop.param, loop.paramBindings),
20669
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings }], undefined)
20670
- }) : "";
20671
- const preambleRegions = buildPreambleRegionPlans(loop.preambleRegions, loop.param, loop.paramBindings);
20808
+ const core = buildPlainRowCore({
20809
+ loop,
20810
+ arrayExpr,
20811
+ callSite: "branch-plain",
20812
+ flatMapLeafItem: Boolean(fm),
20813
+ anchored: false,
20814
+ scope: lazyScope
20815
+ });
20816
+ const { paramHead, paramUnwrap, indexParam, preambleRegions, lazyRow } = core;
20672
20817
  const plan = {
20673
20818
  kind: "plain",
20674
20819
  rowConstruction: "string-template",
@@ -20681,30 +20826,20 @@ function buildBranchLoopPlan(loop, profileComponentName, lazyScope) {
20681
20826
  paramHead,
20682
20827
  paramUnwrap,
20683
20828
  indexParam,
20684
- mapPreambleWrapped,
20685
- lazyRow: buildLazyRowPlan({
20686
- loop,
20687
- arrayExpr,
20688
- indexParam,
20689
- paramUnwrap,
20690
- mapPreambleWrapped,
20691
- preambleRegionCount: preambleRegions.length,
20692
- callSite: "branch-plain",
20693
- flatMapLeafItem: Boolean(fm),
20694
- anchored: false,
20695
- scope: lazyScope
20696
- }) ?? undefined,
20697
- template: loop.template,
20829
+ mapPreambleWrapped: core.mapPreambleWrapped,
20830
+ lazyRow,
20831
+ template: core.template,
20698
20832
  reactiveEffects: hasReactiveEffects ? buildReactiveEffectsPlan({
20699
20833
  attrs: loop.bindings.reactiveAttrs,
20700
20834
  texts: loop.bindings.reactiveTexts,
20701
20835
  conditionals: loop.bindings.conditionals,
20702
20836
  loopParam: loop.param,
20703
20837
  loopParamBindings: loop.paramBindings,
20838
+ loopIndex: loop.index,
20704
20839
  profileComponentName
20705
20840
  }) : null,
20706
20841
  eventDelegation: buildBranchLoopDelegationPlan(loop, cv, profileComponentName),
20707
- childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings),
20842
+ childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings, loop.index),
20708
20843
  preambleRegions,
20709
20844
  bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false,
20710
20845
  profileLoopId: profileComponentName ? `${profileComponentName}#binding:${containerSlotId}` : undefined
@@ -20989,7 +21124,7 @@ function emitDynamicTextUpdates(lines, ctx) {
20989
21124
  const __textSlot = (normalElems[0] ?? conditionalElems[0])?.slotId;
20990
21125
  let writer = "";
20991
21126
  if (normalElems.length > 0) {
20992
- const slots = normalElems.map((elem) => ({ id: elem.slotId, kind: "markup", path: [] }));
21127
+ const slots = normalElems.map((elem) => ({ id: elem.slotId, kind: DYNAMIC_ELEMENT_WRITER_KIND, path: [] }));
20993
21128
  writer = claimWriterVarName(slots, varSlotId);
20994
21129
  lines.push(` const ${writer} = lazySlots(__scope, ${claimPlanLiteral(slots)})`);
20995
21130
  }
@@ -21527,6 +21662,8 @@ function stringifyLazyRowLoop(lines, o) {
21527
21662
  lines.push(`${indent}${o.guardContainer ? `if (${o.containerVar}) ` : ""}${call}`);
21528
21663
  const b1 = `${indent} `;
21529
21664
  const b2 = `${indent} `;
21665
+ if (lazyRow.readsIndex)
21666
+ lines.push(`${b1}indexDriven: true,`);
21530
21667
  lines.push(`${b1}createRow: (__e, ${o.indexParam}) => {`);
21531
21668
  lines.push(`${b2}const ${paramHead} = () => __e.item`);
21532
21669
  if (lazyRow.preambleStatements)
@@ -21556,6 +21693,8 @@ function stringifyLazyRowLoop(lines, o) {
21556
21693
  } else {
21557
21694
  lines.push(`${b1}applyItem: (__e) => {`);
21558
21695
  lines.push(`${b2}const ${paramHead} = () => __e.item`);
21696
+ if (lazyRow.readsIndex)
21697
+ lines.push(`${b2}const ${o.indexParam} = __e.index`);
21559
21698
  lines.push(`${b2}const __r = __e.refs ?? (__e.refs = [])`);
21560
21699
  lines.push(`${b2}const __l = __e.last ?? (__e.last = [])`);
21561
21700
  if (lazyRow.itemNeedsPreamble && lazyRow.preambleStatements) {
@@ -21582,6 +21721,8 @@ function stringifyLazyRowLoop(lines, o) {
21582
21721
  lines.push(`${b2}${g}()`);
21583
21722
  lines.push(`${b2}for (const __e of __es) {`);
21584
21723
  lines.push(`${b3}const ${paramHead} = () => __e.item`);
21724
+ if (lazyRow.readsIndex)
21725
+ lines.push(`${b3}const ${o.indexParam} = __e.index`);
21585
21726
  lines.push(`${b3}const __r = __e.refs ?? (__e.refs = [])`);
21586
21727
  lines.push(`${b3}const __l = __e.last ?? (__e.last = [])`);
21587
21728
  if (lazyRow.outerNeedsPreamble && lazyRow.preambleStatements) {
@@ -22106,6 +22247,7 @@ function stringifyCompositeLoop(lines, plan) {
22106
22247
  innerLoops,
22107
22248
  loopParam,
22108
22249
  loopParamBindings,
22250
+ loopIndex,
22109
22251
  reactiveEffects,
22110
22252
  childRefs,
22111
22253
  branchClearChildren,
@@ -22138,7 +22280,7 @@ function stringifyCompositeLoop(lines, plan) {
22138
22280
  singleRootLayout: "multiline",
22139
22281
  mountRow: true
22140
22282
  });
22141
- emitComponentAndEventSetup(lines, bodyIndent, "__el", compsArr, eventsArr, loopParam, loopParamBindings, bodyIsMultiRoot);
22283
+ emitComponentAndEventSetup(lines, bodyIndent, "__el", compsArr, eventsArr, loopParam, loopParamBindings, bodyIsMultiRoot, loopIndex);
22142
22284
  if (innerLoops.length > 0) {
22143
22285
  stringifyInnerLoops(lines, innerLoops, bodyIndent, pc);
22144
22286
  }
@@ -22560,11 +22702,11 @@ function scopeRefToVar(ref) {
22560
22702
  // src/ir-to-client-js/control-flow/plan/build-component-loop.ts
22561
22703
  function buildComponentLoopPlan(elem, profileComponentName) {
22562
22704
  const { name } = elem.childComponent;
22563
- const propsExpr = buildComponentPropsExpr2(elem.childComponent, elem.param);
22564
- const keyExpr = wrapLoopParamAsAccessor(elem.key || "__idx", elem.param, elem.paramBindings);
22705
+ const propsExpr = buildComponentPropsExpr2(elem.childComponent, elem.param, undefined, elem.index);
22706
+ const keyExpr = wrapLoopParamAsAccessor(elem.key || "__idx", elem.param, elem.paramBindings, elem.index);
22565
22707
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
22566
22708
  const mapPreambleWrapped = elem.preamble ? renderPreamble(elem.preamble, {
22567
- transformJs: (text) => wrapLoopParamAsAccessor(text, elem.param, elem.paramBindings),
22709
+ transformJs: (text) => wrapLoopParamAsAccessor(text, elem.param, elem.paramBindings, elem.index),
22568
22710
  renderLeaf: () => {
22569
22711
  internalInvariant(false, "component-root loop received a JSX-bearing preamble — Phase 1 should have refused it");
22570
22712
  }
@@ -22574,12 +22716,12 @@ function buildComponentLoopPlan(elem, profileComponentName) {
22574
22716
  const isTextOnly = comp.children?.length ? comp.children.every((c) => c.type === "expression" || c.type === "text" || isTextOnlyConditional(c)) : false;
22575
22717
  const rawChildrenExpr = isTextOnly ? irChildrenToJsExpr(comp.children) : null;
22576
22718
  const childrenFreeIds = isTextOnly && comp.children ? irChildrenFreeIds(comp.children) : undefined;
22577
- const childrenRefsLoop = rawChildrenExpr != null && childrenFreeIds != null && childrenFreeIds.has(elem.param);
22719
+ const childrenRefsLoop = rawChildrenExpr != null && childrenFreeIds != null && (childrenFreeIds.has(elem.param) || !!elem.index && childrenFreeIds.has(elem.index));
22578
22720
  return {
22579
22721
  componentName: comp.name,
22580
22722
  selector: buildCompSelector(comp),
22581
- propsExpr: buildComponentPropsExpr2(comp, elem.param),
22582
- childrenTextEffect: childrenRefsLoop ? { wrappedChildren: wrapLoopParamAsAccessor(rawChildrenExpr, elem.param, elem.paramBindings) } : null
22723
+ propsExpr: buildComponentPropsExpr2(comp, elem.param, undefined, elem.index),
22724
+ childrenTextEffect: childrenRefsLoop ? { wrappedChildren: wrapLoopParamAsAccessor(rawChildrenExpr, elem.param, elem.paramBindings, elem.index) } : null
22583
22725
  };
22584
22726
  });
22585
22727
  const hasChildConds = elem.bindings.conditionals.length > 0;
@@ -22598,7 +22740,7 @@ function buildComponentLoopPlan(elem, profileComponentName) {
22598
22740
  componentPropsExpr: propsExpr,
22599
22741
  keyExpr,
22600
22742
  nestedComps,
22601
- childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
22743
+ childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings, elem.index),
22602
22744
  profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : undefined,
22603
22745
  childConditionalEffects: hasChildConds ? buildReactiveEffectsPlan({
22604
22746
  attrs: [],
@@ -22606,6 +22748,7 @@ function buildComponentLoopPlan(elem, profileComponentName) {
22606
22748
  conditionals: elem.bindings.conditionals,
22607
22749
  loopParam: elem.param,
22608
22750
  loopParamBindings: elem.paramBindings,
22751
+ loopIndex: elem.index,
22609
22752
  profileComponentName
22610
22753
  }) : null
22611
22754
  };
@@ -22629,8 +22772,6 @@ function buildLoopPlan(elem, opts) {
22629
22772
  return buildPlainLoopPlan(elem, opts.profileComponentName, opts.lazyScope);
22630
22773
  }
22631
22774
  function buildPlainLoopPlan(elem, profileComponentName, lazyScope) {
22632
- const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings);
22633
- const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
22634
22775
  const hasReactive2 = elem.bindings.reactiveAttrs.length > 0 || elem.bindings.reactiveTexts.length > 0 || elem.bindings.conditionals.length > 0;
22635
22776
  if (elem.flatMapClient) {
22636
22777
  return {
@@ -22656,12 +22797,15 @@ function buildPlainLoopPlan(elem, profileComponentName, lazyScope) {
22656
22797
  };
22657
22798
  }
22658
22799
  const arrayExpr = buildChainedArrayExpr(elem);
22659
- const indexParam = elem.index || "__idx";
22660
- const mapPreambleWrapped = elem.preamble ? renderPreamble(elem.preamble, {
22661
- transformJs: wrap,
22662
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined)
22663
- }) : "";
22664
- const preambleRegions = buildPreambleRegionPlans(elem.preambleRegions, elem.param, elem.paramBindings);
22800
+ const core = buildPlainRowCore({
22801
+ loop: elem,
22802
+ arrayExpr,
22803
+ callSite: "plain",
22804
+ flatMapLeafItem: false,
22805
+ anchored: elem.bodyIsItemConditional ?? false,
22806
+ scope: lazyScope
22807
+ });
22808
+ const { paramHead, paramUnwrap, indexParam, preambleRegions, lazyRow, wrapItem: wrap } = core;
22665
22809
  return {
22666
22810
  kind: "plain",
22667
22811
  rowConstruction: "string-template",
@@ -22673,28 +22817,17 @@ function buildPlainLoopPlan(elem, profileComponentName, lazyScope) {
22673
22817
  paramHead,
22674
22818
  paramUnwrap,
22675
22819
  indexParam,
22676
- lazyRow: buildLazyRowPlan({
22677
- loop: elem,
22678
- arrayExpr,
22679
- indexParam,
22680
- paramUnwrap,
22681
- mapPreambleWrapped,
22682
- preambleRegionCount: preambleRegions.length,
22683
- callSite: "plain",
22684
- flatMapLeafItem: false,
22685
- anchored: elem.bodyIsItemConditional ?? false,
22686
- scope: lazyScope
22687
- }) ?? undefined,
22688
- mapPreambleWrapped,
22689
- template: elem.template,
22820
+ lazyRow,
22821
+ mapPreambleWrapped: core.mapPreambleWrapped,
22822
+ template: core.template,
22690
22823
  skeletonTemplate: elem.skeletonTemplate,
22691
22824
  skeletonPaths: elem.skeletonPaths,
22692
22825
  reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
22693
- childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
22826
+ childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings, elem.index),
22694
22827
  preambleRegions,
22695
22828
  bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
22696
22829
  anchored: elem.bodyIsItemConditional ?? false,
22697
- anchorKeyExpr: elem.key ? wrap(elem.key) : elem.index || "__idx"
22830
+ anchorKeyExpr: elem.key ? wrap(elem.key) : `${indexParam}()`
22698
22831
  };
22699
22832
  }
22700
22833
  function buildStaticLoopPlan(elem, unsafeLocalNames, profileComponentName) {
@@ -22736,7 +22869,7 @@ function buildStaticLoopMaterialize(elem, unsafeLocalNames) {
22736
22869
  return {
22737
22870
  itemTemplate: elem.staticItemTemplate,
22738
22871
  mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
22739
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined)
22872
+ renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings, index: elem.index }], undefined)
22740
22873
  }) : "",
22741
22874
  bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false
22742
22875
  };
@@ -23313,7 +23446,7 @@ function generateTemplateOnlyMount(ir, ctx) {
23313
23446
  const restSpreadNames = resolveRestSpreadNames(ctx);
23314
23447
  let templateHtml;
23315
23448
  if (canGenerateStaticTemplate(ir.root, propNamesForStaticCheck, inlinableConstants, unsafeLocalNames)) {
23316
- const markupSlotIds = new Set(ctx.dynamicElements.map((e) => e.slotId));
23449
+ const markupSlotIds = markupSlotIdsOf(ctx);
23317
23450
  templateHtml = irToComponentTemplate(ir.root, inlinableConstants, restSpreadNames, ctx.propsObjectName, markupSlotIds, ctx.restPropsName);
23318
23451
  }
23319
23452
  if (!templateHtml) {
@@ -24050,6 +24183,21 @@ function extractSsrDefaults(metadata) {
24050
24183
  }
24051
24184
  bindings[memo.name] = value;
24052
24185
  }
24186
+ {
24187
+ const getterNames = collectAliasableGetterNames(metadata.signals, metadata.memos);
24188
+ for (const [alias, origin] of resolveGetterAliases(metadata.localConstants ?? [], (n) => getterNames.has(n))) {
24189
+ if (alias in out)
24190
+ continue;
24191
+ out[alias] = out[origin];
24192
+ }
24193
+ }
24194
+ for (const [local, callerKey] of resolveBodyDestructuredPropAliases(metadata.localConstants ?? [], metadata.propsObjectName)) {
24195
+ if (local in out)
24196
+ continue;
24197
+ const origin = out[callerKey];
24198
+ if (origin)
24199
+ out[local] = origin;
24200
+ }
24053
24201
  if (metadata.propsObjectName !== null) {
24054
24202
  const referenced = new Set;
24055
24203
  for (const sig of metadata.signals) {
@@ -26414,6 +26562,17 @@ function emitParsedExpr(expr, emitter) {
26414
26562
  case "literal":
26415
26563
  return emitter.literal(expr.value, expr.literalType);
26416
26564
  case "call": {
26565
+ if (emitter.lowering) {
26566
+ for (const matcher of emitter.lowering.matchers) {
26567
+ const node = matcher(expr.callee, expr.args);
26568
+ if (!node)
26569
+ continue;
26570
+ const rendered = emitter.lowering.render(node, emit);
26571
+ if (rendered !== null)
26572
+ return rendered;
26573
+ break;
26574
+ }
26575
+ }
26417
26576
  const cb = asCallbackMethodCall(expr);
26418
26577
  if (cb)
26419
26578
  return emitter.callbackMethod(cb.method, cb.object, cb.arrow, cb.args, emit);
@@ -29717,7 +29876,9 @@ export {
29717
29876
  rewriteDynamicImportsInSource,
29718
29877
  resolveStaticLoopSource,
29719
29878
  resolveSetters,
29879
+ resolveGetterAliases,
29720
29880
  resolveDangerousInnerHtml,
29881
+ resolveBodyDestructuredPropAliases,
29721
29882
  resetCompilerCounters,
29722
29883
  registerLoweringPlugin,
29723
29884
  registerBuiltinLoweringPlugins,
@@ -29741,6 +29902,7 @@ export {
29741
29902
  matchQueryHrefCall,
29742
29903
  matchLoweringCall,
29743
29904
  makeIdCallRegex,
29905
+ loweringNodeChildren,
29744
29906
  lookupStaticRecordLiteral,
29745
29907
  listComponentFunctions as listExportedComponents,
29746
29908
  listComponentFunctions,
@@ -29829,6 +29991,7 @@ export {
29829
29991
  collectModuleStringConsts,
29830
29992
  collectLoopBoundNames,
29831
29993
  collectContextConsumers,
29994
+ collectAliasableGetterNames,
29832
29995
  buildWhyUpdate,
29833
29996
  buildStaticBudget,
29834
29997
  buildSourceMapFromIR,