@barefootjs/cli 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/docs/core/rendering/jsx-compatibility.md +18 -1
- package/dist/index.js +441 -45
- package/package.json +4 -4
|
@@ -52,6 +52,21 @@ return <div>...</div>
|
|
|
52
52
|
))}
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
`.flatMap()` expands nested collections into a flat run of keyed elements. A pure projection body — the nested `.map()` as the whole body, expression or single-`return` block — compiles on every adapter; a body with statements before the projection (early returns, `const`s) runs as JS on JS-runtime adapters and needs [`/* @client */`](./client-directive.md) on DSL backends:
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
// ✅ Projection — works on every adapter
|
|
59
|
+
{todos().flatMap(todo => todo.tags.map(tag => (
|
|
60
|
+
<li key={`${todo.id}:${tag}`}>{tag}</li>
|
|
61
|
+
)))}
|
|
62
|
+
|
|
63
|
+
// ✅ Statement body — JS-runtime adapters; /* @client */ on Go/Mojo etc.
|
|
64
|
+
{todos().flatMap(todo => {
|
|
65
|
+
if (todo.hidden) return []
|
|
66
|
+
return todo.tags.map(tag => <li key={`${todo.id}:${tag}`}>{tag}</li>)
|
|
67
|
+
})}
|
|
68
|
+
```
|
|
69
|
+
|
|
55
70
|
`.sort()` and `.toSorted()` can be chained with `.map()` and `.filter()`:
|
|
56
71
|
|
|
57
72
|
```tsx
|
|
@@ -105,7 +120,9 @@ Some JavaScript expressions cannot be translated into marked template syntax. Wh
|
|
|
105
120
|
|---|---|---|
|
|
106
121
|
| `.filter()` with destructured param (`({done}) => done`) | works (runs as JS) | **BF101** |
|
|
107
122
|
| `.filter()` with `function` keyword callback | works | **BF101** |
|
|
108
|
-
| `.reduce()`, `.forEach()`, `.flatMap()` | works | **BF101** |
|
|
123
|
+
| `.reduce()`, `.forEach()`, value-returning `.flatMap()` with an off-catalogue projection | works | **BF101** |
|
|
124
|
+
| JSX-returning `.flatMap()` projection (`items.flatMap(it => it.tags.map(tag => <li key={...}/>))`) | works | works |
|
|
125
|
+
| JSX-returning `.flatMap()` with a statement body (early `return`, `const` before the projection) | works (runs as JS) | **BF021** |
|
|
109
126
|
| Nested `.filter()` / `.map()` in a filter predicate (`x => x.tags.filter(...).length > 0`) | works | works |
|
|
110
127
|
| Nested `.some()` / `.find()` / `.reduce()` in a filter predicate | works | **BF101** |
|
|
111
128
|
| Sort comparator that's a multi-statement block body or `localeCompare(b, locale, opts)` | works (runs as JS) | **BF021** |
|
package/dist/index.js
CHANGED
|
@@ -3695,7 +3695,7 @@ function templateAttrExpr(attrName, valExpr, presenceOrUndefined) {
|
|
|
3695
3695
|
return `\${((v) => v != null ? 'style="' + ${escapeAttrValueExpr("v")} + '"' : '')(styleToCss(${valExpr}))}`;
|
|
3696
3696
|
}
|
|
3697
3697
|
if (attrName === "data-key" || attrName.startsWith("data-key-")) {
|
|
3698
|
-
return `${attrName}="\${${valExpr}}"`;
|
|
3698
|
+
return `${attrName}="\${${escapeAttrValueExpr(valExpr)}}"`;
|
|
3699
3699
|
}
|
|
3700
3700
|
return `\${(${valExpr}) != null ? '${attrName}="' + ${escapeAttrValueExpr(valExpr)} + '"' : ''}`;
|
|
3701
3701
|
}
|
|
@@ -3815,12 +3815,54 @@ function renderPreamble(preamble, opts) {
|
|
|
3815
3815
|
if (seg.kind === "js") {
|
|
3816
3816
|
const text = opts.textVariant === "template" ? seg.templateText ?? seg.text : seg.text;
|
|
3817
3817
|
out += opts.transformJs ? opts.transformJs(text) : text;
|
|
3818
|
+
} else if (opts.rawLeaf) {
|
|
3819
|
+
out += opts.renderLeaf(escapeLeafTextExpressions(seg.ir));
|
|
3818
3820
|
} else {
|
|
3819
3821
|
out += "`" + opts.renderLeaf(escapeLeafTextExpressions(seg.ir)) + "`";
|
|
3820
3822
|
}
|
|
3821
3823
|
}
|
|
3822
3824
|
return out;
|
|
3823
3825
|
}
|
|
3826
|
+
function flatMapLeafKeyExpr(ir) {
|
|
3827
|
+
if (ir.type !== "element") return null;
|
|
3828
|
+
const keyAttr = ir.attrs.find((a) => a.name === "key");
|
|
3829
|
+
if (!keyAttr) return null;
|
|
3830
|
+
switch (keyAttr.value.kind) {
|
|
3831
|
+
case "expression":
|
|
3832
|
+
return `(${keyAttr.value.expr})`;
|
|
3833
|
+
case "literal":
|
|
3834
|
+
return JSON.stringify(keyAttr.value.value);
|
|
3835
|
+
case "template":
|
|
3836
|
+
return attrValueToString(keyAttr.value);
|
|
3837
|
+
default:
|
|
3838
|
+
return null;
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
function stripLeafKeyAttr(ir) {
|
|
3842
|
+
if (ir.type !== "element") return ir;
|
|
3843
|
+
return { ...ir, attrs: ir.attrs.filter((a) => a.name !== "key") };
|
|
3844
|
+
}
|
|
3845
|
+
function renderFlatMapClientBody(cb, restSpreadNames) {
|
|
3846
|
+
return renderPreamble(cb, {
|
|
3847
|
+
textVariant: "client",
|
|
3848
|
+
rawLeaf: true,
|
|
3849
|
+
renderLeaf: (ir) => {
|
|
3850
|
+
const key = flatMapLeafKeyExpr(ir);
|
|
3851
|
+
const html = irToHtmlTemplate(stripLeafKeyAttr(ir), restSpreadNames, 1, void 0, void 0, true);
|
|
3852
|
+
return `({ k: ${key ?? "undefined"}, h: \`${html}\` })`;
|
|
3853
|
+
}
|
|
3854
|
+
});
|
|
3855
|
+
}
|
|
3856
|
+
function flatMapCallbackHasKeyedLeaf(cb) {
|
|
3857
|
+
return cb.segments.some((s) => s.kind === "jsx" && flatMapLeafKeyExpr(s.ir) !== null);
|
|
3858
|
+
}
|
|
3859
|
+
function renderFlatMapProjectionClientBody(inner, restSpreadNames) {
|
|
3860
|
+
const chained = applyLoopChain(inner);
|
|
3861
|
+
const params = inner.index ? `(${inner.param}, ${inner.index})` : `(${inner.param})`;
|
|
3862
|
+
const key = inner.key ? `(${inner.key})` : "undefined";
|
|
3863
|
+
const html = inner.children.map((c) => irToHtmlTemplate(escapeLeafTextExpressions(c), restSpreadNames, 1, void 0, void 0, true)).join("");
|
|
3864
|
+
return `${chained}.map(${params} => ({ k: ${key}, h: \`${html}\` }))`;
|
|
3865
|
+
}
|
|
3824
3866
|
function escapeLeafTextExpressions(ir) {
|
|
3825
3867
|
switch (ir.type) {
|
|
3826
3868
|
case "element":
|
|
@@ -3949,7 +3991,7 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
|
|
|
3949
3991
|
let mapExpr;
|
|
3950
3992
|
if (node.flatMapCallback) {
|
|
3951
3993
|
const body2 = renderPreamble(node.flatMapCallback, {
|
|
3952
|
-
renderLeaf: (ir) => irToHtmlTemplate(ir, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop)
|
|
3994
|
+
renderLeaf: (ir) => irToHtmlTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop)
|
|
3953
3995
|
});
|
|
3954
3996
|
mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body2}).join('')}`;
|
|
3955
3997
|
} else if (node.preamble) {
|
|
@@ -4200,7 +4242,8 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
|
|
|
4200
4242
|
let mapExpr;
|
|
4201
4243
|
if (node.flatMapCallback) {
|
|
4202
4244
|
const body2 = renderPreamble(node.flatMapCallback, {
|
|
4203
|
-
|
|
4245
|
+
// Leaf `key` stripped — see the irToHtmlTemplate site above.
|
|
4246
|
+
renderLeaf: (ir) => irToPlaceholderTemplate(stripLeafKeyAttr(ir), restSpreadNames, loopDepth + 1, loopParams)
|
|
4204
4247
|
});
|
|
4205
4248
|
mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body2}).join('')}`;
|
|
4206
4249
|
} else if (node.preamble) {
|
|
@@ -4799,7 +4842,8 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
4799
4842
|
const body2 = renderPreamble(node.flatMapCallback, {
|
|
4800
4843
|
textVariant: "template",
|
|
4801
4844
|
transformJs: (t) => applyPropsRewrite(t, propsObjectName ?? null),
|
|
4802
|
-
|
|
4845
|
+
// Leaf `key` stripped — see the irToHtmlTemplate site above.
|
|
4846
|
+
renderLeaf: (ir) => recurseInLoopBody(stripLeafKeyAttr(ir))
|
|
4803
4847
|
});
|
|
4804
4848
|
mapExpr = `\${${iterArrayExpr}.flatMap(${node.flatMapCallback.params} => ${body2}).join('')}`;
|
|
4805
4849
|
} else if (node.preamble) {
|
|
@@ -11596,6 +11640,76 @@ function checkLoopKey(callback, ctx2, isNested) {
|
|
|
11596
11640
|
return;
|
|
11597
11641
|
}
|
|
11598
11642
|
}
|
|
11643
|
+
function flatMapProjectionCall(body2) {
|
|
11644
|
+
let expr;
|
|
11645
|
+
if (ts11.isBlock(body2)) {
|
|
11646
|
+
const real = body2.statements;
|
|
11647
|
+
if (real.length !== 1 || !ts11.isReturnStatement(real[0]) || !real[0].expression) return null;
|
|
11648
|
+
expr = real[0].expression;
|
|
11649
|
+
} else {
|
|
11650
|
+
expr = body2;
|
|
11651
|
+
}
|
|
11652
|
+
while (ts11.isParenthesizedExpression(expr)) expr = expr.expression;
|
|
11653
|
+
if (!ts11.isCallExpression(expr)) return null;
|
|
11654
|
+
if (!getMapLikeMethod(expr)) return null;
|
|
11655
|
+
const cb = expr.arguments[0];
|
|
11656
|
+
if (!cb || !ts11.isArrowFunction(cb) && !ts11.isFunctionExpression(cb)) return null;
|
|
11657
|
+
for (const p of cb.parameters) {
|
|
11658
|
+
if (!ts11.isIdentifier(p.name)) return null;
|
|
11659
|
+
}
|
|
11660
|
+
let innerBody = cb.body;
|
|
11661
|
+
if (ts11.isBlock(innerBody)) {
|
|
11662
|
+
const ret = innerBody.statements.find(
|
|
11663
|
+
(s) => ts11.isReturnStatement(s) && s.expression != null
|
|
11664
|
+
);
|
|
11665
|
+
if (innerBody.statements.length !== 1 || !ret?.expression) return null;
|
|
11666
|
+
innerBody = ret.expression;
|
|
11667
|
+
}
|
|
11668
|
+
while (ts11.isParenthesizedExpression(innerBody)) innerBody = innerBody.expression;
|
|
11669
|
+
const isElementish = (n) => {
|
|
11670
|
+
let m = n;
|
|
11671
|
+
while (ts11.isParenthesizedExpression(m)) m = m.expression;
|
|
11672
|
+
if (ts11.isJsxElement(m) || ts11.isJsxSelfClosingElement(m)) return leafIsWirelessElement(m);
|
|
11673
|
+
if (ts11.isConditionalExpression(m)) return isElementish(m.whenTrue) && isElementish(m.whenFalse);
|
|
11674
|
+
return false;
|
|
11675
|
+
};
|
|
11676
|
+
if (!isElementish(innerBody)) return null;
|
|
11677
|
+
return expr;
|
|
11678
|
+
}
|
|
11679
|
+
function leafIsWirelessElement(el) {
|
|
11680
|
+
let ok = true;
|
|
11681
|
+
const visit3 = (n) => {
|
|
11682
|
+
if (!ok) return;
|
|
11683
|
+
if (ts11.isJsxOpeningElement(n) || ts11.isJsxSelfClosingElement(n)) {
|
|
11684
|
+
const tagNode = n.tagName;
|
|
11685
|
+
const isIntrinsic = ts11.isIdentifier(tagNode) ? !/^[A-Z]/.test(tagNode.text) : ts11.isJsxNamespacedName(tagNode);
|
|
11686
|
+
if (!isIntrinsic) {
|
|
11687
|
+
ok = false;
|
|
11688
|
+
return;
|
|
11689
|
+
}
|
|
11690
|
+
for (const attr of n.attributes.properties) {
|
|
11691
|
+
if (ts11.isJsxSpreadAttribute(attr)) {
|
|
11692
|
+
ok = false;
|
|
11693
|
+
return;
|
|
11694
|
+
}
|
|
11695
|
+
if (ts11.isJsxAttribute(attr)) {
|
|
11696
|
+
const name2 = attr.name.getText();
|
|
11697
|
+
if (/^on[A-Z]/.test(name2)) {
|
|
11698
|
+
ok = false;
|
|
11699
|
+
return;
|
|
11700
|
+
}
|
|
11701
|
+
}
|
|
11702
|
+
}
|
|
11703
|
+
}
|
|
11704
|
+
if (ts11.isCallExpression(n) && getMapLikeMethod(n) && containsJsxInExpression(n)) {
|
|
11705
|
+
ok = false;
|
|
11706
|
+
return;
|
|
11707
|
+
}
|
|
11708
|
+
ts11.forEachChild(n, visit3);
|
|
11709
|
+
};
|
|
11710
|
+
visit3(el);
|
|
11711
|
+
return ok;
|
|
11712
|
+
}
|
|
11599
11713
|
function loopBodyIsMultiRoot(children2) {
|
|
11600
11714
|
const real = children2.filter(
|
|
11601
11715
|
(c) => !(c.type === "text" && typeof c.value === "string" && !c.value.trim())
|
|
@@ -11641,6 +11755,7 @@ function extractItemConditionalKey(cond) {
|
|
|
11641
11755
|
}
|
|
11642
11756
|
function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
|
|
11643
11757
|
const isNested = ctx2.loopParams.size > 0;
|
|
11758
|
+
const diagCountAtEntry = ctx2.analyzer.errors.length;
|
|
11644
11759
|
const depth = ctx2.loopDepth;
|
|
11645
11760
|
const propAccess = node.expression;
|
|
11646
11761
|
const mapSource = propAccess.expression;
|
|
@@ -11978,12 +12093,39 @@ function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
|
|
|
11978
12093
|
}
|
|
11979
12094
|
}
|
|
11980
12095
|
}
|
|
11981
|
-
if (method2 === "flatMap" && children2.length === 0) {
|
|
12096
|
+
if (method2 === "flatMap" && children2.length === 0 && !flatMapProjectionCall(body2)) {
|
|
11982
12097
|
flatMapCallback = buildFlatMapCallback(callback, body2, ctx2);
|
|
11983
12098
|
}
|
|
11984
12099
|
} else {
|
|
11985
12100
|
tryTransformRenderableBody(body2);
|
|
11986
12101
|
}
|
|
12102
|
+
if (method2 === "flatMap" && children2.length === 0 && !flatMapCallback) {
|
|
12103
|
+
const projection = flatMapProjectionCall(body2);
|
|
12104
|
+
if (projection) {
|
|
12105
|
+
const transformed = transformJsxExpression(projection, ctx2, isClientOnly);
|
|
12106
|
+
if (transformed && transformed.type === "loop") {
|
|
12107
|
+
children2 = [transformed];
|
|
12108
|
+
}
|
|
12109
|
+
}
|
|
12110
|
+
}
|
|
12111
|
+
if (method2 === "flatMap" && children2.length === 0 && !flatMapCallback && !ts11.isBlock(body2)) {
|
|
12112
|
+
flatMapCallback = buildFlatMapCallback(callback, body2, ctx2);
|
|
12113
|
+
}
|
|
12114
|
+
if (flatMapCallback) preamble = void 0;
|
|
12115
|
+
if (flatMapCallback && !isClientOnly && !(ctx2.analyzer.acceptsCallbackBody?.("flatMap") ?? false)) {
|
|
12116
|
+
ctx2.analyzer.errors.push(
|
|
12117
|
+
createError(
|
|
12118
|
+
ErrorCodes.UNSUPPORTED_JSX_PATTERN,
|
|
12119
|
+
getSourceLocation(body2, ctx2.sourceFile, ctx2.filePath),
|
|
12120
|
+
{
|
|
12121
|
+
message: "A .flatMap() callback body with statements or a nested projection cannot be lowered to a template on this backend.",
|
|
12122
|
+
suggestion: {
|
|
12123
|
+
message: "Add /* @client */ to render this loop on the client only"
|
|
12124
|
+
}
|
|
12125
|
+
}
|
|
12126
|
+
)
|
|
12127
|
+
);
|
|
12128
|
+
}
|
|
11987
12129
|
if (paramBindings) {
|
|
11988
12130
|
for (const b of paramBindings) ctx2.loopParams.delete(b.name);
|
|
11989
12131
|
} else {
|
|
@@ -11993,6 +12135,22 @@ function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
|
|
|
11993
12135
|
ctx2.loopDepth--;
|
|
11994
12136
|
}
|
|
11995
12137
|
if (children2.length === 0 && !flatMapCallback) {
|
|
12138
|
+
const cb = node.arguments[0];
|
|
12139
|
+
const cbBody = cb && (ts11.isArrowFunction(cb) || ts11.isFunctionExpression(cb)) ? cb.body : void 0;
|
|
12140
|
+
if (cbBody && containsJsxInExpression(cbBody) && ctx2.analyzer.errors.length === diagCountAtEntry) {
|
|
12141
|
+
ctx2.analyzer.errors.push(
|
|
12142
|
+
createError(
|
|
12143
|
+
ErrorCodes.UNSUPPORTED_JSX_PATTERN,
|
|
12144
|
+
getSourceLocation(cbBody, ctx2.sourceFile, ctx2.filePath),
|
|
12145
|
+
{
|
|
12146
|
+
message: `A .${method2}() callback that builds JSX in this shape cannot be compiled \u2014 the JSX would leak verbatim into the client bundle. Recognized bodies: a JSX element/fragment, a ternary or && / || / ?? expression, an array literal (flatMap), or a block body whose return the compiler can lower.`,
|
|
12147
|
+
suggestion: {
|
|
12148
|
+
message: "Restructure the callback to return the JSX element directly (or via a block body with a plain `return`)."
|
|
12149
|
+
}
|
|
12150
|
+
}
|
|
12151
|
+
)
|
|
12152
|
+
);
|
|
12153
|
+
}
|
|
11996
12154
|
return null;
|
|
11997
12155
|
}
|
|
11998
12156
|
if (ts11.isArrowFunction(node.arguments[0]) && children2.length > 0) {
|
|
@@ -12049,6 +12207,7 @@ function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
|
|
|
12049
12207
|
const hasCalls = exprHasFunctionCalls(arrayExpr);
|
|
12050
12208
|
const isDirectPropArray = method2 !== "flatMap" && isArrayExprDirectPropRef(arrayExpr, ctx2);
|
|
12051
12209
|
const isStaticArray = !isSignalOrMemoArray(array, ctx2) && !isDirectPropArray && !hasCalls && !objectIteration;
|
|
12210
|
+
const preambleRegions = preamble && !isStaticArray ? collectPreambleRegions(children2, new Set(preamble.declaredNames), ctx2) : void 0;
|
|
12052
12211
|
const nestedComponents = collectNestedComponents(children2).filter((c) => c.name !== childComponent?.name);
|
|
12053
12212
|
return {
|
|
12054
12213
|
type: "loop",
|
|
@@ -12086,6 +12245,7 @@ function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
|
|
|
12086
12245
|
depth,
|
|
12087
12246
|
clientOnly: isClientOnly || void 0,
|
|
12088
12247
|
preamble,
|
|
12248
|
+
preambleRegions: preambleRegions && preambleRegions.length > 0 ? preambleRegions : void 0,
|
|
12089
12249
|
paramType,
|
|
12090
12250
|
indexType,
|
|
12091
12251
|
paramBindings,
|
|
@@ -12146,6 +12306,40 @@ function buildFlatMapCallback(callback, body2, ctx2) {
|
|
|
12146
12306
|
);
|
|
12147
12307
|
return void 0;
|
|
12148
12308
|
}
|
|
12309
|
+
for (const leafIr of leafIrs) {
|
|
12310
|
+
if (leafIr.type !== "element") {
|
|
12311
|
+
const loc = "loc" in leafIr && leafIr.loc ? leafIr.loc : getSourceLocation(body2, ctx2.sourceFile, ctx2.filePath);
|
|
12312
|
+
ctx2.analyzer.errors.push(
|
|
12313
|
+
createError(
|
|
12314
|
+
ErrorCodes.UNSUPPORTED_JSX_PATTERN,
|
|
12315
|
+
loc,
|
|
12316
|
+
{
|
|
12317
|
+
message: "A JSX leaf produced by a .flatMap() callback must be a single element \u2014 a fragment or non-element root cannot ride the keyed descriptor path (each leaf hydrates and patches as one element).",
|
|
12318
|
+
suggestion: {
|
|
12319
|
+
message: "Wrap the leaf content in a single keyed element."
|
|
12320
|
+
}
|
|
12321
|
+
}
|
|
12322
|
+
)
|
|
12323
|
+
);
|
|
12324
|
+
return void 0;
|
|
12325
|
+
}
|
|
12326
|
+
if (flatMapLeafNeedsWiring(leafIr)) {
|
|
12327
|
+
const loc = "loc" in leafIr && leafIr.loc ? leafIr.loc : getSourceLocation(body2, ctx2.sourceFile, ctx2.filePath);
|
|
12328
|
+
ctx2.analyzer.errors.push(
|
|
12329
|
+
createError(
|
|
12330
|
+
ErrorCodes.UNSUPPORTED_JSX_PATTERN,
|
|
12331
|
+
loc,
|
|
12332
|
+
{
|
|
12333
|
+
message: "A JSX element produced by a .flatMap() callback cannot carry event handlers, components, nested loops, or spreads \u2014 the leaf renders as a keyed HTML string with no per-element wiring.",
|
|
12334
|
+
suggestion: {
|
|
12335
|
+
message: "Restructure so the interactive element lives in a .map() body \u2014 the descriptor path has no per-element wiring on any backend, so /* @client */ does not lift this."
|
|
12336
|
+
}
|
|
12337
|
+
}
|
|
12338
|
+
)
|
|
12339
|
+
);
|
|
12340
|
+
return void 0;
|
|
12341
|
+
}
|
|
12342
|
+
}
|
|
12149
12343
|
const pieces = reconstructAsSegments(body2, ctx2.sourceFile, ctx2.analyzer.typeExcludeRanges, leafSpans);
|
|
12150
12344
|
const segments = pieces.map((piece) => {
|
|
12151
12345
|
if ("marker" in piece) return { kind: "jsx", ir: leafIrs[piece.marker] };
|
|
@@ -12159,6 +12353,23 @@ function buildFlatMapCallback(callback, body2, ctx2) {
|
|
|
12159
12353
|
rawBody: tsxSourceText(body2.getText(ctx2.sourceFile))
|
|
12160
12354
|
};
|
|
12161
12355
|
}
|
|
12356
|
+
function flatMapLeafNeedsWiring(ir) {
|
|
12357
|
+
switch (ir.type) {
|
|
12358
|
+
case "component":
|
|
12359
|
+
case "loop":
|
|
12360
|
+
return true;
|
|
12361
|
+
case "element":
|
|
12362
|
+
if (ir.events.length > 0) return true;
|
|
12363
|
+
if (ir.attrs.some((a) => a.name.startsWith("..."))) return true;
|
|
12364
|
+
return ir.children.some(flatMapLeafNeedsWiring);
|
|
12365
|
+
case "conditional":
|
|
12366
|
+
return flatMapLeafNeedsWiring(ir.whenTrue) || (ir.whenFalse ? flatMapLeafNeedsWiring(ir.whenFalse) : false);
|
|
12367
|
+
case "fragment":
|
|
12368
|
+
return ir.children.some(flatMapLeafNeedsWiring);
|
|
12369
|
+
default:
|
|
12370
|
+
return false;
|
|
12371
|
+
}
|
|
12372
|
+
}
|
|
12162
12373
|
function preambleFragmentNeedsWiring(ir) {
|
|
12163
12374
|
switch (ir.type) {
|
|
12164
12375
|
case "component":
|
|
@@ -12202,6 +12413,39 @@ function flagArrayChildExpressions(nodes, declared) {
|
|
|
12202
12413
|
}
|
|
12203
12414
|
}
|
|
12204
12415
|
}
|
|
12416
|
+
function collectPreambleRegions(nodes, declared, ctx2) {
|
|
12417
|
+
const regions = [];
|
|
12418
|
+
const visit3 = (list) => {
|
|
12419
|
+
for (const node of list) {
|
|
12420
|
+
switch (node.type) {
|
|
12421
|
+
case "expression": {
|
|
12422
|
+
const refs = extractFreeIdentifiersFromText(node.expr);
|
|
12423
|
+
const usesPreambleLocal = [...refs].some((r2) => declared.has(r2));
|
|
12424
|
+
if (usesPreambleLocal) {
|
|
12425
|
+
if (!node.slotId) node.slotId = generateSlotId(ctx2);
|
|
12426
|
+
node.preambleRegion = true;
|
|
12427
|
+
node.reactive = true;
|
|
12428
|
+
regions.push({
|
|
12429
|
+
slotId: node.slotId,
|
|
12430
|
+
expr: node.expr,
|
|
12431
|
+
joinArrayChild: node.joinArrayChild || void 0
|
|
12432
|
+
});
|
|
12433
|
+
}
|
|
12434
|
+
break;
|
|
12435
|
+
}
|
|
12436
|
+
case "element":
|
|
12437
|
+
case "fragment":
|
|
12438
|
+
visit3(node.children);
|
|
12439
|
+
break;
|
|
12440
|
+
case "conditional":
|
|
12441
|
+
visit3([node.whenTrue, ...node.whenFalse ? [node.whenFalse] : []]);
|
|
12442
|
+
break;
|
|
12443
|
+
}
|
|
12444
|
+
}
|
|
12445
|
+
};
|
|
12446
|
+
visit3(nodes);
|
|
12447
|
+
return regions;
|
|
12448
|
+
}
|
|
12205
12449
|
function collectBindingNames(name2, out) {
|
|
12206
12450
|
if (ts11.isIdentifier(name2)) {
|
|
12207
12451
|
out.add(name2.text);
|
|
@@ -13570,6 +13814,7 @@ function collectLoopChildReactiveTexts(node, ctx2, loopParam, loopParamBindings,
|
|
|
13570
13814
|
...stopAt("loop", "async", "ifStatement"),
|
|
13571
13815
|
expression: ({ node: n, scope: insideConditional }) => {
|
|
13572
13816
|
if (!n.slotId) return;
|
|
13817
|
+
if (n.preambleRegion) return;
|
|
13573
13818
|
const originFreeIds = freeIdsFromRefs(n.origin?.freeRefs);
|
|
13574
13819
|
const expanded = expandConstantForReactivity(n.expr, ctx2, originFreeIds);
|
|
13575
13820
|
const reactive = classifyReactivity(expanded.expr, ctx2, loopParam, loopParamBindings, expanded.freeIds).kind !== "none" || decideWrapFromAstFlags(n).wrap;
|
|
@@ -14102,10 +14347,13 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
|
|
|
14102
14347
|
},
|
|
14103
14348
|
loop: ({ node: l, scope: inCond }) => {
|
|
14104
14349
|
if (!l.slotId || inCond) return;
|
|
14350
|
+
const projectionInner = l.method === "flatMap" && l.children.length === 1 && l.children[0].type === "loop" ? l.children[0] : void 0;
|
|
14105
14351
|
const childHandlers = [];
|
|
14106
|
-
const bindings = collectLoopChildBindings(l.children, ctx2, siblingOffsets, l.param, l.paramBindings);
|
|
14107
|
-
|
|
14108
|
-
|
|
14352
|
+
const bindings = projectionInner ? emptyLoopChildBindings() : collectLoopChildBindings(l.children, ctx2, siblingOffsets, l.param, l.paramBindings);
|
|
14353
|
+
if (!projectionInner) {
|
|
14354
|
+
for (const child of l.children) {
|
|
14355
|
+
childHandlers.push(...collectEventHandlersFromIR(child));
|
|
14356
|
+
}
|
|
14109
14357
|
}
|
|
14110
14358
|
if (l.childComponent) {
|
|
14111
14359
|
for (const prop of l.childComponent.props) {
|
|
@@ -14115,7 +14363,7 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
|
|
|
14115
14363
|
}
|
|
14116
14364
|
}
|
|
14117
14365
|
}
|
|
14118
|
-
const { useElementReconciliation, innerLoops } = decideLoopRendering(l, siblingOffsets, ctx2);
|
|
14366
|
+
const { useElementReconciliation, innerLoops } = projectionInner ? { useElementReconciliation: false, innerLoops: void 0 } : decideLoopRendering(l, siblingOffsets, ctx2);
|
|
14119
14367
|
let template = "";
|
|
14120
14368
|
let staticItemTemplate;
|
|
14121
14369
|
let skeletonTemplate;
|
|
@@ -14133,7 +14381,7 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
|
|
|
14133
14381
|
true
|
|
14134
14382
|
);
|
|
14135
14383
|
}
|
|
14136
|
-
} else if (l.children[0]) {
|
|
14384
|
+
} else if (l.children[0] && !projectionInner) {
|
|
14137
14385
|
const loopParamSpec = [{ param: l.param, bindings: l.paramBindings }];
|
|
14138
14386
|
template = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], buildRestSpreadNames(ctx2), 0, loopParamSpec) : irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx2), 0, loopParamSpec);
|
|
14139
14387
|
if (l.isStaticArray) {
|
|
@@ -14185,7 +14433,17 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
|
|
|
14185
14433
|
raw: l.sortComparator.raw
|
|
14186
14434
|
} : void 0,
|
|
14187
14435
|
chainOrder: l.chainOrder,
|
|
14188
|
-
preamble: l.preamble
|
|
14436
|
+
preamble: l.preamble,
|
|
14437
|
+
preambleRegions: l.preambleRegions,
|
|
14438
|
+
flatMapClient: projectionInner ? {
|
|
14439
|
+
params: l.index ? `(${l.param}, ${l.index})` : `(${l.param})`,
|
|
14440
|
+
body: renderFlatMapProjectionClientBody(projectionInner, buildRestSpreadNames(ctx2)),
|
|
14441
|
+
keyed: projectionInner.key !== null
|
|
14442
|
+
} : l.flatMapCallback ? {
|
|
14443
|
+
params: l.flatMapCallback.params,
|
|
14444
|
+
body: renderFlatMapClientBody(l.flatMapCallback, buildRestSpreadNames(ctx2)),
|
|
14445
|
+
keyed: flatMapCallbackHasKeyedLeaf(l.flatMapCallback)
|
|
14446
|
+
} : void 0
|
|
14189
14447
|
});
|
|
14190
14448
|
},
|
|
14191
14449
|
component: ({ node: c, descend, descendJsxChildren }) => {
|
|
@@ -14340,15 +14598,18 @@ function collectBranchLoops(node, ctx2, siblingOffsets) {
|
|
|
14340
14598
|
loop: ({ node: n, scope: parentSlotId }) => {
|
|
14341
14599
|
const containerSlot = parentSlotId ?? n.slotId;
|
|
14342
14600
|
if (!containerSlot) return;
|
|
14343
|
-
const
|
|
14601
|
+
const projectionInner = n.method === "flatMap" && n.children.length === 1 && n.children[0].type === "loop" ? n.children[0] : void 0;
|
|
14602
|
+
const { useElementReconciliation, innerLoops: innerLoopsCollected } = projectionInner ? { useElementReconciliation: false, innerLoops: void 0 } : decideLoopRendering(n, siblingOffsets, void 0);
|
|
14344
14603
|
let childTemplate;
|
|
14345
14604
|
const branchLoopParamSpec = [{ param: n.param, bindings: n.paramBindings }];
|
|
14346
|
-
if (
|
|
14605
|
+
if (projectionInner) {
|
|
14606
|
+
childTemplate = "";
|
|
14607
|
+
} else if (useElementReconciliation && n.children[0]) {
|
|
14347
14608
|
childTemplate = irToPlaceholderTemplate(n.children[0], restNames, 0, branchLoopParamSpec);
|
|
14348
14609
|
} else {
|
|
14349
14610
|
childTemplate = n.children.map((c) => irToHtmlTemplate(c, void 0, 0, branchLoopParamSpec)).join("");
|
|
14350
14611
|
}
|
|
14351
|
-
const branchBindings = ctx2 ? collectLoopChildBindings(n.children, ctx2, siblingOffsets, n.param, n.paramBindings) : emptyLoopChildBindings();
|
|
14612
|
+
const branchBindings = ctx2 && !projectionInner ? collectLoopChildBindings(n.children, ctx2, siblingOffsets, n.param, n.paramBindings) : emptyLoopChildBindings();
|
|
14352
14613
|
loops.push({
|
|
14353
14614
|
kind: "branch",
|
|
14354
14615
|
array: n.array,
|
|
@@ -14365,6 +14626,7 @@ function collectBranchLoops(node, ctx2, siblingOffsets) {
|
|
|
14365
14626
|
template: childTemplate,
|
|
14366
14627
|
containerSlotId: containerSlot,
|
|
14367
14628
|
preamble: n.preamble,
|
|
14629
|
+
preambleRegions: n.preambleRegions,
|
|
14368
14630
|
nestedComponents: useElementReconciliation ? n.nestedComponents : void 0,
|
|
14369
14631
|
bindings: branchBindings,
|
|
14370
14632
|
innerLoops: useElementReconciliation ? innerLoopsCollected : void 0,
|
|
@@ -14378,7 +14640,16 @@ function collectBranchLoops(node, ctx2, siblingOffsets) {
|
|
|
14378
14640
|
paramB: n.sortComparator.paramB,
|
|
14379
14641
|
raw: n.sortComparator.raw
|
|
14380
14642
|
} : void 0,
|
|
14381
|
-
chainOrder: n.chainOrder
|
|
14643
|
+
chainOrder: n.chainOrder,
|
|
14644
|
+
flatMapClient: projectionInner ? {
|
|
14645
|
+
params: n.index ? `(${n.param}, ${n.index})` : `(${n.param})`,
|
|
14646
|
+
body: renderFlatMapProjectionClientBody(projectionInner, restNames),
|
|
14647
|
+
keyed: projectionInner.key !== null
|
|
14648
|
+
} : n.flatMapCallback ? {
|
|
14649
|
+
params: n.flatMapCallback.params,
|
|
14650
|
+
body: renderFlatMapClientBody(n.flatMapCallback, restNames),
|
|
14651
|
+
keyed: flatMapCallbackHasKeyedLeaf(n.flatMapCallback)
|
|
14652
|
+
} : void 0
|
|
14382
14653
|
});
|
|
14383
14654
|
}
|
|
14384
14655
|
});
|
|
@@ -14774,6 +15045,12 @@ function buildReferencesGraph(ctx2, irRoot) {
|
|
|
14774
15045
|
if (l.filterPredicate) addExprEdges(ROOT_SOURCE, l.filterPredicate.raw, "template-closure");
|
|
14775
15046
|
if (l.sortComparator) addExprEdges(ROOT_SOURCE, l.sortComparator.raw, "template-closure");
|
|
14776
15047
|
if (l.preamble) addExprEdges(ROOT_SOURCE, preambleAnalysisText(l.preamble), "template-closure");
|
|
15048
|
+
if (l.flatMapCallback) {
|
|
15049
|
+
addExprEdges(ROOT_SOURCE, preambleAnalysisText(l.flatMapCallback), "template-closure");
|
|
15050
|
+
for (const seg of l.flatMapCallback.segments) {
|
|
15051
|
+
if (seg.kind === "jsx") walkIR(seg.ir, null, visitor);
|
|
15052
|
+
}
|
|
15053
|
+
}
|
|
14777
15054
|
descend();
|
|
14778
15055
|
if (l.childComponent) walkChildComponent(l.childComponent);
|
|
14779
15056
|
if (l.nestedComponents) {
|
|
@@ -15055,6 +15332,8 @@ var init_imports = __esm({
|
|
|
15055
15332
|
"getLoopNodes",
|
|
15056
15333
|
"mapArray",
|
|
15057
15334
|
"mapArrayAnchored",
|
|
15335
|
+
"patchLeaf",
|
|
15336
|
+
"patchSlotRange",
|
|
15058
15337
|
"createDisposableEffect",
|
|
15059
15338
|
"createComponent",
|
|
15060
15339
|
"renderChild",
|
|
@@ -17006,6 +17285,14 @@ function destructureLoopParam(param, paramBindings) {
|
|
|
17006
17285
|
}
|
|
17007
17286
|
return { head: param, unwrap: "" };
|
|
17008
17287
|
}
|
|
17288
|
+
function buildPreambleRegionPlans(regions, loopParam, loopParamBindings) {
|
|
17289
|
+
if (!regions || regions.length === 0) return [];
|
|
17290
|
+
return regions.map((r2) => {
|
|
17291
|
+
const wrapped = wrapLoopParamAsAccessor(r2.expr, loopParam, loopParamBindings);
|
|
17292
|
+
const valueExpr = r2.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : `escapeText(${wrapped})`;
|
|
17293
|
+
return { slotId: r2.slotId, valueExpr };
|
|
17294
|
+
});
|
|
17295
|
+
}
|
|
17009
17296
|
function buildComponentPropsExpr2(comp, loopParam, loopParamBindings) {
|
|
17010
17297
|
const wrap = loopParam ? (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings) : (expr) => expr;
|
|
17011
17298
|
const entries2 = comp.props.map((p) => {
|
|
@@ -18104,9 +18391,16 @@ function buildDynamicLoopDelegationPlan(elem, profileComponentName) {
|
|
|
18104
18391
|
paramBindings: elem.paramBindings,
|
|
18105
18392
|
key: elem.key,
|
|
18106
18393
|
index: elem.index,
|
|
18394
|
+
// No loopParams spec here (unlike the row-render context) — in the
|
|
18395
|
+
// delegated handler `elem.param` is bound to the plain `.find()`/
|
|
18396
|
+
// indexed result, not a signal accessor, so leaf refs must stay in
|
|
18397
|
+
// their literal (`t.name`) form. Passing a loopParams spec here was
|
|
18398
|
+
// BUG-3: it rewrote leaf refs to accessor-call form (`t().name`),
|
|
18399
|
+
// which throws since `t` is a plain object in this scope.
|
|
18107
18400
|
mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
|
|
18108
|
-
renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1,
|
|
18109
|
-
}) : null
|
|
18401
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1, void 0, void 0, true)
|
|
18402
|
+
}) : null,
|
|
18403
|
+
mapPreambleDeclaredNames: elem.preamble?.declaredNames ?? []
|
|
18110
18404
|
})
|
|
18111
18405
|
};
|
|
18112
18406
|
}
|
|
@@ -18123,9 +18417,12 @@ function buildBranchLoopDelegationPlan(loop, cv, profileComponentName) {
|
|
|
18123
18417
|
paramBindings: loop.paramBindings,
|
|
18124
18418
|
key: loop.key,
|
|
18125
18419
|
index: loop.index,
|
|
18420
|
+
// See note in `buildDynamicLoopDelegationPlan` above (BUG-3): no
|
|
18421
|
+
// loopParams spec — leaf refs must stay in plain-object form here.
|
|
18126
18422
|
mapPreamble: loop.preamble ? renderPreamble(loop.preamble, {
|
|
18127
|
-
renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1,
|
|
18128
|
-
}) : null
|
|
18423
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1, void 0, void 0, true)
|
|
18424
|
+
}) : null,
|
|
18425
|
+
mapPreambleDeclaredNames: loop.preamble?.declaredNames ?? []
|
|
18129
18426
|
})
|
|
18130
18427
|
};
|
|
18131
18428
|
}
|
|
@@ -18142,9 +18439,12 @@ function buildStaticArrayDelegationPlan(elem, profileComponentName) {
|
|
|
18142
18439
|
// array too (#1434).
|
|
18143
18440
|
arrayExpr: buildChainedArrayExpr(elem),
|
|
18144
18441
|
param: elem.param,
|
|
18442
|
+
// See note in `buildDynamicLoopDelegationPlan` above (BUG-3): no
|
|
18443
|
+
// loopParams spec — leaf refs must stay in plain-object form here.
|
|
18145
18444
|
mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
|
|
18146
|
-
renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1,
|
|
18445
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, void 0, 1, void 0, void 0, true)
|
|
18147
18446
|
}) : null,
|
|
18447
|
+
mapPreambleDeclaredNames: elem.preamble?.declaredNames ?? [],
|
|
18148
18448
|
offset: elem.offset ?? null,
|
|
18149
18449
|
indexParam: elem.index ?? null
|
|
18150
18450
|
}
|
|
@@ -18161,6 +18461,7 @@ function buildKeyedOrIndexLookup(args2) {
|
|
|
18161
18461
|
paramBindings: args2.paramBindings,
|
|
18162
18462
|
keyWithItem,
|
|
18163
18463
|
mapPreamble: args2.mapPreamble,
|
|
18464
|
+
mapPreambleDeclaredNames: args2.mapPreambleDeclaredNames,
|
|
18164
18465
|
hasBindings,
|
|
18165
18466
|
indexParam: args2.index
|
|
18166
18467
|
};
|
|
@@ -18170,6 +18471,7 @@ function buildKeyedOrIndexLookup(args2) {
|
|
|
18170
18471
|
arrayExpr: args2.array,
|
|
18171
18472
|
param: args2.param,
|
|
18172
18473
|
mapPreamble: args2.mapPreamble,
|
|
18474
|
+
mapPreambleDeclaredNames: args2.mapPreambleDeclaredNames,
|
|
18173
18475
|
hasBindings,
|
|
18174
18476
|
indexParam: args2.index
|
|
18175
18477
|
};
|
|
@@ -18198,14 +18500,16 @@ function buildBranchLoopPlan(loop, profileComponentName) {
|
|
|
18198
18500
|
}
|
|
18199
18501
|
const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(loop.param, loop.paramBindings);
|
|
18200
18502
|
const hasReactiveEffects = loop.bindings.reactiveAttrs.length > 0 || loop.bindings.reactiveTexts.length > 0 || loop.bindings.conditionals.length > 0;
|
|
18503
|
+
const fm = loop.flatMapClient;
|
|
18201
18504
|
const plan = {
|
|
18202
18505
|
kind: "plain",
|
|
18203
18506
|
rowConstruction: "string-template",
|
|
18204
18507
|
containerSlotId,
|
|
18205
18508
|
containerVar,
|
|
18206
18509
|
markerId: loop.markerId,
|
|
18207
|
-
|
|
18208
|
-
|
|
18510
|
+
flatMapLeafItem: fm ? true : void 0,
|
|
18511
|
+
arrayExpr: fm ? `(${buildChainedArrayExpr(loop)}).flatMap(${fm.params} => ${fm.body})` : buildChainedArrayExpr(loop),
|
|
18512
|
+
keyFn: fm ? fm.keyed ? "(__bfD, __bfI) => String(__bfD.k ?? __bfI)" : "null" : loopKeyFn(loop),
|
|
18209
18513
|
paramHead,
|
|
18210
18514
|
paramUnwrap,
|
|
18211
18515
|
indexParam: loop.index || "__idx",
|
|
@@ -18226,6 +18530,7 @@ function buildBranchLoopPlan(loop, profileComponentName) {
|
|
|
18226
18530
|
}) : null,
|
|
18227
18531
|
eventDelegation: buildBranchLoopDelegationPlan(loop, cv, profileComponentName),
|
|
18228
18532
|
childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings),
|
|
18533
|
+
preambleRegions: buildPreambleRegionPlans(loop.preambleRegions, loop.param, loop.paramBindings),
|
|
18229
18534
|
bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false,
|
|
18230
18535
|
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${containerSlotId}` : void 0
|
|
18231
18536
|
};
|
|
@@ -18937,6 +19242,20 @@ function emitLoopChildRefs(lines, refs, opts) {
|
|
|
18937
19242
|
lines.push(`${indent}if (${varName}) ${emitRefCall(ref.callback, varName)} }`);
|
|
18938
19243
|
}
|
|
18939
19244
|
}
|
|
19245
|
+
function emitPreambleRegionEffects(lines, regions, mapPreambleWrapped, opts) {
|
|
19246
|
+
if (regions.length === 0) return;
|
|
19247
|
+
const { indent, elVar } = opts;
|
|
19248
|
+
for (const region of regions) {
|
|
19249
|
+
const v = varSlotId(region.slotId);
|
|
19250
|
+
lines.push(`${indent}{ let __last_${v}`);
|
|
19251
|
+
lines.push(`${indent}createEffect(() => {`);
|
|
19252
|
+
if (mapPreambleWrapped) lines.push(`${indent} ${mapPreambleWrapped}`);
|
|
19253
|
+
lines.push(`${indent} const __html_${v} = ${region.valueExpr}`);
|
|
19254
|
+
lines.push(`${indent} if (__last_${v} === undefined) { __last_${v} = __html_${v}; return }`);
|
|
19255
|
+
lines.push(`${indent} if (__html_${v} !== __last_${v}) { __last_${v} = __html_${v}; patchSlotRange(${elVar}, '${region.slotId}', __html_${v}) }`);
|
|
19256
|
+
lines.push(`${indent}}) }`);
|
|
19257
|
+
}
|
|
19258
|
+
}
|
|
18940
19259
|
function stringifyLoop(lines, plan) {
|
|
18941
19260
|
switch (plan.kind) {
|
|
18942
19261
|
case "static":
|
|
@@ -18978,8 +19297,24 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
18978
19297
|
childRefs,
|
|
18979
19298
|
bodyIsMultiRoot,
|
|
18980
19299
|
anchored,
|
|
18981
|
-
anchorKeyExpr
|
|
19300
|
+
anchorKeyExpr,
|
|
19301
|
+
preambleRegions
|
|
18982
19302
|
} = plan;
|
|
19303
|
+
if (plan.flatMapLeafItem) {
|
|
19304
|
+
const loopBfIdArg = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
|
|
19305
|
+
lines.push(`${topIndent}mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (__bfD, ${indexParam}, __existing) => {`);
|
|
19306
|
+
lines.push(`${topIndent} let __el = __existing`);
|
|
19307
|
+
lines.push(`${topIndent} if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }`);
|
|
19308
|
+
lines.push(`${topIndent} let __last = __existing ? undefined : __bfD().h`);
|
|
19309
|
+
lines.push(`${topIndent} createEffect(() => {`);
|
|
19310
|
+
lines.push(`${topIndent} const __html = __bfD().h`);
|
|
19311
|
+
lines.push(`${topIndent} if (__last === undefined) { __last = __html; return }`);
|
|
19312
|
+
lines.push(`${topIndent} if (__html !== __last) { __last = __html; patchLeaf(__el, __html) }`);
|
|
19313
|
+
lines.push(`${topIndent} })`);
|
|
19314
|
+
lines.push(`${topIndent} return __el`);
|
|
19315
|
+
lines.push(`${topIndent}}, '${markerId}'${loopBfIdArg})`);
|
|
19316
|
+
return;
|
|
19317
|
+
}
|
|
18983
19318
|
if (anchored) {
|
|
18984
19319
|
stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr);
|
|
18985
19320
|
return;
|
|
@@ -18990,7 +19325,7 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
18990
19325
|
emitHoistedTemplateDecl(lines, topIndent, tplVar, hoistedTpl);
|
|
18991
19326
|
}
|
|
18992
19327
|
const loopBfId = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
|
|
18993
|
-
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
|
|
19328
|
+
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0 && preambleRegions.length === 0) {
|
|
18994
19329
|
const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
|
|
18995
19330
|
const preamble = mapPreambleWrapped ? `${mapPreambleWrapped}; ` : "";
|
|
18996
19331
|
const cloneExpr = hoistedTpl ? `return ${hoistedCloneExpr(tplVar, hoistedTpl)}` : emitTemplateCloneInline(template);
|
|
@@ -19038,6 +19373,7 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
19038
19373
|
});
|
|
19039
19374
|
}
|
|
19040
19375
|
emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot, elementIndexBySlot: pathPlan?.elementIndexBySlot });
|
|
19376
|
+
emitPreambleRegionEffects(lines, preambleRegions, mapPreambleWrapped, { indent: bodyIndent, elVar: "__el" });
|
|
19041
19377
|
lines.push(`${bodyIndent}return __el`);
|
|
19042
19378
|
lines.push(`${topIndent}}, '${markerId}'${loopBfId})`);
|
|
19043
19379
|
}
|
|
@@ -19370,6 +19706,11 @@ function indexBindingLine(handler, indexParam, indexExpr) {
|
|
|
19370
19706
|
if (!extractFreeIdentifiersFromText(handler).has(indexParam)) return null;
|
|
19371
19707
|
return `const ${indexParam} = ${indexExpr}`;
|
|
19372
19708
|
}
|
|
19709
|
+
function preambleLineForHandler(mapPreamble, declaredNames, handler) {
|
|
19710
|
+
if (!mapPreamble || declaredNames.length === 0) return null;
|
|
19711
|
+
const free = extractFreeIdentifiersFromText(handler);
|
|
19712
|
+
return declaredNames.some((name2) => free.has(name2)) ? mapPreamble : null;
|
|
19713
|
+
}
|
|
19373
19714
|
function stringifyEventDelegation(lines, plan) {
|
|
19374
19715
|
const { containerVar, events, itemLookup, profileComponentName } = plan;
|
|
19375
19716
|
const eventsByName = /* @__PURE__ */ new Map();
|
|
@@ -19414,7 +19755,8 @@ function stringifyEventDelegation(lines, plan) {
|
|
|
19414
19755
|
}
|
|
19415
19756
|
}
|
|
19416
19757
|
function emitKeyedLookup(ls, ev, handlerCall, lookup) {
|
|
19417
|
-
const { arrayExpr, param, keyWithItem, mapPreamble, hasBindings, indexParam } = lookup;
|
|
19758
|
+
const { arrayExpr, param, keyWithItem, mapPreamble, mapPreambleDeclaredNames, hasBindings, indexParam } = lookup;
|
|
19759
|
+
const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
|
|
19418
19760
|
if (ev.nestedLoops.length === 0) {
|
|
19419
19761
|
const idxLine2 = indexBindingLine(ev.handler, indexParam, `${arrayExpr}.findIndex(item => String(${keyWithItem}) === key)`);
|
|
19420
19762
|
ls.push(` const li = ${varSlotId(ev.childSlotId)}El.closest('[${BF_KEY}]')`);
|
|
@@ -19424,15 +19766,17 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
|
|
|
19424
19766
|
ls.push(` const __bfLoopItem = ${arrayExpr}.find(item => String(${keyWithItem}) === key)`);
|
|
19425
19767
|
ls.push(` if (__bfLoopItem) {`);
|
|
19426
19768
|
ls.push(` const ${param} = __bfLoopItem`);
|
|
19427
|
-
if (
|
|
19769
|
+
if (preambleLine) ls.push(` ${preambleLine}`);
|
|
19428
19770
|
if (idxLine2) ls.push(` ${idxLine2}`);
|
|
19429
|
-
ls.push(`
|
|
19771
|
+
ls.push(` ;${handlerCall}`);
|
|
19430
19772
|
ls.push(` }`);
|
|
19431
19773
|
} else {
|
|
19432
19774
|
ls.push(` const ${param} = ${arrayExpr}.find(item => String(${keyWithItem}) === key)`);
|
|
19433
|
-
|
|
19434
|
-
if (
|
|
19435
|
-
|
|
19775
|
+
ls.push(` if (${param}) {`);
|
|
19776
|
+
if (preambleLine) ls.push(` ${preambleLine}`);
|
|
19777
|
+
if (idxLine2) ls.push(` ${idxLine2}`);
|
|
19778
|
+
ls.push(` ;${handlerCall}`);
|
|
19779
|
+
ls.push(` }`);
|
|
19436
19780
|
}
|
|
19437
19781
|
ls.push(` }`);
|
|
19438
19782
|
return;
|
|
@@ -19459,13 +19803,16 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
|
|
|
19459
19803
|
}
|
|
19460
19804
|
const outerGuard = hasBindings ? "__bfLoopItem" : param;
|
|
19461
19805
|
const allParams = [outerGuard, ...ev.nestedLoops.map((n) => n.param)];
|
|
19462
|
-
if (mapPreamble) ls.push(` ${mapPreamble}`);
|
|
19463
19806
|
const idxLine = indexBindingLine(ev.handler, indexParam, `${arrayExpr}.findIndex(item => String(${keyWithItem}) === outerKey)`);
|
|
19464
|
-
|
|
19465
|
-
|
|
19807
|
+
ls.push(` if (${allParams.join(" && ")}) {`);
|
|
19808
|
+
if (preambleLine) ls.push(` ${preambleLine}`);
|
|
19809
|
+
if (idxLine) ls.push(` ${idxLine}`);
|
|
19810
|
+
ls.push(` ;${handlerCall}`);
|
|
19811
|
+
ls.push(` }`);
|
|
19466
19812
|
}
|
|
19467
19813
|
function emitDynamicIndexLookup(ls, ev, handlerCall, lookup) {
|
|
19468
|
-
const { arrayExpr, param, mapPreamble, hasBindings, indexParam } = lookup;
|
|
19814
|
+
const { arrayExpr, param, mapPreamble, mapPreambleDeclaredNames, hasBindings, indexParam } = lookup;
|
|
19815
|
+
const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
|
|
19469
19816
|
const idxLine = indexBindingLine(ev.handler, indexParam, "idx");
|
|
19470
19817
|
ls.push(` const li = ${varSlotId(ev.childSlotId)}El.closest('li, [bf-i]')`);
|
|
19471
19818
|
ls.push(` if (li && li.parentElement) {`);
|
|
@@ -19474,20 +19821,23 @@ function emitDynamicIndexLookup(ls, ev, handlerCall, lookup) {
|
|
|
19474
19821
|
ls.push(` const __bfLoopItem = ${arrayExpr}[idx]`);
|
|
19475
19822
|
ls.push(` if (__bfLoopItem) {`);
|
|
19476
19823
|
ls.push(` const ${param} = __bfLoopItem`);
|
|
19477
|
-
if (
|
|
19824
|
+
if (preambleLine) ls.push(` ${preambleLine}`);
|
|
19478
19825
|
if (idxLine) ls.push(` ${idxLine}`);
|
|
19479
|
-
ls.push(`
|
|
19826
|
+
ls.push(` ;${handlerCall}`);
|
|
19480
19827
|
ls.push(` }`);
|
|
19481
19828
|
} else {
|
|
19482
19829
|
ls.push(` const ${param} = ${arrayExpr}[idx]`);
|
|
19483
|
-
|
|
19484
|
-
if (
|
|
19485
|
-
|
|
19830
|
+
ls.push(` if (${param}) {`);
|
|
19831
|
+
if (preambleLine) ls.push(` ${preambleLine}`);
|
|
19832
|
+
if (idxLine) ls.push(` ${idxLine}`);
|
|
19833
|
+
ls.push(` ;${handlerCall}`);
|
|
19834
|
+
ls.push(` }`);
|
|
19486
19835
|
}
|
|
19487
19836
|
ls.push(` }`);
|
|
19488
19837
|
}
|
|
19489
19838
|
function emitStaticIndexLookup(ls, ev, handlerCall, lookup, containerVar) {
|
|
19490
|
-
const { arrayExpr, param, mapPreamble, offset: offset2, indexParam } = lookup;
|
|
19839
|
+
const { arrayExpr, param, mapPreamble, mapPreambleDeclaredNames, offset: offset2, indexParam } = lookup;
|
|
19840
|
+
const preambleLine = preambleLineForHandler(mapPreamble, mapPreambleDeclaredNames, ev.handler);
|
|
19491
19841
|
const idxLine = indexBindingLine(ev.handler, indexParam, "__idx");
|
|
19492
19842
|
ls.push(` let __el = ${varSlotId(ev.childSlotId)}El`);
|
|
19493
19843
|
ls.push(` while (__el.parentElement && __el.parentElement !== ${containerVar}) __el = __el.parentElement`);
|
|
@@ -19495,9 +19845,11 @@ function emitStaticIndexLookup(ls, ev, handlerCall, lookup, containerVar) {
|
|
|
19495
19845
|
const idxOffset = buildLoopChildIndexSubtraction(offset2 ?? void 0);
|
|
19496
19846
|
ls.push(` const __idx = Array.from(${containerVar}.children).indexOf(__el)${idxOffset}`);
|
|
19497
19847
|
ls.push(` const ${param} = ${arrayExpr}[__idx]`);
|
|
19498
|
-
|
|
19499
|
-
if (
|
|
19500
|
-
|
|
19848
|
+
ls.push(` if (${param}) {`);
|
|
19849
|
+
if (preambleLine) ls.push(` ${preambleLine}`);
|
|
19850
|
+
if (idxLine) ls.push(` ${idxLine}`);
|
|
19851
|
+
ls.push(` ;${handlerCall}`);
|
|
19852
|
+
ls.push(` }`);
|
|
19501
19853
|
ls.push(` }`);
|
|
19502
19854
|
}
|
|
19503
19855
|
var NON_BUBBLING_EVENTS;
|
|
@@ -19548,12 +19900,29 @@ function emitPlain(lines, plan) {
|
|
|
19548
19900
|
eventDelegation,
|
|
19549
19901
|
childRefs,
|
|
19550
19902
|
bodyIsMultiRoot,
|
|
19551
|
-
profileLoopId
|
|
19903
|
+
profileLoopId,
|
|
19904
|
+
preambleRegions
|
|
19552
19905
|
} = plan;
|
|
19553
19906
|
const loopBfId = profileLoopId ? `, ${JSON.stringify(profileLoopId)}` : "";
|
|
19554
19907
|
const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
|
|
19555
19908
|
lines.push(` __disposers.push(createDisposableEffect(() => {`);
|
|
19556
|
-
if (
|
|
19909
|
+
if (plan.flatMapLeafItem) {
|
|
19910
|
+
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (__bfD, ${indexParam}, __existing) => {`);
|
|
19911
|
+
lines.push(` let __el = __existing`);
|
|
19912
|
+
lines.push(` if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }`);
|
|
19913
|
+
lines.push(` let __last = __existing ? undefined : __bfD().h`);
|
|
19914
|
+
lines.push(` createEffect(() => {`);
|
|
19915
|
+
lines.push(` const __html = __bfD().h`);
|
|
19916
|
+
lines.push(` if (__last === undefined) { __last = __html; return }`);
|
|
19917
|
+
lines.push(` if (__html !== __last) { __last = __html; patchLeaf(__el, __html) }`);
|
|
19918
|
+
lines.push(` })`);
|
|
19919
|
+
lines.push(` return __el`);
|
|
19920
|
+
lines.push(` }, '${markerId}'${loopBfId})`);
|
|
19921
|
+
lines.push(` }))`);
|
|
19922
|
+
stringifyEventDelegation(lines, eventDelegation);
|
|
19923
|
+
return;
|
|
19924
|
+
}
|
|
19925
|
+
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0 && preambleRegions.length === 0) {
|
|
19557
19926
|
const cloneExpr = emitTemplateCloneInline(template);
|
|
19558
19927
|
if (mapPreambleWrapped) {
|
|
19559
19928
|
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${mapPreambleWrapped}; ${cloneExpr} }, '${markerId}'${loopBfId})`);
|
|
@@ -19578,6 +19947,7 @@ function emitPlain(lines, plan) {
|
|
|
19578
19947
|
stringifyReactiveEffects(lines, reactiveEffects, { indent: " ", elVar: "__el", bodyIsMultiRoot });
|
|
19579
19948
|
}
|
|
19580
19949
|
emitLoopChildRefs(lines, childRefs, { indent: " ", elVar: "__el", bodyIsMultiRoot });
|
|
19950
|
+
emitPreambleRegionEffects(lines, preambleRegions, mapPreambleWrapped, { indent: " ", elVar: "__el" });
|
|
19581
19951
|
lines.push(` return __el`);
|
|
19582
19952
|
lines.push(` }, '${markerId}'${loopBfId})`);
|
|
19583
19953
|
}
|
|
@@ -19795,6 +20165,31 @@ function buildPlainLoopPlan(elem, profileComponentName) {
|
|
|
19795
20165
|
const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings);
|
|
19796
20166
|
const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
|
|
19797
20167
|
const hasReactive2 = elem.bindings.reactiveAttrs.length > 0 || elem.bindings.reactiveTexts.length > 0 || elem.bindings.conditionals.length > 0;
|
|
20168
|
+
if (elem.flatMapClient) {
|
|
20169
|
+
return {
|
|
20170
|
+
kind: "plain",
|
|
20171
|
+
rowConstruction: "string-template",
|
|
20172
|
+
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
20173
|
+
markerId: elem.markerId,
|
|
20174
|
+
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : void 0,
|
|
20175
|
+
arrayExpr: `(${buildChainedArrayExpr(elem)}).flatMap(${elem.flatMapClient.params} => ${elem.flatMapClient.body})`,
|
|
20176
|
+
keyFn: elem.flatMapClient.keyed ? "(__bfD, __bfI) => String(__bfD.k ?? __bfI)" : "null",
|
|
20177
|
+
paramHead: "__bfD",
|
|
20178
|
+
paramUnwrap: "",
|
|
20179
|
+
indexParam: "__idx",
|
|
20180
|
+
mapPreambleWrapped: "",
|
|
20181
|
+
template: "",
|
|
20182
|
+
reactiveEffects: null,
|
|
20183
|
+
childRefs: [],
|
|
20184
|
+
bodyIsMultiRoot: false,
|
|
20185
|
+
anchored: false,
|
|
20186
|
+
anchorKeyExpr: "__idx",
|
|
20187
|
+
flatMapLeafItem: true,
|
|
20188
|
+
// flatMap loops carry no `MapCallbackPreamble` (jsx-to-ir.ts drops it
|
|
20189
|
+
// for flatMapCallback), so there is nothing to patch here.
|
|
20190
|
+
preambleRegions: []
|
|
20191
|
+
};
|
|
20192
|
+
}
|
|
19798
20193
|
return {
|
|
19799
20194
|
kind: "plain",
|
|
19800
20195
|
rowConstruction: "string-template",
|
|
@@ -19818,6 +20213,7 @@ function buildPlainLoopPlan(elem, profileComponentName) {
|
|
|
19818
20213
|
skeletonPaths: elem.skeletonPaths,
|
|
19819
20214
|
reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
|
|
19820
20215
|
childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
|
|
20216
|
+
preambleRegions: buildPreambleRegionPlans(elem.preambleRegions, elem.param, elem.paramBindings),
|
|
19821
20217
|
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
|
|
19822
20218
|
anchored: elem.bodyIsItemConditional ?? false,
|
|
19823
20219
|
// Fall back to the iteration index when the loop has no key. A whole-item
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/cli",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.4",
|
|
4
4
|
"description": "CLI for agent-driven UI component discovery and scaffolding",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"esbuild": "^0.25.0",
|
|
31
31
|
"typescript": "^5.0.0",
|
|
32
|
-
"@barefootjs/client": "0.26.
|
|
33
|
-
"@barefootjs/shared": "0.26.
|
|
32
|
+
"@barefootjs/client": "0.26.4",
|
|
33
|
+
"@barefootjs/shared": "0.26.4"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@barefootjs/jsx": "0.26.
|
|
36
|
+
"@barefootjs/jsx": "0.26.4",
|
|
37
37
|
"@types/node": "^22.0.0",
|
|
38
38
|
"@happy-dom/global-registrator": "^20.0.11",
|
|
39
39
|
"happy-dom": "^20.0.11"
|