@barefootjs/cli 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.
package/dist/index.js CHANGED
@@ -3695,7 +3695,7 @@ function templateAttrExpr(attrName, valExpr, presenceOrUndefined) {
3695
3695
  return `\${((v) => v != null ? 'style="' + ${escapeAttrValueExpr("v")} + '"' : '')(styleToCss(${valExpr}))}`;
3696
3696
  }
3697
3697
  if (attrName === "data-key" || attrName.startsWith("data-key-")) {
3698
- return `${attrName}="\${${valExpr}}"`;
3698
+ return `${attrName}="\${${escapeAttrValueExpr(valExpr)}}"`;
3699
3699
  }
3700
3700
  return `\${(${valExpr}) != null ? '${attrName}="' + ${escapeAttrValueExpr(valExpr)} + '"' : ''}`;
3701
3701
  }
@@ -3815,12 +3815,54 @@ function renderPreamble(preamble, opts) {
3815
3815
  if (seg.kind === "js") {
3816
3816
  const text = opts.textVariant === "template" ? seg.templateText ?? seg.text : seg.text;
3817
3817
  out += opts.transformJs ? opts.transformJs(text) : text;
3818
+ } else if (opts.rawLeaf) {
3819
+ out += opts.renderLeaf(escapeLeafTextExpressions(seg.ir));
3818
3820
  } else {
3819
3821
  out += "`" + opts.renderLeaf(escapeLeafTextExpressions(seg.ir)) + "`";
3820
3822
  }
3821
3823
  }
3822
3824
  return out;
3823
3825
  }
3826
+ function flatMapLeafKeyExpr(ir) {
3827
+ if (ir.type !== "element") return null;
3828
+ const keyAttr = ir.attrs.find((a) => a.name === "key");
3829
+ if (!keyAttr) return null;
3830
+ switch (keyAttr.value.kind) {
3831
+ case "expression":
3832
+ return `(${keyAttr.value.expr})`;
3833
+ case "literal":
3834
+ return JSON.stringify(keyAttr.value.value);
3835
+ case "template":
3836
+ return attrValueToString(keyAttr.value);
3837
+ default:
3838
+ return null;
3839
+ }
3840
+ }
3841
+ function stripLeafKeyAttr(ir) {
3842
+ if (ir.type !== "element") return ir;
3843
+ return { ...ir, attrs: ir.attrs.filter((a) => a.name !== "key") };
3844
+ }
3845
+ function renderFlatMapClientBody(cb, restSpreadNames) {
3846
+ return renderPreamble(cb, {
3847
+ textVariant: "client",
3848
+ rawLeaf: true,
3849
+ renderLeaf: (ir) => {
3850
+ const key = flatMapLeafKeyExpr(ir);
3851
+ const html = irToHtmlTemplate(stripLeafKeyAttr(ir), restSpreadNames, 1, void 0, void 0, true);
3852
+ return `({ k: ${key ?? "undefined"}, h: \`${html}\` })`;
3853
+ }
3854
+ });
3855
+ }
3856
+ function flatMapCallbackHasKeyedLeaf(cb) {
3857
+ return cb.segments.some((s) => s.kind === "jsx" && flatMapLeafKeyExpr(s.ir) !== null);
3858
+ }
3859
+ function renderFlatMapProjectionClientBody(inner, restSpreadNames) {
3860
+ const chained = applyLoopChain(inner);
3861
+ const params = inner.index ? `(${inner.param}, ${inner.index})` : `(${inner.param})`;
3862
+ const key = inner.key ? `(${inner.key})` : "undefined";
3863
+ const html = inner.children.map((c) => irToHtmlTemplate(escapeLeafTextExpressions(c), restSpreadNames, 1, void 0, void 0, true)).join("");
3864
+ return `${chained}.map(${params} => ({ k: ${key}, h: \`${html}\` }))`;
3865
+ }
3824
3866
  function escapeLeafTextExpressions(ir) {
3825
3867
  switch (ir.type) {
3826
3868
  case "element":
@@ -3884,6 +3926,10 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
3884
3926
  return escapeHtml(node.value);
3885
3927
  case "expression": {
3886
3928
  if (node.expr === "null" || node.expr === "undefined") return "";
3929
+ if (node.markerless) {
3930
+ const bare = wrapInterpolation(wrapExpr(node.expr));
3931
+ return `\${${bare}}`;
3932
+ }
3887
3933
  const inner = wrapInterpolation(wrapExpr(node.expr));
3888
3934
  const valueExpr = node.joinArrayChild ? `Array.isArray(${inner}) ? ${inner}.join('') : (${inner} ?? '')` : inner;
3889
3935
  if (node.slotId) {
@@ -3949,7 +3995,7 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
3949
3995
  let mapExpr;
3950
3996
  if (node.flatMapCallback) {
3951
3997
  const body2 = renderPreamble(node.flatMapCallback, {
3952
- renderLeaf: (ir) => irToHtmlTemplate(ir, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop)
3998
+ renderLeaf: (ir) => irToHtmlTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop)
3953
3999
  });
3954
4000
  mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body2}).join('')}`;
3955
4001
  } else if (node.preamble) {
@@ -4200,7 +4246,8 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
4200
4246
  let mapExpr;
4201
4247
  if (node.flatMapCallback) {
4202
4248
  const body2 = renderPreamble(node.flatMapCallback, {
4203
- renderLeaf: (ir) => irToPlaceholderTemplate(ir, restSpreadNames, loopDepth + 1, loopParams)
4249
+ // Leaf `key` stripped see the irToHtmlTemplate site above.
4250
+ renderLeaf: (ir) => irToPlaceholderTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams)
4204
4251
  });
4205
4252
  mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body2}).join('')}`;
4206
4253
  } else if (node.preamble) {
@@ -4698,7 +4745,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4698
4745
  case "expression":
4699
4746
  if (node.expr === "null" || node.expr === "undefined") return "";
4700
4747
  if (node.clientOnly && node.slotId) {
4701
- return `<!--bf-client:${node.slotId}--><!--/-->`;
4748
+ return `<!--bf:${node.slotId}--><!--/-->`;
4702
4749
  }
4703
4750
  {
4704
4751
  const transformed = transformExpr(node.expr, node.templateExpr);
@@ -4799,7 +4846,8 @@ function generateCsrTemplateWithOpts(node, opts) {
4799
4846
  const body2 = renderPreamble(node.flatMapCallback, {
4800
4847
  textVariant: "template",
4801
4848
  transformJs: (t) => applyPropsRewrite(t, propsObjectName ?? null),
4802
- renderLeaf: (ir) => recurseInLoopBody(ir)
4849
+ // Leaf `key` stripped — see the irToHtmlTemplate site above.
4850
+ renderLeaf: (ir) => recurseInLoopBody(stripLeafKeyAttr(ir))
4803
4851
  });
4804
4852
  mapExpr = `\${${iterArrayExpr}.flatMap(${node.flatMapCallback.params} => ${body2}).join('')}`;
4805
4853
  } else if (node.preamble) {
@@ -11596,6 +11644,76 @@ function checkLoopKey(callback, ctx2, isNested) {
11596
11644
  return;
11597
11645
  }
11598
11646
  }
11647
+ function flatMapProjectionCall(body2) {
11648
+ let expr;
11649
+ if (ts11.isBlock(body2)) {
11650
+ const real = body2.statements;
11651
+ if (real.length !== 1 || !ts11.isReturnStatement(real[0]) || !real[0].expression) return null;
11652
+ expr = real[0].expression;
11653
+ } else {
11654
+ expr = body2;
11655
+ }
11656
+ while (ts11.isParenthesizedExpression(expr)) expr = expr.expression;
11657
+ if (!ts11.isCallExpression(expr)) return null;
11658
+ if (!getMapLikeMethod(expr)) return null;
11659
+ const cb = expr.arguments[0];
11660
+ if (!cb || !ts11.isArrowFunction(cb) && !ts11.isFunctionExpression(cb)) return null;
11661
+ for (const p of cb.parameters) {
11662
+ if (!ts11.isIdentifier(p.name)) return null;
11663
+ }
11664
+ let innerBody = cb.body;
11665
+ if (ts11.isBlock(innerBody)) {
11666
+ const ret = innerBody.statements.find(
11667
+ (s) => ts11.isReturnStatement(s) && s.expression != null
11668
+ );
11669
+ if (innerBody.statements.length !== 1 || !ret?.expression) return null;
11670
+ innerBody = ret.expression;
11671
+ }
11672
+ while (ts11.isParenthesizedExpression(innerBody)) innerBody = innerBody.expression;
11673
+ const isElementish = (n) => {
11674
+ let m = n;
11675
+ while (ts11.isParenthesizedExpression(m)) m = m.expression;
11676
+ if (ts11.isJsxElement(m) || ts11.isJsxSelfClosingElement(m)) return leafIsWirelessElement(m);
11677
+ if (ts11.isConditionalExpression(m)) return isElementish(m.whenTrue) && isElementish(m.whenFalse);
11678
+ return false;
11679
+ };
11680
+ if (!isElementish(innerBody)) return null;
11681
+ return expr;
11682
+ }
11683
+ function leafIsWirelessElement(el) {
11684
+ let ok = true;
11685
+ const visit3 = (n) => {
11686
+ if (!ok) return;
11687
+ if (ts11.isJsxOpeningElement(n) || ts11.isJsxSelfClosingElement(n)) {
11688
+ const tagNode = n.tagName;
11689
+ const isIntrinsic = ts11.isIdentifier(tagNode) ? !/^[A-Z]/.test(tagNode.text) : ts11.isJsxNamespacedName(tagNode);
11690
+ if (!isIntrinsic) {
11691
+ ok = false;
11692
+ return;
11693
+ }
11694
+ for (const attr of n.attributes.properties) {
11695
+ if (ts11.isJsxSpreadAttribute(attr)) {
11696
+ ok = false;
11697
+ return;
11698
+ }
11699
+ if (ts11.isJsxAttribute(attr)) {
11700
+ const name2 = attr.name.getText();
11701
+ if (/^on[A-Z]/.test(name2)) {
11702
+ ok = false;
11703
+ return;
11704
+ }
11705
+ }
11706
+ }
11707
+ }
11708
+ if (ts11.isCallExpression(n) && getMapLikeMethod(n) && containsJsxInExpression(n)) {
11709
+ ok = false;
11710
+ return;
11711
+ }
11712
+ ts11.forEachChild(n, visit3);
11713
+ };
11714
+ visit3(el);
11715
+ return ok;
11716
+ }
11599
11717
  function loopBodyIsMultiRoot(children2) {
11600
11718
  const real = children2.filter(
11601
11719
  (c) => !(c.type === "text" && typeof c.value === "string" && !c.value.trim())
@@ -11641,6 +11759,7 @@ function extractItemConditionalKey(cond) {
11641
11759
  }
11642
11760
  function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
11643
11761
  const isNested = ctx2.loopParams.size > 0;
11762
+ const diagCountAtEntry = ctx2.analyzer.errors.length;
11644
11763
  const depth = ctx2.loopDepth;
11645
11764
  const propAccess = node.expression;
11646
11765
  const mapSource = propAccess.expression;
@@ -11978,12 +12097,39 @@ function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
11978
12097
  }
11979
12098
  }
11980
12099
  }
11981
- if (method2 === "flatMap" && children2.length === 0) {
12100
+ if (method2 === "flatMap" && children2.length === 0 && !flatMapProjectionCall(body2)) {
11982
12101
  flatMapCallback = buildFlatMapCallback(callback, body2, ctx2);
11983
12102
  }
11984
12103
  } else {
11985
12104
  tryTransformRenderableBody(body2);
11986
12105
  }
12106
+ if (method2 === "flatMap" && children2.length === 0 && !flatMapCallback) {
12107
+ const projection = flatMapProjectionCall(body2);
12108
+ if (projection) {
12109
+ const transformed = transformJsxExpression(projection, ctx2, isClientOnly);
12110
+ if (transformed && transformed.type === "loop") {
12111
+ children2 = [transformed];
12112
+ }
12113
+ }
12114
+ }
12115
+ if (method2 === "flatMap" && children2.length === 0 && !flatMapCallback && !ts11.isBlock(body2)) {
12116
+ flatMapCallback = buildFlatMapCallback(callback, body2, ctx2);
12117
+ }
12118
+ if (flatMapCallback) preamble = void 0;
12119
+ if (flatMapCallback && !isClientOnly && !(ctx2.analyzer.acceptsCallbackBody?.("flatMap") ?? false)) {
12120
+ ctx2.analyzer.errors.push(
12121
+ createError(
12122
+ ErrorCodes.UNSUPPORTED_JSX_PATTERN,
12123
+ getSourceLocation(body2, ctx2.sourceFile, ctx2.filePath),
12124
+ {
12125
+ message: "A .flatMap() callback body with statements or a nested projection cannot be lowered to a template on this backend.",
12126
+ suggestion: {
12127
+ message: "Add /* @client */ to render this loop on the client only"
12128
+ }
12129
+ }
12130
+ )
12131
+ );
12132
+ }
11987
12133
  if (paramBindings) {
11988
12134
  for (const b of paramBindings) ctx2.loopParams.delete(b.name);
11989
12135
  } else {
@@ -11993,6 +12139,22 @@ function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
11993
12139
  ctx2.loopDepth--;
11994
12140
  }
11995
12141
  if (children2.length === 0 && !flatMapCallback) {
12142
+ const cb = node.arguments[0];
12143
+ const cbBody = cb && (ts11.isArrowFunction(cb) || ts11.isFunctionExpression(cb)) ? cb.body : void 0;
12144
+ if (cbBody && containsJsxInExpression(cbBody) && ctx2.analyzer.errors.length === diagCountAtEntry) {
12145
+ ctx2.analyzer.errors.push(
12146
+ createError(
12147
+ ErrorCodes.UNSUPPORTED_JSX_PATTERN,
12148
+ getSourceLocation(cbBody, ctx2.sourceFile, ctx2.filePath),
12149
+ {
12150
+ message: `A .${method2}() callback that builds JSX in this shape cannot be compiled \u2014 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.`,
12151
+ suggestion: {
12152
+ message: "Restructure the callback to return the JSX element directly (or via a block body with a plain `return`)."
12153
+ }
12154
+ }
12155
+ )
12156
+ );
12157
+ }
11996
12158
  return null;
11997
12159
  }
11998
12160
  if (ts11.isArrowFunction(node.arguments[0]) && children2.length > 0) {
@@ -12049,6 +12211,7 @@ function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
12049
12211
  const hasCalls = exprHasFunctionCalls(arrayExpr);
12050
12212
  const isDirectPropArray = method2 !== "flatMap" && isArrayExprDirectPropRef(arrayExpr, ctx2);
12051
12213
  const isStaticArray = !isSignalOrMemoArray(array, ctx2) && !isDirectPropArray && !hasCalls && !objectIteration;
12214
+ const preambleRegions = preamble && !isStaticArray ? collectPreambleRegions(children2, new Set(preamble.declaredNames), ctx2) : void 0;
12052
12215
  const nestedComponents = collectNestedComponents(children2).filter((c) => c.name !== childComponent?.name);
12053
12216
  return {
12054
12217
  type: "loop",
@@ -12086,6 +12249,7 @@ function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
12086
12249
  depth,
12087
12250
  clientOnly: isClientOnly || void 0,
12088
12251
  preamble,
12252
+ preambleRegions: preambleRegions && preambleRegions.length > 0 ? preambleRegions : void 0,
12089
12253
  paramType,
12090
12254
  indexType,
12091
12255
  paramBindings,
@@ -12146,6 +12310,40 @@ function buildFlatMapCallback(callback, body2, ctx2) {
12146
12310
  );
12147
12311
  return void 0;
12148
12312
  }
12313
+ for (const leafIr of leafIrs) {
12314
+ if (leafIr.type !== "element") {
12315
+ const loc = "loc" in leafIr && leafIr.loc ? leafIr.loc : getSourceLocation(body2, ctx2.sourceFile, ctx2.filePath);
12316
+ ctx2.analyzer.errors.push(
12317
+ createError(
12318
+ ErrorCodes.UNSUPPORTED_JSX_PATTERN,
12319
+ loc,
12320
+ {
12321
+ message: "A JSX leaf produced by a .flatMap() callback must be a single element \u2014 a fragment or non-element root cannot ride the keyed descriptor path (each leaf hydrates and patches as one element).",
12322
+ suggestion: {
12323
+ message: "Wrap the leaf content in a single keyed element."
12324
+ }
12325
+ }
12326
+ )
12327
+ );
12328
+ return void 0;
12329
+ }
12330
+ if (flatMapLeafNeedsWiring(leafIr)) {
12331
+ const loc = "loc" in leafIr && leafIr.loc ? leafIr.loc : getSourceLocation(body2, ctx2.sourceFile, ctx2.filePath);
12332
+ ctx2.analyzer.errors.push(
12333
+ createError(
12334
+ ErrorCodes.UNSUPPORTED_JSX_PATTERN,
12335
+ loc,
12336
+ {
12337
+ message: "A JSX element produced by a .flatMap() callback cannot carry event handlers, components, nested loops, or spreads \u2014 the leaf renders as a keyed HTML string with no per-element wiring.",
12338
+ suggestion: {
12339
+ message: "Restructure so the interactive element lives in a .map() body \u2014 the descriptor path has no per-element wiring on any backend, so /* @client */ does not lift this."
12340
+ }
12341
+ }
12342
+ )
12343
+ );
12344
+ return void 0;
12345
+ }
12346
+ }
12149
12347
  const pieces = reconstructAsSegments(body2, ctx2.sourceFile, ctx2.analyzer.typeExcludeRanges, leafSpans);
12150
12348
  const segments = pieces.map((piece) => {
12151
12349
  if ("marker" in piece) return { kind: "jsx", ir: leafIrs[piece.marker] };
@@ -12159,6 +12357,23 @@ function buildFlatMapCallback(callback, body2, ctx2) {
12159
12357
  rawBody: tsxSourceText(body2.getText(ctx2.sourceFile))
12160
12358
  };
12161
12359
  }
12360
+ function flatMapLeafNeedsWiring(ir) {
12361
+ switch (ir.type) {
12362
+ case "component":
12363
+ case "loop":
12364
+ return true;
12365
+ case "element":
12366
+ if (ir.events.length > 0) return true;
12367
+ if (ir.attrs.some((a) => a.name.startsWith("..."))) return true;
12368
+ return ir.children.some(flatMapLeafNeedsWiring);
12369
+ case "conditional":
12370
+ return flatMapLeafNeedsWiring(ir.whenTrue) || (ir.whenFalse ? flatMapLeafNeedsWiring(ir.whenFalse) : false);
12371
+ case "fragment":
12372
+ return ir.children.some(flatMapLeafNeedsWiring);
12373
+ default:
12374
+ return false;
12375
+ }
12376
+ }
12162
12377
  function preambleFragmentNeedsWiring(ir) {
12163
12378
  switch (ir.type) {
12164
12379
  case "component":
@@ -12202,6 +12417,39 @@ function flagArrayChildExpressions(nodes, declared) {
12202
12417
  }
12203
12418
  }
12204
12419
  }
12420
+ function collectPreambleRegions(nodes, declared, ctx2) {
12421
+ const regions = [];
12422
+ const visit3 = (list) => {
12423
+ for (const node of list) {
12424
+ switch (node.type) {
12425
+ case "expression": {
12426
+ const refs = extractFreeIdentifiersFromText(node.expr);
12427
+ const usesPreambleLocal = [...refs].some((r2) => declared.has(r2));
12428
+ if (usesPreambleLocal) {
12429
+ if (!node.slotId) node.slotId = generateSlotId(ctx2);
12430
+ node.preambleRegion = true;
12431
+ node.reactive = true;
12432
+ regions.push({
12433
+ slotId: node.slotId,
12434
+ expr: node.expr,
12435
+ joinArrayChild: node.joinArrayChild || void 0
12436
+ });
12437
+ }
12438
+ break;
12439
+ }
12440
+ case "element":
12441
+ case "fragment":
12442
+ visit3(node.children);
12443
+ break;
12444
+ case "conditional":
12445
+ visit3([node.whenTrue, ...node.whenFalse ? [node.whenFalse] : []]);
12446
+ break;
12447
+ }
12448
+ }
12449
+ };
12450
+ visit3(nodes);
12451
+ return regions;
12452
+ }
12205
12453
  function collectBindingNames(name2, out) {
12206
12454
  if (ts11.isIdentifier(name2)) {
12207
12455
  out.add(name2.text);
@@ -13570,6 +13818,7 @@ function collectLoopChildReactiveTexts(node, ctx2, loopParam, loopParamBindings,
13570
13818
  ...stopAt("loop", "async", "ifStatement"),
13571
13819
  expression: ({ node: n, scope: insideConditional }) => {
13572
13820
  if (!n.slotId) return;
13821
+ if (n.preambleRegion) return;
13573
13822
  const originFreeIds = freeIdsFromRefs(n.origin?.freeRefs);
13574
13823
  const expanded = expandConstantForReactivity(n.expr, ctx2, originFreeIds);
13575
13824
  const reactive = classifyReactivity(expanded.expr, ctx2, loopParam, loopParamBindings, expanded.freeIds).kind !== "none" || decideWrapFromAstFlags(n).wrap;
@@ -14078,7 +14327,11 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
14078
14327
  },
14079
14328
  expression: ({ node: ex, scope: inCond }) => {
14080
14329
  if (ex.clientOnly && ex.slotId) {
14081
- ctx2.clientOnlyElements.push({ slotId: ex.slotId, expression: ex.expr });
14330
+ ctx2.clientOnlyElements.push({
14331
+ slotId: ex.slotId,
14332
+ expression: ex.expr,
14333
+ elidedPath: ex.markerless ? ex.elidedPath : void 0
14334
+ });
14082
14335
  return;
14083
14336
  }
14084
14337
  if (!ex.slotId || inCond) return;
@@ -14102,10 +14355,13 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
14102
14355
  },
14103
14356
  loop: ({ node: l, scope: inCond }) => {
14104
14357
  if (!l.slotId || inCond) return;
14358
+ const projectionInner = l.method === "flatMap" && l.children.length === 1 && l.children[0].type === "loop" ? l.children[0] : void 0;
14105
14359
  const childHandlers = [];
14106
- const bindings = collectLoopChildBindings(l.children, ctx2, siblingOffsets, l.param, l.paramBindings);
14107
- for (const child of l.children) {
14108
- childHandlers.push(...collectEventHandlersFromIR(child));
14360
+ const bindings = projectionInner ? emptyLoopChildBindings() : collectLoopChildBindings(l.children, ctx2, siblingOffsets, l.param, l.paramBindings);
14361
+ if (!projectionInner) {
14362
+ for (const child of l.children) {
14363
+ childHandlers.push(...collectEventHandlersFromIR(child));
14364
+ }
14109
14365
  }
14110
14366
  if (l.childComponent) {
14111
14367
  for (const prop of l.childComponent.props) {
@@ -14115,7 +14371,7 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
14115
14371
  }
14116
14372
  }
14117
14373
  }
14118
- const { useElementReconciliation, innerLoops } = decideLoopRendering(l, siblingOffsets, ctx2);
14374
+ const { useElementReconciliation, innerLoops } = projectionInner ? { useElementReconciliation: false, innerLoops: void 0 } : decideLoopRendering(l, siblingOffsets, ctx2);
14119
14375
  let template = "";
14120
14376
  let staticItemTemplate;
14121
14377
  let skeletonTemplate;
@@ -14133,7 +14389,7 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
14133
14389
  true
14134
14390
  );
14135
14391
  }
14136
- } else if (l.children[0]) {
14392
+ } else if (l.children[0] && !projectionInner) {
14137
14393
  const loopParamSpec = [{ param: l.param, bindings: l.paramBindings }];
14138
14394
  template = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], buildRestSpreadNames(ctx2), 0, loopParamSpec) : irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx2), 0, loopParamSpec);
14139
14395
  if (l.isStaticArray) {
@@ -14185,7 +14441,17 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
14185
14441
  raw: l.sortComparator.raw
14186
14442
  } : void 0,
14187
14443
  chainOrder: l.chainOrder,
14188
- preamble: l.preamble
14444
+ preamble: l.preamble,
14445
+ preambleRegions: l.preambleRegions,
14446
+ flatMapClient: projectionInner ? {
14447
+ params: l.index ? `(${l.param}, ${l.index})` : `(${l.param})`,
14448
+ body: renderFlatMapProjectionClientBody(projectionInner, buildRestSpreadNames(ctx2)),
14449
+ keyed: projectionInner.key !== null
14450
+ } : l.flatMapCallback ? {
14451
+ params: l.flatMapCallback.params,
14452
+ body: renderFlatMapClientBody(l.flatMapCallback, buildRestSpreadNames(ctx2)),
14453
+ keyed: flatMapCallbackHasKeyedLeaf(l.flatMapCallback)
14454
+ } : void 0
14189
14455
  });
14190
14456
  },
14191
14457
  component: ({ node: c, descend, descendJsxChildren }) => {
@@ -14340,15 +14606,18 @@ function collectBranchLoops(node, ctx2, siblingOffsets) {
14340
14606
  loop: ({ node: n, scope: parentSlotId }) => {
14341
14607
  const containerSlot = parentSlotId ?? n.slotId;
14342
14608
  if (!containerSlot) return;
14343
- const { useElementReconciliation, innerLoops: innerLoopsCollected } = decideLoopRendering(n, siblingOffsets, void 0);
14609
+ const projectionInner = n.method === "flatMap" && n.children.length === 1 && n.children[0].type === "loop" ? n.children[0] : void 0;
14610
+ const { useElementReconciliation, innerLoops: innerLoopsCollected } = projectionInner ? { useElementReconciliation: false, innerLoops: void 0 } : decideLoopRendering(n, siblingOffsets, void 0);
14344
14611
  let childTemplate;
14345
14612
  const branchLoopParamSpec = [{ param: n.param, bindings: n.paramBindings }];
14346
- if (useElementReconciliation && n.children[0]) {
14613
+ if (projectionInner) {
14614
+ childTemplate = "";
14615
+ } else if (useElementReconciliation && n.children[0]) {
14347
14616
  childTemplate = irToPlaceholderTemplate(n.children[0], restNames, 0, branchLoopParamSpec);
14348
14617
  } else {
14349
14618
  childTemplate = n.children.map((c) => irToHtmlTemplate(c, void 0, 0, branchLoopParamSpec)).join("");
14350
14619
  }
14351
- const branchBindings = ctx2 ? collectLoopChildBindings(n.children, ctx2, siblingOffsets, n.param, n.paramBindings) : emptyLoopChildBindings();
14620
+ const branchBindings = ctx2 && !projectionInner ? collectLoopChildBindings(n.children, ctx2, siblingOffsets, n.param, n.paramBindings) : emptyLoopChildBindings();
14352
14621
  loops.push({
14353
14622
  kind: "branch",
14354
14623
  array: n.array,
@@ -14365,6 +14634,7 @@ function collectBranchLoops(node, ctx2, siblingOffsets) {
14365
14634
  template: childTemplate,
14366
14635
  containerSlotId: containerSlot,
14367
14636
  preamble: n.preamble,
14637
+ preambleRegions: n.preambleRegions,
14368
14638
  nestedComponents: useElementReconciliation ? n.nestedComponents : void 0,
14369
14639
  bindings: branchBindings,
14370
14640
  innerLoops: useElementReconciliation ? innerLoopsCollected : void 0,
@@ -14378,7 +14648,16 @@ function collectBranchLoops(node, ctx2, siblingOffsets) {
14378
14648
  paramB: n.sortComparator.paramB,
14379
14649
  raw: n.sortComparator.raw
14380
14650
  } : void 0,
14381
- chainOrder: n.chainOrder
14651
+ chainOrder: n.chainOrder,
14652
+ flatMapClient: projectionInner ? {
14653
+ params: n.index ? `(${n.param}, ${n.index})` : `(${n.param})`,
14654
+ body: renderFlatMapProjectionClientBody(projectionInner, restNames),
14655
+ keyed: projectionInner.key !== null
14656
+ } : n.flatMapCallback ? {
14657
+ params: n.flatMapCallback.params,
14658
+ body: renderFlatMapClientBody(n.flatMapCallback, restNames),
14659
+ keyed: flatMapCallbackHasKeyedLeaf(n.flatMapCallback)
14660
+ } : void 0
14382
14661
  });
14383
14662
  }
14384
14663
  });
@@ -14774,6 +15053,12 @@ function buildReferencesGraph(ctx2, irRoot) {
14774
15053
  if (l.filterPredicate) addExprEdges(ROOT_SOURCE, l.filterPredicate.raw, "template-closure");
14775
15054
  if (l.sortComparator) addExprEdges(ROOT_SOURCE, l.sortComparator.raw, "template-closure");
14776
15055
  if (l.preamble) addExprEdges(ROOT_SOURCE, preambleAnalysisText(l.preamble), "template-closure");
15056
+ if (l.flatMapCallback) {
15057
+ addExprEdges(ROOT_SOURCE, preambleAnalysisText(l.flatMapCallback), "template-closure");
15058
+ for (const seg of l.flatMapCallback.segments) {
15059
+ if (seg.kind === "jsx") walkIR(seg.ir, null, visitor);
15060
+ }
15061
+ }
14777
15062
  descend();
14778
15063
  if (l.childComponent) walkChildComponent(l.childComponent);
14779
15064
  if (l.nestedComponents) {
@@ -15050,11 +15335,11 @@ var init_imports = __esm({
15050
15335
  "onMount",
15051
15336
  "hydrate",
15052
15337
  "insert",
15053
- "reconcileElements",
15054
15338
  "getLoopChildren",
15055
15339
  "getLoopNodes",
15056
15340
  "mapArray",
15057
15341
  "mapArrayAnchored",
15342
+ "patchLeaf",
15058
15343
  "createDisposableEffect",
15059
15344
  "createComponent",
15060
15345
  "renderChild",
@@ -15062,7 +15347,6 @@ var init_imports = __esm({
15062
15347
  "registerTemplate",
15063
15348
  "initChild",
15064
15349
  "upsertChild",
15065
- "updateClientMarker",
15066
15350
  "createPortal",
15067
15351
  "provideContext",
15068
15352
  "createContext",
@@ -15074,6 +15358,7 @@ var init_imports = __esm({
15074
15358
  "styleToCss",
15075
15359
  "escapeAttr",
15076
15360
  "escapeText",
15361
+ "escapeTextOrNode",
15077
15362
  "qsa",
15078
15363
  "qsaItem",
15079
15364
  "qsaChildScope",
@@ -15082,7 +15367,11 @@ var init_imports = __esm({
15082
15367
  "__slot",
15083
15368
  "__bfSlot",
15084
15369
  "__bfText",
15085
- "tAfter",
15370
+ // Claim-plan interpreter (slot unification A2/A3, spec/slot-unification.md)
15371
+ // — the "one claim mechanism" that replaced `patchSlotRange` and
15372
+ // `updateClientMarker` (both deleted) as the content-slot update door.
15373
+ "claimSlots",
15374
+ "lazySlots",
15086
15375
  // Profile mode (#1690, SR3) — turn-boundary markers around event handlers.
15087
15376
  "beginTurn",
15088
15377
  "endTurn",
@@ -17006,6 +17295,14 @@ function destructureLoopParam(param, paramBindings) {
17006
17295
  }
17007
17296
  return { head: param, unwrap: "" };
17008
17297
  }
17298
+ function buildPreambleRegionPlans(regions, loopParam, loopParamBindings) {
17299
+ if (!regions || regions.length === 0) return [];
17300
+ return regions.map((r2) => {
17301
+ const wrapped = wrapLoopParamAsAccessor(r2.expr, loopParam, loopParamBindings);
17302
+ const valueExpr = r2.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : `escapeText(${wrapped})`;
17303
+ return { slotId: r2.slotId, valueExpr };
17304
+ });
17305
+ }
17009
17306
  function buildComponentPropsExpr2(comp, loopParam, loopParamBindings) {
17010
17307
  const wrap = loopParam ? (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings) : (expr) => expr;
17011
17308
  const entries2 = comp.props.map((p) => {
@@ -18104,9 +18401,16 @@ function buildDynamicLoopDelegationPlan(elem, profileComponentName) {
18104
18401
  paramBindings: elem.paramBindings,
18105
18402
  key: elem.key,
18106
18403
  index: elem.index,
18404
+ // No loopParams spec here (unlike the row-render context) — in the
18405
+ // delegated handler `elem.param` is bound to the plain `.find()`/
18406
+ // indexed result, not a signal accessor, so leaf refs must stay in
18407
+ // their literal (`t.name`) form. Passing a loopParams spec here was
18408
+ // BUG-3: it rewrote leaf refs to accessor-call form (`t().name`),
18409
+ // which throws since `t` is a plain object in this scope.
18107
18410
  mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
18108
- renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1, [{ param: elem.param, bindings: elem.paramBindings }], void 0, true)
18109
- }) : null
18411
+ renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1, void 0, void 0, true)
18412
+ }) : null,
18413
+ mapPreambleDeclaredNames: elem.preamble?.declaredNames ?? []
18110
18414
  })
18111
18415
  };
18112
18416
  }
@@ -18123,9 +18427,12 @@ function buildBranchLoopDelegationPlan(loop, cv, profileComponentName) {
18123
18427
  paramBindings: loop.paramBindings,
18124
18428
  key: loop.key,
18125
18429
  index: loop.index,
18430
+ // See note in `buildDynamicLoopDelegationPlan` above (BUG-3): no
18431
+ // loopParams spec — leaf refs must stay in plain-object form here.
18126
18432
  mapPreamble: loop.preamble ? renderPreamble(loop.preamble, {
18127
- renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1, [{ param: loop.param, bindings: loop.paramBindings }], void 0, true)
18128
- }) : null
18433
+ renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1, void 0, void 0, true)
18434
+ }) : null,
18435
+ mapPreambleDeclaredNames: loop.preamble?.declaredNames ?? []
18129
18436
  })
18130
18437
  };
18131
18438
  }
@@ -18142,9 +18449,12 @@ function buildStaticArrayDelegationPlan(elem, profileComponentName) {
18142
18449
  // array too (#1434).
18143
18450
  arrayExpr: buildChainedArrayExpr(elem),
18144
18451
  param: elem.param,
18452
+ // See note in `buildDynamicLoopDelegationPlan` above (BUG-3): no
18453
+ // loopParams spec — leaf refs must stay in plain-object form here.
18145
18454
  mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
18146
- renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1, [{ param: elem.param, bindings: elem.paramBindings }], void 0, true)
18455
+ renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1, void 0, void 0, true)
18147
18456
  }) : null,
18457
+ mapPreambleDeclaredNames: elem.preamble?.declaredNames ?? [],
18148
18458
  offset: elem.offset ?? null,
18149
18459
  indexParam: elem.index ?? null
18150
18460
  }
@@ -18161,6 +18471,7 @@ function buildKeyedOrIndexLookup(args2) {
18161
18471
  paramBindings: args2.paramBindings,
18162
18472
  keyWithItem,
18163
18473
  mapPreamble: args2.mapPreamble,
18474
+ mapPreambleDeclaredNames: args2.mapPreambleDeclaredNames,
18164
18475
  hasBindings,
18165
18476
  indexParam: args2.index
18166
18477
  };
@@ -18170,6 +18481,7 @@ function buildKeyedOrIndexLookup(args2) {
18170
18481
  arrayExpr: args2.array,
18171
18482
  param: args2.param,
18172
18483
  mapPreamble: args2.mapPreamble,
18484
+ mapPreambleDeclaredNames: args2.mapPreambleDeclaredNames,
18173
18485
  hasBindings,
18174
18486
  indexParam: args2.index
18175
18487
  };
@@ -18198,14 +18510,16 @@ function buildBranchLoopPlan(loop, profileComponentName) {
18198
18510
  }
18199
18511
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(loop.param, loop.paramBindings);
18200
18512
  const hasReactiveEffects = loop.bindings.reactiveAttrs.length > 0 || loop.bindings.reactiveTexts.length > 0 || loop.bindings.conditionals.length > 0;
18513
+ const fm = loop.flatMapClient;
18201
18514
  const plan = {
18202
18515
  kind: "plain",
18203
18516
  rowConstruction: "string-template",
18204
18517
  containerSlotId,
18205
18518
  containerVar,
18206
18519
  markerId: loop.markerId,
18207
- arrayExpr: buildChainedArrayExpr(loop),
18208
- keyFn: loopKeyFn(loop),
18520
+ flatMapLeafItem: fm ? true : void 0,
18521
+ arrayExpr: fm ? `(${buildChainedArrayExpr(loop)}).flatMap(${fm.params} => ${fm.body})` : buildChainedArrayExpr(loop),
18522
+ keyFn: fm ? fm.keyed ? "(__bfD, __bfI) => String(__bfD.k ?? __bfI)" : "null" : loopKeyFn(loop),
18209
18523
  paramHead,
18210
18524
  paramUnwrap,
18211
18525
  indexParam: loop.index || "__idx",
@@ -18226,6 +18540,7 @@ function buildBranchLoopPlan(loop, profileComponentName) {
18226
18540
  }) : null,
18227
18541
  eventDelegation: buildBranchLoopDelegationPlan(loop, cv, profileComponentName),
18228
18542
  childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings),
18543
+ preambleRegions: buildPreambleRegionPlans(loop.preambleRegions, loop.param, loop.paramBindings),
18229
18544
  bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false,
18230
18545
  profileLoopId: profileComponentName ? `${profileComponentName}#binding:${containerSlotId}` : void 0
18231
18546
  };
@@ -18312,6 +18627,25 @@ var init_build_insert = __esm({
18312
18627
  }
18313
18628
  });
18314
18629
 
18630
+ // ../jsx/src/ir-to-client-js/control-flow/stringify/claim-plan.ts
18631
+ function slotSpecLiteral(slot) {
18632
+ const pathSrc = slot.pathExpr ?? `[${slot.path.join(", ")}]`;
18633
+ const markerlessSrc = slot.markerless ? ", markerless: true" : "";
18634
+ return `{ id: '${slot.id}', kind: '${slot.kind}', path: ${pathSrc}${markerlessSrc} }`;
18635
+ }
18636
+ function claimPlanLiteral(slots) {
18637
+ return `[${slots.map(slotSpecLiteral).join(", ")}]`;
18638
+ }
18639
+ function claimWriterVarName(slots, sanitize) {
18640
+ const first = slots[0]?.id ?? "0";
18641
+ return `__bfw_${sanitize(first)}`;
18642
+ }
18643
+ var init_claim_plan = __esm({
18644
+ "../jsx/src/ir-to-client-js/control-flow/stringify/claim-plan.ts"() {
18645
+ "use strict";
18646
+ }
18647
+ });
18648
+
18315
18649
  // ../jsx/src/ir-to-client-js/emit-reactive.ts
18316
18650
  import ts14 from "typescript";
18317
18651
  function bindingIdArg(ctx2, slotId) {
@@ -18490,17 +18824,18 @@ function emitDynamicTextUpdates(lines, ctx2) {
18490
18824
  const conditionalElems = elems.filter((e) => e.insideConditional);
18491
18825
  const normalElems = elems.filter((e) => !e.insideConditional);
18492
18826
  if (normalElems.length > 0 || conditionalElems.length > 0) {
18493
- for (const elem of normalElems) {
18494
- const v = varSlotId(elem.slotId);
18495
- lines.push(` let __anchor_${v} = _${v}`);
18496
- }
18497
18827
  const __textSlot = (normalElems[0] ?? conditionalElems[0])?.slotId;
18828
+ let writer = "";
18829
+ if (normalElems.length > 0) {
18830
+ const slots = normalElems.map((elem) => ({ id: elem.slotId, kind: "markup", path: [] }));
18831
+ writer = claimWriterVarName(slots, varSlotId);
18832
+ lines.push(` const ${writer} = lazySlots(__scope, ${claimPlanLiteral(slots)})`);
18833
+ }
18498
18834
  lines.push(` createEffect(() => {`);
18499
18835
  if (normalElems.length > 0) {
18500
18836
  lines.push(` const __val = ${expr}`);
18501
18837
  for (const elem of normalElems) {
18502
- const v = varSlotId(elem.slotId);
18503
- lines.push(` __anchor_${v} = __bfText(__anchor_${v}, __val)`);
18838
+ lines.push(` ${writer}('${elem.slotId}', escapeTextOrNode(__val))`);
18504
18839
  }
18505
18840
  for (const elem of conditionalElems) {
18506
18841
  const v = varSlotId(elem.slotId);
@@ -18523,10 +18858,13 @@ function emitDynamicTextUpdates(lines, ctx2) {
18523
18858
  }
18524
18859
  function emitClientOnlyExpressions(lines, ctx2) {
18525
18860
  for (const elem of ctx2.clientOnlyElements) {
18861
+ const slots = elem.elidedPath ? [{ id: elem.slotId, kind: "text", path: elem.elidedPath, markerless: true }] : [{ id: elem.slotId, kind: "text", path: [] }];
18862
+ const writer = claimWriterVarName(slots, varSlotId);
18526
18863
  lines.push(` // @client: ${elem.slotId}`);
18864
+ lines.push(` { const ${writer} = lazySlots(__scope, ${claimPlanLiteral(slots)})`);
18527
18865
  lines.push(` createEffect(() => {`);
18528
- lines.push(` updateClientMarker(__scope, '${elem.slotId}', ${elem.expression})`);
18529
- lines.push(` }${bindingIdArg(ctx2, elem.slotId)})`);
18866
+ lines.push(` ${writer}('${elem.slotId}', ${elem.expression})`);
18867
+ lines.push(` }${bindingIdArg(ctx2, elem.slotId)}) }`);
18530
18868
  lines.push("");
18531
18869
  }
18532
18870
  }
@@ -18634,6 +18972,7 @@ var init_emit_reactive = __esm({
18634
18972
  "use strict";
18635
18973
  init_html_constants();
18636
18974
  init_utils();
18975
+ init_claim_plan();
18637
18976
  init_html_template();
18638
18977
  init_date_lowering();
18639
18978
  init_to_locale_date_lowering();
@@ -18703,13 +19042,18 @@ function stringifyBranchInnerLoops(lines, plan, indent, pc) {
18703
19042
  inner.outerLoopParamBindings
18704
19043
  );
18705
19044
  }
18706
- for (const text of inner.reactiveTexts) {
19045
+ const conditionalTexts = inner.reactiveTexts.filter((t) => t.insideConditional);
19046
+ const plainTexts = inner.reactiveTexts.filter((t) => !t.insideConditional);
19047
+ for (const text of conditionalTexts) {
18707
19048
  const bf = profileBindingId(pc, text.slotId);
18708
- if (text.insideConditional) {
18709
- lines.push(`${indent} createEffect(() => { const [__rt] = $t(__bel${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) }${bf})`);
18710
- } else {
18711
- lines.push(`${indent} { const [__rt] = $t(__bel${uid}, '${text.slotId}')`);
18712
- lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }${bf}) }`);
19049
+ lines.push(`${indent} createEffect(() => { claimSlots(__bel${uid}, [{ id: '${text.slotId}', kind: 'text', path: [] }]).write('${text.slotId}', String(${text.wrappedExpression})) }${bf})`);
19050
+ }
19051
+ if (plainTexts.length > 0) {
19052
+ const slots = plainTexts.map((t) => ({ id: t.slotId, kind: "text", path: [] }));
19053
+ const writer = claimWriterVarName(slots, varSlotId);
19054
+ lines.push(`${indent} const ${writer} = lazySlots(__bel${uid}, ${claimPlanLiteral(slots)})`);
19055
+ for (const text of plainTexts) {
19056
+ lines.push(`${indent} createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) }${profileBindingId(pc, text.slotId)})`);
18713
19057
  }
18714
19058
  }
18715
19059
  if (inner.nestedConditionals.length > 0) {
@@ -18751,10 +19095,13 @@ function stringifyLoopChildArm(lines, arm, armIndent, pc) {
18751
19095
  stringifyLoopChildConditional(lines, cond, `${armIndent} `, pc);
18752
19096
  lines.push(`${armIndent}}))`);
18753
19097
  }
18754
- for (const text of arm.texts) {
18755
- const varName = `__rt_${varSlotId(text.slotId)}`;
18756
- lines.push(`${armIndent}let ${varName} = $t(__branchScope, '${text.slotId}')[0]`);
18757
- lines.push(`${armIndent}__disposers.push(createDisposableEffect(() => { ${varName} = __bfText(${varName}, ${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}))`);
19098
+ if (arm.texts.length > 0) {
19099
+ const slots = arm.texts.map((t) => ({ id: t.slotId, kind: "markup", path: [] }));
19100
+ const writer = claimWriterVarName(slots, varSlotId);
19101
+ lines.push(`${armIndent}const ${writer} = lazySlots(__branchScope, ${claimPlanLiteral(slots)})`);
19102
+ for (const text of arm.texts) {
19103
+ lines.push(`${armIndent}__disposers.push(createDisposableEffect(() => { ${writer}('${text.slotId}', escapeTextOrNode(${text.wrappedExpression})) }${profileBindingId(pc, text.slotId)}))`);
19104
+ }
18758
19105
  }
18759
19106
  lines.push(`${armIndent}return () => __disposers.forEach(d => d())`);
18760
19107
  }
@@ -18767,19 +19114,45 @@ var init_loop_child_arm = __esm({
18767
19114
  init_template_parse();
18768
19115
  init_event_listener();
18769
19116
  init_component_scope();
19117
+ init_claim_plan();
18770
19118
  }
18771
19119
  });
18772
19120
 
18773
19121
  // ../jsx/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts
18774
19122
  function stringifyReactiveEffects(lines, plan, opts) {
18775
- const { indent, elVar, bodyIsMultiRoot, elementIndexBySlot, textIndexBySlot } = opts;
19123
+ const { indent, elVar, bodyIsMultiRoot, elementIndexBySlot, textClaimPathExprs, preambleRegions = [], mapPreambleWrapped } = opts;
18776
19124
  const lookup = bodyIsMultiRoot ? "qsaItem" : "qsa";
18777
- const pc = plan.profileComponentName;
19125
+ const pc = plan?.profileComponentName;
18778
19126
  const bindingBfId = (slotId) => profileBindingId(pc, slotId);
18779
- for (const slot of plan.attrSlots) {
19127
+ const attrSlots = plan?.attrSlots ?? [];
19128
+ const outerTexts = plan?.outerTexts ?? [];
19129
+ const conditionals = plan?.conditionals ?? [];
19130
+ if (pc) {
19131
+ emitAttrSlotsGranular(lines, indent, elVar, lookup, attrSlots, elementIndexBySlot, bindingBfId);
19132
+ emitOuterTexts(lines, indent, elVar, outerTexts, bindingBfId, textClaimPathExprs);
19133
+ emitPreambleRegionsEffect(lines, indent, elVar, preambleRegions, mapPreambleWrapped);
19134
+ } else {
19135
+ emitConsolidatedRowEffect(
19136
+ lines,
19137
+ indent,
19138
+ elVar,
19139
+ lookup,
19140
+ attrSlots,
19141
+ outerTexts,
19142
+ elementIndexBySlot,
19143
+ textClaimPathExprs,
19144
+ preambleRegions,
19145
+ mapPreambleWrapped
19146
+ );
19147
+ }
19148
+ for (const cond of conditionals) {
19149
+ emitOuterConditional(lines, indent, elVar, cond, pc);
19150
+ }
19151
+ }
19152
+ function emitAttrSlotsGranular(lines, indent, elVar, lookup, attrSlots, elementIndexBySlot, bindingBfId) {
19153
+ for (const slot of attrSlots) {
18780
19154
  const varName = `__ra_${varSlotId(slot.slotId)}`;
18781
- const pIdx = elementIndexBySlot?.get(slot.slotId);
18782
- const lookupExpr = pIdx !== void 0 ? `__p ? __p[${pIdx}] : ${lookup}(${elVar}, '[bf="${slot.slotId}"]')` : `${lookup}(${elVar}, '[bf="${slot.slotId}"]')`;
19155
+ const lookupExpr = attrLookupExpr(slot.slotId, varName, elVar, lookup, elementIndexBySlot);
18783
19156
  lines.push(`${indent}{ const ${varName} = ${lookupExpr}`);
18784
19157
  lines.push(`${indent}if (${varName}) {`);
18785
19158
  for (const attr of slot.attrs) {
@@ -18791,21 +19164,79 @@ function stringifyReactiveEffects(lines, plan, opts) {
18791
19164
  }
18792
19165
  lines.push(`${indent}} }`);
18793
19166
  }
18794
- for (const text of plan.outerTexts) {
18795
- emitOuterText(lines, indent, elVar, text, bindingBfId(text.slotId), textIndexBySlot?.get(text.slotId));
19167
+ }
19168
+ function emitPreambleRegionsEffect(lines, indent, elVar, preambleRegions, mapPreambleWrapped) {
19169
+ if (preambleRegions.length === 0) return;
19170
+ const slots = preambleRegions.map((r2) => ({ id: r2.slotId, kind: "markup", path: [] }));
19171
+ const writer = claimWriterVarName(slots, varSlotId);
19172
+ lines.push(`${indent}const ${writer} = lazySlots(${elVar}, ${claimPlanLiteral(slots)})`);
19173
+ lines.push(`${indent}createEffect(() => {`);
19174
+ if (mapPreambleWrapped) lines.push(`${indent} ${mapPreambleWrapped}`);
19175
+ for (const region of preambleRegions) {
19176
+ lines.push(`${indent} ${writer}('${region.slotId}', ${region.valueExpr})`);
18796
19177
  }
18797
- for (const cond of plan.conditionals) {
18798
- emitOuterConditional(lines, indent, elVar, cond, pc);
19178
+ lines.push(`${indent}})`);
19179
+ }
19180
+ function attrLookupExpr(slotId, varName, elVar, lookup, elementIndexBySlot) {
19181
+ const pIdx = elementIndexBySlot?.get(slotId);
19182
+ return pIdx !== void 0 ? `__p ? __p[${pIdx}] : ${lookup}(${elVar}, '[bf="${slotId}"]')` : `${lookup}(${elVar}, '[bf="${slotId}"]')`;
19183
+ }
19184
+ function emitConsolidatedRowEffect(lines, indent, elVar, lookup, attrSlots, outerTexts, elementIndexBySlot, textClaimPathExprs, preambleRegions, mapPreambleWrapped) {
19185
+ if (attrSlots.length === 0 && outerTexts.length === 0 && preambleRegions.length === 0) return;
19186
+ for (const slot of attrSlots) {
19187
+ const varName = `__ra_${varSlotId(slot.slotId)}`;
19188
+ lines.push(`${indent}const ${varName} = ${attrLookupExpr(slot.slotId, varName, elVar, lookup, elementIndexBySlot)}`);
19189
+ }
19190
+ const claimSlots = [
19191
+ ...outerTexts.map((t) => ({ id: t.slotId, kind: "text", path: [], pathExpr: textClaimPathExprs?.get(t.slotId) })),
19192
+ ...preambleRegions.map((r2) => ({ id: r2.slotId, kind: "markup", path: [] }))
19193
+ ];
19194
+ const writer = claimSlots.length > 0 ? claimWriterVarName(claimSlots, varSlotId) : null;
19195
+ if (writer) {
19196
+ lines.push(`${indent}const ${writer} = lazySlots(${elVar}, ${claimPlanLiteral(claimSlots)})`);
19197
+ }
19198
+ if (attrSlots.length === 0 && preambleRegions.length === 0 && outerTexts.length === 1) {
19199
+ const text = outerTexts[0];
19200
+ lines.push(`${indent}createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) })`);
19201
+ return;
19202
+ }
19203
+ lines.push(`${indent}createEffect(() => {`);
19204
+ for (const slot of attrSlots) {
19205
+ const varName = `__ra_${varSlotId(slot.slotId)}`;
19206
+ lines.push(`${indent} if (${varName}) {`);
19207
+ for (const attr of slot.attrs) {
19208
+ lines.push(`${indent} {`);
19209
+ for (const stmt of emitAttrUpdate(varName, attr.attrName, attr.wrappedExpression, attr.meta)) {
19210
+ lines.push(`${indent} ${stmt}`);
19211
+ }
19212
+ lines.push(`${indent} }`);
19213
+ }
19214
+ lines.push(`${indent} }`);
19215
+ }
19216
+ if (preambleRegions.length > 0 && mapPreambleWrapped) {
19217
+ lines.push(`${indent} ${mapPreambleWrapped}`);
19218
+ }
19219
+ for (const text of outerTexts) {
19220
+ lines.push(`${indent} ${writer}('${text.slotId}', String(${text.wrappedExpression}))`);
19221
+ }
19222
+ for (const region of preambleRegions) {
19223
+ lines.push(`${indent} ${writer}('${region.slotId}', ${region.valueExpr})`);
18799
19224
  }
19225
+ lines.push(`${indent}})`);
18800
19226
  }
18801
- function emitOuterText(lines, indent, elVar, text, bfId = "", pIdx) {
18802
- const varName = `__rt_${varSlotId(text.slotId)}`;
18803
- if (pIdx !== void 0) {
18804
- lines.push(`${indent}{ const ${varName} = __p ? tAfter(__p[${pIdx}]) : $t(${elVar}, '${text.slotId}')[0]`);
18805
- } else {
18806
- lines.push(`${indent}{ const [${varName}] = $t(${elVar}, '${text.slotId}')`);
19227
+ function emitOuterTexts(lines, indent, elVar, texts, bindingBfId, textClaimPathExprs) {
19228
+ if (texts.length === 0) return;
19229
+ const slots = texts.map((t) => ({
19230
+ id: t.slotId,
19231
+ kind: "text",
19232
+ path: [],
19233
+ pathExpr: textClaimPathExprs?.get(t.slotId)
19234
+ }));
19235
+ const writer = claimWriterVarName(slots, varSlotId);
19236
+ lines.push(`${indent}const ${writer} = lazySlots(${elVar}, ${claimPlanLiteral(slots)})`);
19237
+ for (const text of texts) {
19238
+ lines.push(`${indent}createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) }${bindingBfId(text.slotId)})`);
18807
19239
  }
18808
- lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${bfId}) }`);
18809
19240
  }
18810
19241
  function emitOuterConditional(lines, indent, elVar, cond, pc) {
18811
19242
  const armIndent = `${indent} `;
@@ -18827,6 +19258,7 @@ var init_reactive_effects = __esm({
18827
19258
  init_utils();
18828
19259
  init_emit_reactive();
18829
19260
  init_loop_child_arm();
19261
+ init_claim_plan();
18830
19262
  }
18831
19263
  });
18832
19264
 
@@ -18978,8 +19410,24 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
18978
19410
  childRefs,
18979
19411
  bodyIsMultiRoot,
18980
19412
  anchored,
18981
- anchorKeyExpr
19413
+ anchorKeyExpr,
19414
+ preambleRegions
18982
19415
  } = plan;
19416
+ if (plan.flatMapLeafItem) {
19417
+ const loopBfIdArg = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
19418
+ lines.push(`${topIndent}mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (__bfD, ${indexParam}, __existing) => {`);
19419
+ lines.push(`${topIndent} let __el = __existing`);
19420
+ lines.push(`${topIndent} if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }`);
19421
+ lines.push(`${topIndent} let __last = __existing ? undefined : __bfD().h`);
19422
+ lines.push(`${topIndent} createEffect(() => {`);
19423
+ lines.push(`${topIndent} const __html = __bfD().h`);
19424
+ lines.push(`${topIndent} if (__last === undefined) { __last = __html; return }`);
19425
+ lines.push(`${topIndent} if (__html !== __last) { __last = __html; patchLeaf(__el, __html) }`);
19426
+ lines.push(`${topIndent} })`);
19427
+ lines.push(`${topIndent} return __el`);
19428
+ lines.push(`${topIndent}}, '${markerId}'${loopBfIdArg})`);
19429
+ return;
19430
+ }
18983
19431
  if (anchored) {
18984
19432
  stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr);
18985
19433
  return;
@@ -18990,7 +19438,7 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
18990
19438
  emitHoistedTemplateDecl(lines, topIndent, tplVar, hoistedTpl);
18991
19439
  }
18992
19440
  const loopBfId = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
18993
- if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
19441
+ if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0 && preambleRegions.length === 0) {
18994
19442
  const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
18995
19443
  const preamble = mapPreambleWrapped ? `${mapPreambleWrapped}; ` : "";
18996
19444
  const cloneExpr = hoistedTpl ? `return ${hoistedCloneExpr(tplVar, hoistedTpl)}` : emitTemplateCloneInline(template);
@@ -19019,22 +19467,30 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
19019
19467
  ...reactiveEffects?.attrSlots.map((s) => s.slotId) ?? [],
19020
19468
  ...childRefs.map((r2) => r2.childSlotId)
19021
19469
  ];
19022
- const textSlotIds = reactiveEffects?.outerTexts.map((t) => t.slotId) ?? [];
19023
- if (elementSlotIds.length > 0 || textSlotIds.length > 0) {
19024
- const built = buildSkeletonPathPlan(plan.skeletonPaths, "__el", { elementSlotIds, textSlotIds });
19470
+ if (elementSlotIds.length > 0) {
19471
+ const built = buildSkeletonPathPlan(plan.skeletonPaths, "__el", { elementSlotIds, textSlotIds: [] });
19025
19472
  if (built.arrayElems.length > 0) {
19026
19473
  pathPlan = built;
19027
19474
  lines.push(`${bodyIndent}const __p = __existing ? null : [${built.arrayElems.join(", ")}]`);
19028
19475
  }
19029
19476
  }
19030
19477
  }
19031
- if (reactiveEffects !== null) {
19478
+ const textClaimPathExprs = /* @__PURE__ */ new Map();
19479
+ if (hoistedTpl && plan.skeletonPaths) {
19480
+ for (const text of reactiveEffects?.outerTexts ?? []) {
19481
+ const path25 = plan.skeletonPaths.textMarkerPaths.get(text.slotId);
19482
+ if (path25) textClaimPathExprs.set(text.slotId, `__existing ? [] : [${path25.join(", ")}]`);
19483
+ }
19484
+ }
19485
+ if (reactiveEffects !== null || preambleRegions.length > 0) {
19032
19486
  stringifyReactiveEffects(lines, reactiveEffects, {
19033
19487
  indent: bodyIndent,
19034
19488
  elVar: "__el",
19035
19489
  bodyIsMultiRoot,
19036
19490
  elementIndexBySlot: pathPlan?.elementIndexBySlot,
19037
- textIndexBySlot: pathPlan?.textIndexBySlot
19491
+ textClaimPathExprs,
19492
+ preambleRegions,
19493
+ mapPreambleWrapped
19038
19494
  });
19039
19495
  }
19040
19496
  emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot, elementIndexBySlot: pathPlan?.elementIndexBySlot });
@@ -19138,10 +19594,13 @@ function stringifyStaticLoop(lines, plan) {
19138
19594
  }
19139
19595
  lines.push(` }`);
19140
19596
  }
19141
- for (const text of texts) {
19142
- const vn = `__rt_${varSlotId(text.slotId)}`;
19143
- lines.push(` { const [${vn}] = $t(__iterEl, '${text.slotId}')`);
19144
- lines.push(` if (${vn}) createEffect(() => { ${vn}.textContent = String(${text.expression}) }${profileBindingId(pc, text.slotId)}) }`);
19597
+ if (texts.length > 0) {
19598
+ const slots = texts.map((t) => ({ id: t.slotId, kind: "text", path: [] }));
19599
+ const writer = claimWriterVarName(slots, varSlotId);
19600
+ lines.push(` const ${writer} = lazySlots(__iterEl, ${claimPlanLiteral(slots)})`);
19601
+ for (const text of texts) {
19602
+ lines.push(` createEffect(() => { ${writer}('${text.slotId}', String(${text.expression})) }${profileBindingId(pc, text.slotId)})`);
19603
+ }
19145
19604
  }
19146
19605
  emitLoopChildRefs(lines, childRefs, { indent: " ", elVar: "__iterEl", bodyIsMultiRoot: false });
19147
19606
  lines.push(` }`);
@@ -19159,6 +19618,7 @@ var init_loop = __esm({
19159
19618
  init_skeleton_paths();
19160
19619
  init_component_loop();
19161
19620
  init_composite_loop();
19621
+ init_claim_plan();
19162
19622
  }
19163
19623
  });
19164
19624
 
@@ -19214,13 +19674,18 @@ function emitReactive(lines, inner, indent, pc) {
19214
19674
  if (inner.childLevels.length > 0) {
19215
19675
  stringifyInnerLoops(lines, inner.childLevels, `${indent} `, pc);
19216
19676
  }
19217
- for (const text of emit.reactiveTexts) {
19677
+ const conditionalTexts = emit.reactiveTexts.filter((t) => t.insideConditional);
19678
+ const plainTexts = emit.reactiveTexts.filter((t) => !t.insideConditional);
19679
+ for (const text of conditionalTexts) {
19218
19680
  const bf = profileBindingId(pc, text.slotId);
19219
- if (text.insideConditional) {
19220
- lines.push(`${indent} createEffect(() => { const [__rt] = $t(__innerEl${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) }${bf})`);
19221
- } else {
19222
- lines.push(`${indent} { const [__rt] = $t(__innerEl${uid}, '${text.slotId}')`);
19223
- lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }${bf}) }`);
19681
+ lines.push(`${indent} createEffect(() => { claimSlots(__innerEl${uid}, [{ id: '${text.slotId}', kind: 'text', path: [] }]).write('${text.slotId}', String(${text.wrappedExpression})) }${bf})`);
19682
+ }
19683
+ if (plainTexts.length > 0) {
19684
+ const slots = plainTexts.map((t) => ({ id: t.slotId, kind: "text", path: [] }));
19685
+ const writer = claimWriterVarName(slots, varSlotId);
19686
+ lines.push(`${indent} const ${writer} = lazySlots(__innerEl${uid}, ${claimPlanLiteral(slots)})`);
19687
+ for (const text of plainTexts) {
19688
+ lines.push(`${indent} createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) }${profileBindingId(pc, text.slotId)})`);
19224
19689
  }
19225
19690
  }
19226
19691
  for (const attr of emit.reactiveAttrs) {
@@ -19282,6 +19747,7 @@ var init_inner_loop = __esm({
19282
19747
  init_emit_reactive();
19283
19748
  init_template_parse();
19284
19749
  init_loop();
19750
+ init_claim_plan();
19285
19751
  }
19286
19752
  });
19287
19753
 
@@ -19370,6 +19836,11 @@ function indexBindingLine(handler, indexParam, indexExpr) {
19370
19836
  if (!extractFreeIdentifiersFromText(handler).has(indexParam)) return null;
19371
19837
  return `const ${indexParam} = ${indexExpr}`;
19372
19838
  }
19839
+ function preambleLineForHandler(mapPreamble, declaredNames, handler) {
19840
+ if (!mapPreamble || declaredNames.length === 0) return null;
19841
+ const free = extractFreeIdentifiersFromText(handler);
19842
+ return declaredNames.some((name2) => free.has(name2)) ? mapPreamble : null;
19843
+ }
19373
19844
  function stringifyEventDelegation(lines, plan) {
19374
19845
  const { containerVar, events, itemLookup, profileComponentName } = plan;
19375
19846
  const eventsByName = /* @__PURE__ */ new Map();
@@ -19414,7 +19885,8 @@ function stringifyEventDelegation(lines, plan) {
19414
19885
  }
19415
19886
  }
19416
19887
  function emitKeyedLookup(ls, ev, handlerCall, lookup) {
19417
- const { arrayExpr, param, keyWithItem, mapPreamble, hasBindings, indexParam } = lookup;
19888
+ const { arrayExpr, param, keyWithItem, mapPreamble, mapPreambleDeclaredNames, hasBindings, indexParam } = lookup;
19889
+ const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
19418
19890
  if (ev.nestedLoops.length === 0) {
19419
19891
  const idxLine2 = indexBindingLine(ev.handler, indexParam, `${arrayExpr}.findIndex(item => String(${keyWithItem}) === key)`);
19420
19892
  ls.push(` const li = ${varSlotId(ev.childSlotId)}El.closest('[${BF_KEY}]')`);
@@ -19424,15 +19896,17 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
19424
19896
  ls.push(` const __bfLoopItem = ${arrayExpr}.find(item => String(${keyWithItem}) === key)`);
19425
19897
  ls.push(` if (__bfLoopItem) {`);
19426
19898
  ls.push(` const ${param} = __bfLoopItem`);
19427
- if (mapPreamble) ls.push(` ${mapPreamble}`);
19899
+ if (preambleLine) ls.push(` ${preambleLine}`);
19428
19900
  if (idxLine2) ls.push(` ${idxLine2}`);
19429
- ls.push(` ${handlerCall}`);
19901
+ ls.push(` ;${handlerCall}`);
19430
19902
  ls.push(` }`);
19431
19903
  } else {
19432
19904
  ls.push(` const ${param} = ${arrayExpr}.find(item => String(${keyWithItem}) === key)`);
19433
- if (mapPreamble) ls.push(` ${mapPreamble}`);
19434
- if (idxLine2) ls.push(` if (${param}) { ${idxLine2}; ${handlerCall} }`);
19435
- else ls.push(` if (${param}) ${handlerCall}`);
19905
+ ls.push(` if (${param}) {`);
19906
+ if (preambleLine) ls.push(` ${preambleLine}`);
19907
+ if (idxLine2) ls.push(` ${idxLine2}`);
19908
+ ls.push(` ;${handlerCall}`);
19909
+ ls.push(` }`);
19436
19910
  }
19437
19911
  ls.push(` }`);
19438
19912
  return;
@@ -19459,13 +19933,16 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
19459
19933
  }
19460
19934
  const outerGuard = hasBindings ? "__bfLoopItem" : param;
19461
19935
  const allParams = [outerGuard, ...ev.nestedLoops.map((n) => n.param)];
19462
- if (mapPreamble) ls.push(` ${mapPreamble}`);
19463
19936
  const idxLine = indexBindingLine(ev.handler, indexParam, `${arrayExpr}.findIndex(item => String(${keyWithItem}) === outerKey)`);
19464
- if (idxLine) ls.push(` if (${allParams.join(" && ")}) { ${idxLine}; ${handlerCall} }`);
19465
- else ls.push(` if (${allParams.join(" && ")}) ${handlerCall}`);
19937
+ ls.push(` if (${allParams.join(" && ")}) {`);
19938
+ if (preambleLine) ls.push(` ${preambleLine}`);
19939
+ if (idxLine) ls.push(` ${idxLine}`);
19940
+ ls.push(` ;${handlerCall}`);
19941
+ ls.push(` }`);
19466
19942
  }
19467
19943
  function emitDynamicIndexLookup(ls, ev, handlerCall, lookup) {
19468
- const { arrayExpr, param, mapPreamble, hasBindings, indexParam } = lookup;
19944
+ const { arrayExpr, param, mapPreamble, mapPreambleDeclaredNames, hasBindings, indexParam } = lookup;
19945
+ const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
19469
19946
  const idxLine = indexBindingLine(ev.handler, indexParam, "idx");
19470
19947
  ls.push(` const li = ${varSlotId(ev.childSlotId)}El.closest('li, [bf-i]')`);
19471
19948
  ls.push(` if (li && li.parentElement) {`);
@@ -19474,20 +19951,23 @@ function emitDynamicIndexLookup(ls, ev, handlerCall, lookup) {
19474
19951
  ls.push(` const __bfLoopItem = ${arrayExpr}[idx]`);
19475
19952
  ls.push(` if (__bfLoopItem) {`);
19476
19953
  ls.push(` const ${param} = __bfLoopItem`);
19477
- if (mapPreamble) ls.push(` ${mapPreamble}`);
19954
+ if (preambleLine) ls.push(` ${preambleLine}`);
19478
19955
  if (idxLine) ls.push(` ${idxLine}`);
19479
- ls.push(` ${handlerCall}`);
19956
+ ls.push(` ;${handlerCall}`);
19480
19957
  ls.push(` }`);
19481
19958
  } else {
19482
19959
  ls.push(` const ${param} = ${arrayExpr}[idx]`);
19483
- if (mapPreamble) ls.push(` ${mapPreamble}`);
19484
- if (idxLine) ls.push(` if (${param}) { ${idxLine}; ${handlerCall} }`);
19485
- else ls.push(` if (${param}) ${handlerCall}`);
19960
+ ls.push(` if (${param}) {`);
19961
+ if (preambleLine) ls.push(` ${preambleLine}`);
19962
+ if (idxLine) ls.push(` ${idxLine}`);
19963
+ ls.push(` ;${handlerCall}`);
19964
+ ls.push(` }`);
19486
19965
  }
19487
19966
  ls.push(` }`);
19488
19967
  }
19489
19968
  function emitStaticIndexLookup(ls, ev, handlerCall, lookup, containerVar) {
19490
- const { arrayExpr, param, mapPreamble, offset: offset2, indexParam } = lookup;
19969
+ const { arrayExpr, param, mapPreamble, mapPreambleDeclaredNames, offset: offset2, indexParam } = lookup;
19970
+ const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
19491
19971
  const idxLine = indexBindingLine(ev.handler, indexParam, "__idx");
19492
19972
  ls.push(` let __el = ${varSlotId(ev.childSlotId)}El`);
19493
19973
  ls.push(` while (__el.parentElement && __el.parentElement !== ${containerVar}) __el = __el.parentElement`);
@@ -19495,9 +19975,11 @@ function emitStaticIndexLookup(ls, ev, handlerCall, lookup, containerVar) {
19495
19975
  const idxOffset = buildLoopChildIndexSubtraction(offset2 ?? void 0);
19496
19976
  ls.push(` const __idx = Array.from(${containerVar}.children).indexOf(__el)${idxOffset}`);
19497
19977
  ls.push(` const ${param} = ${arrayExpr}[__idx]`);
19498
- if (mapPreamble) ls.push(` ${mapPreamble}`);
19499
- if (idxLine) ls.push(` if (${param}) { ${idxLine}; ${handlerCall} }`);
19500
- else ls.push(` if (${param}) ${handlerCall}`);
19978
+ ls.push(` if (${param}) {`);
19979
+ if (preambleLine) ls.push(` ${preambleLine}`);
19980
+ if (idxLine) ls.push(` ${idxLine}`);
19981
+ ls.push(` ;${handlerCall}`);
19982
+ ls.push(` }`);
19501
19983
  ls.push(` }`);
19502
19984
  }
19503
19985
  var NON_BUBBLING_EVENTS;
@@ -19548,12 +20030,29 @@ function emitPlain(lines, plan) {
19548
20030
  eventDelegation,
19549
20031
  childRefs,
19550
20032
  bodyIsMultiRoot,
19551
- profileLoopId
20033
+ profileLoopId,
20034
+ preambleRegions
19552
20035
  } = plan;
19553
20036
  const loopBfId = profileLoopId ? `, ${JSON.stringify(profileLoopId)}` : "";
19554
20037
  const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
19555
20038
  lines.push(` __disposers.push(createDisposableEffect(() => {`);
19556
- if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
20039
+ if (plan.flatMapLeafItem) {
20040
+ lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (__bfD, ${indexParam}, __existing) => {`);
20041
+ lines.push(` let __el = __existing`);
20042
+ lines.push(` if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }`);
20043
+ lines.push(` let __last = __existing ? undefined : __bfD().h`);
20044
+ lines.push(` createEffect(() => {`);
20045
+ lines.push(` const __html = __bfD().h`);
20046
+ lines.push(` if (__last === undefined) { __last = __html; return }`);
20047
+ lines.push(` if (__html !== __last) { __last = __html; patchLeaf(__el, __html) }`);
20048
+ lines.push(` })`);
20049
+ lines.push(` return __el`);
20050
+ lines.push(` }, '${markerId}'${loopBfId})`);
20051
+ lines.push(` }))`);
20052
+ stringifyEventDelegation(lines, eventDelegation);
20053
+ return;
20054
+ }
20055
+ if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0 && preambleRegions.length === 0) {
19557
20056
  const cloneExpr = emitTemplateCloneInline(template);
19558
20057
  if (mapPreambleWrapped) {
19559
20058
  lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${mapPreambleWrapped}; ${cloneExpr} }, '${markerId}'${loopBfId})`);
@@ -19574,8 +20073,14 @@ function emitPlain(lines, plan) {
19574
20073
  indent: " ",
19575
20074
  singleRootLayout: "inline"
19576
20075
  });
19577
- if (reactiveEffects !== null) {
19578
- stringifyReactiveEffects(lines, reactiveEffects, { indent: " ", elVar: "__el", bodyIsMultiRoot });
20076
+ if (reactiveEffects !== null || preambleRegions.length > 0) {
20077
+ stringifyReactiveEffects(lines, reactiveEffects, {
20078
+ indent: " ",
20079
+ elVar: "__el",
20080
+ bodyIsMultiRoot,
20081
+ preambleRegions,
20082
+ mapPreambleWrapped
20083
+ });
19579
20084
  }
19580
20085
  emitLoopChildRefs(lines, childRefs, { indent: " ", elVar: "__el", bodyIsMultiRoot });
19581
20086
  lines.push(` return __el`);
@@ -19668,13 +20173,13 @@ function emitArmBody(lines, body2, mode2, indent, profileComponentName) {
19668
20173
  }
19669
20174
  lines.push(`${indent}} }`);
19670
20175
  }
19671
- for (const te of body2.textEffects) {
19672
- const v = varSlotId(te.slotId);
19673
- lines.push(`${indent}let __anchor_${v} = $t(__branchScope, '${te.slotId}')[0]`);
19674
- lines.push(`${indent}__disposers.push(createDisposableEffect(() => {`);
19675
- lines.push(`${indent} const __val = ${te.expression}`);
19676
- lines.push(`${indent} __anchor_${v} = __bfText(__anchor_${v}, __val)`);
19677
- lines.push(`${indent}}${bindingBfId(te.slotId)}))`);
20176
+ if (body2.textEffects.length > 0) {
20177
+ const slots = body2.textEffects.map((te) => ({ id: te.slotId, kind: "markup", path: [] }));
20178
+ const writer = claimWriterVarName(slots, varSlotId);
20179
+ lines.push(`${indent}const ${writer} = lazySlots(__branchScope, ${claimPlanLiteral(slots)})`);
20180
+ for (const te of body2.textEffects) {
20181
+ lines.push(`${indent}__disposers.push(createDisposableEffect(() => { ${writer}('${te.slotId}', escapeTextOrNode(${te.expression})) }${bindingBfId(te.slotId)}))`);
20182
+ }
19678
20183
  }
19679
20184
  if (body2.loops.length > 0) {
19680
20185
  stringifyBranchLoops(lines, body2.loops);
@@ -19707,6 +20212,7 @@ var init_insert = __esm({
19707
20212
  init_branch_loop();
19708
20213
  init_event_listener();
19709
20214
  init_component_scope();
20215
+ init_claim_plan();
19710
20216
  }
19711
20217
  });
19712
20218
 
@@ -19795,6 +20301,31 @@ function buildPlainLoopPlan(elem, profileComponentName) {
19795
20301
  const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings);
19796
20302
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
19797
20303
  const hasReactive2 = elem.bindings.reactiveAttrs.length > 0 || elem.bindings.reactiveTexts.length > 0 || elem.bindings.conditionals.length > 0;
20304
+ if (elem.flatMapClient) {
20305
+ return {
20306
+ kind: "plain",
20307
+ rowConstruction: "string-template",
20308
+ containerVar: `_${varSlotId(elem.slotId)}`,
20309
+ markerId: elem.markerId,
20310
+ profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : void 0,
20311
+ arrayExpr: `(${buildChainedArrayExpr(elem)}).flatMap(${elem.flatMapClient.params} => ${elem.flatMapClient.body})`,
20312
+ keyFn: elem.flatMapClient.keyed ? "(__bfD, __bfI) => String(__bfD.k ?? __bfI)" : "null",
20313
+ paramHead: "__bfD",
20314
+ paramUnwrap: "",
20315
+ indexParam: "__idx",
20316
+ mapPreambleWrapped: "",
20317
+ template: "",
20318
+ reactiveEffects: null,
20319
+ childRefs: [],
20320
+ bodyIsMultiRoot: false,
20321
+ anchored: false,
20322
+ anchorKeyExpr: "__idx",
20323
+ flatMapLeafItem: true,
20324
+ // flatMap loops carry no `MapCallbackPreamble` (jsx-to-ir.ts drops it
20325
+ // for flatMapCallback), so there is nothing to patch here.
20326
+ preambleRegions: []
20327
+ };
20328
+ }
19798
20329
  return {
19799
20330
  kind: "plain",
19800
20331
  rowConstruction: "string-template",
@@ -19818,6 +20349,7 @@ function buildPlainLoopPlan(elem, profileComponentName) {
19818
20349
  skeletonPaths: elem.skeletonPaths,
19819
20350
  reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
19820
20351
  childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
20352
+ preambleRegions: buildPreambleRegionPlans(elem.preambleRegions, elem.param, elem.paramBindings),
19821
20353
  bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
19822
20354
  anchored: elem.bodyIsItemConditional ?? false,
19823
20355
  // Fall back to the iteration index when the loop has no key. A whole-item
@@ -19944,7 +20476,6 @@ var init_control_flow = __esm({
19944
20476
  // ../jsx/src/ir-to-client-js/element-refs.ts
19945
20477
  function generateElementRefs(ctx2) {
19946
20478
  const regularSlots = /* @__PURE__ */ new Set();
19947
- const textSlots = /* @__PURE__ */ new Set();
19948
20479
  const componentSlots = /* @__PURE__ */ new Set();
19949
20480
  const conditionalSlotIds = collectConditionalSlotIds(ctx2);
19950
20481
  for (const elem of ctx2.interactiveElements) {
@@ -19952,11 +20483,6 @@ function generateElementRefs(ctx2) {
19952
20483
  regularSlots.add(elem.slotId);
19953
20484
  }
19954
20485
  }
19955
- for (const elem of ctx2.dynamicElements) {
19956
- if (!elem.insideConditional) {
19957
- textSlots.add(elem.slotId);
19958
- }
19959
- }
19960
20486
  for (const elem of ctx2.conditionalElements) {
19961
20487
  regularSlots.add(elem.slotId);
19962
20488
  }
@@ -19985,10 +20511,9 @@ function generateElementRefs(ctx2) {
19985
20511
  for (const slotId of componentSlots) {
19986
20512
  regularSlots.delete(slotId);
19987
20513
  }
19988
- if (regularSlots.size === 0 && textSlots.size === 0 && componentSlots.size === 0) return "";
20514
+ if (regularSlots.size === 0 && componentSlots.size === 0) return "";
19989
20515
  const refLines = [];
19990
20516
  emitSlotRefs(refLines, [...regularSlots], "$");
19991
- emitSlotRefs(refLines, [...textSlots], "$t");
19992
20517
  emitSlotRefs(refLines, [...componentSlots], "$c");
19993
20518
  return refLines.join("\n");
19994
20519
  }
@@ -20638,6 +21163,107 @@ var init_ir_to_client_js = __esm({
20638
21163
  }
20639
21164
  });
20640
21165
 
21166
+ // ../jsx/src/ir-to-client-js/client-only-elision.ts
21167
+ function decideClientOnlyElision(root2) {
21168
+ walkNode(root2, [], /* @__PURE__ */ new Set(), { bailed: false });
21169
+ }
21170
+ function walkNode(node, path25, forceCloseAncestors, state2) {
21171
+ if (state2.bailed) return;
21172
+ if (node.type === "element") {
21173
+ if (!elementIsPathSafe(node.tag, flattenSkeletonChildren(node.children))) {
21174
+ state2.bailed = true;
21175
+ return;
21176
+ }
21177
+ const groupIdx = skeletonForceCloseGroup(node.tag);
21178
+ if (groupIdx >= 0 && forceCloseAncestors.has(groupIdx)) {
21179
+ state2.bailed = true;
21180
+ return;
21181
+ }
21182
+ const nextAncestors = groupIdx >= 0 ? /* @__PURE__ */ new Set([...forceCloseAncestors, groupIdx]) : forceCloseAncestors;
21183
+ walkChildren(flattenSkeletonChildren(node.children), path25, nextAncestors, state2);
21184
+ } else if (node.type === "fragment") {
21185
+ walkChildren(flattenSkeletonChildren(node.children), path25, forceCloseAncestors, state2);
21186
+ }
21187
+ }
21188
+ function walkChildren(children2, parentPath, forceCloseAncestors, state2) {
21189
+ let frozen = false;
21190
+ let idx = 0;
21191
+ let pendingText = false;
21192
+ for (let i = 0; i < children2.length; i++) {
21193
+ if (state2.bailed) return;
21194
+ const child = children2[i];
21195
+ switch (child.type) {
21196
+ case "text": {
21197
+ if (child.value === "") continue;
21198
+ if (!pendingText) idx += 1;
21199
+ pendingText = true;
21200
+ continue;
21201
+ }
21202
+ case "expression": {
21203
+ if (child.expr === "null" || child.expr === "undefined") continue;
21204
+ if (child.clientOnly && child.slotId) {
21205
+ const adjacent = isTextLike(children2[i - 1]) || isTextLike(children2[i + 1]);
21206
+ if (!frozen && !adjacent) {
21207
+ markElided(child, [...parentPath, idx]);
21208
+ frozen = true;
21209
+ } else {
21210
+ frozen = true;
21211
+ }
21212
+ idx += 1;
21213
+ pendingText = false;
21214
+ continue;
21215
+ }
21216
+ frozen = true;
21217
+ idx += 1;
21218
+ pendingText = false;
21219
+ continue;
21220
+ }
21221
+ case "element": {
21222
+ if (!elementIsPathSafe(child.tag, flattenSkeletonChildren(child.children))) {
21223
+ state2.bailed = true;
21224
+ return;
21225
+ }
21226
+ if (!frozen) {
21227
+ walkNode(child, [...parentPath, idx], forceCloseAncestors, state2);
21228
+ }
21229
+ idx += 1;
21230
+ pendingText = false;
21231
+ continue;
21232
+ }
21233
+ case "fragment":
21234
+ continue;
21235
+ // already flattened
21236
+ default:
21237
+ frozen = true;
21238
+ idx += 1;
21239
+ pendingText = false;
21240
+ continue;
21241
+ }
21242
+ }
21243
+ }
21244
+ function isTextLike(node) {
21245
+ if (!node) return false;
21246
+ if (node.type === "text") return node.value !== "";
21247
+ if (node.type === "expression") return node.expr !== "null" && node.expr !== "undefined";
21248
+ return false;
21249
+ }
21250
+ function markElided(expr, path25) {
21251
+ expr.markerless = true;
21252
+ expr.elidedPath = path25;
21253
+ }
21254
+ function elementIsPathSafe(tag, flatChildren) {
21255
+ if (SKELETON_PATH_HAZARD_TAGS.has(tag)) return false;
21256
+ if (VOID_ELEMENTS.has(tag) && flatChildren.length > 0) return false;
21257
+ if (tag === "tr" && hasForeignTableRowContent(flatChildren)) return false;
21258
+ return true;
21259
+ }
21260
+ var init_client_only_elision = __esm({
21261
+ "../jsx/src/ir-to-client-js/client-only-elision.ts"() {
21262
+ "use strict";
21263
+ init_html_template();
21264
+ }
21265
+ });
21266
+
20641
21267
  // ../jsx/src/css-layer-prefixer.ts
20642
21268
  function prefixClass(cls, layerName) {
20643
21269
  if (!cls || cls.startsWith("layer-")) return cls;
@@ -21812,7 +22438,7 @@ function checkRichTypeMethodCalls(root2, metadata, errors) {
21812
22438
  if (!metadata.propsType) return;
21813
22439
  const matchers = prepareLoweringMatchers(metadata);
21814
22440
  const seen = /* @__PURE__ */ new Set();
21815
- walkNode(root2, metadata, EMPTY_BINDINGS2, matchers, errors, seen);
22441
+ walkNode2(root2, metadata, EMPTY_BINDINGS2, matchers, errors, seen);
21816
22442
  }
21817
22443
  function isLoweringClaimed(matchers, callee, args2) {
21818
22444
  return matchers.some((m) => m(callee, args2) !== null);
@@ -21940,7 +22566,7 @@ function walkAttrValue(value2, clientOnly, loc, meta, bindings, matchers, errors
21940
22566
  walkTemplateParts(value2.parts, loc, meta, bindings, matchers, errors, seen);
21941
22567
  }
21942
22568
  }
21943
- function walkNode(node, meta, bindings, matchers, errors, seen) {
22569
+ function walkNode2(node, meta, bindings, matchers, errors, seen) {
21944
22570
  if (node.type === "expression") {
21945
22571
  if (!node.clientOnly && node.parsed) checkExpr(node.parsed, node.loc, meta, bindings, matchers, errors, seen);
21946
22572
  } else if (node.type === "conditional") {
@@ -21960,11 +22586,11 @@ function walkNode(node, meta, bindings, matchers, errors, seen) {
21960
22586
  case "component":
21961
22587
  case "fragment":
21962
22588
  case "provider":
21963
- for (const child of node.children) walkNode(child, meta, bindings, matchers, errors, seen);
22589
+ for (const child of node.children) walkNode2(child, meta, bindings, matchers, errors, seen);
21964
22590
  break;
21965
22591
  case "async":
21966
- walkNode(node.fallback, meta, bindings, matchers, errors, seen);
21967
- for (const child of node.children) walkNode(child, meta, bindings, matchers, errors, seen);
22592
+ walkNode2(node.fallback, meta, bindings, matchers, errors, seen);
22593
+ for (const child of node.children) walkNode2(child, meta, bindings, matchers, errors, seen);
21968
22594
  break;
21969
22595
  case "loop": {
21970
22596
  if (node.clientOnly) break;
@@ -21973,29 +22599,29 @@ function walkNode(node, meta, bindings, matchers, errors, seen) {
21973
22599
  const arrayType = node.arrayParsed ? resolveReceiverType(node.arrayParsed, meta, bindings) : null;
21974
22600
  loopBindings.set(node.param, arrayType?.kind === "array" ? arrayType.elementType ?? null : null);
21975
22601
  if (node.index) loopBindings.set(node.index, null);
21976
- for (const child of node.children) walkNode(child, meta, loopBindings, matchers, errors, seen);
22602
+ for (const child of node.children) walkNode2(child, meta, loopBindings, matchers, errors, seen);
21977
22603
  if (node.childComponent) {
21978
- for (const child of node.childComponent.children) walkNode(child, meta, loopBindings, matchers, errors, seen);
22604
+ for (const child of node.childComponent.children) walkNode2(child, meta, loopBindings, matchers, errors, seen);
21979
22605
  }
21980
22606
  for (const nested of node.nestedComponents ?? []) {
21981
- for (const child of nested.children) walkNode(child, meta, loopBindings, matchers, errors, seen);
22607
+ for (const child of nested.children) walkNode2(child, meta, loopBindings, matchers, errors, seen);
21982
22608
  }
21983
22609
  for (const seg of node.flatMapCallback?.segments ?? []) {
21984
- if (seg.kind === "jsx") walkNode(seg.ir, meta, loopBindings, matchers, errors, seen);
22610
+ if (seg.kind === "jsx") walkNode2(seg.ir, meta, loopBindings, matchers, errors, seen);
21985
22611
  }
21986
22612
  for (const seg of node.preamble?.segments ?? []) {
21987
- if (seg.kind === "jsx") walkNode(seg.ir, meta, loopBindings, matchers, errors, seen);
22613
+ if (seg.kind === "jsx") walkNode2(seg.ir, meta, loopBindings, matchers, errors, seen);
21988
22614
  }
21989
22615
  break;
21990
22616
  }
21991
22617
  case "conditional":
21992
22618
  if (node.clientOnly) break;
21993
- walkNode(node.whenTrue, meta, bindings, matchers, errors, seen);
21994
- walkNode(node.whenFalse, meta, bindings, matchers, errors, seen);
22619
+ walkNode2(node.whenTrue, meta, bindings, matchers, errors, seen);
22620
+ walkNode2(node.whenFalse, meta, bindings, matchers, errors, seen);
21995
22621
  break;
21996
22622
  case "if-statement":
21997
- walkNode(node.consequent, meta, bindings, matchers, errors, seen);
21998
- if (node.alternate) walkNode(node.alternate, meta, bindings, matchers, errors, seen);
22623
+ walkNode2(node.consequent, meta, bindings, matchers, errors, seen);
22624
+ if (node.alternate) walkNode2(node.alternate, meta, bindings, matchers, errors, seen);
21999
22625
  break;
22000
22626
  }
22001
22627
  }
@@ -22068,6 +22694,7 @@ function compileMultipleComponents(source, filePath, componentNames, options2) {
22068
22694
  };
22069
22695
  componentIR.metadata.clientAnalysis = analyzeClientNeeds(componentIR);
22070
22696
  checkRichTypeMethodCalls(componentIR.root, componentIR.metadata, errors);
22697
+ decideClientOnlyElision(componentIR.root);
22071
22698
  if (options2.cssLayerPrefix) {
22072
22699
  applyCssLayerPrefix(componentIR, options2.cssLayerPrefix);
22073
22700
  }
@@ -22405,6 +23032,7 @@ function compileJSX(source, filePath, options2) {
22405
23032
  };
22406
23033
  componentIR.metadata.clientAnalysis = analyzeClientNeeds(componentIR);
22407
23034
  checkRichTypeMethodCalls(componentIR.root, componentIR.metadata, errors);
23035
+ decideClientOnlyElision(componentIR.root);
22408
23036
  if (ctx2.importedClientSignalNames.size > 0) {
22409
23037
  const sources = /* @__PURE__ */ new Set();
22410
23038
  for (const imp of ctx2.imports) {
@@ -22526,6 +23154,7 @@ var init_compiler = __esm({
22526
23154
  init_jsx_to_ir();
22527
23155
  init_builtins();
22528
23156
  init_ir_to_client_js();
23157
+ init_client_only_elision();
22529
23158
  init_emit_module_level();
22530
23159
  init_imports();
22531
23160
  init_component_scope();
@@ -26471,6 +27100,7 @@ __export(src_exports, {
26471
27100
  createProgramForFile: () => createProgramForFile,
26472
27101
  dangerousInnerHtmlDiagnostic: () => dangerousInnerHtmlDiagnostic,
26473
27102
  dangerousInnerHtmlMetacharViolation: () => dangerousInnerHtmlMetacharViolation,
27103
+ decideClientOnlyElision: () => decideClientOnlyElision,
26474
27104
  describeFallback: () => describeFallback,
26475
27105
  diffProfiles: () => diffProfiles,
26476
27106
  diffStaticBudget: () => diffStaticBudget,
@@ -26585,6 +27215,7 @@ var init_src2 = __esm({
26585
27215
  init_analyzer();
26586
27216
  init_shared_program();
26587
27217
  init_jsx_to_ir();
27218
+ init_client_only_elision();
26588
27219
  init_module_exports();
26589
27220
  init_interface();
26590
27221
  init_test_adapter();