@barefootjs/jsx 0.26.3 → 0.26.4
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 +433 -55
- package/dist/ir-to-client-js/build-references.d.ts.map +1 -1
- 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 +12 -1
- package/dist/ir-to-client-js/control-flow/shared.d.ts.map +1 -1
- 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/loop.d.ts +19 -0
- package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/html-template.d.ts +49 -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 +26 -1
- package/dist/ir-to-client-js/types.d.ts.map +1 -1
- package/dist/jsx-to-ir.d.ts.map +1 -1
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +187 -37
- package/src/__tests__/client-js-generation.test.ts +8 -2
- package/src/__tests__/compiler-runtime-contract.test.ts +4 -4
- package/src/__tests__/delegated-handler-preamble.test.ts +153 -0
- package/src/__tests__/flatmap-segments.test.ts +182 -0
- package/src/__tests__/map-body-no-silent-divergence.test.ts +45 -0
- package/src/__tests__/preamble-region-patch.test.ts +150 -0
- package/src/__tests__/static-loop-csr-materialize.test.ts +3 -2
- package/src/ir-to-client-js/build-references.ts +16 -0
- package/src/ir-to-client-js/collect-elements.ts +66 -10
- 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 +26 -1
- package/src/ir-to-client-js/control-flow/stringify/branch-loop.ts +25 -3
- package/src/ir-to-client-js/control-flow/stringify/event-delegation.ts +67 -19
- package/src/ir-to-client-js/control-flow/stringify/loop.ts +65 -1
- package/src/ir-to-client-js/html-template.ts +115 -6
- package/src/ir-to-client-js/imports.ts +1 -1
- package/src/ir-to-client-js/reactivity.ts +6 -0
- package/src/ir-to-client-js/types.ts +24 -0
- package/src/jsx-to-ir.ts +374 -8
- package/src/types.ts +49 -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":
|
|
@@ -3527,7 +3572,7 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
|
|
|
3527
3572
|
let mapExpr;
|
|
3528
3573
|
if (node.flatMapCallback) {
|
|
3529
3574
|
const body = renderPreamble(node.flatMapCallback, {
|
|
3530
|
-
renderLeaf: (ir) => irToHtmlTemplate(ir, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop)
|
|
3575
|
+
renderLeaf: (ir) => irToHtmlTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop)
|
|
3531
3576
|
});
|
|
3532
3577
|
mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
|
|
3533
3578
|
} else if (node.preamble) {
|
|
@@ -3817,7 +3862,7 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
|
|
|
3817
3862
|
let mapExpr;
|
|
3818
3863
|
if (node.flatMapCallback) {
|
|
3819
3864
|
const body = renderPreamble(node.flatMapCallback, {
|
|
3820
|
-
renderLeaf: (ir) => irToPlaceholderTemplate(ir, restSpreadNames, loopDepth + 1, loopParams)
|
|
3865
|
+
renderLeaf: (ir) => irToPlaceholderTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams)
|
|
3821
3866
|
});
|
|
3822
3867
|
mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
|
|
3823
3868
|
} else if (node.preamble) {
|
|
@@ -4443,7 +4488,7 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
4443
4488
|
const body = renderPreamble(node.flatMapCallback, {
|
|
4444
4489
|
textVariant: "template",
|
|
4445
4490
|
transformJs: (t) => applyPropsRewrite(t, propsObjectName ?? null),
|
|
4446
|
-
renderLeaf: (ir) => recurseInLoopBody(ir)
|
|
4491
|
+
renderLeaf: (ir) => recurseInLoopBody(stripLeafKeyAttr(ir))
|
|
4447
4492
|
});
|
|
4448
4493
|
mapExpr = `\${${iterArrayExpr}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
|
|
4449
4494
|
} else if (node.preamble) {
|
|
@@ -11710,6 +11755,87 @@ function checkLoopKey(callback, ctx, isNested) {
|
|
|
11710
11755
|
return;
|
|
11711
11756
|
}
|
|
11712
11757
|
}
|
|
11758
|
+
function flatMapProjectionCall(body) {
|
|
11759
|
+
let expr;
|
|
11760
|
+
if (ts11.isBlock(body)) {
|
|
11761
|
+
const real = body.statements;
|
|
11762
|
+
if (real.length !== 1 || !ts11.isReturnStatement(real[0]) || !real[0].expression)
|
|
11763
|
+
return null;
|
|
11764
|
+
expr = real[0].expression;
|
|
11765
|
+
} else {
|
|
11766
|
+
expr = body;
|
|
11767
|
+
}
|
|
11768
|
+
while (ts11.isParenthesizedExpression(expr))
|
|
11769
|
+
expr = expr.expression;
|
|
11770
|
+
if (!ts11.isCallExpression(expr))
|
|
11771
|
+
return null;
|
|
11772
|
+
if (!getMapLikeMethod(expr))
|
|
11773
|
+
return null;
|
|
11774
|
+
const cb = expr.arguments[0];
|
|
11775
|
+
if (!cb || !ts11.isArrowFunction(cb) && !ts11.isFunctionExpression(cb))
|
|
11776
|
+
return null;
|
|
11777
|
+
for (const p of cb.parameters) {
|
|
11778
|
+
if (!ts11.isIdentifier(p.name))
|
|
11779
|
+
return null;
|
|
11780
|
+
}
|
|
11781
|
+
let innerBody = cb.body;
|
|
11782
|
+
if (ts11.isBlock(innerBody)) {
|
|
11783
|
+
const ret = innerBody.statements.find((s) => ts11.isReturnStatement(s) && s.expression != null);
|
|
11784
|
+
if (innerBody.statements.length !== 1 || !ret?.expression)
|
|
11785
|
+
return null;
|
|
11786
|
+
innerBody = ret.expression;
|
|
11787
|
+
}
|
|
11788
|
+
while (ts11.isParenthesizedExpression(innerBody))
|
|
11789
|
+
innerBody = innerBody.expression;
|
|
11790
|
+
const isElementish = (n) => {
|
|
11791
|
+
let m = n;
|
|
11792
|
+
while (ts11.isParenthesizedExpression(m))
|
|
11793
|
+
m = m.expression;
|
|
11794
|
+
if (ts11.isJsxElement(m) || ts11.isJsxSelfClosingElement(m))
|
|
11795
|
+
return leafIsWirelessElement(m);
|
|
11796
|
+
if (ts11.isConditionalExpression(m))
|
|
11797
|
+
return isElementish(m.whenTrue) && isElementish(m.whenFalse);
|
|
11798
|
+
return false;
|
|
11799
|
+
};
|
|
11800
|
+
if (!isElementish(innerBody))
|
|
11801
|
+
return null;
|
|
11802
|
+
return expr;
|
|
11803
|
+
}
|
|
11804
|
+
function leafIsWirelessElement(el) {
|
|
11805
|
+
let ok = true;
|
|
11806
|
+
const visit2 = (n) => {
|
|
11807
|
+
if (!ok)
|
|
11808
|
+
return;
|
|
11809
|
+
if (ts11.isJsxOpeningElement(n) || ts11.isJsxSelfClosingElement(n)) {
|
|
11810
|
+
const tagNode = n.tagName;
|
|
11811
|
+
const isIntrinsic = ts11.isIdentifier(tagNode) ? !/^[A-Z]/.test(tagNode.text) : ts11.isJsxNamespacedName(tagNode);
|
|
11812
|
+
if (!isIntrinsic) {
|
|
11813
|
+
ok = false;
|
|
11814
|
+
return;
|
|
11815
|
+
}
|
|
11816
|
+
for (const attr of n.attributes.properties) {
|
|
11817
|
+
if (ts11.isJsxSpreadAttribute(attr)) {
|
|
11818
|
+
ok = false;
|
|
11819
|
+
return;
|
|
11820
|
+
}
|
|
11821
|
+
if (ts11.isJsxAttribute(attr)) {
|
|
11822
|
+
const name = attr.name.getText();
|
|
11823
|
+
if (/^on[A-Z]/.test(name)) {
|
|
11824
|
+
ok = false;
|
|
11825
|
+
return;
|
|
11826
|
+
}
|
|
11827
|
+
}
|
|
11828
|
+
}
|
|
11829
|
+
}
|
|
11830
|
+
if (ts11.isCallExpression(n) && getMapLikeMethod(n) && containsJsxInExpression(n)) {
|
|
11831
|
+
ok = false;
|
|
11832
|
+
return;
|
|
11833
|
+
}
|
|
11834
|
+
ts11.forEachChild(n, visit2);
|
|
11835
|
+
};
|
|
11836
|
+
visit2(el);
|
|
11837
|
+
return ok;
|
|
11838
|
+
}
|
|
11713
11839
|
function loopBodyIsMultiRoot(children) {
|
|
11714
11840
|
const real = children.filter((c) => !(c.type === "text" && typeof c.value === "string" && !c.value.trim()));
|
|
11715
11841
|
if (real.length === 0)
|
|
@@ -11755,6 +11881,7 @@ function extractItemConditionalKey(cond) {
|
|
|
11755
11881
|
}
|
|
11756
11882
|
function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
11757
11883
|
const isNested = ctx.loopParams.size > 0;
|
|
11884
|
+
const diagCountAtEntry = ctx.analyzer.errors.length;
|
|
11758
11885
|
const depth = ctx.loopDepth;
|
|
11759
11886
|
const propAccess = node.expression;
|
|
11760
11887
|
const mapSource = propAccess.expression;
|
|
@@ -12041,12 +12168,34 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
12041
12168
|
}
|
|
12042
12169
|
}
|
|
12043
12170
|
}
|
|
12044
|
-
if (method === "flatMap" && children.length === 0) {
|
|
12171
|
+
if (method === "flatMap" && children.length === 0 && !flatMapProjectionCall(body)) {
|
|
12045
12172
|
flatMapCallback = buildFlatMapCallback(callback, body, ctx);
|
|
12046
12173
|
}
|
|
12047
12174
|
} else {
|
|
12048
12175
|
tryTransformRenderableBody(body);
|
|
12049
12176
|
}
|
|
12177
|
+
if (method === "flatMap" && children.length === 0 && !flatMapCallback) {
|
|
12178
|
+
const projection = flatMapProjectionCall(body);
|
|
12179
|
+
if (projection) {
|
|
12180
|
+
const transformed = transformJsxExpression(projection, ctx, isClientOnly);
|
|
12181
|
+
if (transformed && transformed.type === "loop") {
|
|
12182
|
+
children = [transformed];
|
|
12183
|
+
}
|
|
12184
|
+
}
|
|
12185
|
+
}
|
|
12186
|
+
if (method === "flatMap" && children.length === 0 && !flatMapCallback && !ts11.isBlock(body)) {
|
|
12187
|
+
flatMapCallback = buildFlatMapCallback(callback, body, ctx);
|
|
12188
|
+
}
|
|
12189
|
+
if (flatMapCallback)
|
|
12190
|
+
preamble = undefined;
|
|
12191
|
+
if (flatMapCallback && !isClientOnly && !(ctx.analyzer.acceptsCallbackBody?.("flatMap") ?? false)) {
|
|
12192
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(body, ctx.sourceFile, ctx.filePath), {
|
|
12193
|
+
message: "A .flatMap() callback body with statements or a nested projection " + "cannot be lowered to a template on this backend.",
|
|
12194
|
+
suggestion: {
|
|
12195
|
+
message: "Add /* @client */ to render this loop on the client only"
|
|
12196
|
+
}
|
|
12197
|
+
}));
|
|
12198
|
+
}
|
|
12050
12199
|
if (paramBindings) {
|
|
12051
12200
|
for (const b of paramBindings)
|
|
12052
12201
|
ctx.loopParams.delete(b.name);
|
|
@@ -12058,6 +12207,16 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
12058
12207
|
ctx.loopDepth--;
|
|
12059
12208
|
}
|
|
12060
12209
|
if (children.length === 0 && !flatMapCallback) {
|
|
12210
|
+
const cb = node.arguments[0];
|
|
12211
|
+
const cbBody = cb && (ts11.isArrowFunction(cb) || ts11.isFunctionExpression(cb)) ? cb.body : undefined;
|
|
12212
|
+
if (cbBody && containsJsxInExpression(cbBody) && ctx.analyzer.errors.length === diagCountAtEntry) {
|
|
12213
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(cbBody, ctx.sourceFile, ctx.filePath), {
|
|
12214
|
+
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.",
|
|
12215
|
+
suggestion: {
|
|
12216
|
+
message: "Restructure the callback to return the JSX element directly " + "(or via a block body with a plain `return`)."
|
|
12217
|
+
}
|
|
12218
|
+
}));
|
|
12219
|
+
}
|
|
12061
12220
|
return null;
|
|
12062
12221
|
}
|
|
12063
12222
|
if (ts11.isArrowFunction(node.arguments[0]) && children.length > 0) {
|
|
@@ -12110,6 +12269,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
12110
12269
|
const hasCalls = exprHasFunctionCalls(arrayExpr);
|
|
12111
12270
|
const isDirectPropArray = method !== "flatMap" && isArrayExprDirectPropRef(arrayExpr, ctx);
|
|
12112
12271
|
const isStaticArray = !isSignalOrMemoArray(array, ctx) && !isDirectPropArray && !hasCalls && !objectIteration;
|
|
12272
|
+
const preambleRegions = preamble && !isStaticArray ? collectPreambleRegions(children, new Set(preamble.declaredNames), ctx) : undefined;
|
|
12113
12273
|
const nestedComponents = collectNestedComponents(children).filter((c) => c.name !== childComponent?.name);
|
|
12114
12274
|
return {
|
|
12115
12275
|
type: "loop",
|
|
@@ -12140,6 +12300,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
12140
12300
|
depth,
|
|
12141
12301
|
clientOnly: isClientOnly || undefined,
|
|
12142
12302
|
preamble,
|
|
12303
|
+
preambleRegions: preambleRegions && preambleRegions.length > 0 ? preambleRegions : undefined,
|
|
12143
12304
|
paramType,
|
|
12144
12305
|
indexType,
|
|
12145
12306
|
paramBindings,
|
|
@@ -12202,6 +12363,28 @@ function buildFlatMapCallback(callback, body, ctx) {
|
|
|
12202
12363
|
}));
|
|
12203
12364
|
return;
|
|
12204
12365
|
}
|
|
12366
|
+
for (const leafIr of leafIrs) {
|
|
12367
|
+
if (leafIr.type !== "element") {
|
|
12368
|
+
const loc = "loc" in leafIr && leafIr.loc ? leafIr.loc : getSourceLocation(body, ctx.sourceFile, ctx.filePath);
|
|
12369
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, loc, {
|
|
12370
|
+
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).",
|
|
12371
|
+
suggestion: {
|
|
12372
|
+
message: "Wrap the leaf content in a single keyed element."
|
|
12373
|
+
}
|
|
12374
|
+
}));
|
|
12375
|
+
return;
|
|
12376
|
+
}
|
|
12377
|
+
if (flatMapLeafNeedsWiring(leafIr)) {
|
|
12378
|
+
const loc = "loc" in leafIr && leafIr.loc ? leafIr.loc : getSourceLocation(body, ctx.sourceFile, ctx.filePath);
|
|
12379
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, loc, {
|
|
12380
|
+
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.",
|
|
12381
|
+
suggestion: {
|
|
12382
|
+
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."
|
|
12383
|
+
}
|
|
12384
|
+
}));
|
|
12385
|
+
return;
|
|
12386
|
+
}
|
|
12387
|
+
}
|
|
12205
12388
|
const pieces = reconstructAsSegments(body, ctx.sourceFile, ctx.analyzer.typeExcludeRanges, leafSpans);
|
|
12206
12389
|
const segments = pieces.map((piece) => {
|
|
12207
12390
|
if ("marker" in piece)
|
|
@@ -12216,6 +12399,25 @@ function buildFlatMapCallback(callback, body, ctx) {
|
|
|
12216
12399
|
rawBody: tsxSourceText(body.getText(ctx.sourceFile))
|
|
12217
12400
|
};
|
|
12218
12401
|
}
|
|
12402
|
+
function flatMapLeafNeedsWiring(ir) {
|
|
12403
|
+
switch (ir.type) {
|
|
12404
|
+
case "component":
|
|
12405
|
+
case "loop":
|
|
12406
|
+
return true;
|
|
12407
|
+
case "element":
|
|
12408
|
+
if (ir.events.length > 0)
|
|
12409
|
+
return true;
|
|
12410
|
+
if (ir.attrs.some((a) => a.name.startsWith("...")))
|
|
12411
|
+
return true;
|
|
12412
|
+
return ir.children.some(flatMapLeafNeedsWiring);
|
|
12413
|
+
case "conditional":
|
|
12414
|
+
return flatMapLeafNeedsWiring(ir.whenTrue) || (ir.whenFalse ? flatMapLeafNeedsWiring(ir.whenFalse) : false);
|
|
12415
|
+
case "fragment":
|
|
12416
|
+
return ir.children.some(flatMapLeafNeedsWiring);
|
|
12417
|
+
default:
|
|
12418
|
+
return false;
|
|
12419
|
+
}
|
|
12420
|
+
}
|
|
12219
12421
|
function preambleFragmentNeedsWiring(ir) {
|
|
12220
12422
|
switch (ir.type) {
|
|
12221
12423
|
case "component":
|
|
@@ -12258,6 +12460,40 @@ function flagArrayChildExpressions(nodes, declared) {
|
|
|
12258
12460
|
}
|
|
12259
12461
|
}
|
|
12260
12462
|
}
|
|
12463
|
+
function collectPreambleRegions(nodes, declared, ctx) {
|
|
12464
|
+
const regions = [];
|
|
12465
|
+
const visit2 = (list) => {
|
|
12466
|
+
for (const node of list) {
|
|
12467
|
+
switch (node.type) {
|
|
12468
|
+
case "expression": {
|
|
12469
|
+
const refs = extractFreeIdentifiersFromText(node.expr);
|
|
12470
|
+
const usesPreambleLocal = [...refs].some((r) => declared.has(r));
|
|
12471
|
+
if (usesPreambleLocal) {
|
|
12472
|
+
if (!node.slotId)
|
|
12473
|
+
node.slotId = generateSlotId(ctx);
|
|
12474
|
+
node.preambleRegion = true;
|
|
12475
|
+
node.reactive = true;
|
|
12476
|
+
regions.push({
|
|
12477
|
+
slotId: node.slotId,
|
|
12478
|
+
expr: node.expr,
|
|
12479
|
+
joinArrayChild: node.joinArrayChild || undefined
|
|
12480
|
+
});
|
|
12481
|
+
}
|
|
12482
|
+
break;
|
|
12483
|
+
}
|
|
12484
|
+
case "element":
|
|
12485
|
+
case "fragment":
|
|
12486
|
+
visit2(node.children);
|
|
12487
|
+
break;
|
|
12488
|
+
case "conditional":
|
|
12489
|
+
visit2([node.whenTrue, ...node.whenFalse ? [node.whenFalse] : []]);
|
|
12490
|
+
break;
|
|
12491
|
+
}
|
|
12492
|
+
}
|
|
12493
|
+
};
|
|
12494
|
+
visit2(nodes);
|
|
12495
|
+
return regions;
|
|
12496
|
+
}
|
|
12261
12497
|
function collectBindingNames(name, out) {
|
|
12262
12498
|
if (ts11.isIdentifier(name)) {
|
|
12263
12499
|
out.add(name.text);
|
|
@@ -13635,6 +13871,8 @@ function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings,
|
|
|
13635
13871
|
expression: ({ node: n, scope: insideConditional }) => {
|
|
13636
13872
|
if (!n.slotId)
|
|
13637
13873
|
return;
|
|
13874
|
+
if (n.preambleRegion)
|
|
13875
|
+
return;
|
|
13638
13876
|
const originFreeIds = freeIdsFromRefs(n.origin?.freeRefs);
|
|
13639
13877
|
const expanded = expandConstantForReactivity(n.expr, ctx, originFreeIds);
|
|
13640
13878
|
const reactive = classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind !== "none" || decideWrapFromAstFlags(n).wrap;
|
|
@@ -14190,10 +14428,13 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
14190
14428
|
loop: ({ node: l, scope: inCond }) => {
|
|
14191
14429
|
if (!l.slotId || inCond)
|
|
14192
14430
|
return;
|
|
14431
|
+
const projectionInner = l.method === "flatMap" && l.children.length === 1 && l.children[0].type === "loop" ? l.children[0] : undefined;
|
|
14193
14432
|
const childHandlers = [];
|
|
14194
|
-
const bindings = collectLoopChildBindings(l.children, ctx, siblingOffsets, l.param, l.paramBindings);
|
|
14195
|
-
|
|
14196
|
-
|
|
14433
|
+
const bindings = projectionInner ? emptyLoopChildBindings() : collectLoopChildBindings(l.children, ctx, siblingOffsets, l.param, l.paramBindings);
|
|
14434
|
+
if (!projectionInner) {
|
|
14435
|
+
for (const child of l.children) {
|
|
14436
|
+
childHandlers.push(...collectEventHandlersFromIR(child));
|
|
14437
|
+
}
|
|
14197
14438
|
}
|
|
14198
14439
|
if (l.childComponent) {
|
|
14199
14440
|
for (const prop of l.childComponent.props) {
|
|
@@ -14204,7 +14445,7 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
14204
14445
|
}
|
|
14205
14446
|
}
|
|
14206
14447
|
}
|
|
14207
|
-
const { useElementReconciliation, innerLoops } = decideLoopRendering(l, siblingOffsets, ctx);
|
|
14448
|
+
const { useElementReconciliation, innerLoops } = projectionInner ? { useElementReconciliation: false, innerLoops: undefined } : decideLoopRendering(l, siblingOffsets, ctx);
|
|
14208
14449
|
let template = "";
|
|
14209
14450
|
let staticItemTemplate;
|
|
14210
14451
|
let skeletonTemplate;
|
|
@@ -14214,7 +14455,7 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
14214
14455
|
if (l.isStaticArray && l.children[0]) {
|
|
14215
14456
|
staticItemTemplate = irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx), 0, undefined, undefined, true);
|
|
14216
14457
|
}
|
|
14217
|
-
} else if (l.children[0]) {
|
|
14458
|
+
} else if (l.children[0] && !projectionInner) {
|
|
14218
14459
|
const loopParamSpec = [{ param: l.param, bindings: l.paramBindings }];
|
|
14219
14460
|
template = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], buildRestSpreadNames(ctx), 0, loopParamSpec) : irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx), 0, loopParamSpec);
|
|
14220
14461
|
if (l.isStaticArray) {
|
|
@@ -14266,7 +14507,17 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
14266
14507
|
raw: l.sortComparator.raw
|
|
14267
14508
|
} : undefined,
|
|
14268
14509
|
chainOrder: l.chainOrder,
|
|
14269
|
-
preamble: l.preamble
|
|
14510
|
+
preamble: l.preamble,
|
|
14511
|
+
preambleRegions: l.preambleRegions,
|
|
14512
|
+
flatMapClient: projectionInner ? {
|
|
14513
|
+
params: l.index ? `(${l.param}, ${l.index})` : `(${l.param})`,
|
|
14514
|
+
body: renderFlatMapProjectionClientBody(projectionInner, buildRestSpreadNames(ctx)),
|
|
14515
|
+
keyed: projectionInner.key !== null
|
|
14516
|
+
} : l.flatMapCallback ? {
|
|
14517
|
+
params: l.flatMapCallback.params,
|
|
14518
|
+
body: renderFlatMapClientBody(l.flatMapCallback, buildRestSpreadNames(ctx)),
|
|
14519
|
+
keyed: flatMapCallbackHasKeyedLeaf(l.flatMapCallback)
|
|
14520
|
+
} : undefined
|
|
14270
14521
|
});
|
|
14271
14522
|
},
|
|
14272
14523
|
component: ({ node: c, descend, descendJsxChildren }) => {
|
|
@@ -14423,15 +14674,18 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
|
|
|
14423
14674
|
const containerSlot = parentSlotId ?? n.slotId;
|
|
14424
14675
|
if (!containerSlot)
|
|
14425
14676
|
return;
|
|
14426
|
-
const
|
|
14677
|
+
const projectionInner = n.method === "flatMap" && n.children.length === 1 && n.children[0].type === "loop" ? n.children[0] : undefined;
|
|
14678
|
+
const { useElementReconciliation, innerLoops: innerLoopsCollected } = projectionInner ? { useElementReconciliation: false, innerLoops: undefined } : decideLoopRendering(n, siblingOffsets, undefined);
|
|
14427
14679
|
let childTemplate;
|
|
14428
14680
|
const branchLoopParamSpec = [{ param: n.param, bindings: n.paramBindings }];
|
|
14429
|
-
if (
|
|
14681
|
+
if (projectionInner) {
|
|
14682
|
+
childTemplate = "";
|
|
14683
|
+
} else if (useElementReconciliation && n.children[0]) {
|
|
14430
14684
|
childTemplate = irToPlaceholderTemplate(n.children[0], restNames, 0, branchLoopParamSpec);
|
|
14431
14685
|
} else {
|
|
14432
14686
|
childTemplate = n.children.map((c) => irToHtmlTemplate(c, undefined, 0, branchLoopParamSpec)).join("");
|
|
14433
14687
|
}
|
|
14434
|
-
const branchBindings = ctx ? collectLoopChildBindings(n.children, ctx, siblingOffsets, n.param, n.paramBindings) : emptyLoopChildBindings();
|
|
14688
|
+
const branchBindings = ctx && !projectionInner ? collectLoopChildBindings(n.children, ctx, siblingOffsets, n.param, n.paramBindings) : emptyLoopChildBindings();
|
|
14435
14689
|
loops.push({
|
|
14436
14690
|
kind: "branch",
|
|
14437
14691
|
array: n.array,
|
|
@@ -14448,6 +14702,7 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
|
|
|
14448
14702
|
template: childTemplate,
|
|
14449
14703
|
containerSlotId: containerSlot,
|
|
14450
14704
|
preamble: n.preamble,
|
|
14705
|
+
preambleRegions: n.preambleRegions,
|
|
14451
14706
|
nestedComponents: useElementReconciliation ? n.nestedComponents : undefined,
|
|
14452
14707
|
bindings: branchBindings,
|
|
14453
14708
|
innerLoops: useElementReconciliation ? innerLoopsCollected : undefined,
|
|
@@ -14461,7 +14716,16 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
|
|
|
14461
14716
|
paramB: n.sortComparator.paramB,
|
|
14462
14717
|
raw: n.sortComparator.raw
|
|
14463
14718
|
} : undefined,
|
|
14464
|
-
chainOrder: n.chainOrder
|
|
14719
|
+
chainOrder: n.chainOrder,
|
|
14720
|
+
flatMapClient: projectionInner ? {
|
|
14721
|
+
params: n.index ? `(${n.param}, ${n.index})` : `(${n.param})`,
|
|
14722
|
+
body: renderFlatMapProjectionClientBody(projectionInner, restNames),
|
|
14723
|
+
keyed: projectionInner.key !== null
|
|
14724
|
+
} : n.flatMapCallback ? {
|
|
14725
|
+
params: n.flatMapCallback.params,
|
|
14726
|
+
body: renderFlatMapClientBody(n.flatMapCallback, restNames),
|
|
14727
|
+
keyed: flatMapCallbackHasKeyedLeaf(n.flatMapCallback)
|
|
14728
|
+
} : undefined
|
|
14465
14729
|
});
|
|
14466
14730
|
}
|
|
14467
14731
|
});
|
|
@@ -14836,6 +15100,13 @@ function buildReferencesGraph(ctx, irRoot) {
|
|
|
14836
15100
|
addExprEdges(ROOT_SOURCE, l.sortComparator.raw, "template-closure");
|
|
14837
15101
|
if (l.preamble)
|
|
14838
15102
|
addExprEdges(ROOT_SOURCE, preambleAnalysisText(l.preamble), "template-closure");
|
|
15103
|
+
if (l.flatMapCallback) {
|
|
15104
|
+
addExprEdges(ROOT_SOURCE, preambleAnalysisText(l.flatMapCallback), "template-closure");
|
|
15105
|
+
for (const seg of l.flatMapCallback.segments) {
|
|
15106
|
+
if (seg.kind === "jsx")
|
|
15107
|
+
walkIR(seg.ir, null, visitor);
|
|
15108
|
+
}
|
|
15109
|
+
}
|
|
14839
15110
|
descend();
|
|
14840
15111
|
if (l.childComponent)
|
|
14841
15112
|
walkChildComponent(l.childComponent);
|
|
@@ -15013,6 +15284,8 @@ var RUNTIME_IMPORT_CANDIDATES = [
|
|
|
15013
15284
|
"getLoopNodes",
|
|
15014
15285
|
"mapArray",
|
|
15015
15286
|
"mapArrayAnchored",
|
|
15287
|
+
"patchLeaf",
|
|
15288
|
+
"patchSlotRange",
|
|
15016
15289
|
"createDisposableEffect",
|
|
15017
15290
|
"createComponent",
|
|
15018
15291
|
"renderChild",
|
|
@@ -17015,6 +17288,15 @@ function destructureLoopParam(param, paramBindings) {
|
|
|
17015
17288
|
}
|
|
17016
17289
|
return { head: param, unwrap: "" };
|
|
17017
17290
|
}
|
|
17291
|
+
function buildPreambleRegionPlans(regions, loopParam, loopParamBindings) {
|
|
17292
|
+
if (!regions || regions.length === 0)
|
|
17293
|
+
return [];
|
|
17294
|
+
return regions.map((r) => {
|
|
17295
|
+
const wrapped = wrapLoopParamAsAccessor(r.expr, loopParam, loopParamBindings);
|
|
17296
|
+
const valueExpr = r.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : `escapeText(${wrapped})`;
|
|
17297
|
+
return { slotId: r.slotId, valueExpr };
|
|
17298
|
+
});
|
|
17299
|
+
}
|
|
17018
17300
|
function buildComponentPropsExpr2(comp, loopParam, loopParamBindings) {
|
|
17019
17301
|
const wrap = loopParam ? (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings) : (expr) => expr;
|
|
17020
17302
|
const entries = comp.props.map((p) => {
|
|
@@ -18029,8 +18311,9 @@ function buildDynamicLoopDelegationPlan(elem, profileComponentName) {
|
|
|
18029
18311
|
key: elem.key,
|
|
18030
18312
|
index: elem.index,
|
|
18031
18313
|
mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
|
|
18032
|
-
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1,
|
|
18033
|
-
}) : null
|
|
18314
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, undefined, undefined, true)
|
|
18315
|
+
}) : null,
|
|
18316
|
+
mapPreambleDeclaredNames: elem.preamble?.declaredNames ?? []
|
|
18034
18317
|
})
|
|
18035
18318
|
};
|
|
18036
18319
|
}
|
|
@@ -18047,8 +18330,9 @@ function buildBranchLoopDelegationPlan(loop, cv, profileComponentName) {
|
|
|
18047
18330
|
key: loop.key,
|
|
18048
18331
|
index: loop.index,
|
|
18049
18332
|
mapPreamble: loop.preamble ? renderPreamble(loop.preamble, {
|
|
18050
|
-
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1,
|
|
18051
|
-
}) : null
|
|
18333
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, undefined, undefined, true)
|
|
18334
|
+
}) : null,
|
|
18335
|
+
mapPreambleDeclaredNames: loop.preamble?.declaredNames ?? []
|
|
18052
18336
|
})
|
|
18053
18337
|
};
|
|
18054
18338
|
}
|
|
@@ -18063,8 +18347,9 @@ function buildStaticArrayDelegationPlan(elem, profileComponentName) {
|
|
|
18063
18347
|
arrayExpr: buildChainedArrayExpr(elem),
|
|
18064
18348
|
param: elem.param,
|
|
18065
18349
|
mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
|
|
18066
|
-
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1,
|
|
18350
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, undefined, undefined, true)
|
|
18067
18351
|
}) : null,
|
|
18352
|
+
mapPreambleDeclaredNames: elem.preamble?.declaredNames ?? [],
|
|
18068
18353
|
offset: elem.offset ?? null,
|
|
18069
18354
|
indexParam: elem.index ?? null
|
|
18070
18355
|
}
|
|
@@ -18081,6 +18366,7 @@ function buildKeyedOrIndexLookup(args) {
|
|
|
18081
18366
|
paramBindings: args.paramBindings,
|
|
18082
18367
|
keyWithItem,
|
|
18083
18368
|
mapPreamble: args.mapPreamble,
|
|
18369
|
+
mapPreambleDeclaredNames: args.mapPreambleDeclaredNames,
|
|
18084
18370
|
hasBindings,
|
|
18085
18371
|
indexParam: args.index
|
|
18086
18372
|
};
|
|
@@ -18090,6 +18376,7 @@ function buildKeyedOrIndexLookup(args) {
|
|
|
18090
18376
|
arrayExpr: args.array,
|
|
18091
18377
|
param: args.param,
|
|
18092
18378
|
mapPreamble: args.mapPreamble,
|
|
18379
|
+
mapPreambleDeclaredNames: args.mapPreambleDeclaredNames,
|
|
18093
18380
|
hasBindings,
|
|
18094
18381
|
indexParam: args.index
|
|
18095
18382
|
};
|
|
@@ -18111,14 +18398,16 @@ function buildBranchLoopPlan(loop, profileComponentName) {
|
|
|
18111
18398
|
}
|
|
18112
18399
|
const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(loop.param, loop.paramBindings);
|
|
18113
18400
|
const hasReactiveEffects = loop.bindings.reactiveAttrs.length > 0 || loop.bindings.reactiveTexts.length > 0 || loop.bindings.conditionals.length > 0;
|
|
18401
|
+
const fm = loop.flatMapClient;
|
|
18114
18402
|
const plan = {
|
|
18115
18403
|
kind: "plain",
|
|
18116
18404
|
rowConstruction: "string-template",
|
|
18117
18405
|
containerSlotId,
|
|
18118
18406
|
containerVar,
|
|
18119
18407
|
markerId: loop.markerId,
|
|
18120
|
-
|
|
18121
|
-
|
|
18408
|
+
flatMapLeafItem: fm ? true : undefined,
|
|
18409
|
+
arrayExpr: fm ? `(${buildChainedArrayExpr(loop)}).flatMap(${fm.params} => ${fm.body})` : buildChainedArrayExpr(loop),
|
|
18410
|
+
keyFn: fm ? fm.keyed ? "(__bfD, __bfI) => String(__bfD.k ?? __bfI)" : "null" : loopKeyFn(loop),
|
|
18122
18411
|
paramHead,
|
|
18123
18412
|
paramUnwrap,
|
|
18124
18413
|
indexParam: loop.index || "__idx",
|
|
@@ -18137,6 +18426,7 @@ function buildBranchLoopPlan(loop, profileComponentName) {
|
|
|
18137
18426
|
}) : null,
|
|
18138
18427
|
eventDelegation: buildBranchLoopDelegationPlan(loop, cv, profileComponentName),
|
|
18139
18428
|
childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings),
|
|
18429
|
+
preambleRegions: buildPreambleRegionPlans(loop.preambleRegions, loop.param, loop.paramBindings),
|
|
18140
18430
|
bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false,
|
|
18141
18431
|
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${containerSlotId}` : undefined
|
|
18142
18432
|
};
|
|
@@ -18784,6 +19074,22 @@ function emitLoopChildRefs(lines, refs, opts) {
|
|
|
18784
19074
|
lines.push(`${indent}if (${varName}) ${emitRefCall(ref.callback, varName)} }`);
|
|
18785
19075
|
}
|
|
18786
19076
|
}
|
|
19077
|
+
function emitPreambleRegionEffects(lines, regions, mapPreambleWrapped, opts) {
|
|
19078
|
+
if (regions.length === 0)
|
|
19079
|
+
return;
|
|
19080
|
+
const { indent, elVar } = opts;
|
|
19081
|
+
for (const region of regions) {
|
|
19082
|
+
const v = varSlotId(region.slotId);
|
|
19083
|
+
lines.push(`${indent}{ let __last_${v}`);
|
|
19084
|
+
lines.push(`${indent}createEffect(() => {`);
|
|
19085
|
+
if (mapPreambleWrapped)
|
|
19086
|
+
lines.push(`${indent} ${mapPreambleWrapped}`);
|
|
19087
|
+
lines.push(`${indent} const __html_${v} = ${region.valueExpr}`);
|
|
19088
|
+
lines.push(`${indent} if (__last_${v} === undefined) { __last_${v} = __html_${v}; return }`);
|
|
19089
|
+
lines.push(`${indent} if (__html_${v} !== __last_${v}) { __last_${v} = __html_${v}; patchSlotRange(${elVar}, '${region.slotId}', __html_${v}) }`);
|
|
19090
|
+
lines.push(`${indent}}) }`);
|
|
19091
|
+
}
|
|
19092
|
+
}
|
|
18787
19093
|
function stringifyLoop(lines, plan) {
|
|
18788
19094
|
switch (plan.kind) {
|
|
18789
19095
|
case "static":
|
|
@@ -18825,8 +19131,24 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
18825
19131
|
childRefs,
|
|
18826
19132
|
bodyIsMultiRoot,
|
|
18827
19133
|
anchored,
|
|
18828
|
-
anchorKeyExpr
|
|
19134
|
+
anchorKeyExpr,
|
|
19135
|
+
preambleRegions
|
|
18829
19136
|
} = plan;
|
|
19137
|
+
if (plan.flatMapLeafItem) {
|
|
19138
|
+
const loopBfIdArg = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
|
|
19139
|
+
lines.push(`${topIndent}mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (__bfD, ${indexParam}, __existing) => {`);
|
|
19140
|
+
lines.push(`${topIndent} let __el = __existing`);
|
|
19141
|
+
lines.push(`${topIndent} if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }`);
|
|
19142
|
+
lines.push(`${topIndent} let __last = __existing ? undefined : __bfD().h`);
|
|
19143
|
+
lines.push(`${topIndent} createEffect(() => {`);
|
|
19144
|
+
lines.push(`${topIndent} const __html = __bfD().h`);
|
|
19145
|
+
lines.push(`${topIndent} if (__last === undefined) { __last = __html; return }`);
|
|
19146
|
+
lines.push(`${topIndent} if (__html !== __last) { __last = __html; patchLeaf(__el, __html) }`);
|
|
19147
|
+
lines.push(`${topIndent} })`);
|
|
19148
|
+
lines.push(`${topIndent} return __el`);
|
|
19149
|
+
lines.push(`${topIndent}}, '${markerId}'${loopBfIdArg})`);
|
|
19150
|
+
return;
|
|
19151
|
+
}
|
|
18830
19152
|
if (anchored) {
|
|
18831
19153
|
stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr);
|
|
18832
19154
|
return;
|
|
@@ -18837,7 +19159,7 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
18837
19159
|
emitHoistedTemplateDecl(lines, topIndent, tplVar, hoistedTpl);
|
|
18838
19160
|
}
|
|
18839
19161
|
const loopBfId = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
|
|
18840
|
-
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
|
|
19162
|
+
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0 && preambleRegions.length === 0) {
|
|
18841
19163
|
const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
|
|
18842
19164
|
const preamble = mapPreambleWrapped ? `${mapPreambleWrapped}; ` : "";
|
|
18843
19165
|
const cloneExpr = hoistedTpl ? `return ${hoistedCloneExpr(tplVar, hoistedTpl)}` : emitTemplateCloneInline(template);
|
|
@@ -18885,6 +19207,7 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
18885
19207
|
});
|
|
18886
19208
|
}
|
|
18887
19209
|
emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot, elementIndexBySlot: pathPlan?.elementIndexBySlot });
|
|
19210
|
+
emitPreambleRegionEffects(lines, preambleRegions, mapPreambleWrapped, { indent: bodyIndent, elVar: "__el" });
|
|
18888
19211
|
lines.push(`${bodyIndent}return __el`);
|
|
18889
19212
|
lines.push(`${topIndent}}, '${markerId}'${loopBfId})`);
|
|
18890
19213
|
}
|
|
@@ -19195,6 +19518,12 @@ function indexBindingLine(handler, indexParam, indexExpr) {
|
|
|
19195
19518
|
return null;
|
|
19196
19519
|
return `const ${indexParam} = ${indexExpr}`;
|
|
19197
19520
|
}
|
|
19521
|
+
function preambleLineForHandler(mapPreamble, declaredNames, handler) {
|
|
19522
|
+
if (!mapPreamble || declaredNames.length === 0)
|
|
19523
|
+
return null;
|
|
19524
|
+
const free = extractFreeIdentifiersFromText(handler);
|
|
19525
|
+
return declaredNames.some((name) => free.has(name)) ? mapPreamble : null;
|
|
19526
|
+
}
|
|
19198
19527
|
function stringifyEventDelegation(lines, plan) {
|
|
19199
19528
|
const { containerVar, events, itemLookup, profileComponentName } = plan;
|
|
19200
19529
|
const eventsByName = new Map;
|
|
@@ -19240,7 +19569,8 @@ function stringifyEventDelegation(lines, plan) {
|
|
|
19240
19569
|
}
|
|
19241
19570
|
}
|
|
19242
19571
|
function emitKeyedLookup(ls, ev, handlerCall, lookup) {
|
|
19243
|
-
const { arrayExpr, param, keyWithItem, mapPreamble, hasBindings, indexParam } = lookup;
|
|
19572
|
+
const { arrayExpr, param, keyWithItem, mapPreamble, mapPreambleDeclaredNames, hasBindings, indexParam } = lookup;
|
|
19573
|
+
const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
|
|
19244
19574
|
if (ev.nestedLoops.length === 0) {
|
|
19245
19575
|
const idxLine2 = indexBindingLine(ev.handler, indexParam, `${arrayExpr}.findIndex(item => String(${keyWithItem}) === key)`);
|
|
19246
19576
|
ls.push(` const li = ${varSlotId(ev.childSlotId)}El.closest('[${DATA_KEY}]')`);
|
|
@@ -19250,20 +19580,21 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
|
|
|
19250
19580
|
ls.push(` const __bfLoopItem = ${arrayExpr}.find(item => String(${keyWithItem}) === key)`);
|
|
19251
19581
|
ls.push(` if (__bfLoopItem) {`);
|
|
19252
19582
|
ls.push(` const ${param} = __bfLoopItem`);
|
|
19253
|
-
if (
|
|
19254
|
-
ls.push(` ${
|
|
19583
|
+
if (preambleLine)
|
|
19584
|
+
ls.push(` ${preambleLine}`);
|
|
19255
19585
|
if (idxLine2)
|
|
19256
19586
|
ls.push(` ${idxLine2}`);
|
|
19257
|
-
ls.push(`
|
|
19587
|
+
ls.push(` ;${handlerCall}`);
|
|
19258
19588
|
ls.push(` }`);
|
|
19259
19589
|
} else {
|
|
19260
19590
|
ls.push(` const ${param} = ${arrayExpr}.find(item => String(${keyWithItem}) === key)`);
|
|
19261
|
-
if (
|
|
19262
|
-
|
|
19591
|
+
ls.push(` if (${param}) {`);
|
|
19592
|
+
if (preambleLine)
|
|
19593
|
+
ls.push(` ${preambleLine}`);
|
|
19263
19594
|
if (idxLine2)
|
|
19264
|
-
ls.push(`
|
|
19265
|
-
|
|
19266
|
-
|
|
19595
|
+
ls.push(` ${idxLine2}`);
|
|
19596
|
+
ls.push(` ;${handlerCall}`);
|
|
19597
|
+
ls.push(` }`);
|
|
19267
19598
|
}
|
|
19268
19599
|
ls.push(` }`);
|
|
19269
19600
|
return;
|
|
@@ -19290,16 +19621,18 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
|
|
|
19290
19621
|
}
|
|
19291
19622
|
const outerGuard = hasBindings ? "__bfLoopItem" : param;
|
|
19292
19623
|
const allParams = [outerGuard, ...ev.nestedLoops.map((n) => n.param)];
|
|
19293
|
-
if (mapPreamble)
|
|
19294
|
-
ls.push(` ${mapPreamble}`);
|
|
19295
19624
|
const idxLine = indexBindingLine(ev.handler, indexParam, `${arrayExpr}.findIndex(item => String(${keyWithItem}) === outerKey)`);
|
|
19625
|
+
ls.push(` if (${allParams.join(" && ")}) {`);
|
|
19626
|
+
if (preambleLine)
|
|
19627
|
+
ls.push(` ${preambleLine}`);
|
|
19296
19628
|
if (idxLine)
|
|
19297
|
-
ls.push(`
|
|
19298
|
-
|
|
19299
|
-
|
|
19629
|
+
ls.push(` ${idxLine}`);
|
|
19630
|
+
ls.push(` ;${handlerCall}`);
|
|
19631
|
+
ls.push(` }`);
|
|
19300
19632
|
}
|
|
19301
19633
|
function emitDynamicIndexLookup(ls, ev, handlerCall, lookup) {
|
|
19302
|
-
const { arrayExpr, param, mapPreamble, hasBindings, indexParam } = lookup;
|
|
19634
|
+
const { arrayExpr, param, mapPreamble, mapPreambleDeclaredNames, hasBindings, indexParam } = lookup;
|
|
19635
|
+
const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
|
|
19303
19636
|
const idxLine = indexBindingLine(ev.handler, indexParam, "idx");
|
|
19304
19637
|
ls.push(` const li = ${varSlotId(ev.childSlotId)}El.closest('li, [bf-i]')`);
|
|
19305
19638
|
ls.push(` if (li && li.parentElement) {`);
|
|
@@ -19308,25 +19641,27 @@ function emitDynamicIndexLookup(ls, ev, handlerCall, lookup) {
|
|
|
19308
19641
|
ls.push(` const __bfLoopItem = ${arrayExpr}[idx]`);
|
|
19309
19642
|
ls.push(` if (__bfLoopItem) {`);
|
|
19310
19643
|
ls.push(` const ${param} = __bfLoopItem`);
|
|
19311
|
-
if (
|
|
19312
|
-
ls.push(` ${
|
|
19644
|
+
if (preambleLine)
|
|
19645
|
+
ls.push(` ${preambleLine}`);
|
|
19313
19646
|
if (idxLine)
|
|
19314
19647
|
ls.push(` ${idxLine}`);
|
|
19315
|
-
ls.push(`
|
|
19648
|
+
ls.push(` ;${handlerCall}`);
|
|
19316
19649
|
ls.push(` }`);
|
|
19317
19650
|
} else {
|
|
19318
19651
|
ls.push(` const ${param} = ${arrayExpr}[idx]`);
|
|
19319
|
-
if (
|
|
19320
|
-
|
|
19652
|
+
ls.push(` if (${param}) {`);
|
|
19653
|
+
if (preambleLine)
|
|
19654
|
+
ls.push(` ${preambleLine}`);
|
|
19321
19655
|
if (idxLine)
|
|
19322
|
-
ls.push(`
|
|
19323
|
-
|
|
19324
|
-
|
|
19656
|
+
ls.push(` ${idxLine}`);
|
|
19657
|
+
ls.push(` ;${handlerCall}`);
|
|
19658
|
+
ls.push(` }`);
|
|
19325
19659
|
}
|
|
19326
19660
|
ls.push(` }`);
|
|
19327
19661
|
}
|
|
19328
19662
|
function emitStaticIndexLookup(ls, ev, handlerCall, lookup, containerVar) {
|
|
19329
|
-
const { arrayExpr, param, mapPreamble, offset, indexParam } = lookup;
|
|
19663
|
+
const { arrayExpr, param, mapPreamble, mapPreambleDeclaredNames, offset, indexParam } = lookup;
|
|
19664
|
+
const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
|
|
19330
19665
|
const idxLine = indexBindingLine(ev.handler, indexParam, "__idx");
|
|
19331
19666
|
ls.push(` let __el = ${varSlotId(ev.childSlotId)}El`);
|
|
19332
19667
|
ls.push(` while (__el.parentElement && __el.parentElement !== ${containerVar}) __el = __el.parentElement`);
|
|
@@ -19334,12 +19669,13 @@ function emitStaticIndexLookup(ls, ev, handlerCall, lookup, containerVar) {
|
|
|
19334
19669
|
const idxOffset = buildLoopChildIndexSubtraction(offset ?? undefined);
|
|
19335
19670
|
ls.push(` const __idx = Array.from(${containerVar}.children).indexOf(__el)${idxOffset}`);
|
|
19336
19671
|
ls.push(` const ${param} = ${arrayExpr}[__idx]`);
|
|
19337
|
-
if (
|
|
19338
|
-
|
|
19672
|
+
ls.push(` if (${param}) {`);
|
|
19673
|
+
if (preambleLine)
|
|
19674
|
+
ls.push(` ${preambleLine}`);
|
|
19339
19675
|
if (idxLine)
|
|
19340
|
-
ls.push(`
|
|
19341
|
-
|
|
19342
|
-
|
|
19676
|
+
ls.push(` ${idxLine}`);
|
|
19677
|
+
ls.push(` ;${handlerCall}`);
|
|
19678
|
+
ls.push(` }`);
|
|
19343
19679
|
ls.push(` }`);
|
|
19344
19680
|
}
|
|
19345
19681
|
|
|
@@ -19372,12 +19708,29 @@ function emitPlain(lines, plan) {
|
|
|
19372
19708
|
eventDelegation,
|
|
19373
19709
|
childRefs,
|
|
19374
19710
|
bodyIsMultiRoot,
|
|
19375
|
-
profileLoopId
|
|
19711
|
+
profileLoopId,
|
|
19712
|
+
preambleRegions
|
|
19376
19713
|
} = plan;
|
|
19377
19714
|
const loopBfId = profileLoopId ? `, ${JSON.stringify(profileLoopId)}` : "";
|
|
19378
19715
|
const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
|
|
19379
19716
|
lines.push(` __disposers.push(createDisposableEffect(() => {`);
|
|
19380
|
-
if (
|
|
19717
|
+
if (plan.flatMapLeafItem) {
|
|
19718
|
+
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (__bfD, ${indexParam}, __existing) => {`);
|
|
19719
|
+
lines.push(` let __el = __existing`);
|
|
19720
|
+
lines.push(` if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }`);
|
|
19721
|
+
lines.push(` let __last = __existing ? undefined : __bfD().h`);
|
|
19722
|
+
lines.push(` createEffect(() => {`);
|
|
19723
|
+
lines.push(` const __html = __bfD().h`);
|
|
19724
|
+
lines.push(` if (__last === undefined) { __last = __html; return }`);
|
|
19725
|
+
lines.push(` if (__html !== __last) { __last = __html; patchLeaf(__el, __html) }`);
|
|
19726
|
+
lines.push(` })`);
|
|
19727
|
+
lines.push(` return __el`);
|
|
19728
|
+
lines.push(` }, '${markerId}'${loopBfId})`);
|
|
19729
|
+
lines.push(` }))`);
|
|
19730
|
+
stringifyEventDelegation(lines, eventDelegation);
|
|
19731
|
+
return;
|
|
19732
|
+
}
|
|
19733
|
+
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0 && preambleRegions.length === 0) {
|
|
19381
19734
|
const cloneExpr = emitTemplateCloneInline(template);
|
|
19382
19735
|
if (mapPreambleWrapped) {
|
|
19383
19736
|
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${mapPreambleWrapped}; ${cloneExpr} }, '${markerId}'${loopBfId})`);
|
|
@@ -19402,6 +19755,7 @@ function emitPlain(lines, plan) {
|
|
|
19402
19755
|
stringifyReactiveEffects(lines, reactiveEffects, { indent: " ", elVar: "__el", bodyIsMultiRoot });
|
|
19403
19756
|
}
|
|
19404
19757
|
emitLoopChildRefs(lines, childRefs, { indent: " ", elVar: "__el", bodyIsMultiRoot });
|
|
19758
|
+
emitPreambleRegionEffects(lines, preambleRegions, mapPreambleWrapped, { indent: " ", elVar: "__el" });
|
|
19405
19759
|
lines.push(` return __el`);
|
|
19406
19760
|
lines.push(` }, '${markerId}'${loopBfId})`);
|
|
19407
19761
|
}
|
|
@@ -19587,6 +19941,29 @@ function buildPlainLoopPlan(elem, profileComponentName) {
|
|
|
19587
19941
|
const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings);
|
|
19588
19942
|
const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
|
|
19589
19943
|
const hasReactive2 = elem.bindings.reactiveAttrs.length > 0 || elem.bindings.reactiveTexts.length > 0 || elem.bindings.conditionals.length > 0;
|
|
19944
|
+
if (elem.flatMapClient) {
|
|
19945
|
+
return {
|
|
19946
|
+
kind: "plain",
|
|
19947
|
+
rowConstruction: "string-template",
|
|
19948
|
+
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
19949
|
+
markerId: elem.markerId,
|
|
19950
|
+
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : undefined,
|
|
19951
|
+
arrayExpr: `(${buildChainedArrayExpr(elem)}).flatMap(${elem.flatMapClient.params} => ${elem.flatMapClient.body})`,
|
|
19952
|
+
keyFn: elem.flatMapClient.keyed ? "(__bfD, __bfI) => String(__bfD.k ?? __bfI)" : "null",
|
|
19953
|
+
paramHead: "__bfD",
|
|
19954
|
+
paramUnwrap: "",
|
|
19955
|
+
indexParam: "__idx",
|
|
19956
|
+
mapPreambleWrapped: "",
|
|
19957
|
+
template: "",
|
|
19958
|
+
reactiveEffects: null,
|
|
19959
|
+
childRefs: [],
|
|
19960
|
+
bodyIsMultiRoot: false,
|
|
19961
|
+
anchored: false,
|
|
19962
|
+
anchorKeyExpr: "__idx",
|
|
19963
|
+
flatMapLeafItem: true,
|
|
19964
|
+
preambleRegions: []
|
|
19965
|
+
};
|
|
19966
|
+
}
|
|
19590
19967
|
return {
|
|
19591
19968
|
kind: "plain",
|
|
19592
19969
|
rowConstruction: "string-template",
|
|
@@ -19607,6 +19984,7 @@ function buildPlainLoopPlan(elem, profileComponentName) {
|
|
|
19607
19984
|
skeletonPaths: elem.skeletonPaths,
|
|
19608
19985
|
reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
|
|
19609
19986
|
childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
|
|
19987
|
+
preambleRegions: buildPreambleRegionPlans(elem.preambleRegions, elem.param, elem.paramBindings),
|
|
19610
19988
|
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
|
|
19611
19989
|
anchored: elem.bodyIsItemConditional ?? false,
|
|
19612
19990
|
anchorKeyExpr: elem.key ? wrap(elem.key) : elem.index || "__idx"
|