@barefootjs/jsx 0.34.0 → 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 (101) hide show
  1. package/dist/analyzer.d.ts.map +1 -1
  2. package/dist/index.d.ts +2 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +344 -215
  5. package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
  6. package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts.map +1 -1
  7. package/dist/ir-to-client-js/control-flow/plan/build-component-loop.d.ts.map +1 -1
  8. package/dist/ir-to-client-js/control-flow/plan/build-composite-loop.d.ts.map +1 -1
  9. package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts +11 -0
  10. package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts.map +1 -1
  11. package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
  12. package/dist/ir-to-client-js/control-flow/plan/build-plain-row.d.ts +52 -0
  13. package/dist/ir-to-client-js/control-flow/plan/build-plain-row.d.ts.map +1 -0
  14. package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts +2 -0
  15. package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts.map +1 -1
  16. package/dist/ir-to-client-js/control-flow/plan/lazy-conditional.d.ts +6 -7
  17. package/dist/ir-to-client-js/control-flow/plan/lazy-conditional.d.ts.map +1 -1
  18. package/dist/ir-to-client-js/control-flow/plan/lazy-preamble.d.ts +5 -7
  19. package/dist/ir-to-client-js/control-flow/plan/lazy-preamble.d.ts.map +1 -1
  20. package/dist/ir-to-client-js/control-flow/plan/lazy-row-eligibility.d.ts.map +1 -1
  21. package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +10 -6
  22. package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
  23. package/dist/ir-to-client-js/control-flow/shared.d.ts +11 -9
  24. package/dist/ir-to-client-js/control-flow/shared.d.ts.map +1 -1
  25. package/dist/ir-to-client-js/control-flow/stringify/composite-loop.d.ts.map +1 -1
  26. package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts.map +1 -1
  27. package/dist/ir-to-client-js/csr-substitute.d.ts +16 -0
  28. package/dist/ir-to-client-js/csr-substitute.d.ts.map +1 -1
  29. package/dist/ir-to-client-js/emit-reactive.d.ts.map +1 -1
  30. package/dist/ir-to-client-js/emit-registration.d.ts.map +1 -1
  31. package/dist/ir-to-client-js/html-template.d.ts +2 -2
  32. package/dist/ir-to-client-js/html-template.d.ts.map +1 -1
  33. package/dist/ir-to-client-js/imports.d.ts +2 -2
  34. package/dist/ir-to-client-js/imports.d.ts.map +1 -1
  35. package/dist/ir-to-client-js/index.d.ts.map +1 -1
  36. package/dist/ir-to-client-js/markup-slots.d.ts +21 -0
  37. package/dist/ir-to-client-js/markup-slots.d.ts.map +1 -0
  38. package/dist/ir-to-client-js/safe-html.d.ts +158 -0
  39. package/dist/ir-to-client-js/safe-html.d.ts.map +1 -0
  40. package/dist/ir-to-client-js/utils.d.ts +34 -1
  41. package/dist/ir-to-client-js/utils.d.ts.map +1 -1
  42. package/dist/jsx-to-ir.d.ts.map +1 -1
  43. package/dist/prop-rewrite.d.ts +8 -3
  44. package/dist/prop-rewrite.d.ts.map +1 -1
  45. package/dist/props-binding.d.ts +22 -1
  46. package/dist/props-binding.d.ts.map +1 -1
  47. package/dist/ssr-defaults.d.ts.map +1 -1
  48. package/dist/types.d.ts +5 -10
  49. package/dist/types.d.ts.map +1 -1
  50. package/package.json +2 -2
  51. package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +10 -12
  52. package/src/__tests__/build-plain-row-consolidation.test.ts +120 -0
  53. package/src/__tests__/client-template-escape-soundness.test.ts +538 -0
  54. package/src/__tests__/csr-template-loop-shadowing.test.ts +4 -2
  55. package/src/__tests__/flatmap-segments.test.ts +1 -1
  56. package/src/__tests__/lazy-conditional.test.ts +52 -12
  57. package/src/__tests__/lazy-preamble.test.ts +11 -11
  58. package/src/__tests__/lazy-row-eligibility.test.ts +8 -3
  59. package/src/__tests__/loop-child-reactive-attr-const-shadow.test.ts +25 -3
  60. package/src/__tests__/loop-item-conditional-codegen.test.ts +4 -2
  61. package/src/__tests__/map-arbitrary-body.test.ts +1 -1
  62. package/src/__tests__/nested-loop-index-param.test.ts +14 -3
  63. package/src/__tests__/nested-loop-reactive-attrs.test.ts +4 -2
  64. package/src/__tests__/reactive-attrs-in-map.test.ts +13 -10
  65. package/src/__tests__/rewrite-destructured-props.test.ts +37 -0
  66. package/src/__tests__/safe-html-splice-door.test.ts +61 -0
  67. package/src/__tests__/ssr-defaults.test.ts +59 -0
  68. package/src/analyzer.ts +4 -0
  69. package/src/index.ts +8 -2
  70. package/src/ir-to-client-js/collect-elements.ts +18 -2
  71. package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +20 -28
  72. package/src/ir-to-client-js/control-flow/plan/build-component-loop.ts +9 -7
  73. package/src/ir-to-client-js/control-flow/plan/build-composite-loop.ts +10 -6
  74. package/src/ir-to-client-js/control-flow/plan/build-inner-loop.ts +8 -8
  75. package/src/ir-to-client-js/control-flow/plan/build-lazy-row.ts +18 -2
  76. package/src/ir-to-client-js/control-flow/plan/build-loop-child-arm.ts +3 -3
  77. package/src/ir-to-client-js/control-flow/plan/build-loop.ts +35 -35
  78. package/src/ir-to-client-js/control-flow/plan/build-plain-row.ts +110 -0
  79. package/src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts +5 -2
  80. package/src/ir-to-client-js/control-flow/plan/lazy-conditional.ts +11 -10
  81. package/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts +11 -13
  82. package/src/ir-to-client-js/control-flow/plan/lazy-row-eligibility.ts +13 -3
  83. package/src/ir-to-client-js/control-flow/plan/loop.ts +10 -6
  84. package/src/ir-to-client-js/control-flow/shared.ts +40 -17
  85. package/src/ir-to-client-js/control-flow/stringify/composite-loop.ts +2 -1
  86. package/src/ir-to-client-js/control-flow/stringify/lazy-row.ts +10 -0
  87. package/src/ir-to-client-js/csr-substitute.ts +29 -0
  88. package/src/ir-to-client-js/emit-reactive.ts +4 -1
  89. package/src/ir-to-client-js/emit-registration.ts +2 -4
  90. package/src/ir-to-client-js/html-template.ts +58 -244
  91. package/src/ir-to-client-js/imports.ts +3 -0
  92. package/src/ir-to-client-js/index.ts +4 -4
  93. package/src/ir-to-client-js/markup-slots.ts +25 -0
  94. package/src/ir-to-client-js/safe-html.ts +243 -0
  95. package/src/ir-to-client-js/utils.ts +58 -10
  96. package/src/jsx-to-ir.ts +115 -20
  97. package/src/prop-rewrite.ts +8 -4
  98. package/src/props-binding.ts +37 -1
  99. package/src/ssr-defaults.ts +45 -0
  100. package/src/types.ts +5 -10
  101. package/src/__tests__/text-slot-escaping.test.ts +0 -65
package/dist/index.js CHANGED
@@ -2689,12 +2689,22 @@ function renderLoopBindingAccess(b, base) {
2689
2689
  }
2690
2690
  return parent;
2691
2691
  }
2692
- function wrapLoopParamAsAccessor(expr, paramName, bindings) {
2692
+ function wrapLoopParamAsAccessor(expr, paramName, bindings, indexParam) {
2693
+ let result;
2693
2694
  if (bindings && bindings.length > 0) {
2694
- 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}()`);
2699
+ }
2700
+ if (indexParam && indexParam !== paramName) {
2701
+ result = wrapIndexParamAsAccessor(result, indexParam);
2695
2702
  }
2696
- const re = new RegExp(`${ID_BOUNDARY_BEFORE}${escapeIdentifierForRegex(paramName)}(?!\\s*\\()(?!-)${ID_BOUNDARY_AFTER}`, "gu");
2697
- return replaceInExprContexts(expr, re, () => `${paramName}()`);
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}()`);
2698
2708
  }
2699
2709
  function rewriteLoopBindingRefs(expr, bindings, accessor) {
2700
2710
  const byName = new Map;
@@ -2741,7 +2751,7 @@ function wrapExprWithLoopParams(expr, loopParams) {
2741
2751
  let result = expr;
2742
2752
  for (const p of loopParams) {
2743
2753
  const spec = typeof p === "string" ? { param: p } : p;
2744
- result = wrapLoopParamAsAccessor(result, spec.param, spec.bindings);
2754
+ result = wrapLoopParamAsAccessor(result, spec.param, spec.bindings, spec.index);
2745
2755
  }
2746
2756
  return result;
2747
2757
  }
@@ -3028,6 +3038,20 @@ function buildPropAliasMap(params) {
3028
3038
  }
3029
3039
  return map;
3030
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
+ }
3031
3055
  function boundPropLocalNames(b) {
3032
3056
  if (b.propsObjectName !== null)
3033
3057
  return EMPTY_SET;
@@ -3260,6 +3284,18 @@ function resolveGetterAliases(localConstants, isGetter) {
3260
3284
  }
3261
3285
  return aliases;
3262
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
+ }
3263
3299
  function buildSignalMemoEnv(signals, memos, propsObjectName, localConstants = []) {
3264
3300
  const substitutions = new Map;
3265
3301
  for (const s of signals) {
@@ -3439,6 +3475,65 @@ class BindingScope {
3439
3475
  }
3440
3476
  }
3441
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
+
3442
3537
  // src/ir-to-client-js/html-template.ts
3443
3538
  function createStringProtector() {
3444
3539
  const strings = [];
@@ -3608,22 +3703,11 @@ function templateAttrExpr(attrName, valExpr, presenceOrUndefined) {
3608
3703
  function escapeAttrValueExpr(valExpr) {
3609
3704
  return `escapeAttr(${valExpr})`;
3610
3705
  }
3611
- function escapeTextSlotExpr(innerExpr, isMarkup = false) {
3612
- return `${isMarkup ? "escapeTextOrMarkup" : "escapeText"}(${innerExpr})`;
3613
- }
3614
- function isChildrenPassthroughExpr(expr) {
3615
- return /^([A-Za-z_$][\w$]*\.)?children$/.test(expr.trim());
3616
- }
3617
- function bareSpliceExpr(node, valueExpr) {
3618
- const resolved = valueExpr.trim().replace(/^\(+|\)+$/g, "");
3619
- const isChildren = isChildrenPassthroughExpr(node.expr) || isChildrenPassthroughExpr(resolved);
3620
- return !node.joinArrayChild && isChildren ? `markupOrEmpty(${valueExpr})` : valueExpr;
3621
- }
3622
3706
  function dangerouslyHtmlChildren(attrs, toExpr) {
3623
3707
  const attr = attrs.find((a) => a.name === "dangerouslySetInnerHTML");
3624
3708
  if (!attr || attr.value.kind !== "expression")
3625
3709
  return null;
3626
- return `\${((${toExpr(attr.value)}) ?? {}).__html ?? ''}`;
3710
+ return interp(dangerousInnerHtml(toExpr(attr.value)));
3627
3711
  }
3628
3712
  function transformKeyValue(value, transformExpr) {
3629
3713
  switch (value.kind) {
@@ -3739,7 +3823,7 @@ function buildSpreadAttrsMergeCall(args) {
3739
3823
  return `\${spreadAttrs({${objMembers.join(", ")}})}`;
3740
3824
  }
3741
3825
  function itemAnchorTemplate(keyExpr) {
3742
- return `<!--${loopItemMarker("${" + keyExpr + "}")}-->`;
3826
+ return `<!--${loopItemMarker("${escapeCommentText(" + keyExpr + ")}")}-->`;
3743
3827
  }
3744
3828
  function renderPreamble(preamble, opts) {
3745
3829
  let out = "";
@@ -3748,9 +3832,9 @@ function renderPreamble(preamble, opts) {
3748
3832
  const text = opts.textVariant === "template" ? seg.templateText ?? seg.text : seg.text;
3749
3833
  out += opts.transformJs ? opts.transformJs(text) : text;
3750
3834
  } else if (opts.rawLeaf) {
3751
- out += opts.renderLeaf(escapeLeafTextExpressions(seg.ir));
3835
+ out += opts.renderLeaf(seg.ir);
3752
3836
  } else {
3753
- out += "`" + opts.renderLeaf(escapeLeafTextExpressions(seg.ir)) + "`";
3837
+ out += "`" + opts.renderLeaf(seg.ir) + "`";
3754
3838
  }
3755
3839
  }
3756
3840
  return out;
@@ -3795,36 +3879,12 @@ function renderFlatMapProjectionClientBody(inner, restSpreadNames) {
3795
3879
  const chained = applyLoopChain(inner);
3796
3880
  const params = inner.index ? `(${inner.param}, ${inner.index})` : `(${inner.param})`;
3797
3881
  const key = inner.key ? `(${inner.key})` : "undefined";
3798
- 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("");
3799
3883
  return `${chained}.map(${params} => ({ k: ${key}, h: \`${html}\` }))`;
3800
3884
  }
3801
- function escapeLeafTextExpressions(ir) {
3802
- switch (ir.type) {
3803
- case "element":
3804
- return { ...ir, children: ir.children.map(escapeLeafTextExpressions) };
3805
- case "fragment":
3806
- return { ...ir, children: ir.children.map(escapeLeafTextExpressions) };
3807
- case "expression": {
3808
- if (ir.expr === "null" || ir.expr === "undefined")
3809
- return ir;
3810
- if (ir.slotId || ir.expr.trimStart().startsWith("escapeText("))
3811
- return ir;
3812
- return { ...ir, expr: `escapeText((${ir.expr}))`, templateExpr: ir.templateExpr ? `escapeText((${ir.templateExpr}))` : ir.templateExpr };
3813
- }
3814
- case "conditional":
3815
- return {
3816
- ...ir,
3817
- whenTrue: escapeLeafTextExpressions(ir.whenTrue),
3818
- whenFalse: ir.whenFalse ? escapeLeafTextExpressions(ir.whenFalse) : ir.whenFalse
3819
- };
3820
- default:
3821
- return ir;
3822
- }
3823
- }
3824
3885
  function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, branchSlotsVar, inHoistedChildren = false) {
3825
3886
  const recurse = (n) => irToHtmlTemplate(n, restSpreadNames, loopDepth, loopParams, branchSlotsVar, inHoistedChildren);
3826
3887
  const wrapExpr = (expr) => wrapExprWithLoopParams(expr, loopParams);
3827
- const wrapInterpolation = (expr) => branchSlotsVar ? `__bfSlot(${expr}, ${branchSlotsVar})` : expr;
3828
3888
  switch (node.type) {
3829
3889
  case "element": {
3830
3890
  const mergeCtx = {
@@ -3870,25 +3930,17 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
3870
3930
  case "expression": {
3871
3931
  if (node.expr === "null" || node.expr === "undefined")
3872
3932
  return "";
3873
- const escapeForClient = (e) => node.escapeInClientTemplate ? `escapeText(${e})` : e;
3874
- if (node.markerless) {
3875
- const bare = escapeForClient(wrapInterpolation(wrapExpr(node.expr)));
3876
- return `\${${bare}}`;
3877
- }
3878
- const inner = escapeForClient(wrapInterpolation(wrapExpr(node.expr)));
3879
- const valueExpr = node.joinArrayChild ? `Array.isArray(${inner}) ? ${inner}.join('') : (${inner} ?? '')` : inner;
3880
- if (node.slotId) {
3881
- const slotted = branchSlotsVar || node.joinArrayChild ? valueExpr : escapeTextSlotExpr(valueExpr);
3882
- return `<!--bf:${node.slotId}-->\${${slotted}}<!--/-->`;
3883
- }
3884
- 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;
3885
3937
  }
3886
3938
  case "conditional": {
3887
3939
  const trueBranch = recurse(node.whenTrue);
3888
3940
  const falseBranch = recurse(node.whenFalse);
3889
3941
  const trueHtml = node.slotId ? addCondAttrToTemplate(trueBranch, node.slotId) : trueBranch;
3890
3942
  const falseHtml = node.slotId ? addCondAttrToTemplate(falseBranch, node.slotId) : falseBranch;
3891
- return `\${${wrapExpr(node.condition)} ? \`${trueHtml}\` : \`${falseHtml}\`}`;
3943
+ return interp(conditionalMarkup(wrapExpr(node.condition), trueHtml, falseHtml));
3892
3944
  }
3893
3945
  case "fragment":
3894
3946
  return node.children.map(recurse).join("");
@@ -3925,7 +3977,7 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
3925
3977
  const propsExpr = propsEntries.length > 0 ? `{${propsEntries.join(", ")}}` : "{}";
3926
3978
  const keyProp = node.props.find((p) => p.name === "key");
3927
3979
  const keyArg = keyProp ? `, ${attrValueToString(keyProp.value) ?? "undefined"}` : "";
3928
- return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${renderChildScopeArgs(node, keyArg)})}`;
3980
+ return interp(renderChildCall(nameForRegistryRef(node.name), propsExpr, renderChildScopeArgs(node, keyArg)));
3929
3981
  }
3930
3982
  case "loop": {
3931
3983
  const innerRecurse = (n) => irToHtmlTemplate(n, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar);
@@ -3943,12 +3995,12 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
3943
3995
  const body = renderPreamble(node.flatMapCallback, {
3944
3996
  renderLeaf: (ir) => irToHtmlTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar)
3945
3997
  });
3946
- mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
3998
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, "flatMap", node.flatMapCallback.params, body));
3947
3999
  } else if (node.preamble) {
3948
4000
  const preamble = renderPreamble(node.preamble, { textVariant: "client", renderLeaf: (ir) => irToHtmlTemplate(ir, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar) });
3949
- mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => { ${preamble} return \`${childTemplate}\` }).join('')}`;
4001
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, iterMethod, callbackParam, `{ ${preamble} return \`${childTemplate}\` }`));
3950
4002
  } else {
3951
- mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => \`${childTemplate}\`).join('')}`;
4003
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, iterMethod, callbackParam, `\`${childTemplate}\``));
3952
4004
  }
3953
4005
  return `<!--${loopStartMarker(node.markerId)}-->${mapExpr}<!--${loopEndMarker(node.markerId)}-->`;
3954
4006
  }
@@ -4207,20 +4259,15 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
4207
4259
  case "expression": {
4208
4260
  if (node.expr === "null" || node.expr === "undefined")
4209
4261
  return "";
4210
- const wrapped = wrapExpr(node.expr);
4211
- const value = node.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : wrapped;
4212
- if (node.slotId) {
4213
- return `<!--bf:${node.slotId}-->\${${node.joinArrayChild ? value : escapeTextSlotExpr(wrapped)}}<!--/-->`;
4214
- }
4215
- const spliced = bareSpliceExpr(node, value);
4216
- return `\${${node.escapeInClientTemplate ? `escapeText(${spliced})` : spliced}}`;
4262
+ const hole = interp(spliceChildValue(node, wrapExpr(node.expr), {}));
4263
+ return node.slotId ? `<!--bf:${node.slotId}-->${hole}<!--/-->` : hole;
4217
4264
  }
4218
4265
  case "conditional": {
4219
4266
  const trueBranch = recurse(node.whenTrue);
4220
4267
  const falseBranch = recurse(node.whenFalse);
4221
4268
  const trueHtml = node.slotId ? addCondAttrToTemplate(trueBranch, node.slotId) : trueBranch;
4222
4269
  const falseHtml = node.slotId ? addCondAttrToTemplate(falseBranch, node.slotId) : falseBranch;
4223
- return `\${${wrapExpr(node.condition)} ? \`${trueHtml}\` : \`${falseHtml}\`}`;
4270
+ return interp(conditionalMarkup(wrapExpr(node.condition), trueHtml, falseHtml));
4224
4271
  }
4225
4272
  case "fragment":
4226
4273
  return node.children.map(recurse).join("");
@@ -4244,12 +4291,12 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
4244
4291
  const body = renderPreamble(node.flatMapCallback, {
4245
4292
  renderLeaf: (ir) => irToPlaceholderTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams)
4246
4293
  });
4247
- mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
4294
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, "flatMap", node.flatMapCallback.params, body));
4248
4295
  } else if (node.preamble) {
4249
4296
  const preamble = renderPreamble(node.preamble, { textVariant: "client", renderLeaf: (ir) => irToPlaceholderTemplate(ir, restSpreadNames, loopDepth + 1, loopParams) });
4250
- mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => { ${preamble} return \`${childTemplate}\` }).join('')}`;
4297
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, iterMethod, callbackParam, `{ ${preamble} return \`${childTemplate}\` }`));
4251
4298
  } else {
4252
- mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => \`${childTemplate}\`).join('')}`;
4299
+ mapExpr = interp(mappedRowsMarkup(wrappedArray, iterMethod, callbackParam, `\`${childTemplate}\``));
4253
4300
  }
4254
4301
  return `<!--${loopStartMarker(node.markerId)}-->${mapExpr}<!--${loopEndMarker(node.markerId)}-->`;
4255
4302
  }
@@ -4451,13 +4498,8 @@ function irToComponentTemplateWithOpts(node, opts) {
4451
4498
  return "";
4452
4499
  return `<!--bf:${node.slotId}--><!--/-->`;
4453
4500
  }
4454
- const wrapped = transformExpr(node.expr, node.templateExpr);
4455
- const value = node.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : wrapped;
4456
- if (node.slotId) {
4457
- const isMarkup = opts.markupSlotIds?.has(node.slotId) ?? false;
4458
- return `<!--bf:${node.slotId}-->\${${node.joinArrayChild ? value : escapeTextSlotExpr(wrapped, isMarkup)}}<!--/-->`;
4459
- }
4460
- 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;
4461
4503
  }
4462
4504
  case "conditional": {
4463
4505
  if (node.clientOnly && node.slotId) {
@@ -4467,7 +4509,7 @@ function irToComponentTemplateWithOpts(node, opts) {
4467
4509
  const falseBranch = recurse(node.whenFalse);
4468
4510
  const trueHtml = node.slotId ? addCondAttrToTemplate(trueBranch, node.slotId) : trueBranch;
4469
4511
  const falseHtml = node.slotId ? addCondAttrToTemplate(falseBranch, node.slotId) : falseBranch;
4470
- return `\${${transformExpr(node.condition, node.templateCondition)} ? \`${trueHtml}\` : \`${falseHtml}\`}`;
4512
+ return interp(conditionalMarkup(transformExpr(node.condition, node.templateCondition), trueHtml, falseHtml));
4471
4513
  }
4472
4514
  case "fragment":
4473
4515
  return node.children.map(recurse).join("");
@@ -4505,7 +4547,7 @@ function irToComponentTemplateWithOpts(node, opts) {
4505
4547
  const propsExpr = propsEntries.length > 0 ? `{${propsEntries.join(", ")}}` : "{}";
4506
4548
  const keyProp = node.props.find((p) => p.name === "key");
4507
4549
  const keyArg = keyProp ? `, ${transformKeyValue(keyProp.value, transformExpr)}` : "";
4508
- return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${keyArg})}`;
4550
+ return interp(renderChildCall(nameForRegistryRef(node.name), propsExpr, keyArg));
4509
4551
  }
4510
4552
  case "loop": {
4511
4553
  const innerOpts = { ...opts, loopDepth: loopDepth + 1 };
@@ -4515,7 +4557,7 @@ function irToComponentTemplateWithOpts(node, opts) {
4515
4557
  case "if-statement": {
4516
4558
  const consequent = recurse(node.consequent);
4517
4559
  const alternate = node.alternate ? recurse(node.alternate) : "";
4518
- return `\${${transformExpr(node.condition, node.templateCondition)} ? \`${consequent}\` : \`${alternate}\`}`;
4560
+ return interp(conditionalMarkup(transformExpr(node.condition, node.templateCondition), consequent, alternate));
4519
4561
  }
4520
4562
  case "provider":
4521
4563
  case "async":
@@ -4603,7 +4645,7 @@ function generateCsrTemplate(node, inlinableConstants, ctx, restSpreadNames, pro
4603
4645
  }
4604
4646
  }
4605
4647
  const effectiveUnsafeLocalNames = mergeCsrNullUnsafe(ctx, unsafeLocalNames);
4606
- const markupSlotIds = new Set(ctx.dynamicElements.map((e) => e.slotId));
4648
+ const markupSlotIds = markupSlotIdsOf(ctx);
4607
4649
  return generateCsrTemplateWithOpts(node, { inlinableConstants, restSpreadNames, propsObjectName, csrEnv, unsafeLocalNames: effectiveUnsafeLocalNames, deferredChildSlots, loopDepth: -1, markupSlotIds, restPropsName: ctx.restPropsName });
4608
4650
  }
4609
4651
  function mergeCsrNullUnsafe(ctx, unsafeLocalNames) {
@@ -4802,13 +4844,8 @@ function generateCsrTemplateWithOpts(node, opts) {
4802
4844
  }
4803
4845
  {
4804
4846
  const transformed = transformExpr(node.expr, node.templateExpr);
4805
- const expr = transformed === UNSAFE_TEMPLATE_EXPR ? "''" : transformed;
4806
- const value = node.joinArrayChild ? `Array.isArray(${expr}) ? ${expr}.join('') : (${expr} ?? '')` : expr;
4807
- if (node.slotId) {
4808
- const isMarkup = opts.markupSlotIds?.has(node.slotId) ?? false;
4809
- return `<!--bf:${node.slotId}-->\${${node.joinArrayChild ? value : escapeTextSlotExpr(expr, isMarkup)}}<!--/-->`;
4810
- }
4811
- 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;
4812
4849
  }
4813
4850
  case "conditional": {
4814
4851
  if (node.clientOnly && node.slotId) {
@@ -4818,7 +4855,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4818
4855
  const falseBranch = recurse(node.whenFalse);
4819
4856
  const trueHtml = node.slotId ? addCondAttrToTemplate(trueBranch, node.slotId) : trueBranch;
4820
4857
  const falseHtml = node.slotId ? addCondAttrToTemplate(falseBranch, node.slotId) : falseBranch;
4821
- return `\${${transformExpr(node.condition, node.templateCondition)} ? \`${trueHtml}\` : \`${falseHtml}\`}`;
4858
+ return interp(conditionalMarkup(transformExpr(node.condition, node.templateCondition), trueHtml, falseHtml));
4822
4859
  }
4823
4860
  case "fragment":
4824
4861
  return node.children.map(recurse).join("");
@@ -4867,7 +4904,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4867
4904
  const propsExpr = propsEntries.length > 0 ? `{${propsEntries.join(", ")}}` : "{}";
4868
4905
  const keyProp = node.props.find((p) => p.name === "key");
4869
4906
  const keyArg = keyProp ? `, ${transformKeyValue(keyProp.value, transformExpr)}` : "";
4870
- return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${renderChildScopeArgs(node, keyArg)})}`;
4907
+ return interp(renderChildCall(nameForRegistryRef(node.name), propsExpr, renderChildScopeArgs(node, keyArg)));
4871
4908
  }
4872
4909
  case "loop": {
4873
4910
  const childScope = (opts.scope ?? BindingScope.EMPTY).enterLoopRow(node);
@@ -4900,19 +4937,19 @@ function generateCsrTemplateWithOpts(node, opts) {
4900
4937
  transformJs: (t) => rewritePropsObjectRef(t, propsObjectName ?? null, restPropsName ?? null, { enclosingScope: childScope }),
4901
4938
  renderLeaf: (ir) => recurseInLoopBody(stripLeafKeyAttr(ir))
4902
4939
  });
4903
- mapExpr = `\${${iterArrayExpr}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
4940
+ mapExpr = interp(mappedRowsMarkup(iterArrayExpr, "flatMap", node.flatMapCallback.params, body));
4904
4941
  } else if (node.preamble) {
4905
4942
  const preamble = renderPreamble(node.preamble, { textVariant: "template", transformJs: (t) => rewritePropsObjectRef(t, propsObjectName ?? null, restPropsName ?? null, { enclosingScope: childScope }), renderLeaf: (ir) => recurseInLoopBody(ir) });
4906
- mapExpr = `\${${iterArrayExpr}.${iterMethod}(${callbackParam} => { ${preamble} return \`${childTemplate}\` }).join('')}`;
4943
+ mapExpr = interp(mappedRowsMarkup(iterArrayExpr, iterMethod, callbackParam, `{ ${preamble} return \`${childTemplate}\` }`));
4907
4944
  } else {
4908
- mapExpr = `\${${iterArrayExpr}.${iterMethod}(${callbackParam} => \`${childTemplate}\`).join('')}`;
4945
+ mapExpr = interp(mappedRowsMarkup(iterArrayExpr, iterMethod, callbackParam, `\`${childTemplate}\``));
4909
4946
  }
4910
4947
  return `<!--${loopStartMarker(node.markerId)}-->${mapExpr}<!--${loopEndMarker(node.markerId)}-->`;
4911
4948
  }
4912
4949
  case "if-statement": {
4913
4950
  const consequent = recurse(node.consequent);
4914
4951
  const alternate = node.alternate ? recurse(node.alternate) : "";
4915
- return `\${${transformExpr(node.condition, node.templateCondition)} ? \`${consequent}\` : \`${alternate}\`}`;
4952
+ return interp(conditionalMarkup(transformExpr(node.condition, node.templateCondition), consequent, alternate));
4916
4953
  }
4917
4954
  case "provider":
4918
4955
  case "async":
@@ -5013,8 +5050,6 @@ function collectAstPropRefs(node, propNames, out) {
5013
5050
  walkWithScope(node, (n, parent, shadowed) => {
5014
5051
  if (shadowed || !propNames.has(n.text))
5015
5052
  return;
5016
- if (parent && ts7.isShorthandPropertyAssignment(parent) && parent.name === n)
5017
- return;
5018
5053
  if (isNonValuePosition(n, parent))
5019
5054
  return;
5020
5055
  out.add(n.text);
@@ -7240,6 +7275,7 @@ var CLIENT_EXPORTS = new Set([
7240
7275
  "isSSRPortal",
7241
7276
  "findSiblingSlot",
7242
7277
  "cleanupPortalPlaceholder",
7278
+ "trackPosition",
7243
7279
  "createSearchParams",
7244
7280
  "queryHref",
7245
7281
  "formatDate",
@@ -8559,7 +8595,8 @@ var BROWSER_ONLY_CLIENT_APIS = new Set([
8559
8595
  "createPortal",
8560
8596
  "isSSRPortal",
8561
8597
  "findSiblingSlot",
8562
- "cleanupPortalPlaceholder"
8598
+ "cleanupPortalPlaceholder",
8599
+ "trackPosition"
8563
8600
  ]);
8564
8601
  function importsBrowserOnlyClientApi(ctx) {
8565
8602
  for (const imp of ctx.imports) {
@@ -10795,9 +10832,8 @@ function collectBranchLocalPropRefsViaSubstitution(node, ctx) {
10795
10832
  function visit2(n, parent) {
10796
10833
  if (ts14.isIdentifier(n) && propDepsMap.has(n.text)) {
10797
10834
  const isObjectKey = parent && ts14.isPropertyAssignment(parent) && parent.name === n;
10798
- const isShorthand = parent && ts14.isShorthandPropertyAssignment(parent) && parent.name === n;
10799
10835
  const isAccessName = parent && ts14.isPropertyAccessExpression(parent) && parent.name === n;
10800
- if (!isObjectKey && !isShorthand && !isAccessName) {
10836
+ if (!isObjectKey && !isAccessName) {
10801
10837
  const deps = propDepsMap.get(n.text);
10802
10838
  if (deps && deps.size > 0) {
10803
10839
  if (!acc)
@@ -11275,8 +11311,7 @@ function lowerFormControlValueSsr(tagName, attrs, children) {
11275
11311
  children.push({
11276
11312
  type: "expression",
11277
11313
  expr,
11278
- templateExpr: `escapeText(${templateExpr ?? expr})`,
11279
- escapeInClientTemplate: true,
11314
+ templateExpr,
11280
11315
  typeInfo: null,
11281
11316
  reactive: false,
11282
11317
  slotId: null,
@@ -11287,26 +11322,76 @@ function lowerFormControlValueSsr(tagName, attrs, children) {
11287
11322
  }
11288
11323
  const selectedForLiteral = (optValue) => AttrValueOf.expression(`(${expr}) === ${JSON.stringify(optValue)}`, templateExpr !== undefined ? { templateExpr: `(${templateExpr}) === ${JSON.stringify(optValue)}` } : undefined);
11289
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;
11290
11327
  const distribute = (nodes) => {
11291
11328
  for (const n of nodes) {
11329
+ if (n.type === "text")
11330
+ continue;
11292
11331
  if (n.type === "element" && n.tag === "option") {
11293
- if (n.attrs.some((a) => a.name === "selected"))
11332
+ if (n.attrs.some((a) => a.name === "selected")) {
11333
+ optionSetIsDynamic = true;
11294
11334
  continue;
11335
+ }
11295
11336
  const optValue = n.attrs.find((a) => a.name === "value");
11296
- if (!optValue)
11337
+ if (!optValue) {
11338
+ optionSetIsDynamic = true;
11297
11339
  continue;
11340
+ }
11298
11341
  if (optValue.value.kind === "literal") {
11299
- 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);
11300
11345
  } else if (optValue.value.kind === "expression") {
11301
11346
  const selected = selectedForExpr(optValue.value.expr, optValue.value.templateExpr);
11302
11347
  n.attrs.push({ name: "selected", value: selected, loc: n.loc });
11348
+ matchConditions.push(selected);
11349
+ } else {
11350
+ optionSetIsDynamic = true;
11303
11351
  }
11304
- } 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") {
11353
+ distribute(n.children);
11354
+ } else if (n.type === "loop") {
11355
+ optionSetIsDynamic = true;
11305
11356
  distribute(n.children);
11357
+ } else {
11358
+ optionSetIsDynamic = true;
11306
11359
  }
11307
11360
  }
11308
11361
  };
11309
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;
11310
11395
  }
11311
11396
  function transformHtmlElement(node, ctx, tagName) {
11312
11397
  const { attrs, events, ref } = processAttributes(node.openingElement.attributes, ctx);
@@ -15669,7 +15754,7 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx, options)
15669
15754
  },
15670
15755
  loop: ({ node: n, scope, descend }) => {
15671
15756
  const emitDepth = fixedDepth ?? scope.depth + 1;
15672
- 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;
15673
15758
  const template = n.children.map((c) => irToPlaceholderTemplate(c, undefined, emitDepth, loopParamsForTemplate)).join("");
15674
15759
  const refsOuter = outerLoopParam ? identifierPattern(outerLoopParam).test(n.array) : false;
15675
15760
  const bindings = emptyLoopChildBindings();
@@ -16266,7 +16351,7 @@ function collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loop
16266
16351
  const readsPreamble = preambleNames !== undefined && preambleNames.size > 0 && anyNameIn(expanded.freeIds ?? extractFreeIdentifiersFromText(expanded.expr), preambleNames);
16267
16352
  if (!readsPreamble && classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind === "none")
16268
16353
  return;
16269
- const loopParamsForCond = loopParam ? [{ param: loopParam, bindings: loopParamBindings }] : undefined;
16354
+ const loopParamsForCond = loopParam ? [{ param: loopParam, bindings: loopParamBindings, index: loopIndex }] : undefined;
16270
16355
  const whenTrueHtml = irToHtmlTemplate(n.whenTrue, undefined, 0, loopParamsForCond, "__slots");
16271
16356
  const whenFalseHtml = irToHtmlTemplate(n.whenFalse, undefined, 0, loopParamsForCond, "__slots");
16272
16357
  conditionals.push({
@@ -16850,6 +16935,7 @@ var RUNTIME_IMPORT_CANDIDATES = [
16850
16935
  "escapeAttr",
16851
16936
  "escapeText",
16852
16937
  "escapeTextOrNode",
16938
+ "escapeCommentText",
16853
16939
  "bfMarkup",
16854
16940
  "escapeTextOrMarkup",
16855
16941
  "markupOrEmpty",
@@ -17909,7 +17995,7 @@ function emitRegistrationAndHydration(lines, ctx, _ir, graph, inlinability) {
17909
17995
  const isCommentScope = isFragmentRoot || _ir.root.type === "component";
17910
17996
  const defParts = [`init: init${name}`];
17911
17997
  if (canGenerateStaticTemplate(_ir.root, propNamesForStaticCheck, inlinableConstants, unsafeLocalNames)) {
17912
- const markupSlotIds = new Set(ctx.dynamicElements.map((e) => e.slotId));
17998
+ const markupSlotIds = markupSlotIdsOf(ctx);
17913
17999
  const templateHtml = irToComponentTemplate(_ir.root, inlinableConstants, restSpreadNames, ctx.propsObjectName, markupSlotIds, ctx.restPropsName);
17914
18000
  if (templateHtml) {
17915
18001
  defParts.push(buildTemplateDefPart(ctx, templateHtml));
@@ -18934,12 +19020,12 @@ function nestedLoopIndexAlias(inner, syntheticIndexVar, paramHead, comps, events
18934
19020
  return null;
18935
19021
  return `const ${index} = ${syntheticIndexVar}`;
18936
19022
  }
18937
- function buildChildRefBindings(refs, loopParam, loopParamBindings) {
19023
+ function buildChildRefBindings(refs, loopParam, loopParamBindings, loopIndex) {
18938
19024
  if (refs.length === 0)
18939
19025
  return [];
18940
19026
  return refs.map((r) => ({
18941
19027
  childSlotId: r.childSlotId,
18942
- callback: wrapLoopParamAsAccessor(r.callback, loopParam, loopParamBindings)
19028
+ callback: wrapLoopParamAsAccessor(r.callback, loopParam, loopParamBindings, loopIndex)
18943
19029
  }));
18944
19030
  }
18945
19031
  function buildStaticChildRefBindings(refs) {
@@ -18972,17 +19058,17 @@ function destructureLoopParam(param, paramBindings) {
18972
19058
  }
18973
19059
  return { head: param, unwrap: "" };
18974
19060
  }
18975
- function buildPreambleRegionPlans(regions, loopParam, loopParamBindings) {
19061
+ function buildPreambleRegionPlans(regions, loopParam, loopParamBindings, loopIndex) {
18976
19062
  if (!regions || regions.length === 0)
18977
19063
  return [];
18978
19064
  return regions.map((r) => {
18979
- const wrapped = wrapLoopParamAsAccessor(r.expr, loopParam, loopParamBindings);
18980
- 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, {});
18981
19067
  return { slotId: r.slotId, valueExpr };
18982
19068
  });
18983
19069
  }
18984
- function buildComponentPropsExpr2(comp, loopParam, loopParamBindings) {
18985
- 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;
18986
19072
  const entries = comp.props.map((p) => {
18987
19073
  if (p.isEventHandler) {
18988
19074
  const handlerExpr = attrValueToString(p.value) ?? "undefined";
@@ -19026,8 +19112,8 @@ function buildDepthLevels(innerLoops, nestedComps, childEvents) {
19026
19112
  loopInfo: loop
19027
19113
  }));
19028
19114
  }
19029
- function emitEventSetup(ls, indent, elVar, ev, loopParam, loopParamBindings, bodyIsMultiRoot = false) {
19030
- 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;
19031
19117
  emitListenerBlock(ls, indent, elVar, ev.childSlotId, "__e", ev.eventName, handler, "dom", bodyIsMultiRoot);
19032
19118
  }
19033
19119
  function buildCompSelector(comp) {
@@ -19039,15 +19125,15 @@ function isTextOnlyConditional(node) {
19039
19125
  const checkNode = (n) => n.type === "text" || n.type === "expression" || n.type === "conditional" && isTextOnlyConditional(n);
19040
19126
  return checkNode(node.whenTrue) && checkNode(node.whenFalse);
19041
19127
  }
19042
- function emitComponentAndEventSetup(ls, indent, elVar, comps, events, loopParam, loopParamBindings, bodyIsMultiRoot = false) {
19043
- 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;
19044
19130
  const upsertFn = bodyIsMultiRoot ? "upsertChildItem" : "upsertChild";
19045
19131
  for (const comp of comps) {
19046
- const propsExpr = buildComponentPropsExpr2(comp, loopParam, loopParamBindings);
19132
+ const propsExpr = buildComponentPropsExpr2(comp, loopParam, loopParamBindings, loopIndex);
19047
19133
  const isTextOnly = comp.children?.length ? comp.children.every((c) => c.type === "expression" || c.type === "text" || isTextOnlyConditional(c)) : false;
19048
19134
  const rawChildrenExpr = isTextOnly ? irChildrenToJsExpr(comp.children) : null;
19049
19135
  const childrenFreeIds = isTextOnly && comp.children ? irChildrenFreeIds(comp.children) : undefined;
19050
- 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));
19051
19137
  const slotIdLit = comp.slotId ? `'${comp.slotId}'` : "null";
19052
19138
  const keyProp = comp.props.find((p) => p.name === "key");
19053
19139
  const keyArg = keyProp ? `, ${wrap(attrValueToString(keyProp.value) ?? "undefined")}` : ", undefined";
@@ -19060,7 +19146,7 @@ function emitComponentAndEventSetup(ls, indent, elVar, comps, events, loopParam,
19060
19146
  }
19061
19147
  }
19062
19148
  for (const ev of events) {
19063
- emitEventSetup(ls, indent, elVar, ev, loopParam, loopParamBindings, bodyIsMultiRoot);
19149
+ emitEventSetup(ls, indent, elVar, ev, loopParam, loopParamBindings, bodyIsMultiRoot, loopIndex);
19064
19150
  }
19065
19151
  }
19066
19152
 
@@ -19441,12 +19527,12 @@ function buildBranchInnerLoopsPlan(args) {
19441
19527
  const inner = innerLoops[i];
19442
19528
  if (!inner.refsOuterParam || !inner.template)
19443
19529
  continue;
19444
- const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings);
19445
- 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);
19446
19532
  const csl = inner.containerSlotId;
19447
19533
  const containerExpr = csl ? `(${scopeVar}.querySelector('[bf="${csl}"]') ?? ${scopeVar}.querySelector(\`[${BF_HOST2}="\${__scopeId}"][${BF_AT2}="${csl}"]\`) ?? ${scopeVar})` : `findCondContainer(${scopeVar}, '${condSlotId}')`;
19448
19534
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(inner.param, inner.paramBindings);
19449
- 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;
19450
19536
  const wrapIRNode = (node) => {
19451
19537
  if (node.type === "component") {
19452
19538
  return {
@@ -19608,8 +19694,8 @@ function buildLoopChildArmPlan(args) {
19608
19694
 
19609
19695
  // src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts
19610
19696
  function buildReactiveEffectsPlan(args) {
19611
- const { attrs, texts, conditionals, loopParam, loopParamBindings, profileComponentName } = args;
19612
- 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);
19613
19699
  const attrsBySlot = new Map;
19614
19700
  for (const attr of attrs) {
19615
19701
  let bucket = attrsBySlot.get(attr.childSlotId);
@@ -19693,6 +19779,7 @@ function buildLoopReactiveEffectsPlan(elem, profileComponentName) {
19693
19779
  conditionals: elem.bindings.conditionals,
19694
19780
  loopParam: elem.param,
19695
19781
  loopParamBindings: elem.paramBindings,
19782
+ loopIndex: elem.index,
19696
19783
  profileComponentName
19697
19784
  });
19698
19785
  }
@@ -19771,10 +19858,10 @@ function buildInnerLoopsPlan(args) {
19771
19858
  return plan;
19772
19859
  }
19773
19860
  function buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, outerLoopParamBindings) {
19774
- const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings);
19775
- 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);
19776
19863
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(inner.param, inner.paramBindings);
19777
- 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;
19778
19865
  const wrapIRNode = (node) => {
19779
19866
  if (node.type === "component") {
19780
19867
  return {
@@ -19805,11 +19892,11 @@ function buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, o
19805
19892
  }));
19806
19893
  const reactiveTexts = inner.bindings.reactiveTexts.map((text) => ({
19807
19894
  slotId: text.slotId,
19808
- wrappedExpression: wrapLoopParamAsAccessor(wrapOuter(text.expression), inner.param, inner.paramBindings),
19895
+ wrappedExpression: wrapLoopParamAsAccessor(wrapOuter(text.expression), inner.param, inner.paramBindings, inner.index),
19809
19896
  insideConditional: !!text.insideConditional
19810
19897
  }));
19811
19898
  const reactiveAttrs = inner.bindings.reactiveAttrs.map((attr) => {
19812
- const wrapped = wrapLoopParamAsAccessor(wrapOuter(attr.expression), inner.param, inner.paramBindings);
19899
+ const wrapped = wrapLoopParamAsAccessor(wrapOuter(attr.expression), inner.param, inner.paramBindings, inner.index);
19813
19900
  return {
19814
19901
  slotId: attr.childSlotId,
19815
19902
  attrName: attr.attrName,
@@ -19826,14 +19913,14 @@ function buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, o
19826
19913
  if (inner.preamble) {
19827
19914
  const leafLoopParams = outerLoopParam ? [
19828
19915
  { param: outerLoopParam, bindings: outerLoopParamBindings },
19829
- { param: inner.param, bindings: inner.paramBindings }
19830
- ] : [{ 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 }];
19831
19918
  preludeStatements.push(renderPreamble(inner.preamble, {
19832
19919
  transformJs: (t) => wrapInner(wrapOuter(t)),
19833
19920
  renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, leafLoopParams, undefined)
19834
19921
  }));
19835
19922
  }
19836
- const childRefs = buildChildRefBindings(inner.bindings.refs, inner.param, inner.paramBindings);
19923
+ const childRefs = buildChildRefBindings(inner.bindings.refs, inner.param, inner.paramBindings, inner.index);
19837
19924
  const conditionals = buildLoopChildConditionalsPlan({
19838
19925
  conditionals: inner.bindings.conditionals,
19839
19926
  scopeVar: `__innerEl${uidSuffix}`,
@@ -19882,7 +19969,7 @@ function buildTopLevelCompositePlan(elem, profileComponentName) {
19882
19969
  const nestedComps = elem.nestedComponents;
19883
19970
  const depthLevels = buildDepthLevels(elem.innerLoops ?? [], nestedComps, elem.bindings.events);
19884
19971
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
19885
- const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings);
19972
+ const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings, elem.index);
19886
19973
  const outerCompsByDepth = nestedComps.filter((c) => !c.loopDepth || c.loopDepth === 0);
19887
19974
  return {
19888
19975
  kind: "composite",
@@ -19896,12 +19983,12 @@ function buildTopLevelCompositePlan(elem, profileComponentName) {
19896
19983
  indexParam: elem.index || "__idx",
19897
19984
  mapPreambleWrapped: elem.preamble ? renderPreamble(elem.preamble, {
19898
19985
  transformJs: wrap,
19899
- 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)
19900
19987
  }) : "",
19901
19988
  template: elem.template,
19902
19989
  outerComps: filterCondCompsOut(outerCompsByDepth, elem.bindings.conditionals),
19903
19990
  outerEvents: elem.bindings.events.filter((ev) => ev.nestedLoops.length === 0),
19904
- childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
19991
+ childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings, elem.index),
19905
19992
  innerLoops: buildInnerLoopsPlan({
19906
19993
  levels: depthLevels,
19907
19994
  parentElVar: "__el",
@@ -19910,12 +19997,14 @@ function buildTopLevelCompositePlan(elem, profileComponentName) {
19910
19997
  }),
19911
19998
  loopParam: elem.param,
19912
19999
  loopParamBindings: elem.paramBindings,
20000
+ loopIndex: elem.index,
19913
20001
  reactiveEffects: hasReactive(elem) ? buildReactiveEffectsPlan({
19914
20002
  attrs: elem.bindings.reactiveAttrs,
19915
20003
  texts: elem.bindings.reactiveTexts,
19916
20004
  conditionals: elem.bindings.conditionals,
19917
20005
  loopParam: elem.param,
19918
20006
  loopParamBindings: elem.paramBindings,
20007
+ loopIndex: elem.index,
19919
20008
  profileComponentName
19920
20009
  }) : null,
19921
20010
  branchClearChildren: false,
@@ -19932,7 +20021,7 @@ function buildBranchCompositePlan(loop, cv, profileComponentName) {
19932
20021
  const childEvents = loop.bindings.events;
19933
20022
  const depthLevels = buildDepthLevels(innerLoops, nestedComps, childEvents);
19934
20023
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(loop.param, loop.paramBindings);
19935
- const wrap = (expr) => wrapLoopParamAsAccessor(expr, loop.param, loop.paramBindings);
20024
+ const wrap = (expr) => wrapLoopParamAsAccessor(expr, loop.param, loop.paramBindings, loop.index);
19936
20025
  const outerCompsByDepth = nestedComps.filter((c) => !c.loopDepth || c.loopDepth === 0);
19937
20026
  return {
19938
20027
  kind: "composite",
@@ -19946,12 +20035,12 @@ function buildBranchCompositePlan(loop, cv, profileComponentName) {
19946
20035
  indexParam: loop.index || "__idx",
19947
20036
  mapPreambleWrapped: loop.preamble ? renderPreamble(loop.preamble, {
19948
20037
  transformJs: wrap,
19949
- 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)
19950
20039
  }) : "",
19951
20040
  template: loop.template,
19952
20041
  outerComps: filterCondCompsOut(outerCompsByDepth, loop.bindings.conditionals),
19953
20042
  outerEvents: childEvents.filter((ev) => ev.nestedLoops.length === 0),
19954
- childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings),
20043
+ childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings, loop.index),
19955
20044
  innerLoops: buildInnerLoopsPlan({
19956
20045
  levels: depthLevels,
19957
20046
  parentElVar: "__el",
@@ -19960,12 +20049,14 @@ function buildBranchCompositePlan(loop, cv, profileComponentName) {
19960
20049
  }),
19961
20050
  loopParam: loop.param,
19962
20051
  loopParamBindings: loop.paramBindings,
20052
+ loopIndex: loop.index,
19963
20053
  reactiveEffects: hasReactiveBranch(loop) ? buildReactiveEffectsPlan({
19964
20054
  attrs: loop.bindings.reactiveAttrs,
19965
20055
  texts: loop.bindings.reactiveTexts,
19966
20056
  conditionals: loop.bindings.conditionals,
19967
20057
  loopParam: loop.param,
19968
20058
  loopParamBindings: loop.paramBindings,
20059
+ loopIndex: loop.index,
19969
20060
  profileComponentName
19970
20061
  }) : null,
19971
20062
  branchClearChildren: true,
@@ -20100,7 +20191,7 @@ function wiringOn(branch) {
20100
20191
  found.push("reactive text");
20101
20192
  return found;
20102
20193
  }
20103
- function analyzeLazyConditional(cond, indexParam, arms) {
20194
+ function analyzeLazyConditional(cond, arms) {
20104
20195
  for (const [label, branch] of [["true", cond.whenTrue], ["false", cond.whenFalse]]) {
20105
20196
  const wiring = wiringOn(branch);
20106
20197
  if (wiring.length > 0) {
@@ -20121,9 +20212,6 @@ function analyzeLazyConditional(cond, indexParam, arms) {
20121
20212
  if (!cond.conditionFreeIdentifiers) {
20122
20213
  return NO(`conditional on slot ${cond.slotId}: condition has no analyzable identifier set`);
20123
20214
  }
20124
- if (cond.conditionFreeIdentifiers.has(indexParam)) {
20125
- return NO(`conditional on slot ${cond.slotId}: condition reads the loop index parameter '${indexParam}'`);
20126
- }
20127
20215
  return {
20128
20216
  lazySafe: true,
20129
20217
  facts: {
@@ -20142,7 +20230,7 @@ var NO_PREAMBLE = {
20142
20230
  facts: { declaredNames: new Set, freeNames: new Set }
20143
20231
  };
20144
20232
  var NO2 = (reason) => ({ lazySafe: false, reason });
20145
- function analyzeLazyPreamble(preamble, indexParam, primableNames) {
20233
+ function analyzeLazyPreamble(preamble, primableNames) {
20146
20234
  if (!preamble)
20147
20235
  return NO_PREAMBLE;
20148
20236
  if (preamble.builderNames.length > 0) {
@@ -20181,9 +20269,6 @@ function analyzeLazyPreamble(preamble, indexParam, primableNames) {
20181
20269
  }
20182
20270
  }
20183
20271
  const readNames = extractFreeIdentifiersFromStatementText(text);
20184
- if (readNames.has(indexParam) && !declaredNames.has(indexParam)) {
20185
- return NO2(`map-callback preamble reads the loop index parameter '${indexParam}'`);
20186
- }
20187
20272
  const freeNames = new Set(readNames);
20188
20273
  for (const declared of declaredNames)
20189
20274
  freeNames.delete(declared);
@@ -20333,9 +20418,6 @@ function lazyRowEligibility(args) {
20333
20418
  if (shape.hasParamUnwrap)
20334
20419
  return NO3("destructured loop param without param bindings");
20335
20420
  for (const b of bindings) {
20336
- if (b.referencesIndex) {
20337
- return NO3(`binding on slot ${b.slotId} references the loop index parameter`);
20338
- }
20339
20421
  if (b.opaqueOuterNames.includes(UNKNOWN_IDENTIFIERS)) {
20340
20422
  return NO3(`binding on slot ${b.slotId} has no analyzable identifier set`);
20341
20423
  }
@@ -20420,6 +20502,7 @@ function classifyLazyBinding(args) {
20420
20502
  }
20421
20503
  if (name === indexParam) {
20422
20504
  referencesIndex = true;
20505
+ readsItem = true;
20423
20506
  return;
20424
20507
  }
20425
20508
  if (INERT_BINDING_GLOBALS.has(name))
@@ -20469,12 +20552,12 @@ function decideLazyRow(args) {
20469
20552
  for (const b of loop.paramBindings ?? [])
20470
20553
  rowLocalNames.add(b.name);
20471
20554
  const primableNames = new Set([...scope.signals.keys(), ...scope.memos]);
20472
- const preambleAnalysis = analyzeLazyPreamble(loop.preamble, args.indexParam, primableNames);
20555
+ const preambleAnalysis = analyzeLazyPreamble(loop.preamble, primableNames);
20473
20556
  const rawConditionals = loop.bindings.conditionals ?? [];
20474
20557
  const condFacts = [];
20475
20558
  let conditionalRefusal = null;
20476
20559
  for (const cond of rawConditionals) {
20477
- const verdict = analyzeLazyConditional(cond, args.indexParam, {
20560
+ const verdict = analyzeLazyConditional(cond, {
20478
20561
  whenTrueHtml: addCondAttrToTemplate(wrap(cond.whenTrueHtml), cond.slotId),
20479
20562
  whenFalseHtml: addCondAttrToTemplate(wrap(cond.whenFalseHtml), cond.slotId)
20480
20563
  });
@@ -20616,7 +20699,8 @@ function decideLazyRow(args) {
20616
20699
  preambleStatements: args.mapPreambleWrapped,
20617
20700
  itemNeedsPreamble: [...attrs, ...texts, ...conditionals].some((b) => b.readsItem && b.readsPreamble),
20618
20701
  outerNeedsPreamble: [...attrs, ...texts, ...conditionals].some((b) => b.readsOuter && b.readsPreamble),
20619
- conditionals
20702
+ conditionals,
20703
+ readsIndex: classified.some((c) => c.referencesIndex)
20620
20704
  },
20621
20705
  decision
20622
20706
  };
@@ -20667,6 +20751,43 @@ function loopSourceIdentifiers(loop, arrayExpr) {
20667
20751
  return names;
20668
20752
  }
20669
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
+
20670
20791
  // src/ir-to-client-js/control-flow/plan/build-branch-loop.ts
20671
20792
  function buildBranchLoopPlan(loop, profileComponentName, lazyScope) {
20672
20793
  const containerSlotId = loop.containerSlotId;
@@ -20681,16 +20802,18 @@ function buildBranchLoopPlan(loop, profileComponentName, lazyScope) {
20681
20802
  };
20682
20803
  return composite;
20683
20804
  }
20684
- const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(loop.param, loop.paramBindings);
20685
20805
  const hasReactiveEffects = loop.bindings.reactiveAttrs.length > 0 || loop.bindings.reactiveTexts.length > 0 || loop.bindings.conditionals.length > 0;
20686
20806
  const fm = loop.flatMapClient;
20687
20807
  const arrayExpr = fm ? `(${buildChainedArrayExpr(loop)}).flatMap(${fm.params} => ${fm.body})` : buildChainedArrayExpr(loop);
20688
- const indexParam = loop.index || "__idx";
20689
- const mapPreambleWrapped = loop.preamble ? renderPreamble(loop.preamble, {
20690
- transformJs: (t) => wrapLoopParamAsAccessor(t, loop.param, loop.paramBindings),
20691
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings }], undefined)
20692
- }) : "";
20693
- 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;
20694
20817
  const plan = {
20695
20818
  kind: "plain",
20696
20819
  rowConstruction: "string-template",
@@ -20703,30 +20826,20 @@ function buildBranchLoopPlan(loop, profileComponentName, lazyScope) {
20703
20826
  paramHead,
20704
20827
  paramUnwrap,
20705
20828
  indexParam,
20706
- mapPreambleWrapped,
20707
- lazyRow: buildLazyRowPlan({
20708
- loop,
20709
- arrayExpr,
20710
- indexParam,
20711
- paramUnwrap,
20712
- mapPreambleWrapped,
20713
- preambleRegionCount: preambleRegions.length,
20714
- callSite: "branch-plain",
20715
- flatMapLeafItem: Boolean(fm),
20716
- anchored: false,
20717
- scope: lazyScope
20718
- }) ?? undefined,
20719
- template: loop.template,
20829
+ mapPreambleWrapped: core.mapPreambleWrapped,
20830
+ lazyRow,
20831
+ template: core.template,
20720
20832
  reactiveEffects: hasReactiveEffects ? buildReactiveEffectsPlan({
20721
20833
  attrs: loop.bindings.reactiveAttrs,
20722
20834
  texts: loop.bindings.reactiveTexts,
20723
20835
  conditionals: loop.bindings.conditionals,
20724
20836
  loopParam: loop.param,
20725
20837
  loopParamBindings: loop.paramBindings,
20838
+ loopIndex: loop.index,
20726
20839
  profileComponentName
20727
20840
  }) : null,
20728
20841
  eventDelegation: buildBranchLoopDelegationPlan(loop, cv, profileComponentName),
20729
- childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings),
20842
+ childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings, loop.index),
20730
20843
  preambleRegions,
20731
20844
  bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false,
20732
20845
  profileLoopId: profileComponentName ? `${profileComponentName}#binding:${containerSlotId}` : undefined
@@ -21011,7 +21124,7 @@ function emitDynamicTextUpdates(lines, ctx) {
21011
21124
  const __textSlot = (normalElems[0] ?? conditionalElems[0])?.slotId;
21012
21125
  let writer = "";
21013
21126
  if (normalElems.length > 0) {
21014
- 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: [] }));
21015
21128
  writer = claimWriterVarName(slots, varSlotId);
21016
21129
  lines.push(` const ${writer} = lazySlots(__scope, ${claimPlanLiteral(slots)})`);
21017
21130
  }
@@ -21549,6 +21662,8 @@ function stringifyLazyRowLoop(lines, o) {
21549
21662
  lines.push(`${indent}${o.guardContainer ? `if (${o.containerVar}) ` : ""}${call}`);
21550
21663
  const b1 = `${indent} `;
21551
21664
  const b2 = `${indent} `;
21665
+ if (lazyRow.readsIndex)
21666
+ lines.push(`${b1}indexDriven: true,`);
21552
21667
  lines.push(`${b1}createRow: (__e, ${o.indexParam}) => {`);
21553
21668
  lines.push(`${b2}const ${paramHead} = () => __e.item`);
21554
21669
  if (lazyRow.preambleStatements)
@@ -21578,6 +21693,8 @@ function stringifyLazyRowLoop(lines, o) {
21578
21693
  } else {
21579
21694
  lines.push(`${b1}applyItem: (__e) => {`);
21580
21695
  lines.push(`${b2}const ${paramHead} = () => __e.item`);
21696
+ if (lazyRow.readsIndex)
21697
+ lines.push(`${b2}const ${o.indexParam} = __e.index`);
21581
21698
  lines.push(`${b2}const __r = __e.refs ?? (__e.refs = [])`);
21582
21699
  lines.push(`${b2}const __l = __e.last ?? (__e.last = [])`);
21583
21700
  if (lazyRow.itemNeedsPreamble && lazyRow.preambleStatements) {
@@ -21604,6 +21721,8 @@ function stringifyLazyRowLoop(lines, o) {
21604
21721
  lines.push(`${b2}${g}()`);
21605
21722
  lines.push(`${b2}for (const __e of __es) {`);
21606
21723
  lines.push(`${b3}const ${paramHead} = () => __e.item`);
21724
+ if (lazyRow.readsIndex)
21725
+ lines.push(`${b3}const ${o.indexParam} = __e.index`);
21607
21726
  lines.push(`${b3}const __r = __e.refs ?? (__e.refs = [])`);
21608
21727
  lines.push(`${b3}const __l = __e.last ?? (__e.last = [])`);
21609
21728
  if (lazyRow.outerNeedsPreamble && lazyRow.preambleStatements) {
@@ -22128,6 +22247,7 @@ function stringifyCompositeLoop(lines, plan) {
22128
22247
  innerLoops,
22129
22248
  loopParam,
22130
22249
  loopParamBindings,
22250
+ loopIndex,
22131
22251
  reactiveEffects,
22132
22252
  childRefs,
22133
22253
  branchClearChildren,
@@ -22160,7 +22280,7 @@ function stringifyCompositeLoop(lines, plan) {
22160
22280
  singleRootLayout: "multiline",
22161
22281
  mountRow: true
22162
22282
  });
22163
- emitComponentAndEventSetup(lines, bodyIndent, "__el", compsArr, eventsArr, loopParam, loopParamBindings, bodyIsMultiRoot);
22283
+ emitComponentAndEventSetup(lines, bodyIndent, "__el", compsArr, eventsArr, loopParam, loopParamBindings, bodyIsMultiRoot, loopIndex);
22164
22284
  if (innerLoops.length > 0) {
22165
22285
  stringifyInnerLoops(lines, innerLoops, bodyIndent, pc);
22166
22286
  }
@@ -22582,11 +22702,11 @@ function scopeRefToVar(ref) {
22582
22702
  // src/ir-to-client-js/control-flow/plan/build-component-loop.ts
22583
22703
  function buildComponentLoopPlan(elem, profileComponentName) {
22584
22704
  const { name } = elem.childComponent;
22585
- const propsExpr = buildComponentPropsExpr2(elem.childComponent, elem.param);
22586
- 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);
22587
22707
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
22588
22708
  const mapPreambleWrapped = elem.preamble ? renderPreamble(elem.preamble, {
22589
- transformJs: (text) => wrapLoopParamAsAccessor(text, elem.param, elem.paramBindings),
22709
+ transformJs: (text) => wrapLoopParamAsAccessor(text, elem.param, elem.paramBindings, elem.index),
22590
22710
  renderLeaf: () => {
22591
22711
  internalInvariant(false, "component-root loop received a JSX-bearing preamble — Phase 1 should have refused it");
22592
22712
  }
@@ -22596,12 +22716,12 @@ function buildComponentLoopPlan(elem, profileComponentName) {
22596
22716
  const isTextOnly = comp.children?.length ? comp.children.every((c) => c.type === "expression" || c.type === "text" || isTextOnlyConditional(c)) : false;
22597
22717
  const rawChildrenExpr = isTextOnly ? irChildrenToJsExpr(comp.children) : null;
22598
22718
  const childrenFreeIds = isTextOnly && comp.children ? irChildrenFreeIds(comp.children) : undefined;
22599
- 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));
22600
22720
  return {
22601
22721
  componentName: comp.name,
22602
22722
  selector: buildCompSelector(comp),
22603
- propsExpr: buildComponentPropsExpr2(comp, elem.param),
22604
- 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
22605
22725
  };
22606
22726
  });
22607
22727
  const hasChildConds = elem.bindings.conditionals.length > 0;
@@ -22620,7 +22740,7 @@ function buildComponentLoopPlan(elem, profileComponentName) {
22620
22740
  componentPropsExpr: propsExpr,
22621
22741
  keyExpr,
22622
22742
  nestedComps,
22623
- childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
22743
+ childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings, elem.index),
22624
22744
  profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : undefined,
22625
22745
  childConditionalEffects: hasChildConds ? buildReactiveEffectsPlan({
22626
22746
  attrs: [],
@@ -22628,6 +22748,7 @@ function buildComponentLoopPlan(elem, profileComponentName) {
22628
22748
  conditionals: elem.bindings.conditionals,
22629
22749
  loopParam: elem.param,
22630
22750
  loopParamBindings: elem.paramBindings,
22751
+ loopIndex: elem.index,
22631
22752
  profileComponentName
22632
22753
  }) : null
22633
22754
  };
@@ -22651,8 +22772,6 @@ function buildLoopPlan(elem, opts) {
22651
22772
  return buildPlainLoopPlan(elem, opts.profileComponentName, opts.lazyScope);
22652
22773
  }
22653
22774
  function buildPlainLoopPlan(elem, profileComponentName, lazyScope) {
22654
- const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings);
22655
- const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
22656
22775
  const hasReactive2 = elem.bindings.reactiveAttrs.length > 0 || elem.bindings.reactiveTexts.length > 0 || elem.bindings.conditionals.length > 0;
22657
22776
  if (elem.flatMapClient) {
22658
22777
  return {
@@ -22678,12 +22797,15 @@ function buildPlainLoopPlan(elem, profileComponentName, lazyScope) {
22678
22797
  };
22679
22798
  }
22680
22799
  const arrayExpr = buildChainedArrayExpr(elem);
22681
- const indexParam = elem.index || "__idx";
22682
- const mapPreambleWrapped = elem.preamble ? renderPreamble(elem.preamble, {
22683
- transformJs: wrap,
22684
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined)
22685
- }) : "";
22686
- 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;
22687
22809
  return {
22688
22810
  kind: "plain",
22689
22811
  rowConstruction: "string-template",
@@ -22695,28 +22817,17 @@ function buildPlainLoopPlan(elem, profileComponentName, lazyScope) {
22695
22817
  paramHead,
22696
22818
  paramUnwrap,
22697
22819
  indexParam,
22698
- lazyRow: buildLazyRowPlan({
22699
- loop: elem,
22700
- arrayExpr,
22701
- indexParam,
22702
- paramUnwrap,
22703
- mapPreambleWrapped,
22704
- preambleRegionCount: preambleRegions.length,
22705
- callSite: "plain",
22706
- flatMapLeafItem: false,
22707
- anchored: elem.bodyIsItemConditional ?? false,
22708
- scope: lazyScope
22709
- }) ?? undefined,
22710
- mapPreambleWrapped,
22711
- template: elem.template,
22820
+ lazyRow,
22821
+ mapPreambleWrapped: core.mapPreambleWrapped,
22822
+ template: core.template,
22712
22823
  skeletonTemplate: elem.skeletonTemplate,
22713
22824
  skeletonPaths: elem.skeletonPaths,
22714
22825
  reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
22715
- childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
22826
+ childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings, elem.index),
22716
22827
  preambleRegions,
22717
22828
  bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
22718
22829
  anchored: elem.bodyIsItemConditional ?? false,
22719
- anchorKeyExpr: elem.key ? wrap(elem.key) : elem.index || "__idx"
22830
+ anchorKeyExpr: elem.key ? wrap(elem.key) : `${indexParam}()`
22720
22831
  };
22721
22832
  }
22722
22833
  function buildStaticLoopPlan(elem, unsafeLocalNames, profileComponentName) {
@@ -22758,7 +22869,7 @@ function buildStaticLoopMaterialize(elem, unsafeLocalNames) {
22758
22869
  return {
22759
22870
  itemTemplate: elem.staticItemTemplate,
22760
22871
  mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
22761
- 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)
22762
22873
  }) : "",
22763
22874
  bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false
22764
22875
  };
@@ -23335,7 +23446,7 @@ function generateTemplateOnlyMount(ir, ctx) {
23335
23446
  const restSpreadNames = resolveRestSpreadNames(ctx);
23336
23447
  let templateHtml;
23337
23448
  if (canGenerateStaticTemplate(ir.root, propNamesForStaticCheck, inlinableConstants, unsafeLocalNames)) {
23338
- const markupSlotIds = new Set(ctx.dynamicElements.map((e) => e.slotId));
23449
+ const markupSlotIds = markupSlotIdsOf(ctx);
23339
23450
  templateHtml = irToComponentTemplate(ir.root, inlinableConstants, restSpreadNames, ctx.propsObjectName, markupSlotIds, ctx.restPropsName);
23340
23451
  }
23341
23452
  if (!templateHtml) {
@@ -24072,6 +24183,21 @@ function extractSsrDefaults(metadata) {
24072
24183
  }
24073
24184
  bindings[memo.name] = value;
24074
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
+ }
24075
24201
  if (metadata.propsObjectName !== null) {
24076
24202
  const referenced = new Set;
24077
24203
  for (const sig of metadata.signals) {
@@ -29750,7 +29876,9 @@ export {
29750
29876
  rewriteDynamicImportsInSource,
29751
29877
  resolveStaticLoopSource,
29752
29878
  resolveSetters,
29879
+ resolveGetterAliases,
29753
29880
  resolveDangerousInnerHtml,
29881
+ resolveBodyDestructuredPropAliases,
29754
29882
  resetCompilerCounters,
29755
29883
  registerLoweringPlugin,
29756
29884
  registerBuiltinLoweringPlugins,
@@ -29863,6 +29991,7 @@ export {
29863
29991
  collectModuleStringConsts,
29864
29992
  collectLoopBoundNames,
29865
29993
  collectContextConsumers,
29994
+ collectAliasableGetterNames,
29866
29995
  buildWhyUpdate,
29867
29996
  buildStaticBudget,
29868
29997
  buildSourceMapFromIR,