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