@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.
- package/dist/index.js +620 -26
- package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts +6 -0
- package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts +2 -1
- package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-insert.d.ts +7 -0
- package/dist/ir-to-client-js/control-flow/plan/build-insert.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts +107 -0
- package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts.map +1 -0
- package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts +9 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/lazy-row-eligibility.d.ts +203 -0
- package/dist/ir-to-client-js/control-flow/plan/lazy-row-eligibility.d.ts.map +1 -0
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +10 -0
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts +98 -0
- package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts.map +1 -0
- package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts +5 -0
- package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow.d.ts.map +1 -1
- package/dist/ir-to-client-js/imports.d.ts +2 -2
- package/dist/ir-to-client-js/imports.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +162 -36
- package/src/__tests__/client-js-generation.test.ts +4 -1
- package/src/__tests__/composite-branch-loop.test.ts +12 -3
- package/src/__tests__/conditional-mapArray-key.test.ts +7 -1
- package/src/__tests__/create-selector.test.ts +21 -7
- package/src/__tests__/lazy-row-eligibility.test.ts +692 -0
- package/src/__tests__/loop-branch-bare-expression-reactive-text.test.ts +98 -0
- package/src/__tests__/loop-fallback-wrap.test.ts +31 -12
- package/src/__tests__/loop-hoisted-template.test.ts +10 -6
- package/src/__tests__/static-loop-csr-materialize.test.ts +6 -1
- package/src/ir-to-client-js/collect-elements.ts +48 -13
- package/src/ir-to-client-js/control-flow/plan/branch-loop.ts +6 -0
- package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +35 -12
- package/src/ir-to-client-js/control-flow/plan/build-insert.ts +9 -2
- package/src/ir-to-client-js/control-flow/plan/build-lazy-row.ts +305 -0
- package/src/ir-to-client-js/control-flow/plan/build-loop.ts +44 -12
- package/src/ir-to-client-js/control-flow/plan/lazy-row-eligibility.ts +440 -0
- package/src/ir-to-client-js/control-flow/plan/loop.ts +10 -0
- package/src/ir-to-client-js/control-flow/stringify/branch-loop.ts +22 -0
- package/src/ir-to-client-js/control-flow/stringify/lazy-row.ts +478 -0
- package/src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts +6 -1
- package/src/ir-to-client-js/control-flow/stringify/loop.ts +23 -0
- package/src/ir-to-client-js/control-flow.ts +7 -2
- package/src/ir-to-client-js/imports.ts +8 -2
- 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
|
|
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
|
|
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
|
|
18421
|
-
mapPreambleWrapped
|
|
18422
|
-
|
|
18423
|
-
|
|
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
|
|
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}
|
|
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
|
|
20672
|
+
arrayExpr,
|
|
20090
20673
|
keyFn: loopKeyFn(elem),
|
|
20091
20674
|
paramHead,
|
|
20092
20675
|
paramUnwrap,
|
|
20093
|
-
indexParam
|
|
20094
|
-
|
|
20095
|
-
|
|
20096
|
-
|
|
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
|
|
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);
|