@barefootjs/jsx 0.26.3 → 0.27.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 (99) hide show
  1. package/dist/compiler.d.ts.map +1 -1
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +735 -140
  5. package/dist/ir-to-client-js/build-references.d.ts.map +1 -1
  6. package/dist/ir-to-client-js/client-only-elision.d.ts +103 -0
  7. package/dist/ir-to-client-js/client-only-elision.d.ts.map +1 -0
  8. package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
  9. package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts +12 -1
  10. package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts.map +1 -1
  11. package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts.map +1 -1
  12. package/dist/ir-to-client-js/control-flow/plan/build-event-delegation.d.ts.map +1 -1
  13. package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
  14. package/dist/ir-to-client-js/control-flow/plan/event-delegation.d.ts +16 -1
  15. package/dist/ir-to-client-js/control-flow/plan/event-delegation.d.ts.map +1 -1
  16. package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +27 -0
  17. package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
  18. package/dist/ir-to-client-js/control-flow/shared.d.ts +13 -2
  19. package/dist/ir-to-client-js/control-flow/shared.d.ts.map +1 -1
  20. package/dist/ir-to-client-js/control-flow/stringify/claim-plan.d.ts +54 -0
  21. package/dist/ir-to-client-js/control-flow/stringify/claim-plan.d.ts.map +1 -0
  22. package/dist/ir-to-client-js/control-flow/stringify/event-delegation.d.ts.map +1 -1
  23. package/dist/ir-to-client-js/control-flow/stringify/inner-loop.d.ts.map +1 -1
  24. package/dist/ir-to-client-js/control-flow/stringify/insert.d.ts.map +1 -1
  25. package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts.map +1 -1
  26. package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts +19 -3
  27. package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts.map +1 -1
  28. package/dist/ir-to-client-js/control-flow/stringify/reactive-effects.d.ts +63 -4
  29. package/dist/ir-to-client-js/control-flow/stringify/reactive-effects.d.ts.map +1 -1
  30. package/dist/ir-to-client-js/control-flow.d.ts +1 -1
  31. package/dist/ir-to-client-js/element-refs.d.ts +8 -3
  32. package/dist/ir-to-client-js/element-refs.d.ts.map +1 -1
  33. package/dist/ir-to-client-js/emit-reactive.d.ts +8 -1
  34. package/dist/ir-to-client-js/emit-reactive.d.ts.map +1 -1
  35. package/dist/ir-to-client-js/html-template.d.ts +73 -1
  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/reactivity.d.ts.map +1 -1
  40. package/dist/ir-to-client-js/types.d.ts +35 -2
  41. package/dist/ir-to-client-js/types.d.ts.map +1 -1
  42. package/dist/ir-to-client-js/utils.d.ts +1 -1
  43. package/dist/jsx-to-ir.d.ts.map +1 -1
  44. package/dist/types.d.ts +70 -0
  45. package/dist/types.d.ts.map +1 -1
  46. package/package.json +2 -2
  47. package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +555 -678
  48. package/src/__tests__/child-components-in-map.test.ts +4 -4
  49. package/src/__tests__/client-js-generation.test.ts +26 -12
  50. package/src/__tests__/compiler-runtime-contract.test.ts +4 -4
  51. package/src/__tests__/composite-branch-loop.test.ts +2 -2
  52. package/src/__tests__/cross-file-client-signal.test.ts +5 -1
  53. package/src/__tests__/delegated-handler-preamble.test.ts +153 -0
  54. package/src/__tests__/early-return-scope-var-ref.test.ts +10 -11
  55. package/src/__tests__/flatmap-segments.test.ts +182 -0
  56. package/src/__tests__/inline-jsx-callback.test.ts +6 -4
  57. package/src/__tests__/ir-jsx-props.test.ts +6 -5
  58. package/src/__tests__/loop-fallback-wrap.test.ts +3 -4
  59. package/src/__tests__/map-body-no-silent-divergence.test.ts +45 -0
  60. package/src/__tests__/module-level-signal.audit.test.ts +7 -2
  61. package/src/__tests__/nested-loop-conditional.test.ts +9 -7
  62. package/src/__tests__/nested-loop-reactive-attrs.test.ts +2 -2
  63. package/src/__tests__/preamble-region-patch.test.ts +156 -0
  64. package/src/__tests__/profile-cond-binding-ids.test.ts +3 -2
  65. package/src/__tests__/profile-loop-binding-ids.test.ts +4 -3
  66. package/src/__tests__/profile-nested-binding-ids.test.ts +11 -6
  67. package/src/__tests__/reactive-attrs-in-map.test.ts +6 -4
  68. package/src/__tests__/static-loop-csr-materialize.test.ts +3 -2
  69. package/src/__tests__/template-closure.test.ts +1 -1
  70. package/src/compiler.ts +11 -0
  71. package/src/index.ts +1 -0
  72. package/src/ir-to-client-js/build-references.ts +16 -0
  73. package/src/ir-to-client-js/client-only-elision.ts +273 -0
  74. package/src/ir-to-client-js/collect-elements.ts +74 -14
  75. package/src/ir-to-client-js/control-flow/plan/branch-loop.ts +12 -1
  76. package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +11 -3
  77. package/src/ir-to-client-js/control-flow/plan/build-event-delegation.ts +19 -3
  78. package/src/ir-to-client-js/control-flow/plan/build-loop.ts +36 -0
  79. package/src/ir-to-client-js/control-flow/plan/event-delegation.ts +16 -1
  80. package/src/ir-to-client-js/control-flow/plan/loop.ts +28 -0
  81. package/src/ir-to-client-js/control-flow/shared.ts +27 -2
  82. package/src/ir-to-client-js/control-flow/stringify/branch-loop.ts +34 -4
  83. package/src/ir-to-client-js/control-flow/stringify/claim-plan.ts +68 -0
  84. package/src/ir-to-client-js/control-flow/stringify/event-delegation.ts +67 -19
  85. package/src/ir-to-client-js/control-flow/stringify/inner-loop.ts +18 -9
  86. package/src/ir-to-client-js/control-flow/stringify/insert.ts +17 -11
  87. package/src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts +35 -16
  88. package/src/ir-to-client-js/control-flow/stringify/loop.ts +80 -14
  89. package/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts +241 -31
  90. package/src/ir-to-client-js/control-flow.ts +1 -1
  91. package/src/ir-to-client-js/element-refs.ts +9 -11
  92. package/src/ir-to-client-js/emit-reactive.ts +50 -14
  93. package/src/ir-to-client-js/html-template.ts +144 -15
  94. package/src/ir-to-client-js/imports.ts +7 -4
  95. package/src/ir-to-client-js/reactivity.ts +7 -0
  96. package/src/ir-to-client-js/types.ts +34 -2
  97. package/src/ir-to-client-js/utils.ts +1 -1
  98. package/src/jsx-to-ir.ts +382 -15
  99. package/src/types.ts +72 -0
package/dist/index.js CHANGED
@@ -3252,7 +3252,7 @@ function templateAttrExpr(attrName, valExpr, presenceOrUndefined) {
3252
3252
  return `\${((v) => v != null ? 'style="' + ${escapeAttrValueExpr("v")} + '"' : '')(styleToCss(${valExpr}))}`;
3253
3253
  }
3254
3254
  if (attrName === "data-key" || attrName.startsWith("data-key-")) {
3255
- return `${attrName}="\${${valExpr}}"`;
3255
+ return `${attrName}="\${${escapeAttrValueExpr(valExpr)}}"`;
3256
3256
  }
3257
3257
  return `\${(${valExpr}) != null ? '${attrName}="' + ${escapeAttrValueExpr(valExpr)} + '"' : ''}`;
3258
3258
  }
@@ -3387,12 +3387,57 @@ function renderPreamble(preamble, opts) {
3387
3387
  if (seg.kind === "js") {
3388
3388
  const text = opts.textVariant === "template" ? seg.templateText ?? seg.text : seg.text;
3389
3389
  out += opts.transformJs ? opts.transformJs(text) : text;
3390
+ } else if (opts.rawLeaf) {
3391
+ out += opts.renderLeaf(escapeLeafTextExpressions(seg.ir));
3390
3392
  } else {
3391
3393
  out += "`" + opts.renderLeaf(escapeLeafTextExpressions(seg.ir)) + "`";
3392
3394
  }
3393
3395
  }
3394
3396
  return out;
3395
3397
  }
3398
+ function flatMapLeafKeyExpr(ir) {
3399
+ if (ir.type !== "element")
3400
+ return null;
3401
+ const keyAttr = ir.attrs.find((a) => a.name === "key");
3402
+ if (!keyAttr)
3403
+ return null;
3404
+ switch (keyAttr.value.kind) {
3405
+ case "expression":
3406
+ return `(${keyAttr.value.expr})`;
3407
+ case "literal":
3408
+ return JSON.stringify(keyAttr.value.value);
3409
+ case "template":
3410
+ return attrValueToString(keyAttr.value);
3411
+ default:
3412
+ return null;
3413
+ }
3414
+ }
3415
+ function stripLeafKeyAttr(ir) {
3416
+ if (ir.type !== "element")
3417
+ return ir;
3418
+ return { ...ir, attrs: ir.attrs.filter((a) => a.name !== "key") };
3419
+ }
3420
+ function renderFlatMapClientBody(cb, restSpreadNames) {
3421
+ return renderPreamble(cb, {
3422
+ textVariant: "client",
3423
+ rawLeaf: true,
3424
+ renderLeaf: (ir) => {
3425
+ const key = flatMapLeafKeyExpr(ir);
3426
+ const html = irToHtmlTemplate(stripLeafKeyAttr(ir), restSpreadNames, 1, undefined, undefined, true);
3427
+ return `({ k: ${key ?? "undefined"}, h: \`${html}\` })`;
3428
+ }
3429
+ });
3430
+ }
3431
+ function flatMapCallbackHasKeyedLeaf(cb) {
3432
+ return cb.segments.some((s) => s.kind === "jsx" && flatMapLeafKeyExpr(s.ir) !== null);
3433
+ }
3434
+ function renderFlatMapProjectionClientBody(inner, restSpreadNames) {
3435
+ const chained = applyLoopChain(inner);
3436
+ const params = inner.index ? `(${inner.param}, ${inner.index})` : `(${inner.param})`;
3437
+ const key = inner.key ? `(${inner.key})` : "undefined";
3438
+ const html = inner.children.map((c) => irToHtmlTemplate(escapeLeafTextExpressions(c), restSpreadNames, 1, undefined, undefined, true)).join("");
3439
+ return `${chained}.map(${params} => ({ k: ${key}, h: \`${html}\` }))`;
3440
+ }
3396
3441
  function escapeLeafTextExpressions(ir) {
3397
3442
  switch (ir.type) {
3398
3443
  case "element":
@@ -3460,6 +3505,10 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
3460
3505
  case "expression": {
3461
3506
  if (node.expr === "null" || node.expr === "undefined")
3462
3507
  return "";
3508
+ if (node.markerless) {
3509
+ const bare = wrapInterpolation(wrapExpr(node.expr));
3510
+ return `\${${bare}}`;
3511
+ }
3463
3512
  const inner = wrapInterpolation(wrapExpr(node.expr));
3464
3513
  const valueExpr = node.joinArrayChild ? `Array.isArray(${inner}) ? ${inner}.join('') : (${inner} ?? '')` : inner;
3465
3514
  if (node.slotId) {
@@ -3527,7 +3576,7 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
3527
3576
  let mapExpr;
3528
3577
  if (node.flatMapCallback) {
3529
3578
  const body = renderPreamble(node.flatMapCallback, {
3530
- renderLeaf: (ir) => irToHtmlTemplate(ir, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop)
3579
+ renderLeaf: (ir) => irToHtmlTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop)
3531
3580
  });
3532
3581
  mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
3533
3582
  } else if (node.preamble) {
@@ -3817,7 +3866,7 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
3817
3866
  let mapExpr;
3818
3867
  if (node.flatMapCallback) {
3819
3868
  const body = renderPreamble(node.flatMapCallback, {
3820
- renderLeaf: (ir) => irToPlaceholderTemplate(ir, restSpreadNames, loopDepth + 1, loopParams)
3869
+ renderLeaf: (ir) => irToPlaceholderTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams)
3821
3870
  });
3822
3871
  mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
3823
3872
  } else if (node.preamble) {
@@ -4335,7 +4384,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4335
4384
  if (node.expr === "null" || node.expr === "undefined")
4336
4385
  return "";
4337
4386
  if (node.clientOnly && node.slotId) {
4338
- return `<!--bf-client:${node.slotId}--><!--/-->`;
4387
+ return `<!--bf:${node.slotId}--><!--/-->`;
4339
4388
  }
4340
4389
  {
4341
4390
  const transformed = transformExpr(node.expr, node.templateExpr);
@@ -4443,7 +4492,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4443
4492
  const body = renderPreamble(node.flatMapCallback, {
4444
4493
  textVariant: "template",
4445
4494
  transformJs: (t) => applyPropsRewrite(t, propsObjectName ?? null),
4446
- renderLeaf: (ir) => recurseInLoopBody(ir)
4495
+ renderLeaf: (ir) => recurseInLoopBody(stripLeafKeyAttr(ir))
4447
4496
  });
4448
4497
  mapExpr = `\${${iterArrayExpr}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
4449
4498
  } else if (node.preamble) {
@@ -11710,6 +11759,87 @@ function checkLoopKey(callback, ctx, isNested) {
11710
11759
  return;
11711
11760
  }
11712
11761
  }
11762
+ function flatMapProjectionCall(body) {
11763
+ let expr;
11764
+ if (ts11.isBlock(body)) {
11765
+ const real = body.statements;
11766
+ if (real.length !== 1 || !ts11.isReturnStatement(real[0]) || !real[0].expression)
11767
+ return null;
11768
+ expr = real[0].expression;
11769
+ } else {
11770
+ expr = body;
11771
+ }
11772
+ while (ts11.isParenthesizedExpression(expr))
11773
+ expr = expr.expression;
11774
+ if (!ts11.isCallExpression(expr))
11775
+ return null;
11776
+ if (!getMapLikeMethod(expr))
11777
+ return null;
11778
+ const cb = expr.arguments[0];
11779
+ if (!cb || !ts11.isArrowFunction(cb) && !ts11.isFunctionExpression(cb))
11780
+ return null;
11781
+ for (const p of cb.parameters) {
11782
+ if (!ts11.isIdentifier(p.name))
11783
+ return null;
11784
+ }
11785
+ let innerBody = cb.body;
11786
+ if (ts11.isBlock(innerBody)) {
11787
+ const ret = innerBody.statements.find((s) => ts11.isReturnStatement(s) && s.expression != null);
11788
+ if (innerBody.statements.length !== 1 || !ret?.expression)
11789
+ return null;
11790
+ innerBody = ret.expression;
11791
+ }
11792
+ while (ts11.isParenthesizedExpression(innerBody))
11793
+ innerBody = innerBody.expression;
11794
+ const isElementish = (n) => {
11795
+ let m = n;
11796
+ while (ts11.isParenthesizedExpression(m))
11797
+ m = m.expression;
11798
+ if (ts11.isJsxElement(m) || ts11.isJsxSelfClosingElement(m))
11799
+ return leafIsWirelessElement(m);
11800
+ if (ts11.isConditionalExpression(m))
11801
+ return isElementish(m.whenTrue) && isElementish(m.whenFalse);
11802
+ return false;
11803
+ };
11804
+ if (!isElementish(innerBody))
11805
+ return null;
11806
+ return expr;
11807
+ }
11808
+ function leafIsWirelessElement(el) {
11809
+ let ok = true;
11810
+ const visit2 = (n) => {
11811
+ if (!ok)
11812
+ return;
11813
+ if (ts11.isJsxOpeningElement(n) || ts11.isJsxSelfClosingElement(n)) {
11814
+ const tagNode = n.tagName;
11815
+ const isIntrinsic = ts11.isIdentifier(tagNode) ? !/^[A-Z]/.test(tagNode.text) : ts11.isJsxNamespacedName(tagNode);
11816
+ if (!isIntrinsic) {
11817
+ ok = false;
11818
+ return;
11819
+ }
11820
+ for (const attr of n.attributes.properties) {
11821
+ if (ts11.isJsxSpreadAttribute(attr)) {
11822
+ ok = false;
11823
+ return;
11824
+ }
11825
+ if (ts11.isJsxAttribute(attr)) {
11826
+ const name = attr.name.getText();
11827
+ if (/^on[A-Z]/.test(name)) {
11828
+ ok = false;
11829
+ return;
11830
+ }
11831
+ }
11832
+ }
11833
+ }
11834
+ if (ts11.isCallExpression(n) && getMapLikeMethod(n) && containsJsxInExpression(n)) {
11835
+ ok = false;
11836
+ return;
11837
+ }
11838
+ ts11.forEachChild(n, visit2);
11839
+ };
11840
+ visit2(el);
11841
+ return ok;
11842
+ }
11713
11843
  function loopBodyIsMultiRoot(children) {
11714
11844
  const real = children.filter((c) => !(c.type === "text" && typeof c.value === "string" && !c.value.trim()));
11715
11845
  if (real.length === 0)
@@ -11755,6 +11885,7 @@ function extractItemConditionalKey(cond) {
11755
11885
  }
11756
11886
  function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
11757
11887
  const isNested = ctx.loopParams.size > 0;
11888
+ const diagCountAtEntry = ctx.analyzer.errors.length;
11758
11889
  const depth = ctx.loopDepth;
11759
11890
  const propAccess = node.expression;
11760
11891
  const mapSource = propAccess.expression;
@@ -12041,12 +12172,34 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
12041
12172
  }
12042
12173
  }
12043
12174
  }
12044
- if (method === "flatMap" && children.length === 0) {
12175
+ if (method === "flatMap" && children.length === 0 && !flatMapProjectionCall(body)) {
12045
12176
  flatMapCallback = buildFlatMapCallback(callback, body, ctx);
12046
12177
  }
12047
12178
  } else {
12048
12179
  tryTransformRenderableBody(body);
12049
12180
  }
12181
+ if (method === "flatMap" && children.length === 0 && !flatMapCallback) {
12182
+ const projection = flatMapProjectionCall(body);
12183
+ if (projection) {
12184
+ const transformed = transformJsxExpression(projection, ctx, isClientOnly);
12185
+ if (transformed && transformed.type === "loop") {
12186
+ children = [transformed];
12187
+ }
12188
+ }
12189
+ }
12190
+ if (method === "flatMap" && children.length === 0 && !flatMapCallback && !ts11.isBlock(body)) {
12191
+ flatMapCallback = buildFlatMapCallback(callback, body, ctx);
12192
+ }
12193
+ if (flatMapCallback)
12194
+ preamble = undefined;
12195
+ if (flatMapCallback && !isClientOnly && !(ctx.analyzer.acceptsCallbackBody?.("flatMap") ?? false)) {
12196
+ ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(body, ctx.sourceFile, ctx.filePath), {
12197
+ message: "A .flatMap() callback body with statements or a nested projection " + "cannot be lowered to a template on this backend.",
12198
+ suggestion: {
12199
+ message: "Add /* @client */ to render this loop on the client only"
12200
+ }
12201
+ }));
12202
+ }
12050
12203
  if (paramBindings) {
12051
12204
  for (const b of paramBindings)
12052
12205
  ctx.loopParams.delete(b.name);
@@ -12058,6 +12211,16 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
12058
12211
  ctx.loopDepth--;
12059
12212
  }
12060
12213
  if (children.length === 0 && !flatMapCallback) {
12214
+ const cb = node.arguments[0];
12215
+ const cbBody = cb && (ts11.isArrowFunction(cb) || ts11.isFunctionExpression(cb)) ? cb.body : undefined;
12216
+ if (cbBody && containsJsxInExpression(cbBody) && ctx.analyzer.errors.length === diagCountAtEntry) {
12217
+ ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(cbBody, ctx.sourceFile, ctx.filePath), {
12218
+ message: `A .${method}() callback that builds JSX in this shape cannot be ` + "compiled — the JSX would leak verbatim into the client bundle. " + "Recognized bodies: a JSX element/fragment, a ternary or " + "&& / || / ?? expression, an array literal (flatMap), or a block " + "body whose return the compiler can lower.",
12219
+ suggestion: {
12220
+ message: "Restructure the callback to return the JSX element directly " + "(or via a block body with a plain `return`)."
12221
+ }
12222
+ }));
12223
+ }
12061
12224
  return null;
12062
12225
  }
12063
12226
  if (ts11.isArrowFunction(node.arguments[0]) && children.length > 0) {
@@ -12110,6 +12273,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
12110
12273
  const hasCalls = exprHasFunctionCalls(arrayExpr);
12111
12274
  const isDirectPropArray = method !== "flatMap" && isArrayExprDirectPropRef(arrayExpr, ctx);
12112
12275
  const isStaticArray = !isSignalOrMemoArray(array, ctx) && !isDirectPropArray && !hasCalls && !objectIteration;
12276
+ const preambleRegions = preamble && !isStaticArray ? collectPreambleRegions(children, new Set(preamble.declaredNames), ctx) : undefined;
12113
12277
  const nestedComponents = collectNestedComponents(children).filter((c) => c.name !== childComponent?.name);
12114
12278
  return {
12115
12279
  type: "loop",
@@ -12140,6 +12304,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
12140
12304
  depth,
12141
12305
  clientOnly: isClientOnly || undefined,
12142
12306
  preamble,
12307
+ preambleRegions: preambleRegions && preambleRegions.length > 0 ? preambleRegions : undefined,
12143
12308
  paramType,
12144
12309
  indexType,
12145
12310
  paramBindings,
@@ -12202,6 +12367,28 @@ function buildFlatMapCallback(callback, body, ctx) {
12202
12367
  }));
12203
12368
  return;
12204
12369
  }
12370
+ for (const leafIr of leafIrs) {
12371
+ if (leafIr.type !== "element") {
12372
+ const loc = "loc" in leafIr && leafIr.loc ? leafIr.loc : getSourceLocation(body, ctx.sourceFile, ctx.filePath);
12373
+ ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, loc, {
12374
+ message: "A JSX leaf produced by a .flatMap() callback must be a single " + "element — a fragment or non-element root cannot ride the keyed " + "descriptor path (each leaf hydrates and patches as one element).",
12375
+ suggestion: {
12376
+ message: "Wrap the leaf content in a single keyed element."
12377
+ }
12378
+ }));
12379
+ return;
12380
+ }
12381
+ if (flatMapLeafNeedsWiring(leafIr)) {
12382
+ const loc = "loc" in leafIr && leafIr.loc ? leafIr.loc : getSourceLocation(body, ctx.sourceFile, ctx.filePath);
12383
+ ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, loc, {
12384
+ message: "A JSX element produced by a .flatMap() callback cannot carry " + "event handlers, components, nested loops, or spreads — the " + "leaf renders as a keyed HTML string with no per-element wiring.",
12385
+ suggestion: {
12386
+ message: "Restructure so the interactive element lives in a .map() body — " + "the descriptor path has no per-element wiring on any backend, " + "so /* @client */ does not lift this."
12387
+ }
12388
+ }));
12389
+ return;
12390
+ }
12391
+ }
12205
12392
  const pieces = reconstructAsSegments(body, ctx.sourceFile, ctx.analyzer.typeExcludeRanges, leafSpans);
12206
12393
  const segments = pieces.map((piece) => {
12207
12394
  if ("marker" in piece)
@@ -12216,6 +12403,25 @@ function buildFlatMapCallback(callback, body, ctx) {
12216
12403
  rawBody: tsxSourceText(body.getText(ctx.sourceFile))
12217
12404
  };
12218
12405
  }
12406
+ function flatMapLeafNeedsWiring(ir) {
12407
+ switch (ir.type) {
12408
+ case "component":
12409
+ case "loop":
12410
+ return true;
12411
+ case "element":
12412
+ if (ir.events.length > 0)
12413
+ return true;
12414
+ if (ir.attrs.some((a) => a.name.startsWith("...")))
12415
+ return true;
12416
+ return ir.children.some(flatMapLeafNeedsWiring);
12417
+ case "conditional":
12418
+ return flatMapLeafNeedsWiring(ir.whenTrue) || (ir.whenFalse ? flatMapLeafNeedsWiring(ir.whenFalse) : false);
12419
+ case "fragment":
12420
+ return ir.children.some(flatMapLeafNeedsWiring);
12421
+ default:
12422
+ return false;
12423
+ }
12424
+ }
12219
12425
  function preambleFragmentNeedsWiring(ir) {
12220
12426
  switch (ir.type) {
12221
12427
  case "component":
@@ -12258,6 +12464,40 @@ function flagArrayChildExpressions(nodes, declared) {
12258
12464
  }
12259
12465
  }
12260
12466
  }
12467
+ function collectPreambleRegions(nodes, declared, ctx) {
12468
+ const regions = [];
12469
+ const visit2 = (list) => {
12470
+ for (const node of list) {
12471
+ switch (node.type) {
12472
+ case "expression": {
12473
+ const refs = extractFreeIdentifiersFromText(node.expr);
12474
+ const usesPreambleLocal = [...refs].some((r) => declared.has(r));
12475
+ if (usesPreambleLocal) {
12476
+ if (!node.slotId)
12477
+ node.slotId = generateSlotId(ctx);
12478
+ node.preambleRegion = true;
12479
+ node.reactive = true;
12480
+ regions.push({
12481
+ slotId: node.slotId,
12482
+ expr: node.expr,
12483
+ joinArrayChild: node.joinArrayChild || undefined
12484
+ });
12485
+ }
12486
+ break;
12487
+ }
12488
+ case "element":
12489
+ case "fragment":
12490
+ visit2(node.children);
12491
+ break;
12492
+ case "conditional":
12493
+ visit2([node.whenTrue, ...node.whenFalse ? [node.whenFalse] : []]);
12494
+ break;
12495
+ }
12496
+ }
12497
+ };
12498
+ visit2(nodes);
12499
+ return regions;
12500
+ }
12261
12501
  function collectBindingNames(name, out) {
12262
12502
  if (ts11.isIdentifier(name)) {
12263
12503
  out.add(name.text);
@@ -13635,6 +13875,8 @@ function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings,
13635
13875
  expression: ({ node: n, scope: insideConditional }) => {
13636
13876
  if (!n.slotId)
13637
13877
  return;
13878
+ if (n.preambleRegion)
13879
+ return;
13638
13880
  const originFreeIds = freeIdsFromRefs(n.origin?.freeRefs);
13639
13881
  const expanded = expandConstantForReactivity(n.expr, ctx, originFreeIds);
13640
13882
  const reactive = classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind !== "none" || decideWrapFromAstFlags(n).wrap;
@@ -14164,7 +14406,11 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
14164
14406
  },
14165
14407
  expression: ({ node: ex, scope: inCond }) => {
14166
14408
  if (ex.clientOnly && ex.slotId) {
14167
- ctx.clientOnlyElements.push({ slotId: ex.slotId, expression: ex.expr });
14409
+ ctx.clientOnlyElements.push({
14410
+ slotId: ex.slotId,
14411
+ expression: ex.expr,
14412
+ elidedPath: ex.markerless ? ex.elidedPath : undefined
14413
+ });
14168
14414
  return;
14169
14415
  }
14170
14416
  if (!ex.slotId || inCond)
@@ -14190,10 +14436,13 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
14190
14436
  loop: ({ node: l, scope: inCond }) => {
14191
14437
  if (!l.slotId || inCond)
14192
14438
  return;
14439
+ const projectionInner = l.method === "flatMap" && l.children.length === 1 && l.children[0].type === "loop" ? l.children[0] : undefined;
14193
14440
  const childHandlers = [];
14194
- const bindings = collectLoopChildBindings(l.children, ctx, siblingOffsets, l.param, l.paramBindings);
14195
- for (const child of l.children) {
14196
- childHandlers.push(...collectEventHandlersFromIR(child));
14441
+ const bindings = projectionInner ? emptyLoopChildBindings() : collectLoopChildBindings(l.children, ctx, siblingOffsets, l.param, l.paramBindings);
14442
+ if (!projectionInner) {
14443
+ for (const child of l.children) {
14444
+ childHandlers.push(...collectEventHandlersFromIR(child));
14445
+ }
14197
14446
  }
14198
14447
  if (l.childComponent) {
14199
14448
  for (const prop of l.childComponent.props) {
@@ -14204,7 +14453,7 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
14204
14453
  }
14205
14454
  }
14206
14455
  }
14207
- const { useElementReconciliation, innerLoops } = decideLoopRendering(l, siblingOffsets, ctx);
14456
+ const { useElementReconciliation, innerLoops } = projectionInner ? { useElementReconciliation: false, innerLoops: undefined } : decideLoopRendering(l, siblingOffsets, ctx);
14208
14457
  let template = "";
14209
14458
  let staticItemTemplate;
14210
14459
  let skeletonTemplate;
@@ -14214,7 +14463,7 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
14214
14463
  if (l.isStaticArray && l.children[0]) {
14215
14464
  staticItemTemplate = irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx), 0, undefined, undefined, true);
14216
14465
  }
14217
- } else if (l.children[0]) {
14466
+ } else if (l.children[0] && !projectionInner) {
14218
14467
  const loopParamSpec = [{ param: l.param, bindings: l.paramBindings }];
14219
14468
  template = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], buildRestSpreadNames(ctx), 0, loopParamSpec) : irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx), 0, loopParamSpec);
14220
14469
  if (l.isStaticArray) {
@@ -14266,7 +14515,17 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
14266
14515
  raw: l.sortComparator.raw
14267
14516
  } : undefined,
14268
14517
  chainOrder: l.chainOrder,
14269
- preamble: l.preamble
14518
+ preamble: l.preamble,
14519
+ preambleRegions: l.preambleRegions,
14520
+ flatMapClient: projectionInner ? {
14521
+ params: l.index ? `(${l.param}, ${l.index})` : `(${l.param})`,
14522
+ body: renderFlatMapProjectionClientBody(projectionInner, buildRestSpreadNames(ctx)),
14523
+ keyed: projectionInner.key !== null
14524
+ } : l.flatMapCallback ? {
14525
+ params: l.flatMapCallback.params,
14526
+ body: renderFlatMapClientBody(l.flatMapCallback, buildRestSpreadNames(ctx)),
14527
+ keyed: flatMapCallbackHasKeyedLeaf(l.flatMapCallback)
14528
+ } : undefined
14270
14529
  });
14271
14530
  },
14272
14531
  component: ({ node: c, descend, descendJsxChildren }) => {
@@ -14423,15 +14682,18 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
14423
14682
  const containerSlot = parentSlotId ?? n.slotId;
14424
14683
  if (!containerSlot)
14425
14684
  return;
14426
- const { useElementReconciliation, innerLoops: innerLoopsCollected } = decideLoopRendering(n, siblingOffsets, undefined);
14685
+ const projectionInner = n.method === "flatMap" && n.children.length === 1 && n.children[0].type === "loop" ? n.children[0] : undefined;
14686
+ const { useElementReconciliation, innerLoops: innerLoopsCollected } = projectionInner ? { useElementReconciliation: false, innerLoops: undefined } : decideLoopRendering(n, siblingOffsets, undefined);
14427
14687
  let childTemplate;
14428
14688
  const branchLoopParamSpec = [{ param: n.param, bindings: n.paramBindings }];
14429
- if (useElementReconciliation && n.children[0]) {
14689
+ if (projectionInner) {
14690
+ childTemplate = "";
14691
+ } else if (useElementReconciliation && n.children[0]) {
14430
14692
  childTemplate = irToPlaceholderTemplate(n.children[0], restNames, 0, branchLoopParamSpec);
14431
14693
  } else {
14432
14694
  childTemplate = n.children.map((c) => irToHtmlTemplate(c, undefined, 0, branchLoopParamSpec)).join("");
14433
14695
  }
14434
- const branchBindings = ctx ? collectLoopChildBindings(n.children, ctx, siblingOffsets, n.param, n.paramBindings) : emptyLoopChildBindings();
14696
+ const branchBindings = ctx && !projectionInner ? collectLoopChildBindings(n.children, ctx, siblingOffsets, n.param, n.paramBindings) : emptyLoopChildBindings();
14435
14697
  loops.push({
14436
14698
  kind: "branch",
14437
14699
  array: n.array,
@@ -14448,6 +14710,7 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
14448
14710
  template: childTemplate,
14449
14711
  containerSlotId: containerSlot,
14450
14712
  preamble: n.preamble,
14713
+ preambleRegions: n.preambleRegions,
14451
14714
  nestedComponents: useElementReconciliation ? n.nestedComponents : undefined,
14452
14715
  bindings: branchBindings,
14453
14716
  innerLoops: useElementReconciliation ? innerLoopsCollected : undefined,
@@ -14461,7 +14724,16 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
14461
14724
  paramB: n.sortComparator.paramB,
14462
14725
  raw: n.sortComparator.raw
14463
14726
  } : undefined,
14464
- chainOrder: n.chainOrder
14727
+ chainOrder: n.chainOrder,
14728
+ flatMapClient: projectionInner ? {
14729
+ params: n.index ? `(${n.param}, ${n.index})` : `(${n.param})`,
14730
+ body: renderFlatMapProjectionClientBody(projectionInner, restNames),
14731
+ keyed: projectionInner.key !== null
14732
+ } : n.flatMapCallback ? {
14733
+ params: n.flatMapCallback.params,
14734
+ body: renderFlatMapClientBody(n.flatMapCallback, restNames),
14735
+ keyed: flatMapCallbackHasKeyedLeaf(n.flatMapCallback)
14736
+ } : undefined
14465
14737
  });
14466
14738
  }
14467
14739
  });
@@ -14836,6 +15108,13 @@ function buildReferencesGraph(ctx, irRoot) {
14836
15108
  addExprEdges(ROOT_SOURCE, l.sortComparator.raw, "template-closure");
14837
15109
  if (l.preamble)
14838
15110
  addExprEdges(ROOT_SOURCE, preambleAnalysisText(l.preamble), "template-closure");
15111
+ if (l.flatMapCallback) {
15112
+ addExprEdges(ROOT_SOURCE, preambleAnalysisText(l.flatMapCallback), "template-closure");
15113
+ for (const seg of l.flatMapCallback.segments) {
15114
+ if (seg.kind === "jsx")
15115
+ walkIR(seg.ir, null, visitor);
15116
+ }
15117
+ }
14839
15118
  descend();
14840
15119
  if (l.childComponent)
14841
15120
  walkChildComponent(l.childComponent);
@@ -15008,11 +15287,11 @@ var RUNTIME_IMPORT_CANDIDATES = [
15008
15287
  "onMount",
15009
15288
  "hydrate",
15010
15289
  "insert",
15011
- "reconcileElements",
15012
15290
  "getLoopChildren",
15013
15291
  "getLoopNodes",
15014
15292
  "mapArray",
15015
15293
  "mapArrayAnchored",
15294
+ "patchLeaf",
15016
15295
  "createDisposableEffect",
15017
15296
  "createComponent",
15018
15297
  "renderChild",
@@ -15020,7 +15299,6 @@ var RUNTIME_IMPORT_CANDIDATES = [
15020
15299
  "registerTemplate",
15021
15300
  "initChild",
15022
15301
  "upsertChild",
15023
- "updateClientMarker",
15024
15302
  "createPortal",
15025
15303
  "provideContext",
15026
15304
  "createContext",
@@ -15032,6 +15310,7 @@ var RUNTIME_IMPORT_CANDIDATES = [
15032
15310
  "styleToCss",
15033
15311
  "escapeAttr",
15034
15312
  "escapeText",
15313
+ "escapeTextOrNode",
15035
15314
  "qsa",
15036
15315
  "qsaItem",
15037
15316
  "qsaChildScope",
@@ -15040,7 +15319,8 @@ var RUNTIME_IMPORT_CANDIDATES = [
15040
15319
  "__slot",
15041
15320
  "__bfSlot",
15042
15321
  "__bfText",
15043
- "tAfter",
15322
+ "claimSlots",
15323
+ "lazySlots",
15044
15324
  "beginTurn",
15045
15325
  "endTurn",
15046
15326
  "date",
@@ -17015,6 +17295,15 @@ function destructureLoopParam(param, paramBindings) {
17015
17295
  }
17016
17296
  return { head: param, unwrap: "" };
17017
17297
  }
17298
+ function buildPreambleRegionPlans(regions, loopParam, loopParamBindings) {
17299
+ if (!regions || regions.length === 0)
17300
+ return [];
17301
+ return regions.map((r) => {
17302
+ const wrapped = wrapLoopParamAsAccessor(r.expr, loopParam, loopParamBindings);
17303
+ const valueExpr = r.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : `escapeText(${wrapped})`;
17304
+ return { slotId: r.slotId, valueExpr };
17305
+ });
17306
+ }
17018
17307
  function buildComponentPropsExpr2(comp, loopParam, loopParamBindings) {
17019
17308
  const wrap = loopParam ? (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings) : (expr) => expr;
17020
17309
  const entries = comp.props.map((p) => {
@@ -18029,8 +18318,9 @@ function buildDynamicLoopDelegationPlan(elem, profileComponentName) {
18029
18318
  key: elem.key,
18030
18319
  index: elem.index,
18031
18320
  mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
18032
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined, true)
18033
- }) : null
18321
+ renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, undefined, undefined, true)
18322
+ }) : null,
18323
+ mapPreambleDeclaredNames: elem.preamble?.declaredNames ?? []
18034
18324
  })
18035
18325
  };
18036
18326
  }
@@ -18047,8 +18337,9 @@ function buildBranchLoopDelegationPlan(loop, cv, profileComponentName) {
18047
18337
  key: loop.key,
18048
18338
  index: loop.index,
18049
18339
  mapPreamble: loop.preamble ? renderPreamble(loop.preamble, {
18050
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings }], undefined, true)
18051
- }) : null
18340
+ renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, undefined, undefined, true)
18341
+ }) : null,
18342
+ mapPreambleDeclaredNames: loop.preamble?.declaredNames ?? []
18052
18343
  })
18053
18344
  };
18054
18345
  }
@@ -18063,8 +18354,9 @@ function buildStaticArrayDelegationPlan(elem, profileComponentName) {
18063
18354
  arrayExpr: buildChainedArrayExpr(elem),
18064
18355
  param: elem.param,
18065
18356
  mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
18066
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined, true)
18357
+ renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, undefined, undefined, true)
18067
18358
  }) : null,
18359
+ mapPreambleDeclaredNames: elem.preamble?.declaredNames ?? [],
18068
18360
  offset: elem.offset ?? null,
18069
18361
  indexParam: elem.index ?? null
18070
18362
  }
@@ -18081,6 +18373,7 @@ function buildKeyedOrIndexLookup(args) {
18081
18373
  paramBindings: args.paramBindings,
18082
18374
  keyWithItem,
18083
18375
  mapPreamble: args.mapPreamble,
18376
+ mapPreambleDeclaredNames: args.mapPreambleDeclaredNames,
18084
18377
  hasBindings,
18085
18378
  indexParam: args.index
18086
18379
  };
@@ -18090,6 +18383,7 @@ function buildKeyedOrIndexLookup(args) {
18090
18383
  arrayExpr: args.array,
18091
18384
  param: args.param,
18092
18385
  mapPreamble: args.mapPreamble,
18386
+ mapPreambleDeclaredNames: args.mapPreambleDeclaredNames,
18093
18387
  hasBindings,
18094
18388
  indexParam: args.index
18095
18389
  };
@@ -18111,14 +18405,16 @@ function buildBranchLoopPlan(loop, profileComponentName) {
18111
18405
  }
18112
18406
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(loop.param, loop.paramBindings);
18113
18407
  const hasReactiveEffects = loop.bindings.reactiveAttrs.length > 0 || loop.bindings.reactiveTexts.length > 0 || loop.bindings.conditionals.length > 0;
18408
+ const fm = loop.flatMapClient;
18114
18409
  const plan = {
18115
18410
  kind: "plain",
18116
18411
  rowConstruction: "string-template",
18117
18412
  containerSlotId,
18118
18413
  containerVar,
18119
18414
  markerId: loop.markerId,
18120
- arrayExpr: buildChainedArrayExpr(loop),
18121
- keyFn: loopKeyFn(loop),
18415
+ flatMapLeafItem: fm ? true : undefined,
18416
+ arrayExpr: fm ? `(${buildChainedArrayExpr(loop)}).flatMap(${fm.params} => ${fm.body})` : buildChainedArrayExpr(loop),
18417
+ keyFn: fm ? fm.keyed ? "(__bfD, __bfI) => String(__bfD.k ?? __bfI)" : "null" : loopKeyFn(loop),
18122
18418
  paramHead,
18123
18419
  paramUnwrap,
18124
18420
  indexParam: loop.index || "__idx",
@@ -18137,6 +18433,7 @@ function buildBranchLoopPlan(loop, profileComponentName) {
18137
18433
  }) : null,
18138
18434
  eventDelegation: buildBranchLoopDelegationPlan(loop, cv, profileComponentName),
18139
18435
  childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings),
18436
+ preambleRegions: buildPreambleRegionPlans(loop.preambleRegions, loop.param, loop.paramBindings),
18140
18437
  bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false,
18141
18438
  profileLoopId: profileComponentName ? `${profileComponentName}#binding:${containerSlotId}` : undefined
18142
18439
  };
@@ -18194,6 +18491,22 @@ function buildArmBody(branch, options) {
18194
18491
 
18195
18492
  // src/ir-to-client-js/emit-reactive.ts
18196
18493
  import ts14 from "typescript";
18494
+
18495
+ // src/ir-to-client-js/control-flow/stringify/claim-plan.ts
18496
+ function slotSpecLiteral(slot) {
18497
+ const pathSrc = slot.pathExpr ?? `[${slot.path.join(", ")}]`;
18498
+ const markerlessSrc = slot.markerless ? ", markerless: true" : "";
18499
+ return `{ id: '${slot.id}', kind: '${slot.kind}', path: ${pathSrc}${markerlessSrc} }`;
18500
+ }
18501
+ function claimPlanLiteral(slots) {
18502
+ return `[${slots.map(slotSpecLiteral).join(", ")}]`;
18503
+ }
18504
+ function claimWriterVarName(slots, sanitize) {
18505
+ const first = slots[0]?.id ?? "0";
18506
+ return `__bfw_${sanitize(first)}`;
18507
+ }
18508
+
18509
+ // src/ir-to-client-js/emit-reactive.ts
18197
18510
  function bindingIdArg(ctx, slotId) {
18198
18511
  if (!ctx.profile || !slotId)
18199
18512
  return "";
@@ -18377,17 +18690,18 @@ function emitDynamicTextUpdates(lines, ctx) {
18377
18690
  const conditionalElems = elems.filter((e) => e.insideConditional);
18378
18691
  const normalElems = elems.filter((e) => !e.insideConditional);
18379
18692
  if (normalElems.length > 0 || conditionalElems.length > 0) {
18380
- for (const elem of normalElems) {
18381
- const v = varSlotId(elem.slotId);
18382
- lines.push(` let __anchor_${v} = _${v}`);
18383
- }
18384
18693
  const __textSlot = (normalElems[0] ?? conditionalElems[0])?.slotId;
18694
+ let writer = "";
18695
+ if (normalElems.length > 0) {
18696
+ const slots = normalElems.map((elem) => ({ id: elem.slotId, kind: "markup", path: [] }));
18697
+ writer = claimWriterVarName(slots, varSlotId);
18698
+ lines.push(` const ${writer} = lazySlots(__scope, ${claimPlanLiteral(slots)})`);
18699
+ }
18385
18700
  lines.push(` createEffect(() => {`);
18386
18701
  if (normalElems.length > 0) {
18387
18702
  lines.push(` const __val = ${expr}`);
18388
18703
  for (const elem of normalElems) {
18389
- const v = varSlotId(elem.slotId);
18390
- lines.push(` __anchor_${v} = __bfText(__anchor_${v}, __val)`);
18704
+ lines.push(` ${writer}('${elem.slotId}', escapeTextOrNode(__val))`);
18391
18705
  }
18392
18706
  for (const elem of conditionalElems) {
18393
18707
  const v = varSlotId(elem.slotId);
@@ -18410,10 +18724,13 @@ function emitDynamicTextUpdates(lines, ctx) {
18410
18724
  }
18411
18725
  function emitClientOnlyExpressions(lines, ctx) {
18412
18726
  for (const elem of ctx.clientOnlyElements) {
18727
+ const slots = elem.elidedPath ? [{ id: elem.slotId, kind: "text", path: elem.elidedPath, markerless: true }] : [{ id: elem.slotId, kind: "text", path: [] }];
18728
+ const writer = claimWriterVarName(slots, varSlotId);
18413
18729
  lines.push(` // @client: ${elem.slotId}`);
18730
+ lines.push(` { const ${writer} = lazySlots(__scope, ${claimPlanLiteral(slots)})`);
18414
18731
  lines.push(` createEffect(() => {`);
18415
- lines.push(` updateClientMarker(__scope, '${elem.slotId}', ${elem.expression})`);
18416
- lines.push(` }${bindingIdArg(ctx, elem.slotId)})`);
18732
+ lines.push(` ${writer}('${elem.slotId}', ${elem.expression})`);
18733
+ lines.push(` }${bindingIdArg(ctx, elem.slotId)}) }`);
18417
18734
  lines.push("");
18418
18735
  }
18419
18736
  }
@@ -18571,13 +18888,18 @@ function stringifyBranchInnerLoops(lines, plan, indent, pc) {
18571
18888
  if (inner.legacyComponents.length > 0 || inner.legacyEvents.length > 0) {
18572
18889
  emitComponentAndEventSetup(lines, `${indent} `, `__bel${uid}`, [...inner.legacyComponents], [...inner.legacyEvents], inner.outerLoopParam, inner.outerLoopParamBindings);
18573
18890
  }
18574
- for (const text of inner.reactiveTexts) {
18891
+ const conditionalTexts = inner.reactiveTexts.filter((t) => t.insideConditional);
18892
+ const plainTexts = inner.reactiveTexts.filter((t) => !t.insideConditional);
18893
+ for (const text of conditionalTexts) {
18575
18894
  const bf = profileBindingId(pc, text.slotId);
18576
- if (text.insideConditional) {
18577
- lines.push(`${indent} createEffect(() => { const [__rt] = $t(__bel${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) }${bf})`);
18578
- } else {
18579
- lines.push(`${indent} { const [__rt] = $t(__bel${uid}, '${text.slotId}')`);
18580
- lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }${bf}) }`);
18895
+ lines.push(`${indent} createEffect(() => { claimSlots(__bel${uid}, [{ id: '${text.slotId}', kind: 'text', path: [] }]).write('${text.slotId}', String(${text.wrappedExpression})) }${bf})`);
18896
+ }
18897
+ if (plainTexts.length > 0) {
18898
+ const slots = plainTexts.map((t) => ({ id: t.slotId, kind: "text", path: [] }));
18899
+ const writer = claimWriterVarName(slots, varSlotId);
18900
+ lines.push(`${indent} const ${writer} = lazySlots(__bel${uid}, ${claimPlanLiteral(slots)})`);
18901
+ for (const text of plainTexts) {
18902
+ lines.push(`${indent} createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) }${profileBindingId(pc, text.slotId)})`);
18581
18903
  }
18582
18904
  }
18583
18905
  if (inner.nestedConditionals.length > 0) {
@@ -18620,24 +18942,41 @@ function stringifyLoopChildArm(lines, arm, armIndent, pc) {
18620
18942
  stringifyLoopChildConditional(lines, cond, `${armIndent} `, pc);
18621
18943
  lines.push(`${armIndent}}))`);
18622
18944
  }
18623
- for (const text of arm.texts) {
18624
- const varName = `__rt_${varSlotId(text.slotId)}`;
18625
- lines.push(`${armIndent}let ${varName} = $t(__branchScope, '${text.slotId}')[0]`);
18626
- lines.push(`${armIndent}__disposers.push(createDisposableEffect(() => { ${varName} = __bfText(${varName}, ${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}))`);
18945
+ if (arm.texts.length > 0) {
18946
+ const slots = arm.texts.map((t) => ({ id: t.slotId, kind: "markup", path: [] }));
18947
+ const writer = claimWriterVarName(slots, varSlotId);
18948
+ lines.push(`${armIndent}const ${writer} = lazySlots(__branchScope, ${claimPlanLiteral(slots)})`);
18949
+ for (const text of arm.texts) {
18950
+ lines.push(`${armIndent}__disposers.push(createDisposableEffect(() => { ${writer}('${text.slotId}', escapeTextOrNode(${text.wrappedExpression})) }${profileBindingId(pc, text.slotId)}))`);
18951
+ }
18627
18952
  }
18628
18953
  lines.push(`${armIndent}return () => __disposers.forEach(d => d())`);
18629
18954
  }
18630
18955
 
18631
18956
  // src/ir-to-client-js/control-flow/stringify/reactive-effects.ts
18632
18957
  function stringifyReactiveEffects(lines, plan, opts) {
18633
- const { indent, elVar, bodyIsMultiRoot, elementIndexBySlot, textIndexBySlot } = opts;
18958
+ const { indent, elVar, bodyIsMultiRoot, elementIndexBySlot, textClaimPathExprs, preambleRegions = [], mapPreambleWrapped } = opts;
18634
18959
  const lookup = bodyIsMultiRoot ? "qsaItem" : "qsa";
18635
- const pc = plan.profileComponentName;
18960
+ const pc = plan?.profileComponentName;
18636
18961
  const bindingBfId = (slotId) => profileBindingId(pc, slotId);
18637
- for (const slot of plan.attrSlots) {
18962
+ const attrSlots = plan?.attrSlots ?? [];
18963
+ const outerTexts = plan?.outerTexts ?? [];
18964
+ const conditionals = plan?.conditionals ?? [];
18965
+ if (pc) {
18966
+ emitAttrSlotsGranular(lines, indent, elVar, lookup, attrSlots, elementIndexBySlot, bindingBfId);
18967
+ emitOuterTexts(lines, indent, elVar, outerTexts, bindingBfId, textClaimPathExprs);
18968
+ emitPreambleRegionsEffect(lines, indent, elVar, preambleRegions, mapPreambleWrapped);
18969
+ } else {
18970
+ emitConsolidatedRowEffect(lines, indent, elVar, lookup, attrSlots, outerTexts, elementIndexBySlot, textClaimPathExprs, preambleRegions, mapPreambleWrapped);
18971
+ }
18972
+ for (const cond of conditionals) {
18973
+ emitOuterConditional(lines, indent, elVar, cond, pc);
18974
+ }
18975
+ }
18976
+ function emitAttrSlotsGranular(lines, indent, elVar, lookup, attrSlots, elementIndexBySlot, bindingBfId) {
18977
+ for (const slot of attrSlots) {
18638
18978
  const varName = `__ra_${varSlotId(slot.slotId)}`;
18639
- const pIdx = elementIndexBySlot?.get(slot.slotId);
18640
- const lookupExpr = pIdx !== undefined ? `__p ? __p[${pIdx}] : ${lookup}(${elVar}, '[bf="${slot.slotId}"]')` : `${lookup}(${elVar}, '[bf="${slot.slotId}"]')`;
18979
+ const lookupExpr = attrLookupExpr(slot.slotId, varName, elVar, lookup, elementIndexBySlot);
18641
18980
  lines.push(`${indent}{ const ${varName} = ${lookupExpr}`);
18642
18981
  lines.push(`${indent}if (${varName}) {`);
18643
18982
  for (const attr of slot.attrs) {
@@ -18649,21 +18988,83 @@ function stringifyReactiveEffects(lines, plan, opts) {
18649
18988
  }
18650
18989
  lines.push(`${indent}} }`);
18651
18990
  }
18652
- for (const text of plan.outerTexts) {
18653
- emitOuterText(lines, indent, elVar, text, bindingBfId(text.slotId), textIndexBySlot?.get(text.slotId));
18991
+ }
18992
+ function emitPreambleRegionsEffect(lines, indent, elVar, preambleRegions, mapPreambleWrapped) {
18993
+ if (preambleRegions.length === 0)
18994
+ return;
18995
+ const slots = preambleRegions.map((r) => ({ id: r.slotId, kind: "markup", path: [] }));
18996
+ const writer = claimWriterVarName(slots, varSlotId);
18997
+ lines.push(`${indent}const ${writer} = lazySlots(${elVar}, ${claimPlanLiteral(slots)})`);
18998
+ lines.push(`${indent}createEffect(() => {`);
18999
+ if (mapPreambleWrapped)
19000
+ lines.push(`${indent} ${mapPreambleWrapped}`);
19001
+ for (const region of preambleRegions) {
19002
+ lines.push(`${indent} ${writer}('${region.slotId}', ${region.valueExpr})`);
18654
19003
  }
18655
- for (const cond of plan.conditionals) {
18656
- emitOuterConditional(lines, indent, elVar, cond, pc);
19004
+ lines.push(`${indent}})`);
19005
+ }
19006
+ function attrLookupExpr(slotId, varName, elVar, lookup, elementIndexBySlot) {
19007
+ const pIdx = elementIndexBySlot?.get(slotId);
19008
+ return pIdx !== undefined ? `__p ? __p[${pIdx}] : ${lookup}(${elVar}, '[bf="${slotId}"]')` : `${lookup}(${elVar}, '[bf="${slotId}"]')`;
19009
+ }
19010
+ function emitConsolidatedRowEffect(lines, indent, elVar, lookup, attrSlots, outerTexts, elementIndexBySlot, textClaimPathExprs, preambleRegions, mapPreambleWrapped) {
19011
+ if (attrSlots.length === 0 && outerTexts.length === 0 && preambleRegions.length === 0)
19012
+ return;
19013
+ for (const slot of attrSlots) {
19014
+ const varName = `__ra_${varSlotId(slot.slotId)}`;
19015
+ lines.push(`${indent}const ${varName} = ${attrLookupExpr(slot.slotId, varName, elVar, lookup, elementIndexBySlot)}`);
19016
+ }
19017
+ const claimSlots = [
19018
+ ...outerTexts.map((t) => ({ id: t.slotId, kind: "text", path: [], pathExpr: textClaimPathExprs?.get(t.slotId) })),
19019
+ ...preambleRegions.map((r) => ({ id: r.slotId, kind: "markup", path: [] }))
19020
+ ];
19021
+ const writer = claimSlots.length > 0 ? claimWriterVarName(claimSlots, varSlotId) : null;
19022
+ if (writer) {
19023
+ lines.push(`${indent}const ${writer} = lazySlots(${elVar}, ${claimPlanLiteral(claimSlots)})`);
19024
+ }
19025
+ if (attrSlots.length === 0 && preambleRegions.length === 0 && outerTexts.length === 1) {
19026
+ const text = outerTexts[0];
19027
+ lines.push(`${indent}createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) })`);
19028
+ return;
18657
19029
  }
19030
+ lines.push(`${indent}createEffect(() => {`);
19031
+ for (const slot of attrSlots) {
19032
+ const varName = `__ra_${varSlotId(slot.slotId)}`;
19033
+ lines.push(`${indent} if (${varName}) {`);
19034
+ for (const attr of slot.attrs) {
19035
+ lines.push(`${indent} {`);
19036
+ for (const stmt of emitAttrUpdate(varName, attr.attrName, attr.wrappedExpression, attr.meta)) {
19037
+ lines.push(`${indent} ${stmt}`);
19038
+ }
19039
+ lines.push(`${indent} }`);
19040
+ }
19041
+ lines.push(`${indent} }`);
19042
+ }
19043
+ if (preambleRegions.length > 0 && mapPreambleWrapped) {
19044
+ lines.push(`${indent} ${mapPreambleWrapped}`);
19045
+ }
19046
+ for (const text of outerTexts) {
19047
+ lines.push(`${indent} ${writer}('${text.slotId}', String(${text.wrappedExpression}))`);
19048
+ }
19049
+ for (const region of preambleRegions) {
19050
+ lines.push(`${indent} ${writer}('${region.slotId}', ${region.valueExpr})`);
19051
+ }
19052
+ lines.push(`${indent}})`);
18658
19053
  }
18659
- function emitOuterText(lines, indent, elVar, text, bfId = "", pIdx) {
18660
- const varName = `__rt_${varSlotId(text.slotId)}`;
18661
- if (pIdx !== undefined) {
18662
- lines.push(`${indent}{ const ${varName} = __p ? tAfter(__p[${pIdx}]) : $t(${elVar}, '${text.slotId}')[0]`);
18663
- } else {
18664
- lines.push(`${indent}{ const [${varName}] = $t(${elVar}, '${text.slotId}')`);
19054
+ function emitOuterTexts(lines, indent, elVar, texts, bindingBfId, textClaimPathExprs) {
19055
+ if (texts.length === 0)
19056
+ return;
19057
+ const slots = texts.map((t) => ({
19058
+ id: t.slotId,
19059
+ kind: "text",
19060
+ path: [],
19061
+ pathExpr: textClaimPathExprs?.get(t.slotId)
19062
+ }));
19063
+ const writer = claimWriterVarName(slots, varSlotId);
19064
+ lines.push(`${indent}const ${writer} = lazySlots(${elVar}, ${claimPlanLiteral(slots)})`);
19065
+ for (const text of texts) {
19066
+ lines.push(`${indent}createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) }${bindingBfId(text.slotId)})`);
18665
19067
  }
18666
- lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${bfId}) }`);
18667
19068
  }
18668
19069
  function emitOuterConditional(lines, indent, elVar, cond, pc) {
18669
19070
  const armIndent = `${indent} `;
@@ -18825,8 +19226,24 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
18825
19226
  childRefs,
18826
19227
  bodyIsMultiRoot,
18827
19228
  anchored,
18828
- anchorKeyExpr
19229
+ anchorKeyExpr,
19230
+ preambleRegions
18829
19231
  } = plan;
19232
+ if (plan.flatMapLeafItem) {
19233
+ const loopBfIdArg = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
19234
+ lines.push(`${topIndent}mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (__bfD, ${indexParam}, __existing) => {`);
19235
+ lines.push(`${topIndent} let __el = __existing`);
19236
+ lines.push(`${topIndent} if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }`);
19237
+ lines.push(`${topIndent} let __last = __existing ? undefined : __bfD().h`);
19238
+ lines.push(`${topIndent} createEffect(() => {`);
19239
+ lines.push(`${topIndent} const __html = __bfD().h`);
19240
+ lines.push(`${topIndent} if (__last === undefined) { __last = __html; return }`);
19241
+ lines.push(`${topIndent} if (__html !== __last) { __last = __html; patchLeaf(__el, __html) }`);
19242
+ lines.push(`${topIndent} })`);
19243
+ lines.push(`${topIndent} return __el`);
19244
+ lines.push(`${topIndent}}, '${markerId}'${loopBfIdArg})`);
19245
+ return;
19246
+ }
18830
19247
  if (anchored) {
18831
19248
  stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr);
18832
19249
  return;
@@ -18837,7 +19254,7 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
18837
19254
  emitHoistedTemplateDecl(lines, topIndent, tplVar, hoistedTpl);
18838
19255
  }
18839
19256
  const loopBfId = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
18840
- if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
19257
+ if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0 && preambleRegions.length === 0) {
18841
19258
  const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
18842
19259
  const preamble = mapPreambleWrapped ? `${mapPreambleWrapped}; ` : "";
18843
19260
  const cloneExpr = hoistedTpl ? `return ${hoistedCloneExpr(tplVar, hoistedTpl)}` : emitTemplateCloneInline(template);
@@ -18866,22 +19283,31 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
18866
19283
  ...reactiveEffects?.attrSlots.map((s) => s.slotId) ?? [],
18867
19284
  ...childRefs.map((r) => r.childSlotId)
18868
19285
  ];
18869
- const textSlotIds = reactiveEffects?.outerTexts.map((t) => t.slotId) ?? [];
18870
- if (elementSlotIds.length > 0 || textSlotIds.length > 0) {
18871
- const built = buildSkeletonPathPlan(plan.skeletonPaths, "__el", { elementSlotIds, textSlotIds });
19286
+ if (elementSlotIds.length > 0) {
19287
+ const built = buildSkeletonPathPlan(plan.skeletonPaths, "__el", { elementSlotIds, textSlotIds: [] });
18872
19288
  if (built.arrayElems.length > 0) {
18873
19289
  pathPlan = built;
18874
19290
  lines.push(`${bodyIndent}const __p = __existing ? null : [${built.arrayElems.join(", ")}]`);
18875
19291
  }
18876
19292
  }
18877
19293
  }
18878
- if (reactiveEffects !== null) {
19294
+ const textClaimPathExprs = new Map;
19295
+ if (hoistedTpl && plan.skeletonPaths) {
19296
+ for (const text of reactiveEffects?.outerTexts ?? []) {
19297
+ const path = plan.skeletonPaths.textMarkerPaths.get(text.slotId);
19298
+ if (path)
19299
+ textClaimPathExprs.set(text.slotId, `__existing ? [] : [${path.join(", ")}]`);
19300
+ }
19301
+ }
19302
+ if (reactiveEffects !== null || preambleRegions.length > 0) {
18879
19303
  stringifyReactiveEffects(lines, reactiveEffects, {
18880
19304
  indent: bodyIndent,
18881
19305
  elVar: "__el",
18882
19306
  bodyIsMultiRoot,
18883
19307
  elementIndexBySlot: pathPlan?.elementIndexBySlot,
18884
- textIndexBySlot: pathPlan?.textIndexBySlot
19308
+ textClaimPathExprs,
19309
+ preambleRegions,
19310
+ mapPreambleWrapped
18885
19311
  });
18886
19312
  }
18887
19313
  emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot, elementIndexBySlot: pathPlan?.elementIndexBySlot });
@@ -18993,10 +19419,13 @@ function stringifyStaticLoop(lines, plan) {
18993
19419
  }
18994
19420
  lines.push(` }`);
18995
19421
  }
18996
- for (const text of texts) {
18997
- const vn = `__rt_${varSlotId(text.slotId)}`;
18998
- lines.push(` { const [${vn}] = $t(__iterEl, '${text.slotId}')`);
18999
- lines.push(` if (${vn}) createEffect(() => { ${vn}.textContent = String(${text.expression}) }${profileBindingId(pc, text.slotId)}) }`);
19422
+ if (texts.length > 0) {
19423
+ const slots = texts.map((t) => ({ id: t.slotId, kind: "text", path: [] }));
19424
+ const writer = claimWriterVarName(slots, varSlotId);
19425
+ lines.push(` const ${writer} = lazySlots(__iterEl, ${claimPlanLiteral(slots)})`);
19426
+ for (const text of texts) {
19427
+ lines.push(` createEffect(() => { ${writer}('${text.slotId}', String(${text.expression})) }${profileBindingId(pc, text.slotId)})`);
19428
+ }
19000
19429
  }
19001
19430
  emitLoopChildRefs(lines, childRefs, { indent: " ", elVar: "__iterEl", bodyIsMultiRoot: false });
19002
19431
  lines.push(` }`);
@@ -19051,13 +19480,18 @@ function emitReactive(lines, inner, indent, pc) {
19051
19480
  if (inner.childLevels.length > 0) {
19052
19481
  stringifyInnerLoops(lines, inner.childLevels, `${indent} `, pc);
19053
19482
  }
19054
- for (const text of emit.reactiveTexts) {
19483
+ const conditionalTexts = emit.reactiveTexts.filter((t) => t.insideConditional);
19484
+ const plainTexts = emit.reactiveTexts.filter((t) => !t.insideConditional);
19485
+ for (const text of conditionalTexts) {
19055
19486
  const bf = profileBindingId(pc, text.slotId);
19056
- if (text.insideConditional) {
19057
- lines.push(`${indent} createEffect(() => { const [__rt] = $t(__innerEl${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) }${bf})`);
19058
- } else {
19059
- lines.push(`${indent} { const [__rt] = $t(__innerEl${uid}, '${text.slotId}')`);
19060
- lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }${bf}) }`);
19487
+ lines.push(`${indent} createEffect(() => { claimSlots(__innerEl${uid}, [{ id: '${text.slotId}', kind: 'text', path: [] }]).write('${text.slotId}', String(${text.wrappedExpression})) }${bf})`);
19488
+ }
19489
+ if (plainTexts.length > 0) {
19490
+ const slots = plainTexts.map((t) => ({ id: t.slotId, kind: "text", path: [] }));
19491
+ const writer = claimWriterVarName(slots, varSlotId);
19492
+ lines.push(`${indent} const ${writer} = lazySlots(__innerEl${uid}, ${claimPlanLiteral(slots)})`);
19493
+ for (const text of plainTexts) {
19494
+ lines.push(`${indent} createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) }${profileBindingId(pc, text.slotId)})`);
19061
19495
  }
19062
19496
  }
19063
19497
  for (const attr of emit.reactiveAttrs) {
@@ -19195,6 +19629,12 @@ function indexBindingLine(handler, indexParam, indexExpr) {
19195
19629
  return null;
19196
19630
  return `const ${indexParam} = ${indexExpr}`;
19197
19631
  }
19632
+ function preambleLineForHandler(mapPreamble, declaredNames, handler) {
19633
+ if (!mapPreamble || declaredNames.length === 0)
19634
+ return null;
19635
+ const free = extractFreeIdentifiersFromText(handler);
19636
+ return declaredNames.some((name) => free.has(name)) ? mapPreamble : null;
19637
+ }
19198
19638
  function stringifyEventDelegation(lines, plan) {
19199
19639
  const { containerVar, events, itemLookup, profileComponentName } = plan;
19200
19640
  const eventsByName = new Map;
@@ -19240,7 +19680,8 @@ function stringifyEventDelegation(lines, plan) {
19240
19680
  }
19241
19681
  }
19242
19682
  function emitKeyedLookup(ls, ev, handlerCall, lookup) {
19243
- const { arrayExpr, param, keyWithItem, mapPreamble, hasBindings, indexParam } = lookup;
19683
+ const { arrayExpr, param, keyWithItem, mapPreamble, mapPreambleDeclaredNames, hasBindings, indexParam } = lookup;
19684
+ const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
19244
19685
  if (ev.nestedLoops.length === 0) {
19245
19686
  const idxLine2 = indexBindingLine(ev.handler, indexParam, `${arrayExpr}.findIndex(item => String(${keyWithItem}) === key)`);
19246
19687
  ls.push(` const li = ${varSlotId(ev.childSlotId)}El.closest('[${DATA_KEY}]')`);
@@ -19250,20 +19691,21 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
19250
19691
  ls.push(` const __bfLoopItem = ${arrayExpr}.find(item => String(${keyWithItem}) === key)`);
19251
19692
  ls.push(` if (__bfLoopItem) {`);
19252
19693
  ls.push(` const ${param} = __bfLoopItem`);
19253
- if (mapPreamble)
19254
- ls.push(` ${mapPreamble}`);
19694
+ if (preambleLine)
19695
+ ls.push(` ${preambleLine}`);
19255
19696
  if (idxLine2)
19256
19697
  ls.push(` ${idxLine2}`);
19257
- ls.push(` ${handlerCall}`);
19698
+ ls.push(` ;${handlerCall}`);
19258
19699
  ls.push(` }`);
19259
19700
  } else {
19260
19701
  ls.push(` const ${param} = ${arrayExpr}.find(item => String(${keyWithItem}) === key)`);
19261
- if (mapPreamble)
19262
- ls.push(` ${mapPreamble}`);
19702
+ ls.push(` if (${param}) {`);
19703
+ if (preambleLine)
19704
+ ls.push(` ${preambleLine}`);
19263
19705
  if (idxLine2)
19264
- ls.push(` if (${param}) { ${idxLine2}; ${handlerCall} }`);
19265
- else
19266
- ls.push(` if (${param}) ${handlerCall}`);
19706
+ ls.push(` ${idxLine2}`);
19707
+ ls.push(` ;${handlerCall}`);
19708
+ ls.push(` }`);
19267
19709
  }
19268
19710
  ls.push(` }`);
19269
19711
  return;
@@ -19290,16 +19732,18 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
19290
19732
  }
19291
19733
  const outerGuard = hasBindings ? "__bfLoopItem" : param;
19292
19734
  const allParams = [outerGuard, ...ev.nestedLoops.map((n) => n.param)];
19293
- if (mapPreamble)
19294
- ls.push(` ${mapPreamble}`);
19295
19735
  const idxLine = indexBindingLine(ev.handler, indexParam, `${arrayExpr}.findIndex(item => String(${keyWithItem}) === outerKey)`);
19736
+ ls.push(` if (${allParams.join(" && ")}) {`);
19737
+ if (preambleLine)
19738
+ ls.push(` ${preambleLine}`);
19296
19739
  if (idxLine)
19297
- ls.push(` if (${allParams.join(" && ")}) { ${idxLine}; ${handlerCall} }`);
19298
- else
19299
- ls.push(` if (${allParams.join(" && ")}) ${handlerCall}`);
19740
+ ls.push(` ${idxLine}`);
19741
+ ls.push(` ;${handlerCall}`);
19742
+ ls.push(` }`);
19300
19743
  }
19301
19744
  function emitDynamicIndexLookup(ls, ev, handlerCall, lookup) {
19302
- const { arrayExpr, param, mapPreamble, hasBindings, indexParam } = lookup;
19745
+ const { arrayExpr, param, mapPreamble, mapPreambleDeclaredNames, hasBindings, indexParam } = lookup;
19746
+ const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
19303
19747
  const idxLine = indexBindingLine(ev.handler, indexParam, "idx");
19304
19748
  ls.push(` const li = ${varSlotId(ev.childSlotId)}El.closest('li, [bf-i]')`);
19305
19749
  ls.push(` if (li && li.parentElement) {`);
@@ -19308,25 +19752,27 @@ function emitDynamicIndexLookup(ls, ev, handlerCall, lookup) {
19308
19752
  ls.push(` const __bfLoopItem = ${arrayExpr}[idx]`);
19309
19753
  ls.push(` if (__bfLoopItem) {`);
19310
19754
  ls.push(` const ${param} = __bfLoopItem`);
19311
- if (mapPreamble)
19312
- ls.push(` ${mapPreamble}`);
19755
+ if (preambleLine)
19756
+ ls.push(` ${preambleLine}`);
19313
19757
  if (idxLine)
19314
19758
  ls.push(` ${idxLine}`);
19315
- ls.push(` ${handlerCall}`);
19759
+ ls.push(` ;${handlerCall}`);
19316
19760
  ls.push(` }`);
19317
19761
  } else {
19318
19762
  ls.push(` const ${param} = ${arrayExpr}[idx]`);
19319
- if (mapPreamble)
19320
- ls.push(` ${mapPreamble}`);
19763
+ ls.push(` if (${param}) {`);
19764
+ if (preambleLine)
19765
+ ls.push(` ${preambleLine}`);
19321
19766
  if (idxLine)
19322
- ls.push(` if (${param}) { ${idxLine}; ${handlerCall} }`);
19323
- else
19324
- ls.push(` if (${param}) ${handlerCall}`);
19767
+ ls.push(` ${idxLine}`);
19768
+ ls.push(` ;${handlerCall}`);
19769
+ ls.push(` }`);
19325
19770
  }
19326
19771
  ls.push(` }`);
19327
19772
  }
19328
19773
  function emitStaticIndexLookup(ls, ev, handlerCall, lookup, containerVar) {
19329
- const { arrayExpr, param, mapPreamble, offset, indexParam } = lookup;
19774
+ const { arrayExpr, param, mapPreamble, mapPreambleDeclaredNames, offset, indexParam } = lookup;
19775
+ const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
19330
19776
  const idxLine = indexBindingLine(ev.handler, indexParam, "__idx");
19331
19777
  ls.push(` let __el = ${varSlotId(ev.childSlotId)}El`);
19332
19778
  ls.push(` while (__el.parentElement && __el.parentElement !== ${containerVar}) __el = __el.parentElement`);
@@ -19334,12 +19780,13 @@ function emitStaticIndexLookup(ls, ev, handlerCall, lookup, containerVar) {
19334
19780
  const idxOffset = buildLoopChildIndexSubtraction(offset ?? undefined);
19335
19781
  ls.push(` const __idx = Array.from(${containerVar}.children).indexOf(__el)${idxOffset}`);
19336
19782
  ls.push(` const ${param} = ${arrayExpr}[__idx]`);
19337
- if (mapPreamble)
19338
- ls.push(` ${mapPreamble}`);
19783
+ ls.push(` if (${param}) {`);
19784
+ if (preambleLine)
19785
+ ls.push(` ${preambleLine}`);
19339
19786
  if (idxLine)
19340
- ls.push(` if (${param}) { ${idxLine}; ${handlerCall} }`);
19341
- else
19342
- ls.push(` if (${param}) ${handlerCall}`);
19787
+ ls.push(` ${idxLine}`);
19788
+ ls.push(` ;${handlerCall}`);
19789
+ ls.push(` }`);
19343
19790
  ls.push(` }`);
19344
19791
  }
19345
19792
 
@@ -19372,12 +19819,29 @@ function emitPlain(lines, plan) {
19372
19819
  eventDelegation,
19373
19820
  childRefs,
19374
19821
  bodyIsMultiRoot,
19375
- profileLoopId
19822
+ profileLoopId,
19823
+ preambleRegions
19376
19824
  } = plan;
19377
19825
  const loopBfId = profileLoopId ? `, ${JSON.stringify(profileLoopId)}` : "";
19378
19826
  const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
19379
19827
  lines.push(` __disposers.push(createDisposableEffect(() => {`);
19380
- if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
19828
+ if (plan.flatMapLeafItem) {
19829
+ lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (__bfD, ${indexParam}, __existing) => {`);
19830
+ lines.push(` let __el = __existing`);
19831
+ lines.push(` if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }`);
19832
+ lines.push(` let __last = __existing ? undefined : __bfD().h`);
19833
+ lines.push(` createEffect(() => {`);
19834
+ lines.push(` const __html = __bfD().h`);
19835
+ lines.push(` if (__last === undefined) { __last = __html; return }`);
19836
+ lines.push(` if (__html !== __last) { __last = __html; patchLeaf(__el, __html) }`);
19837
+ lines.push(` })`);
19838
+ lines.push(` return __el`);
19839
+ lines.push(` }, '${markerId}'${loopBfId})`);
19840
+ lines.push(` }))`);
19841
+ stringifyEventDelegation(lines, eventDelegation);
19842
+ return;
19843
+ }
19844
+ if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0 && preambleRegions.length === 0) {
19381
19845
  const cloneExpr = emitTemplateCloneInline(template);
19382
19846
  if (mapPreambleWrapped) {
19383
19847
  lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${mapPreambleWrapped}; ${cloneExpr} }, '${markerId}'${loopBfId})`);
@@ -19398,8 +19862,14 @@ function emitPlain(lines, plan) {
19398
19862
  indent: " ",
19399
19863
  singleRootLayout: "inline"
19400
19864
  });
19401
- if (reactiveEffects !== null) {
19402
- stringifyReactiveEffects(lines, reactiveEffects, { indent: " ", elVar: "__el", bodyIsMultiRoot });
19865
+ if (reactiveEffects !== null || preambleRegions.length > 0) {
19866
+ stringifyReactiveEffects(lines, reactiveEffects, {
19867
+ indent: " ",
19868
+ elVar: "__el",
19869
+ bodyIsMultiRoot,
19870
+ preambleRegions,
19871
+ mapPreambleWrapped
19872
+ });
19403
19873
  }
19404
19874
  emitLoopChildRefs(lines, childRefs, { indent: " ", elVar: "__el", bodyIsMultiRoot });
19405
19875
  lines.push(` return __el`);
@@ -19487,13 +19957,13 @@ function emitArmBody(lines, body, mode, indent, profileComponentName) {
19487
19957
  }
19488
19958
  lines.push(`${indent}} }`);
19489
19959
  }
19490
- for (const te of body.textEffects) {
19491
- const v = varSlotId(te.slotId);
19492
- lines.push(`${indent}let __anchor_${v} = $t(__branchScope, '${te.slotId}')[0]`);
19493
- lines.push(`${indent}__disposers.push(createDisposableEffect(() => {`);
19494
- lines.push(`${indent} const __val = ${te.expression}`);
19495
- lines.push(`${indent} __anchor_${v} = __bfText(__anchor_${v}, __val)`);
19496
- lines.push(`${indent}}${bindingBfId(te.slotId)}))`);
19960
+ if (body.textEffects.length > 0) {
19961
+ const slots = body.textEffects.map((te) => ({ id: te.slotId, kind: "markup", path: [] }));
19962
+ const writer = claimWriterVarName(slots, varSlotId);
19963
+ lines.push(`${indent}const ${writer} = lazySlots(__branchScope, ${claimPlanLiteral(slots)})`);
19964
+ for (const te of body.textEffects) {
19965
+ lines.push(`${indent}__disposers.push(createDisposableEffect(() => { ${writer}('${te.slotId}', escapeTextOrNode(${te.expression})) }${bindingBfId(te.slotId)}))`);
19966
+ }
19497
19967
  }
19498
19968
  if (body.loops.length > 0) {
19499
19969
  stringifyBranchLoops(lines, body.loops);
@@ -19587,6 +20057,29 @@ function buildPlainLoopPlan(elem, profileComponentName) {
19587
20057
  const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings);
19588
20058
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
19589
20059
  const hasReactive2 = elem.bindings.reactiveAttrs.length > 0 || elem.bindings.reactiveTexts.length > 0 || elem.bindings.conditionals.length > 0;
20060
+ if (elem.flatMapClient) {
20061
+ return {
20062
+ kind: "plain",
20063
+ rowConstruction: "string-template",
20064
+ containerVar: `_${varSlotId(elem.slotId)}`,
20065
+ markerId: elem.markerId,
20066
+ profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : undefined,
20067
+ arrayExpr: `(${buildChainedArrayExpr(elem)}).flatMap(${elem.flatMapClient.params} => ${elem.flatMapClient.body})`,
20068
+ keyFn: elem.flatMapClient.keyed ? "(__bfD, __bfI) => String(__bfD.k ?? __bfI)" : "null",
20069
+ paramHead: "__bfD",
20070
+ paramUnwrap: "",
20071
+ indexParam: "__idx",
20072
+ mapPreambleWrapped: "",
20073
+ template: "",
20074
+ reactiveEffects: null,
20075
+ childRefs: [],
20076
+ bodyIsMultiRoot: false,
20077
+ anchored: false,
20078
+ anchorKeyExpr: "__idx",
20079
+ flatMapLeafItem: true,
20080
+ preambleRegions: []
20081
+ };
20082
+ }
19590
20083
  return {
19591
20084
  kind: "plain",
19592
20085
  rowConstruction: "string-template",
@@ -19607,6 +20100,7 @@ function buildPlainLoopPlan(elem, profileComponentName) {
19607
20100
  skeletonPaths: elem.skeletonPaths,
19608
20101
  reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
19609
20102
  childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
20103
+ preambleRegions: buildPreambleRegionPlans(elem.preambleRegions, elem.param, elem.paramBindings),
19610
20104
  bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
19611
20105
  anchored: elem.bodyIsItemConditional ?? false,
19612
20106
  anchorKeyExpr: elem.key ? wrap(elem.key) : elem.index || "__idx"
@@ -19701,7 +20195,6 @@ function emitLoopEventDelegation(lines, elem, kind, profileComponentName) {
19701
20195
  // src/ir-to-client-js/element-refs.ts
19702
20196
  function generateElementRefs(ctx) {
19703
20197
  const regularSlots = new Set;
19704
- const textSlots = new Set;
19705
20198
  const componentSlots = new Set;
19706
20199
  const conditionalSlotIds = collectConditionalSlotIds(ctx);
19707
20200
  for (const elem of ctx.interactiveElements) {
@@ -19709,11 +20202,6 @@ function generateElementRefs(ctx) {
19709
20202
  regularSlots.add(elem.slotId);
19710
20203
  }
19711
20204
  }
19712
- for (const elem of ctx.dynamicElements) {
19713
- if (!elem.insideConditional) {
19714
- textSlots.add(elem.slotId);
19715
- }
19716
- }
19717
20205
  for (const elem of ctx.conditionalElements) {
19718
20206
  regularSlots.add(elem.slotId);
19719
20207
  }
@@ -19742,11 +20230,10 @@ function generateElementRefs(ctx) {
19742
20230
  for (const slotId of componentSlots) {
19743
20231
  regularSlots.delete(slotId);
19744
20232
  }
19745
- if (regularSlots.size === 0 && textSlots.size === 0 && componentSlots.size === 0)
20233
+ if (regularSlots.size === 0 && componentSlots.size === 0)
19746
20234
  return "";
19747
20235
  const refLines = [];
19748
20236
  emitSlotRefs(refLines, [...regularSlots], "$");
19749
- emitSlotRefs(refLines, [...textSlots], "$t");
19750
20237
  emitSlotRefs(refLines, [...componentSlots], "$c");
19751
20238
  return refLines.join(`
19752
20239
  `);
@@ -20302,6 +20789,111 @@ ${externalImports.join(`
20302
20789
  return generatedCode.replace(IMPORT_PLACEHOLDER, allImports);
20303
20790
  }
20304
20791
 
20792
+ // src/ir-to-client-js/client-only-elision.ts
20793
+ function decideClientOnlyElision(root) {
20794
+ walkNode(root, [], new Set, { bailed: false });
20795
+ }
20796
+ function walkNode(node, path, forceCloseAncestors, state) {
20797
+ if (state.bailed)
20798
+ return;
20799
+ if (node.type === "element") {
20800
+ if (!elementIsPathSafe(node.tag, flattenSkeletonChildren(node.children))) {
20801
+ state.bailed = true;
20802
+ return;
20803
+ }
20804
+ const groupIdx = skeletonForceCloseGroup(node.tag);
20805
+ if (groupIdx >= 0 && forceCloseAncestors.has(groupIdx)) {
20806
+ state.bailed = true;
20807
+ return;
20808
+ }
20809
+ const nextAncestors = groupIdx >= 0 ? new Set([...forceCloseAncestors, groupIdx]) : forceCloseAncestors;
20810
+ walkChildren(flattenSkeletonChildren(node.children), path, nextAncestors, state);
20811
+ } else if (node.type === "fragment") {
20812
+ walkChildren(flattenSkeletonChildren(node.children), path, forceCloseAncestors, state);
20813
+ }
20814
+ }
20815
+ function walkChildren(children, parentPath, forceCloseAncestors, state) {
20816
+ let frozen = false;
20817
+ let idx = 0;
20818
+ let pendingText = false;
20819
+ for (let i = 0;i < children.length; i++) {
20820
+ if (state.bailed)
20821
+ return;
20822
+ const child = children[i];
20823
+ switch (child.type) {
20824
+ case "text": {
20825
+ if (child.value === "")
20826
+ continue;
20827
+ if (!pendingText)
20828
+ idx += 1;
20829
+ pendingText = true;
20830
+ continue;
20831
+ }
20832
+ case "expression": {
20833
+ if (child.expr === "null" || child.expr === "undefined")
20834
+ continue;
20835
+ if (child.clientOnly && child.slotId) {
20836
+ const adjacent = isTextLike(children[i - 1]) || isTextLike(children[i + 1]);
20837
+ if (!frozen && !adjacent) {
20838
+ markElided(child, [...parentPath, idx]);
20839
+ frozen = true;
20840
+ } else {
20841
+ frozen = true;
20842
+ }
20843
+ idx += 1;
20844
+ pendingText = false;
20845
+ continue;
20846
+ }
20847
+ frozen = true;
20848
+ idx += 1;
20849
+ pendingText = false;
20850
+ continue;
20851
+ }
20852
+ case "element": {
20853
+ if (!elementIsPathSafe(child.tag, flattenSkeletonChildren(child.children))) {
20854
+ state.bailed = true;
20855
+ return;
20856
+ }
20857
+ if (!frozen) {
20858
+ walkNode(child, [...parentPath, idx], forceCloseAncestors, state);
20859
+ }
20860
+ idx += 1;
20861
+ pendingText = false;
20862
+ continue;
20863
+ }
20864
+ case "fragment":
20865
+ continue;
20866
+ default:
20867
+ frozen = true;
20868
+ idx += 1;
20869
+ pendingText = false;
20870
+ continue;
20871
+ }
20872
+ }
20873
+ }
20874
+ function isTextLike(node) {
20875
+ if (!node)
20876
+ return false;
20877
+ if (node.type === "text")
20878
+ return node.value !== "";
20879
+ if (node.type === "expression")
20880
+ return node.expr !== "null" && node.expr !== "undefined";
20881
+ return false;
20882
+ }
20883
+ function markElided(expr, path) {
20884
+ expr.markerless = true;
20885
+ expr.elidedPath = path;
20886
+ }
20887
+ function elementIsPathSafe(tag, flatChildren) {
20888
+ if (SKELETON_PATH_HAZARD_TAGS.has(tag))
20889
+ return false;
20890
+ if (VOID_ELEMENTS.has(tag) && flatChildren.length > 0)
20891
+ return false;
20892
+ if (tag === "tr" && hasForeignTableRowContent(flatChildren))
20893
+ return false;
20894
+ return true;
20895
+ }
20896
+
20305
20897
  // src/css-layer-prefixer.ts
20306
20898
  function prefixClass(cls, layerName) {
20307
20899
  if (!cls || cls.startsWith("layer-"))
@@ -21594,7 +22186,7 @@ function checkRichTypeMethodCalls(root, metadata, errors) {
21594
22186
  return;
21595
22187
  const matchers = prepareLoweringMatchers(metadata);
21596
22188
  const seen = new Set;
21597
- walkNode(root, metadata, EMPTY_BINDINGS2, matchers, errors, seen);
22189
+ walkNode2(root, metadata, EMPTY_BINDINGS2, matchers, errors, seen);
21598
22190
  }
21599
22191
  function isLoweringClaimed(matchers, callee, args) {
21600
22192
  return matchers.some((m) => m(callee, args) !== null);
@@ -21731,7 +22323,7 @@ function walkAttrValue(value, clientOnly, loc, meta, bindings, matchers, errors,
21731
22323
  walkTemplateParts(value.parts, loc, meta, bindings, matchers, errors, seen);
21732
22324
  }
21733
22325
  }
21734
- function walkNode(node, meta, bindings, matchers, errors, seen) {
22326
+ function walkNode2(node, meta, bindings, matchers, errors, seen) {
21735
22327
  if (node.type === "expression") {
21736
22328
  if (!node.clientOnly && node.parsed)
21737
22329
  checkExpr(node.parsed, node.loc, meta, bindings, matchers, errors, seen);
@@ -21757,12 +22349,12 @@ function walkNode(node, meta, bindings, matchers, errors, seen) {
21757
22349
  case "fragment":
21758
22350
  case "provider":
21759
22351
  for (const child of node.children)
21760
- walkNode(child, meta, bindings, matchers, errors, seen);
22352
+ walkNode2(child, meta, bindings, matchers, errors, seen);
21761
22353
  break;
21762
22354
  case "async":
21763
- walkNode(node.fallback, meta, bindings, matchers, errors, seen);
22355
+ walkNode2(node.fallback, meta, bindings, matchers, errors, seen);
21764
22356
  for (const child of node.children)
21765
- walkNode(child, meta, bindings, matchers, errors, seen);
22357
+ walkNode2(child, meta, bindings, matchers, errors, seen);
21766
22358
  break;
21767
22359
  case "loop": {
21768
22360
  if (node.clientOnly)
@@ -21775,35 +22367,35 @@ function walkNode(node, meta, bindings, matchers, errors, seen) {
21775
22367
  if (node.index)
21776
22368
  loopBindings.set(node.index, null);
21777
22369
  for (const child of node.children)
21778
- walkNode(child, meta, loopBindings, matchers, errors, seen);
22370
+ walkNode2(child, meta, loopBindings, matchers, errors, seen);
21779
22371
  if (node.childComponent) {
21780
22372
  for (const child of node.childComponent.children)
21781
- walkNode(child, meta, loopBindings, matchers, errors, seen);
22373
+ walkNode2(child, meta, loopBindings, matchers, errors, seen);
21782
22374
  }
21783
22375
  for (const nested of node.nestedComponents ?? []) {
21784
22376
  for (const child of nested.children)
21785
- walkNode(child, meta, loopBindings, matchers, errors, seen);
22377
+ walkNode2(child, meta, loopBindings, matchers, errors, seen);
21786
22378
  }
21787
22379
  for (const seg of node.flatMapCallback?.segments ?? []) {
21788
22380
  if (seg.kind === "jsx")
21789
- walkNode(seg.ir, meta, loopBindings, matchers, errors, seen);
22381
+ walkNode2(seg.ir, meta, loopBindings, matchers, errors, seen);
21790
22382
  }
21791
22383
  for (const seg of node.preamble?.segments ?? []) {
21792
22384
  if (seg.kind === "jsx")
21793
- walkNode(seg.ir, meta, loopBindings, matchers, errors, seen);
22385
+ walkNode2(seg.ir, meta, loopBindings, matchers, errors, seen);
21794
22386
  }
21795
22387
  break;
21796
22388
  }
21797
22389
  case "conditional":
21798
22390
  if (node.clientOnly)
21799
22391
  break;
21800
- walkNode(node.whenTrue, meta, bindings, matchers, errors, seen);
21801
- walkNode(node.whenFalse, meta, bindings, matchers, errors, seen);
22392
+ walkNode2(node.whenTrue, meta, bindings, matchers, errors, seen);
22393
+ walkNode2(node.whenFalse, meta, bindings, matchers, errors, seen);
21802
22394
  break;
21803
22395
  case "if-statement":
21804
- walkNode(node.consequent, meta, bindings, matchers, errors, seen);
22396
+ walkNode2(node.consequent, meta, bindings, matchers, errors, seen);
21805
22397
  if (node.alternate)
21806
- walkNode(node.alternate, meta, bindings, matchers, errors, seen);
22398
+ walkNode2(node.alternate, meta, bindings, matchers, errors, seen);
21807
22399
  break;
21808
22400
  }
21809
22401
  }
@@ -21869,6 +22461,7 @@ function compileMultipleComponents(source, filePath, componentNames, options) {
21869
22461
  };
21870
22462
  componentIR.metadata.clientAnalysis = analyzeClientNeeds(componentIR);
21871
22463
  checkRichTypeMethodCalls(componentIR.root, componentIR.metadata, errors);
22464
+ decideClientOnlyElision(componentIR.root);
21872
22465
  if (options.cssLayerPrefix) {
21873
22466
  applyCssLayerPrefix(componentIR, options.cssLayerPrefix);
21874
22467
  }
@@ -22208,6 +22801,7 @@ function compileJSX(source, filePath, options) {
22208
22801
  };
22209
22802
  componentIR.metadata.clientAnalysis = analyzeClientNeeds(componentIR);
22210
22803
  checkRichTypeMethodCalls(componentIR.root, componentIR.metadata, errors);
22804
+ decideClientOnlyElision(componentIR.root);
22211
22805
  if (ctx.importedClientSignalNames.size > 0) {
22212
22806
  const sources = new Set;
22213
22807
  for (const imp of ctx.imports) {
@@ -26275,6 +26869,7 @@ export {
26275
26869
  diffStaticBudget,
26276
26870
  diffProfiles,
26277
26871
  describeFallback,
26872
+ decideClientOnlyElision,
26278
26873
  dangerousInnerHtmlMetacharViolation,
26279
26874
  dangerousInnerHtmlDiagnostic,
26280
26875
  createProgramForFile,