@barefootjs/jsx 0.27.0 → 0.28.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 (50) hide show
  1. package/dist/index.js +620 -26
  2. package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts +6 -0
  3. package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts.map +1 -1
  4. package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts +2 -1
  5. package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts.map +1 -1
  6. package/dist/ir-to-client-js/control-flow/plan/build-insert.d.ts +7 -0
  7. package/dist/ir-to-client-js/control-flow/plan/build-insert.d.ts.map +1 -1
  8. package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts +107 -0
  9. package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts.map +1 -0
  10. package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts +9 -1
  11. package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
  12. package/dist/ir-to-client-js/control-flow/plan/lazy-row-eligibility.d.ts +203 -0
  13. package/dist/ir-to-client-js/control-flow/plan/lazy-row-eligibility.d.ts.map +1 -0
  14. package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +10 -0
  15. package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
  16. package/dist/ir-to-client-js/control-flow/stringify/branch-loop.d.ts.map +1 -1
  17. package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts +98 -0
  18. package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts.map +1 -0
  19. package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts +5 -0
  20. package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts.map +1 -1
  21. package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts.map +1 -1
  22. package/dist/ir-to-client-js/control-flow.d.ts.map +1 -1
  23. package/dist/ir-to-client-js/imports.d.ts +2 -2
  24. package/dist/ir-to-client-js/imports.d.ts.map +1 -1
  25. package/package.json +2 -2
  26. package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +162 -36
  27. package/src/__tests__/client-js-generation.test.ts +4 -1
  28. package/src/__tests__/composite-branch-loop.test.ts +12 -3
  29. package/src/__tests__/conditional-mapArray-key.test.ts +7 -1
  30. package/src/__tests__/create-selector.test.ts +21 -7
  31. package/src/__tests__/lazy-row-eligibility.test.ts +692 -0
  32. package/src/__tests__/loop-branch-bare-expression-reactive-text.test.ts +98 -0
  33. package/src/__tests__/loop-fallback-wrap.test.ts +31 -12
  34. package/src/__tests__/loop-hoisted-template.test.ts +10 -6
  35. package/src/__tests__/static-loop-csr-materialize.test.ts +6 -1
  36. package/src/ir-to-client-js/collect-elements.ts +48 -13
  37. package/src/ir-to-client-js/control-flow/plan/branch-loop.ts +6 -0
  38. package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +35 -12
  39. package/src/ir-to-client-js/control-flow/plan/build-insert.ts +9 -2
  40. package/src/ir-to-client-js/control-flow/plan/build-lazy-row.ts +305 -0
  41. package/src/ir-to-client-js/control-flow/plan/build-loop.ts +44 -12
  42. package/src/ir-to-client-js/control-flow/plan/lazy-row-eligibility.ts +440 -0
  43. package/src/ir-to-client-js/control-flow/plan/loop.ts +10 -0
  44. package/src/ir-to-client-js/control-flow/stringify/branch-loop.ts +22 -0
  45. package/src/ir-to-client-js/control-flow/stringify/lazy-row.ts +478 -0
  46. package/src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts +6 -1
  47. package/src/ir-to-client-js/control-flow/stringify/loop.ts +23 -0
  48. package/src/ir-to-client-js/control-flow.ts +7 -2
  49. package/src/ir-to-client-js/imports.ts +8 -2
  50. package/src/jsx-to-ir.ts +18 -1
package/dist/index.js CHANGED
@@ -11237,7 +11237,8 @@ function transformConditionalBranch(node, ctx) {
11237
11237
  const callsReactive = exprCallsReactiveGetters(node, ctx);
11238
11238
  const hasCalls = exprHasFunctionCalls(node);
11239
11239
  const reactive = isReactiveExpression(exprText, ctx, node) || isReactiveOrigin(branchOrigin);
11240
- const needsSlot = reactive || callsReactive;
11240
+ const refsLoopParam = branchOrigin.freeRefs?.some((r) => r.kind === "render-item") ?? false;
11241
+ const needsSlot = reactive || callsReactive || refsLoopParam;
11241
11242
  const slotId = needsSlot ? generateSlotId(ctx) : null;
11242
11243
  return {
11243
11244
  type: "expression",
@@ -14832,7 +14833,7 @@ function summarizeLoopChildBranch(node, ctx, siblingOffsets, loopParam, loopPara
14832
14833
  conditionals: collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings),
14833
14834
  events: collectConditionalBranchEvents(node),
14834
14835
  reactiveAttrs: collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, true),
14835
- reactiveTexts: node.type === "expression" ? [] : collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, true)
14836
+ reactiveTexts: node.type === "expression" && node.hasFunctionCalls ? [] : collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, true)
14836
14837
  };
14837
14838
  }
14838
14839
 
@@ -15291,6 +15292,7 @@ var RUNTIME_IMPORT_CANDIDATES = [
15291
15292
  "getLoopNodes",
15292
15293
  "mapArray",
15293
15294
  "mapArrayAnchored",
15295
+ "mapArrayLazy",
15294
15296
  "patchLeaf",
15295
15297
  "createDisposableEffect",
15296
15298
  "createComponent",
@@ -15321,6 +15323,8 @@ var RUNTIME_IMPORT_CANDIDATES = [
15321
15323
  "__bfText",
15322
15324
  "claimSlots",
15323
15325
  "lazySlots",
15326
+ "lazyClaimSlots",
15327
+ "textOrNode",
15324
15328
  "beginTurn",
15325
15329
  "endTurn",
15326
15330
  "date",
@@ -18389,8 +18393,352 @@ function buildKeyedOrIndexLookup(args) {
18389
18393
  };
18390
18394
  }
18391
18395
 
18396
+ // src/ir-to-client-js/control-flow/plan/lazy-row-eligibility.ts
18397
+ var PURE_SOURCE_GLOBALS = new Set([
18398
+ "Object",
18399
+ "Array",
18400
+ "JSON",
18401
+ "Number",
18402
+ "String",
18403
+ "Boolean"
18404
+ ]);
18405
+ var INERT_BINDING_GLOBALS = new Set([
18406
+ "Object",
18407
+ "Array",
18408
+ "JSON",
18409
+ "Number",
18410
+ "String",
18411
+ "Boolean",
18412
+ "Math",
18413
+ "Date",
18414
+ "Intl",
18415
+ "Symbol",
18416
+ "Map",
18417
+ "Set",
18418
+ "WeakMap",
18419
+ "WeakSet",
18420
+ "Promise",
18421
+ "RegExp",
18422
+ "Error",
18423
+ "BigInt",
18424
+ "console",
18425
+ "undefined",
18426
+ "NaN",
18427
+ "Infinity",
18428
+ "globalThis",
18429
+ "parseInt",
18430
+ "parseFloat",
18431
+ "isNaN",
18432
+ "isFinite",
18433
+ "encodeURIComponent",
18434
+ "decodeURIComponent",
18435
+ "encodeURI",
18436
+ "decodeURI"
18437
+ ]);
18438
+ var UNKNOWN_IDENTIFIERS = "<unknown>";
18439
+ var NO = (reason) => ({ eligible: false, reason });
18440
+ function lazyRowEligibility(args) {
18441
+ const { shape, bindings, arraySourceIdentifiers, scope } = args;
18442
+ if (scope.profile)
18443
+ return NO("profile mode keeps the granular eager emission");
18444
+ if (shape.callSite !== "plain" && shape.callSite !== "branch-plain") {
18445
+ return NO(`call site '${shape.callSite}' is not a plain loop row`);
18446
+ }
18447
+ if (shape.flatMapLeafItem)
18448
+ return NO("flatMap descriptor loop (build-or-patch renderItem)");
18449
+ if (shape.anchored)
18450
+ return NO("anchored whole-item-conditional loop");
18451
+ if (shape.bodyIsMultiRoot)
18452
+ return NO("multi-root (Fragment) row");
18453
+ if (!shape.hasExplicitKey)
18454
+ return NO("index-keyed loop (no explicit key)");
18455
+ if (shape.conditionalCount > 0)
18456
+ return NO("row contains a reactive conditional");
18457
+ if (shape.childRefCount > 0)
18458
+ return NO("row has imperative child refs");
18459
+ if (shape.hasChildComponent)
18460
+ return NO("row body is a child component");
18461
+ if (shape.nestedComponentCount > 0)
18462
+ return NO("row contains nested child components");
18463
+ if (shape.innerLoopCount > 0)
18464
+ return NO("row contains an inner loop");
18465
+ if (shape.hasMapPreamble)
18466
+ return NO("row has a map-callback preamble (may declare row-local reactivity)");
18467
+ if (shape.preambleRegionCount > 0)
18468
+ return NO("row has preamble-patched regions");
18469
+ if (shape.hasParamUnwrap)
18470
+ return NO("destructured loop param without param bindings");
18471
+ for (const b of bindings) {
18472
+ if (b.referencesIndex) {
18473
+ return NO(`binding on slot ${b.slotId} references the loop index parameter`);
18474
+ }
18475
+ if (b.opaqueOuterNames.includes(UNKNOWN_IDENTIFIERS)) {
18476
+ return NO(`binding on slot ${b.slotId} has no analyzable identifier set`);
18477
+ }
18478
+ }
18479
+ if (!arraySourceIdentifiers)
18480
+ return NO("loop source free identifiers unavailable");
18481
+ const sourceGate = checkSourceConsistency(arraySourceIdentifiers, scope);
18482
+ if (sourceGate)
18483
+ return NO(`loop source is not provably hydration-consistent: ${sourceGate}`);
18484
+ return { eligible: true };
18485
+ }
18486
+ function checkSourceConsistency(names, scope) {
18487
+ const seen = new Set;
18488
+ const resolve2 = (name) => {
18489
+ if (seen.has(name))
18490
+ return null;
18491
+ seen.add(name);
18492
+ if (scope.props.has(name))
18493
+ return null;
18494
+ if (PURE_SOURCE_GLOBALS.has(name))
18495
+ return null;
18496
+ const constFree = scope.constants.get(name);
18497
+ if (constFree !== undefined) {
18498
+ if (constFree === null)
18499
+ return `constant '${name}' has no analyzable value`;
18500
+ for (const inner of constFree) {
18501
+ const failure = resolve2(inner);
18502
+ if (failure)
18503
+ return failure;
18504
+ }
18505
+ return null;
18506
+ }
18507
+ const signal = scope.signals.get(name);
18508
+ if (signal !== undefined) {
18509
+ if (signal.initializerFreeIdentifiers === null) {
18510
+ return `signal '${name}' has no structured initializer to prove props/literal derivation`;
18511
+ }
18512
+ for (const inner of signal.initializerFreeIdentifiers) {
18513
+ const failure = resolve2(inner);
18514
+ if (failure)
18515
+ return failure;
18516
+ }
18517
+ return null;
18518
+ }
18519
+ if (scope.memos.has(name))
18520
+ return `memo '${name}' initializer is not analyzed by the v1 gate`;
18521
+ if (scope.inert.has(name))
18522
+ return `'${name}' is an import or local function`;
18523
+ return `'${name}' does not resolve to a prop, literal-derived const, or props/literal-derived signal`;
18524
+ };
18525
+ for (const name of names) {
18526
+ const failure = resolve2(name);
18527
+ if (failure)
18528
+ return failure;
18529
+ }
18530
+ return null;
18531
+ }
18532
+ function classifyLazyBinding(args) {
18533
+ const { kind, slotId, free, rowLocalNames, indexParam, scope } = args;
18534
+ if (free === null) {
18535
+ return {
18536
+ kind,
18537
+ slotId,
18538
+ readsItem: true,
18539
+ readsOuter: true,
18540
+ reactiveOuterNames: [],
18541
+ opaqueOuterNames: [UNKNOWN_IDENTIFIERS],
18542
+ referencesIndex: false
18543
+ };
18544
+ }
18545
+ let readsItem = false;
18546
+ let referencesIndex = false;
18547
+ const reactiveOuterNames = [];
18548
+ const opaqueOuterNames = [];
18549
+ for (const name of free) {
18550
+ if (rowLocalNames.has(name)) {
18551
+ readsItem = true;
18552
+ continue;
18553
+ }
18554
+ if (name === indexParam) {
18555
+ referencesIndex = true;
18556
+ continue;
18557
+ }
18558
+ if (INERT_BINDING_GLOBALS.has(name))
18559
+ continue;
18560
+ if (scope.signals.has(name) || scope.memos.has(name)) {
18561
+ if (!reactiveOuterNames.includes(name))
18562
+ reactiveOuterNames.push(name);
18563
+ continue;
18564
+ }
18565
+ const constFree = scope.constants.get(name);
18566
+ if (constFree !== undefined && constFree !== null && constFree.size === 0)
18567
+ continue;
18568
+ opaqueOuterNames.push(name);
18569
+ }
18570
+ return {
18571
+ kind,
18572
+ slotId,
18573
+ readsItem,
18574
+ readsOuter: reactiveOuterNames.length > 0 || opaqueOuterNames.length > 0,
18575
+ reactiveOuterNames,
18576
+ opaqueOuterNames,
18577
+ referencesIndex
18578
+ };
18579
+ }
18580
+
18581
+ // src/ir-to-client-js/control-flow/plan/build-lazy-row.ts
18582
+ function buildLazyRowPlan(args) {
18583
+ return decideLazyRow(args).plan;
18584
+ }
18585
+ function decideLazyRow(args) {
18586
+ const { loop, scope } = args;
18587
+ if (!scope) {
18588
+ return { plan: null, decision: { eligible: false, reason: "no component scope info supplied" } };
18589
+ }
18590
+ const wrap = (expr) => wrapLoopParamAsAccessor(expr, loop.param, loop.paramBindings);
18591
+ const rowLocalNames = new Set([loop.param]);
18592
+ for (const b of loop.paramBindings ?? [])
18593
+ rowLocalNames.add(b.name);
18594
+ const classified = [];
18595
+ const attrClass = new Map;
18596
+ loop.bindings.reactiveAttrs.forEach((attr, i) => {
18597
+ const c = classifyLazyBinding({
18598
+ kind: "attr",
18599
+ slotId: attr.childSlotId,
18600
+ free: attrFreeIdentifiers2(attr.expression),
18601
+ rowLocalNames,
18602
+ indexParam: args.indexParam,
18603
+ scope
18604
+ });
18605
+ attrClass.set(i, c);
18606
+ classified.push(c);
18607
+ });
18608
+ const textClass = new Map;
18609
+ loop.bindings.reactiveTexts.forEach((text, i) => {
18610
+ const c = classifyLazyBinding({
18611
+ kind: "text",
18612
+ slotId: text.slotId,
18613
+ free: text.freeIdentifiers ?? null,
18614
+ rowLocalNames,
18615
+ indexParam: args.indexParam,
18616
+ scope
18617
+ });
18618
+ textClass.set(i, c);
18619
+ classified.push(c);
18620
+ });
18621
+ const shape = {
18622
+ callSite: args.callSite,
18623
+ flatMapLeafItem: args.flatMapLeafItem,
18624
+ anchored: args.anchored,
18625
+ bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false,
18626
+ hasExplicitKey: loop.key != null,
18627
+ conditionalCount: loop.bindings.conditionals?.length ?? 0,
18628
+ childRefCount: loop.bindings.refs?.length ?? 0,
18629
+ nestedComponentCount: loop.nestedComponents?.length ?? 0,
18630
+ innerLoopCount: loop.innerLoops?.length ?? 0,
18631
+ hasChildComponent: "childComponent" in loop && loop.childComponent != null,
18632
+ hasMapPreamble: args.mapPreambleWrapped.length > 0,
18633
+ preambleRegionCount: args.preambleRegionCount,
18634
+ hasParamUnwrap: args.paramUnwrap.length > 0
18635
+ };
18636
+ const decision = lazyRowEligibility({
18637
+ shape,
18638
+ bindings: classified,
18639
+ arraySourceIdentifiers: loopSourceIdentifiers(loop, args.arrayExpr),
18640
+ scope
18641
+ });
18642
+ if (!decision.eligible)
18643
+ return { plan: null, decision };
18644
+ const attrSlotIds = [];
18645
+ for (const attr of loop.bindings.reactiveAttrs) {
18646
+ if (!attrSlotIds.includes(attr.childSlotId))
18647
+ attrSlotIds.push(attr.childSlotId);
18648
+ }
18649
+ let ordinal = 0;
18650
+ const attrs = loop.bindings.reactiveAttrs.map((attr, i) => {
18651
+ const c = attrClass.get(i);
18652
+ return {
18653
+ slotId: attr.childSlotId,
18654
+ attrName: attr.attrName,
18655
+ wrappedExpression: wrap(attr.expression),
18656
+ meta: pickAttrMeta(attr),
18657
+ refIndex: attrSlotIds.indexOf(attr.childSlotId),
18658
+ ordinal: ordinal++,
18659
+ readsItem: c.readsItem,
18660
+ readsOuter: c.readsOuter
18661
+ };
18662
+ });
18663
+ const texts = loop.bindings.reactiveTexts.map((text, i) => {
18664
+ const c = textClass.get(i);
18665
+ return {
18666
+ slotId: text.slotId,
18667
+ wrappedExpression: wrap(text.expression),
18668
+ ordinal: ordinal++,
18669
+ readsItem: c.readsItem || !c.readsOuter,
18670
+ readsOuter: c.readsOuter
18671
+ };
18672
+ });
18673
+ const outerPrimeGetters = [];
18674
+ for (const c of classified) {
18675
+ for (const name of c.reactiveOuterNames) {
18676
+ if (!outerPrimeGetters.includes(name))
18677
+ outerPrimeGetters.push(name);
18678
+ }
18679
+ }
18680
+ return {
18681
+ plan: {
18682
+ attrSlotIds,
18683
+ attrs,
18684
+ texts,
18685
+ writerIndex: texts.length > 0 ? attrSlotIds.length : -1,
18686
+ textNeedsRead: texts.some((t) => t.readsOuter),
18687
+ lastCount: ordinal,
18688
+ outerPrimeGetters,
18689
+ hasOuter: attrs.some((a) => a.readsOuter) || texts.some((t) => t.readsOuter)
18690
+ },
18691
+ decision
18692
+ };
18693
+ }
18694
+ function buildLazyRowScopeInfo(ctx) {
18695
+ const signals = new Map;
18696
+ for (const s of ctx.signals) {
18697
+ signals.set(s.getter, {
18698
+ initializerFreeIdentifiers: s.parsed ? freeIdentifiers(s.parsed) : null
18699
+ });
18700
+ }
18701
+ const memos = new Set(ctx.memos.map((m) => m.name));
18702
+ const props = new Set([PROPS_PARAM]);
18703
+ if (ctx.propsObjectName)
18704
+ props.add(ctx.propsObjectName);
18705
+ for (const p of ctx.propsParams)
18706
+ props.add(p.name);
18707
+ const constants = new Map;
18708
+ for (const c of ctx.localConstants) {
18709
+ constants.set(c.name, c.freeIdentifiers ?? null);
18710
+ }
18711
+ const inert = new Set;
18712
+ for (const f of ctx.localFunctions)
18713
+ inert.add(f.name);
18714
+ for (const imp of ctx.imports) {
18715
+ if (imp.isTypeOnly)
18716
+ continue;
18717
+ for (const spec of imp.specifiers)
18718
+ inert.add(spec.alias || spec.name);
18719
+ }
18720
+ return { signals, memos, props, constants, inert, profile: ctx.profile };
18721
+ }
18722
+ function attrFreeIdentifiers2(expression) {
18723
+ if (!expression || expression.trim().length === 0)
18724
+ return new Set;
18725
+ try {
18726
+ return freeIdentifiers(parseExpression(expression));
18727
+ } catch {
18728
+ return null;
18729
+ }
18730
+ }
18731
+ function loopSourceIdentifiers(loop, arrayExpr) {
18732
+ if (!loop.arrayFreeIdentifiers)
18733
+ return null;
18734
+ const names = new Set(loop.arrayFreeIdentifiers);
18735
+ for (const name of extractFreeIdentifiersFromText(arrayExpr))
18736
+ names.add(name);
18737
+ return names;
18738
+ }
18739
+
18392
18740
  // src/ir-to-client-js/control-flow/plan/build-branch-loop.ts
18393
- function buildBranchLoopPlan(loop, profileComponentName) {
18741
+ function buildBranchLoopPlan(loop, profileComponentName, lazyScope) {
18394
18742
  const containerSlotId = loop.containerSlotId;
18395
18743
  const cv = varSlotId(containerSlotId);
18396
18744
  const containerVar = `__loop_${cv}`;
@@ -18406,6 +18754,13 @@ function buildBranchLoopPlan(loop, profileComponentName) {
18406
18754
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(loop.param, loop.paramBindings);
18407
18755
  const hasReactiveEffects = loop.bindings.reactiveAttrs.length > 0 || loop.bindings.reactiveTexts.length > 0 || loop.bindings.conditionals.length > 0;
18408
18756
  const fm = loop.flatMapClient;
18757
+ const arrayExpr = fm ? `(${buildChainedArrayExpr(loop)}).flatMap(${fm.params} => ${fm.body})` : buildChainedArrayExpr(loop);
18758
+ const indexParam = loop.index || "__idx";
18759
+ const mapPreambleWrapped = loop.preamble ? renderPreamble(loop.preamble, {
18760
+ transformJs: (t) => wrapLoopParamAsAccessor(t, loop.param, loop.paramBindings),
18761
+ renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings }], undefined, true)
18762
+ }) : "";
18763
+ const preambleRegions = buildPreambleRegionPlans(loop.preambleRegions, loop.param, loop.paramBindings);
18409
18764
  const plan = {
18410
18765
  kind: "plain",
18411
18766
  rowConstruction: "string-template",
@@ -18413,15 +18768,24 @@ function buildBranchLoopPlan(loop, profileComponentName) {
18413
18768
  containerVar,
18414
18769
  markerId: loop.markerId,
18415
18770
  flatMapLeafItem: fm ? true : undefined,
18416
- arrayExpr: fm ? `(${buildChainedArrayExpr(loop)}).flatMap(${fm.params} => ${fm.body})` : buildChainedArrayExpr(loop),
18771
+ arrayExpr,
18417
18772
  keyFn: fm ? fm.keyed ? "(__bfD, __bfI) => String(__bfD.k ?? __bfI)" : "null" : loopKeyFn(loop),
18418
18773
  paramHead,
18419
18774
  paramUnwrap,
18420
- indexParam: loop.index || "__idx",
18421
- mapPreambleWrapped: loop.preamble ? renderPreamble(loop.preamble, {
18422
- transformJs: (t) => wrapLoopParamAsAccessor(t, loop.param, loop.paramBindings),
18423
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings }], undefined, true)
18424
- }) : "",
18775
+ indexParam,
18776
+ mapPreambleWrapped,
18777
+ lazyRow: buildLazyRowPlan({
18778
+ loop,
18779
+ arrayExpr,
18780
+ indexParam,
18781
+ paramUnwrap,
18782
+ mapPreambleWrapped,
18783
+ preambleRegionCount: preambleRegions.length,
18784
+ callSite: "branch-plain",
18785
+ flatMapLeafItem: Boolean(fm),
18786
+ anchored: false,
18787
+ scope: lazyScope
18788
+ }) ?? undefined,
18425
18789
  template: loop.template,
18426
18790
  reactiveEffects: hasReactiveEffects ? buildReactiveEffectsPlan({
18427
18791
  attrs: loop.bindings.reactiveAttrs,
@@ -18433,7 +18797,7 @@ function buildBranchLoopPlan(loop, profileComponentName) {
18433
18797
  }) : null,
18434
18798
  eventDelegation: buildBranchLoopDelegationPlan(loop, cv, profileComponentName),
18435
18799
  childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings),
18436
- preambleRegions: buildPreambleRegionPlans(loop.preambleRegions, loop.param, loop.paramBindings),
18800
+ preambleRegions,
18437
18801
  bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false,
18438
18802
  profileLoopId: profileComponentName ? `${profileComponentName}#binding:${containerSlotId}` : undefined
18439
18803
  };
@@ -18484,8 +18848,8 @@ function buildArmBody(branch, options) {
18484
18848
  slotId: t.slotId,
18485
18849
  expression: t.expression
18486
18850
  })),
18487
- loops: branch.loops.map((l) => buildBranchLoopPlan(l, pc)),
18488
- conditionals: branch.conditionals.map((c) => buildInsertPlan(c, { scope: { kind: "branchScope" }, eventNameMode: options.eventNameMode, profileComponentName: pc }))
18851
+ loops: branch.loops.map((l) => buildBranchLoopPlan(l, pc, options.lazyScope)),
18852
+ conditionals: branch.conditionals.map((c) => buildInsertPlan(c, { scope: { kind: "branchScope" }, eventNameMode: options.eventNameMode, profileComponentName: pc, lazyScope: options.lazyScope }))
18489
18853
  };
18490
18854
  }
18491
18855
 
@@ -18862,7 +19226,7 @@ function stringifyBranchEventBindings(lines, plan, indent) {
18862
19226
  }
18863
19227
  function stringifyBranchChildComponentInits(lines, plan, indent) {
18864
19228
  for (const init of plan) {
18865
- lines.push(`${indent}{ let __c = qsa(__branchScope, ${init.selector}); if (!__c) { const __ph = __branchScope.querySelector('[${DATA_BF_PH}="${init.placeholderId}"]'); if (__ph) { __c = createComponent('${nameForRegistryRef(init.name)}', ${init.propsExpr}); __ph.replaceWith(__c) } } if (__c) initChild('${nameForRegistryRef(init.name)}', __c, ${init.propsExpr}) }`);
19229
+ lines.push(`${indent}{ let __c = qsa(__branchScope, ${init.selector}); if (!__c) { const __ph = __branchScope.querySelector('[${DATA_BF_PH}="${init.placeholderId}"]'); if (__ph) { __c = createComponent('${nameForRegistryRef(init.name)}', ${init.propsExpr}, undefined, undefined, __ph) } } if (__c) initChild('${nameForRegistryRef(init.name)}', __c, ${init.propsExpr}) }`);
18866
19230
  }
18867
19231
  }
18868
19232
  function stringifyBranchInnerLoops(lines, plan, indent, pc) {
@@ -19171,6 +19535,184 @@ function emitNestedInit(lines, indent, parentVar, nc) {
19171
19535
  }
19172
19536
  }
19173
19537
 
19538
+ // src/ir-to-client-js/control-flow/stringify/lazy-row.ts
19539
+ function stringifyLazyRowLoop(lines, o) {
19540
+ const { indent, lazyRow, paramHead } = o;
19541
+ const mid = o.markerId.replace(/[^A-Za-z0-9_$]/g, "_");
19542
+ const tplVar = `__tpl_${mid}`;
19543
+ const claimVar = `__lzc_${mid}`;
19544
+ const hasRefs = lazyRow.attrSlotIds.length > 0 || lazyRow.texts.length > 0;
19545
+ const hasBindings = lazyRow.attrs.length > 0 || lazyRow.texts.length > 0;
19546
+ const rwDoor = lazyRow.textNeedsRead;
19547
+ const paths = o.skeletonPaths;
19548
+ const useHoisted = Boolean(o.skeletonTemplate);
19549
+ if (useHoisted)
19550
+ emitHoistedTemplateDecl(lines, indent, tplVar, o.skeletonTemplate);
19551
+ const textPathVar = useHoisted && paths && lazyRow.texts.length > 0 ? `__lzp_${mid}` : null;
19552
+ if (textPathVar) {
19553
+ const arrays = lazyRow.texts.map((t) => `[${(paths.textMarkerPaths.get(t.slotId) ?? []).join(", ")}]`);
19554
+ lines.push(`${indent}const ${textPathVar} = [${arrays.join(", ")}]`);
19555
+ }
19556
+ let adoptedPlanVar = null;
19557
+ let freshPlanVar = null;
19558
+ if (lazyRow.texts.length > 0) {
19559
+ adoptedPlanVar = `__lzs_${mid}`;
19560
+ const adoptedSlots = lazyRow.texts.map((t) => ({ id: t.slotId, kind: "text", path: [] }));
19561
+ lines.push(`${indent}const ${adoptedPlanVar} = ${claimPlanLiteral(adoptedSlots)}`);
19562
+ if (textPathVar) {
19563
+ freshPlanVar = `__lzsc_${mid}`;
19564
+ const freshSlots = lazyRow.texts.map((t, i) => ({
19565
+ id: t.slotId,
19566
+ kind: "text",
19567
+ path: [],
19568
+ pathExpr: `${textPathVar}[${i}]`
19569
+ }));
19570
+ lines.push(`${indent}const ${freshPlanVar} = ${claimPlanLiteral(freshSlots)}`);
19571
+ } else {
19572
+ freshPlanVar = adoptedPlanVar;
19573
+ }
19574
+ }
19575
+ if (hasRefs) {
19576
+ const parts = refParts(lazyRow, "__el", null, adoptedPlanVar, true);
19577
+ lines.push(`${indent}const ${claimVar} = (__e) => {`);
19578
+ if (parts.some((p) => p.includes("__el")))
19579
+ lines.push(`${indent} const __el = __e.primaryEl`);
19580
+ lines.push(`${indent} return [${parts.join(", ")}]`);
19581
+ lines.push(`${indent}}`);
19582
+ }
19583
+ const call = `mapArrayLazy(() => ${o.arrayExpr}, ${o.containerVar}, ${o.keyFn}, {`;
19584
+ lines.push(`${indent}${o.guardContainer ? `if (${o.containerVar}) ` : ""}${call}`);
19585
+ const b1 = `${indent} `;
19586
+ const b2 = `${indent} `;
19587
+ lines.push(`${b1}createRow: (__e, ${o.indexParam}) => {`);
19588
+ lines.push(`${b2}const ${paramHead} = () => __e.item`);
19589
+ const cloneExpr = useHoisted ? hoistedCloneExpr(tplVar, o.skeletonTemplate) : `(() => { ${emitTemplateCloneInline(o.template)} })()`;
19590
+ lines.push(`${b2}const __el = ${cloneExpr}`);
19591
+ if (hasRefs) {
19592
+ lines.push(`${b2}const __r = __e.refs = [${refParts(lazyRow, "__el", useHoisted ? paths ?? null : null, freshPlanVar).join(", ")}]`);
19593
+ }
19594
+ if (hasBindings) {
19595
+ lines.push(`${b2}const __l = __e.last = []`);
19596
+ for (const a of lazyRow.attrs)
19597
+ emitAttrBinding(lines, b2, a, "create");
19598
+ const createDoor = `__r[${lazyRow.writerIndex}]`;
19599
+ for (const t of lazyRow.texts)
19600
+ emitTextBinding(lines, b2, t, createDoor, "create", rwDoor);
19601
+ }
19602
+ lines.push(`${b2}return __el`);
19603
+ lines.push(`${b1}},`);
19604
+ const itemAttrs = lazyRow.attrs.filter((a) => a.readsItem);
19605
+ const itemTexts = lazyRow.texts.filter((t) => t.readsItem);
19606
+ if (itemAttrs.length === 0 && itemTexts.length === 0) {
19607
+ lines.push(`${b1}applyItem: () => {},`);
19608
+ } else {
19609
+ lines.push(`${b1}applyItem: (__e) => {`);
19610
+ lines.push(`${b2}const ${paramHead} = () => __e.item`);
19611
+ lines.push(`${b2}const __r = __e.refs ?? (__e.refs = ${claimVar}(__e))`);
19612
+ lines.push(`${b2}const __l = __e.last ?? (__e.last = [])`);
19613
+ for (const a of itemAttrs)
19614
+ emitAttrBinding(lines, b2, a, "item");
19615
+ if (itemTexts.length > 0) {
19616
+ lines.push(`${b2}const __d = ${doorAccess(lazyRow, lazyRow.writerIndex, adoptedPlanVar)}`);
19617
+ for (const t of itemTexts)
19618
+ emitTextBinding(lines, b2, t, "__d", "item", rwDoor);
19619
+ }
19620
+ lines.push(`${b1}},`);
19621
+ }
19622
+ const outerAttrs = lazyRow.attrs.filter((a) => a.readsOuter);
19623
+ const outerTexts = lazyRow.texts.filter((t) => t.readsOuter);
19624
+ if (outerAttrs.length > 0 || outerTexts.length > 0) {
19625
+ const b3 = `${indent} `;
19626
+ lines.push(`${b1}applyOuter: (__es, __seed) => {`);
19627
+ for (const g of lazyRow.outerPrimeGetters)
19628
+ lines.push(`${b2}${g}()`);
19629
+ lines.push(`${b2}for (const __e of __es) {`);
19630
+ lines.push(`${b3}const ${paramHead} = () => __e.item`);
19631
+ lines.push(`${b3}const __r = __e.refs ?? (__e.refs = ${claimVar}(__e))`);
19632
+ lines.push(`${b3}const __l = __e.last ?? (__e.last = [])`);
19633
+ for (const a of outerAttrs)
19634
+ emitAttrBinding(lines, b3, a, "outer");
19635
+ if (outerTexts.length > 0) {
19636
+ lines.push(`${b3}const __d = ${doorAccess(lazyRow, lazyRow.writerIndex, adoptedPlanVar)}`);
19637
+ for (const t of outerTexts)
19638
+ emitTextBinding(lines, b3, t, "__d", "outer", rwDoor);
19639
+ }
19640
+ lines.push(`${b2}}`);
19641
+ lines.push(`${b1}},`);
19642
+ }
19643
+ lines.push(`${indent}}, '${o.markerId}')`);
19644
+ }
19645
+ function refParts(lazyRow, elVar, skeletonPaths, planVar, deferDoor = false) {
19646
+ const parts = [];
19647
+ for (const slotId of lazyRow.attrSlotIds) {
19648
+ const path = skeletonPaths?.elementPaths.get(slotId);
19649
+ parts.push(path ? pathExpr(elVar, path) : `qsa(${elVar}, '[bf="${slotId}"]')`);
19650
+ }
19651
+ if (lazyRow.texts.length > 0) {
19652
+ parts.push(deferDoor ? "null" : `${doorCtor(lazyRow)}(${elVar}, ${planVar})`);
19653
+ }
19654
+ return parts;
19655
+ }
19656
+ function doorCtor(lazyRow) {
19657
+ return lazyRow.textNeedsRead ? "lazyClaimSlots" : "lazySlots";
19658
+ }
19659
+ function doorAccess(lazyRow, writerIndex, adoptedPlanVar) {
19660
+ const slot = `__r[${writerIndex}]`;
19661
+ return `${slot} ?? (${slot} = ${doorCtor(lazyRow)}(__e.primaryEl, ${adoptedPlanVar}))`;
19662
+ }
19663
+ function dedupGuard(ordinal) {
19664
+ return `!(${ordinal} in __l) || !Object.is(__l[${ordinal}], __x)`;
19665
+ }
19666
+ function emitAttrBinding(lines, ind, a, mode) {
19667
+ lines.push(`${ind}{ const __t = __r[${a.refIndex}]`);
19668
+ lines.push(`${ind}if (__t) {`);
19669
+ lines.push(`${ind} const __x = ${a.wrappedExpression}`);
19670
+ const guard = mode === "create" ? null : mode === "item" ? dedupGuard(a.ordinal) : `__seed ? (${seedDiffersExpr("__t", a)}) : (${dedupGuard(a.ordinal)})`;
19671
+ const writeIndent = guard ? `${ind} ` : `${ind} `;
19672
+ if (guard)
19673
+ lines.push(`${ind} if (${guard}) {`);
19674
+ for (const stmt of emitAttrUpdate("__t", a.attrName, "__x", a.meta)) {
19675
+ lines.push(`${writeIndent}${stmt}`);
19676
+ }
19677
+ if (guard)
19678
+ lines.push(`${ind} }`);
19679
+ lines.push(`${ind} __l[${a.ordinal}] = __x`);
19680
+ lines.push(`${ind}} }`);
19681
+ }
19682
+ function emitTextBinding(lines, ind, t, doorExpr, mode, rwDoor) {
19683
+ lines.push(`${ind}{ const __x = ${t.wrappedExpression}`);
19684
+ const writeOf = (valueExpr) => rwDoor ? `${doorExpr}.write('${t.slotId}', ${valueExpr})` : `${doorExpr}('${t.slotId}', ${valueExpr})`;
19685
+ if (mode === "outer") {
19686
+ lines.push(`${ind}if (__seed) {`);
19687
+ lines.push(`${ind} const __s = textOrNode(__x)`);
19688
+ lines.push(`${ind} if (${doorExpr}.read('${t.slotId}') !== __s) ${writeOf("__s")}`);
19689
+ lines.push(`${ind}} else if (${dedupGuard(t.ordinal)}) ${writeOf("textOrNode(__x)")}`);
19690
+ } else if (mode === "item") {
19691
+ lines.push(`${ind}if (${dedupGuard(t.ordinal)}) ${writeOf("textOrNode(__x)")}`);
19692
+ } else {
19693
+ lines.push(`${ind}${writeOf("textOrNode(__x)")}`);
19694
+ }
19695
+ lines.push(`${ind}__l[${t.ordinal}] = __x }`);
19696
+ }
19697
+ function seedDiffersExpr(target, a) {
19698
+ const html = toHtmlAttrName(a.attrName);
19699
+ if (a.attrName === "dangerouslySetInnerHTML" || html === "dangerouslySetInnerHTML")
19700
+ return "true";
19701
+ if (html === "style")
19702
+ return `${target}.getAttribute('style') !== styleToCss(__x)`;
19703
+ if (html === "class")
19704
+ return `${target}.getAttribute('class') !== (__x != null ? String(__x) : null)`;
19705
+ if (html === "value")
19706
+ return `${target}.value !== String(__x)`;
19707
+ if (isBooleanAttr(html))
19708
+ return `${target}.${html} !== !!(__x)`;
19709
+ if (a.meta.presenceOrUndefined) {
19710
+ const written = html.startsWith("aria-") ? "true" : "";
19711
+ return `${target}.getAttribute('${html}') !== (__x ? '${written}' : null)`;
19712
+ }
19713
+ return `${target}.getAttribute('${html}') !== (__x != null ? String(__x) : null)`;
19714
+ }
19715
+
19174
19716
  // src/ir-to-client-js/control-flow/stringify/loop.ts
19175
19717
  function emitLoopChildRefs(lines, refs, opts) {
19176
19718
  if (refs.length === 0)
@@ -19248,6 +19790,23 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
19248
19790
  stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr);
19249
19791
  return;
19250
19792
  }
19793
+ if (plan.lazyRow) {
19794
+ stringifyLazyRowLoop(lines, {
19795
+ indent: topIndent,
19796
+ containerVar,
19797
+ guardContainer: false,
19798
+ markerId,
19799
+ arrayExpr,
19800
+ keyFn,
19801
+ paramHead,
19802
+ indexParam,
19803
+ template,
19804
+ skeletonTemplate,
19805
+ skeletonPaths: plan.skeletonPaths,
19806
+ lazyRow: plan.lazyRow
19807
+ });
19808
+ return;
19809
+ }
19251
19810
  const hoistedTpl = !bodyIsMultiRoot && skeletonTemplate ? skeletonTemplate : null;
19252
19811
  const tplVar = `__tpl_${markerId.replace(/[^A-Za-z0-9_$]/g, "_")}`;
19253
19812
  if (hoistedTpl) {
@@ -19841,6 +20400,23 @@ function emitPlain(lines, plan) {
19841
20400
  stringifyEventDelegation(lines, eventDelegation);
19842
20401
  return;
19843
20402
  }
20403
+ if (plan.lazyRow) {
20404
+ stringifyLazyRowLoop(lines, {
20405
+ indent: " ",
20406
+ containerVar,
20407
+ guardContainer: true,
20408
+ markerId,
20409
+ arrayExpr,
20410
+ keyFn,
20411
+ paramHead,
20412
+ indexParam,
20413
+ template,
20414
+ lazyRow: plan.lazyRow
20415
+ });
20416
+ lines.push(` }))`);
20417
+ stringifyEventDelegation(lines, eventDelegation);
20418
+ return;
20419
+ }
19844
20420
  if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0 && preambleRegions.length === 0) {
19845
20421
  const cloneExpr = emitTemplateCloneInline(template);
19846
20422
  if (mapPreambleWrapped) {
@@ -20039,7 +20615,7 @@ function buildComponentLoopPlan(elem, profileComponentName) {
20039
20615
  // src/ir-to-client-js/control-flow/plan/build-loop.ts
20040
20616
  function buildLoopPlan(elem, opts) {
20041
20617
  if (elem.bodyIsItemConditional) {
20042
- return buildPlainLoopPlan(elem, opts.profileComponentName);
20618
+ return buildPlainLoopPlan(elem, opts.profileComponentName, opts.lazyScope);
20043
20619
  }
20044
20620
  if (elem.isStaticArray) {
20045
20621
  return buildStaticLoopPlan(elem, opts.unsafeLocalNames, opts.profileComponentName);
@@ -20051,9 +20627,9 @@ function buildLoopPlan(elem, opts) {
20051
20627
  if (elem.childComponent) {
20052
20628
  return buildComponentLoopPlan(elem, opts.profileComponentName);
20053
20629
  }
20054
- return buildPlainLoopPlan(elem, opts.profileComponentName);
20630
+ return buildPlainLoopPlan(elem, opts.profileComponentName, opts.lazyScope);
20055
20631
  }
20056
- function buildPlainLoopPlan(elem, profileComponentName) {
20632
+ function buildPlainLoopPlan(elem, profileComponentName, lazyScope) {
20057
20633
  const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings);
20058
20634
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
20059
20635
  const hasReactive2 = elem.bindings.reactiveAttrs.length > 0 || elem.bindings.reactiveTexts.length > 0 || elem.bindings.conditionals.length > 0;
@@ -20080,27 +20656,43 @@ function buildPlainLoopPlan(elem, profileComponentName) {
20080
20656
  preambleRegions: []
20081
20657
  };
20082
20658
  }
20659
+ const arrayExpr = buildChainedArrayExpr(elem);
20660
+ const indexParam = elem.index || "__idx";
20661
+ const mapPreambleWrapped = elem.preamble ? renderPreamble(elem.preamble, {
20662
+ transformJs: wrap,
20663
+ renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined, true)
20664
+ }) : "";
20665
+ const preambleRegions = buildPreambleRegionPlans(elem.preambleRegions, elem.param, elem.paramBindings);
20083
20666
  return {
20084
20667
  kind: "plain",
20085
20668
  rowConstruction: "string-template",
20086
20669
  containerVar: `_${varSlotId(elem.slotId)}`,
20087
20670
  markerId: elem.markerId,
20088
20671
  profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : undefined,
20089
- arrayExpr: buildChainedArrayExpr(elem),
20672
+ arrayExpr,
20090
20673
  keyFn: loopKeyFn(elem),
20091
20674
  paramHead,
20092
20675
  paramUnwrap,
20093
- indexParam: elem.index || "__idx",
20094
- mapPreambleWrapped: elem.preamble ? renderPreamble(elem.preamble, {
20095
- transformJs: wrap,
20096
- renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined, true)
20097
- }) : "",
20676
+ indexParam,
20677
+ lazyRow: buildLazyRowPlan({
20678
+ loop: elem,
20679
+ arrayExpr,
20680
+ indexParam,
20681
+ paramUnwrap,
20682
+ mapPreambleWrapped,
20683
+ preambleRegionCount: preambleRegions.length,
20684
+ callSite: "plain",
20685
+ flatMapLeafItem: false,
20686
+ anchored: elem.bodyIsItemConditional ?? false,
20687
+ scope: lazyScope
20688
+ }) ?? undefined,
20689
+ mapPreambleWrapped,
20098
20690
  template: elem.template,
20099
20691
  skeletonTemplate: elem.skeletonTemplate,
20100
20692
  skeletonPaths: elem.skeletonPaths,
20101
20693
  reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
20102
20694
  childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
20103
- preambleRegions: buildPreambleRegionPlans(elem.preambleRegions, elem.param, elem.paramBindings),
20695
+ preambleRegions,
20104
20696
  bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
20105
20697
  anchored: elem.bodyIsItemConditional ?? false,
20106
20698
  anchorKeyExpr: elem.key ? wrap(elem.key) : elem.index || "__idx"
@@ -20155,7 +20747,7 @@ function buildStaticLoopMaterialize(elem, unsafeLocalNames) {
20155
20747
  function emitConditionalUpdates(lines, ctx) {
20156
20748
  const profileComponentName = ctx.profile ? ctx.componentName : undefined;
20157
20749
  for (const elem of ctx.conditionalElements) {
20158
- const plan = buildInsertPlan(elem, { scope: { kind: "top" }, eventNameMode: "dom", profileComponentName });
20750
+ const plan = buildInsertPlan(elem, { scope: { kind: "top" }, eventNameMode: "dom", profileComponentName, lazyScope: buildLazyRowScopeInfo(ctx) });
20159
20751
  stringifyInsert(lines, plan, { leadingIndent: " ", bodyIndent: " " });
20160
20752
  lines.push("");
20161
20753
  }
@@ -20163,17 +20755,19 @@ function emitConditionalUpdates(lines, ctx) {
20163
20755
  function emitClientOnlyConditionals(lines, ctx) {
20164
20756
  const profileComponentName = ctx.profile ? ctx.componentName : undefined;
20165
20757
  for (const elem of ctx.clientOnlyConditionals) {
20166
- const plan = buildInsertPlan(elem, { scope: { kind: "top" }, eventNameMode: "raw", profileComponentName });
20758
+ const plan = buildInsertPlan(elem, { scope: { kind: "top" }, eventNameMode: "raw", profileComponentName, lazyScope: buildLazyRowScopeInfo(ctx) });
20167
20759
  lines.push(` // @client conditional: ${elem.slotId}`);
20168
20760
  stringifyInsert(lines, plan, { leadingIndent: " ", bodyIndent: " " });
20169
20761
  lines.push("");
20170
20762
  }
20171
20763
  }
20172
20764
  function emitLoopUpdates(lines, ctx, unsafeLocalNames) {
20765
+ const lazyScope = buildLazyRowScopeInfo(ctx);
20173
20766
  for (const elem of ctx.loopElements) {
20174
20767
  const plan = buildLoopPlan(elem, {
20175
20768
  unsafeLocalNames,
20176
- profileComponentName: ctx.profile ? ctx.componentName : undefined
20769
+ profileComponentName: ctx.profile ? ctx.componentName : undefined,
20770
+ lazyScope
20177
20771
  });
20178
20772
  internalInvariant(!(elem.preamble && elem.preamble.builderNames.length > 0 && plan.rowConstruction === "dom-ops"), `loop variant '${plan.kind}' declares dom-ops row construction but received a JSX-bearing preamble — add a Phase-1 refusal (or wire renderPreamble support) for this shape`);
20179
20773
  stringifyLoop(lines, plan);