@barefootjs/jsx 0.26.1 → 0.26.3
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/adapters/interface.d.ts +29 -0
- package/dist/adapters/interface.d.ts.map +1 -1
- package/dist/adapters/jsx-adapter.d.ts +9 -0
- package/dist/adapters/jsx-adapter.d.ts.map +1 -1
- package/dist/adapters/loop-bound-names.d.ts.map +1 -1
- package/dist/adapters/parsed-expr-emitter.d.ts +0 -10
- package/dist/adapters/parsed-expr-emitter.d.ts.map +1 -1
- package/dist/adapters/test-adapter.d.ts.map +1 -1
- package/dist/analyzer-context.d.ts +11 -1
- package/dist/analyzer-context.d.ts.map +1 -1
- package/dist/analyzer.d.ts +40 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/expression-parser.d.ts.map +1 -1
- package/dist/index.js +662 -223
- package/dist/ir-to-client-js/build-references.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts +2 -0
- package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-component-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-composite-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-inner-loop.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/loop.d.ts +12 -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.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/event-delegation.d.ts +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.d.ts.map +1 -1
- package/dist/ir-to-client-js/html-template.d.ts +19 -1
- package/dist/ir-to-client-js/html-template.d.ts.map +1 -1
- package/dist/ir-to-client-js/plan/build-static-array-child-init.d.ts.map +1 -1
- package/dist/ir-to-client-js/types.d.ts +4 -4
- package/dist/ir-to-client-js/types.d.ts.map +1 -1
- package/dist/jsx-to-ir.d.ts.map +1 -1
- package/dist/loop-destructure.d.ts.map +1 -1
- package/dist/strip-types.d.ts +18 -0
- package/dist/strip-types.d.ts.map +1 -1
- package/dist/types.d.ts +96 -32
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +94 -0
- package/src/__tests__/client-js-generation.test.ts +10 -3
- package/src/__tests__/compiler-stress-1244.test.ts +12 -1
- package/src/__tests__/event-delegation-scope-2367.test.ts +108 -0
- package/src/__tests__/flatmap-segments.test.ts +80 -0
- package/src/__tests__/map-arbitrary-body.test.ts +226 -0
- package/src/__tests__/map-body-no-silent-divergence.test.ts +360 -0
- package/src/__tests__/map-multi-return-body.test.ts +172 -0
- package/src/__tests__/unsupported-expression.test.ts +20 -2
- package/src/adapters/interface.ts +40 -0
- package/src/adapters/jsx-adapter.ts +10 -0
- package/src/adapters/loop-bound-names.ts +6 -2
- package/src/adapters/parsed-expr-emitter.ts +5 -10
- package/src/adapters/test-adapter.ts +10 -0
- package/src/analyzer-context.ts +35 -1
- package/src/analyzer.ts +162 -24
- package/src/compiler.ts +2 -2
- package/src/expression-parser.ts +27 -19
- package/src/ir-to-client-js/build-references.ts +3 -2
- package/src/ir-to-client-js/collect-elements.ts +3 -3
- package/src/ir-to-client-js/control-flow/plan/branch-loop.ts +2 -0
- package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +7 -2
- package/src/ir-to-client-js/control-flow/plan/build-component-loop.ts +4 -0
- package/src/ir-to-client-js/control-flow/plan/build-composite-loop.ts +15 -2
- package/src/ir-to-client-js/control-flow/plan/build-event-delegation.ts +16 -3
- package/src/ir-to-client-js/control-flow/plan/build-inner-loop.ts +26 -3
- package/src/ir-to-client-js/control-flow/plan/build-loop.ts +17 -2
- package/src/ir-to-client-js/control-flow/plan/loop.ts +12 -0
- package/src/ir-to-client-js/control-flow/shared.ts +2 -1
- package/src/ir-to-client-js/control-flow/stringify/event-delegation.ts +9 -2
- package/src/ir-to-client-js/control-flow.ts +11 -0
- package/src/ir-to-client-js/html-template.ts +133 -37
- package/src/ir-to-client-js/plan/build-static-array-child-init.ts +22 -10
- package/src/ir-to-client-js/types.ts +4 -3
- package/src/jsx-to-ir.ts +690 -164
- package/src/loop-destructure.ts +9 -5
- package/src/rich-type-refusal.ts +6 -2
- package/src/strip-types.ts +47 -0
- package/src/types.ts +110 -35
package/dist/index.js
CHANGED
|
@@ -250,6 +250,7 @@ var UNSUPPORTED_METHODS = new Set([
|
|
|
250
250
|
"some",
|
|
251
251
|
"forEach",
|
|
252
252
|
"flatMap",
|
|
253
|
+
"fill",
|
|
253
254
|
"charAt",
|
|
254
255
|
"charCodeAt",
|
|
255
256
|
"codePointAt",
|
|
@@ -3380,6 +3381,41 @@ function buildSpreadAttrsMergeCall(args) {
|
|
|
3380
3381
|
function itemAnchorTemplate(keyExpr) {
|
|
3381
3382
|
return `<!--${loopItemMarker("${" + keyExpr + "}")}-->`;
|
|
3382
3383
|
}
|
|
3384
|
+
function renderPreamble(preamble, opts) {
|
|
3385
|
+
let out = "";
|
|
3386
|
+
for (const seg of preamble.segments) {
|
|
3387
|
+
if (seg.kind === "js") {
|
|
3388
|
+
const text = opts.textVariant === "template" ? seg.templateText ?? seg.text : seg.text;
|
|
3389
|
+
out += opts.transformJs ? opts.transformJs(text) : text;
|
|
3390
|
+
} else {
|
|
3391
|
+
out += "`" + opts.renderLeaf(escapeLeafTextExpressions(seg.ir)) + "`";
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
return out;
|
|
3395
|
+
}
|
|
3396
|
+
function escapeLeafTextExpressions(ir) {
|
|
3397
|
+
switch (ir.type) {
|
|
3398
|
+
case "element":
|
|
3399
|
+
return { ...ir, children: ir.children.map(escapeLeafTextExpressions) };
|
|
3400
|
+
case "fragment":
|
|
3401
|
+
return { ...ir, children: ir.children.map(escapeLeafTextExpressions) };
|
|
3402
|
+
case "expression": {
|
|
3403
|
+
if (ir.expr === "null" || ir.expr === "undefined")
|
|
3404
|
+
return ir;
|
|
3405
|
+
if (ir.slotId || ir.expr.trimStart().startsWith("escapeText("))
|
|
3406
|
+
return ir;
|
|
3407
|
+
return { ...ir, expr: `escapeText((${ir.expr}))`, templateExpr: ir.templateExpr ? `escapeText((${ir.templateExpr}))` : ir.templateExpr };
|
|
3408
|
+
}
|
|
3409
|
+
case "conditional":
|
|
3410
|
+
return {
|
|
3411
|
+
...ir,
|
|
3412
|
+
whenTrue: escapeLeafTextExpressions(ir.whenTrue),
|
|
3413
|
+
whenFalse: ir.whenFalse ? escapeLeafTextExpressions(ir.whenFalse) : ir.whenFalse
|
|
3414
|
+
};
|
|
3415
|
+
default:
|
|
3416
|
+
return ir;
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3383
3419
|
function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, branchSlotsVar, insideLoop = false, inHoistedChildren = false) {
|
|
3384
3420
|
const recurse = (n) => irToHtmlTemplate(n, restSpreadNames, loopDepth, loopParams, branchSlotsVar, insideLoop, inHoistedChildren);
|
|
3385
3421
|
const wrapExpr = (expr) => wrapExprWithLoopParams(expr, loopParams);
|
|
@@ -3421,15 +3457,17 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
|
|
|
3421
3457
|
}
|
|
3422
3458
|
case "text":
|
|
3423
3459
|
return escapeHtml(node.value);
|
|
3424
|
-
case "expression":
|
|
3460
|
+
case "expression": {
|
|
3425
3461
|
if (node.expr === "null" || node.expr === "undefined")
|
|
3426
3462
|
return "";
|
|
3463
|
+
const inner = wrapInterpolation(wrapExpr(node.expr));
|
|
3464
|
+
const valueExpr = node.joinArrayChild ? `Array.isArray(${inner}) ? ${inner}.join('') : (${inner} ?? '')` : inner;
|
|
3427
3465
|
if (node.slotId) {
|
|
3428
|
-
const
|
|
3429
|
-
const slotted = branchSlotsVar ? inner : escapeTextSlotExpr(inner);
|
|
3466
|
+
const slotted = branchSlotsVar || node.joinArrayChild ? valueExpr : escapeTextSlotExpr(valueExpr);
|
|
3430
3467
|
return `<!--bf:${node.slotId}-->\${${slotted}}<!--/-->`;
|
|
3431
3468
|
}
|
|
3432
|
-
return `\${${
|
|
3469
|
+
return `\${${valueExpr}}`;
|
|
3470
|
+
}
|
|
3433
3471
|
case "conditional": {
|
|
3434
3472
|
const trueBranch = recurse(node.whenTrue);
|
|
3435
3473
|
const falseBranch = recurse(node.whenFalse);
|
|
@@ -3488,14 +3526,13 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
|
|
|
3488
3526
|
const iterMethod = node.method ?? "map";
|
|
3489
3527
|
let mapExpr;
|
|
3490
3528
|
if (node.flatMapCallback) {
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
body = body.replace(frag.placeholder, `\`${renderedIr}\``);
|
|
3495
|
-
}
|
|
3529
|
+
const body = renderPreamble(node.flatMapCallback, {
|
|
3530
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop)
|
|
3531
|
+
});
|
|
3496
3532
|
mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
|
|
3497
|
-
} else if (node.
|
|
3498
|
-
|
|
3533
|
+
} else if (node.preamble) {
|
|
3534
|
+
const preamble = renderPreamble(node.preamble, { textVariant: "client", renderLeaf: (ir) => irToHtmlTemplate(ir, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop) });
|
|
3535
|
+
mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => { ${preamble} return \`${childTemplate}\` }).join('')}`;
|
|
3499
3536
|
} else {
|
|
3500
3537
|
mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => \`${childTemplate}\`).join('')}`;
|
|
3501
3538
|
}
|
|
@@ -3743,13 +3780,16 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
|
|
|
3743
3780
|
}
|
|
3744
3781
|
case "text":
|
|
3745
3782
|
return escapeHtml(node.value);
|
|
3746
|
-
case "expression":
|
|
3783
|
+
case "expression": {
|
|
3747
3784
|
if (node.expr === "null" || node.expr === "undefined")
|
|
3748
3785
|
return "";
|
|
3786
|
+
const wrapped = wrapExpr(node.expr);
|
|
3787
|
+
const value = node.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : wrapped;
|
|
3749
3788
|
if (node.slotId) {
|
|
3750
|
-
return `<!--bf:${node.slotId}-->\${${escapeTextSlotExpr(
|
|
3789
|
+
return `<!--bf:${node.slotId}-->\${${node.joinArrayChild ? value : escapeTextSlotExpr(wrapped)}}<!--/-->`;
|
|
3751
3790
|
}
|
|
3752
|
-
return `\${${
|
|
3791
|
+
return `\${${value}}`;
|
|
3792
|
+
}
|
|
3753
3793
|
case "conditional": {
|
|
3754
3794
|
const trueBranch = recurse(node.whenTrue);
|
|
3755
3795
|
const falseBranch = recurse(node.whenFalse);
|
|
@@ -3776,14 +3816,13 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
|
|
|
3776
3816
|
const iterMethod = node.method ?? "map";
|
|
3777
3817
|
let mapExpr;
|
|
3778
3818
|
if (node.flatMapCallback) {
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
body = body.replace(frag.placeholder, `\`${renderedIr}\``);
|
|
3783
|
-
}
|
|
3819
|
+
const body = renderPreamble(node.flatMapCallback, {
|
|
3820
|
+
renderLeaf: (ir) => irToPlaceholderTemplate(ir, restSpreadNames, loopDepth + 1, loopParams)
|
|
3821
|
+
});
|
|
3784
3822
|
mapExpr = `\${${wrappedArray}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
|
|
3785
|
-
} else if (node.
|
|
3786
|
-
|
|
3823
|
+
} else if (node.preamble) {
|
|
3824
|
+
const preamble = renderPreamble(node.preamble, { textVariant: "client", renderLeaf: (ir) => irToPlaceholderTemplate(ir, restSpreadNames, loopDepth + 1, loopParams) });
|
|
3825
|
+
mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => { ${preamble} return \`${childTemplate}\` }).join('')}`;
|
|
3787
3826
|
} else {
|
|
3788
3827
|
mapExpr = `\${${wrappedArray}.${iterMethod}(${callbackParam} => \`${childTemplate}\`).join('')}`;
|
|
3789
3828
|
}
|
|
@@ -3971,13 +4010,16 @@ function irToComponentTemplateWithOpts(node, opts) {
|
|
|
3971
4010
|
}
|
|
3972
4011
|
case "text":
|
|
3973
4012
|
return escapeHtml(node.value);
|
|
3974
|
-
case "expression":
|
|
4013
|
+
case "expression": {
|
|
3975
4014
|
if (node.expr === "null" || node.expr === "undefined")
|
|
3976
4015
|
return "";
|
|
4016
|
+
const wrapped = transformExpr(node.expr, node.templateExpr);
|
|
4017
|
+
const value = node.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : wrapped;
|
|
3977
4018
|
if (node.slotId) {
|
|
3978
|
-
return `<!--bf:${node.slotId}-->\${${
|
|
4019
|
+
return `<!--bf:${node.slotId}-->\${${node.joinArrayChild ? value : escapeTextSlotExpr(wrapped)}}<!--/-->`;
|
|
3979
4020
|
}
|
|
3980
|
-
return `\${${
|
|
4021
|
+
return `\${${value}}`;
|
|
4022
|
+
}
|
|
3981
4023
|
case "conditional": {
|
|
3982
4024
|
if (node.clientOnly && node.slotId) {
|
|
3983
4025
|
return `<!--bf-cond-start:${node.slotId}--><!--bf-cond-end:${node.slotId}-->`;
|
|
@@ -4298,10 +4340,11 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
4298
4340
|
{
|
|
4299
4341
|
const transformed = transformExpr(node.expr, node.templateExpr);
|
|
4300
4342
|
const expr = transformed === UNSAFE_TEMPLATE_EXPR ? "''" : transformed;
|
|
4343
|
+
const value = node.joinArrayChild ? `Array.isArray(${expr}) ? ${expr}.join('') : (${expr} ?? '')` : expr;
|
|
4301
4344
|
if (node.slotId) {
|
|
4302
|
-
return `<!--bf:${node.slotId}-->\${${escapeTextSlotExpr(expr)}}<!--/-->`;
|
|
4345
|
+
return `<!--bf:${node.slotId}-->\${${node.joinArrayChild ? value : escapeTextSlotExpr(expr)}}<!--/-->`;
|
|
4303
4346
|
}
|
|
4304
|
-
return `\${${
|
|
4347
|
+
return `\${${value}}`;
|
|
4305
4348
|
}
|
|
4306
4349
|
case "conditional": {
|
|
4307
4350
|
if (node.clientOnly && node.slotId) {
|
|
@@ -4397,16 +4440,14 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
4397
4440
|
const iterMethod = node.method ?? "map";
|
|
4398
4441
|
let mapExpr;
|
|
4399
4442
|
if (node.flatMapCallback) {
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
}
|
|
4405
|
-
body = applyPropsRewrite(body, propsObjectName ?? null);
|
|
4443
|
+
const body = renderPreamble(node.flatMapCallback, {
|
|
4444
|
+
textVariant: "template",
|
|
4445
|
+
transformJs: (t) => applyPropsRewrite(t, propsObjectName ?? null),
|
|
4446
|
+
renderLeaf: (ir) => recurseInLoopBody(ir)
|
|
4447
|
+
});
|
|
4406
4448
|
mapExpr = `\${${iterArrayExpr}.flatMap(${node.flatMapCallback.params} => ${body}).join('')}`;
|
|
4407
|
-
} else if (node.
|
|
4408
|
-
const
|
|
4409
|
-
const preamble = applyPropsRewrite(rawPreamble, propsObjectName ?? null);
|
|
4449
|
+
} else if (node.preamble) {
|
|
4450
|
+
const preamble = renderPreamble(node.preamble, { textVariant: "template", transformJs: (t) => applyPropsRewrite(t, propsObjectName ?? null), renderLeaf: (ir) => recurseInLoopBody(ir) });
|
|
4410
4451
|
mapExpr = `\${${iterArrayExpr}.${iterMethod}(${callbackParam} => { ${preamble} return \`${childTemplate}\` }).join('')}`;
|
|
4411
4452
|
} else {
|
|
4412
4453
|
mapExpr = `\${${iterArrayExpr}.${iterMethod}(${callbackParam} => \`${childTemplate}\`).join('')}`;
|
|
@@ -4561,6 +4602,39 @@ function reconstructWithoutTypes(node, sourceFile, ranges) {
|
|
|
4561
4602
|
}
|
|
4562
4603
|
return result;
|
|
4563
4604
|
}
|
|
4605
|
+
function reconstructAsSegments(node, sourceFile, ranges, markers) {
|
|
4606
|
+
const nodeStart = node.getStart(sourceFile);
|
|
4607
|
+
const nodeEnd = node.getEnd();
|
|
4608
|
+
const fullText = sourceFile.text;
|
|
4609
|
+
const edits = [];
|
|
4610
|
+
for (const r of ranges) {
|
|
4611
|
+
if (r.end <= nodeStart || r.start >= nodeEnd)
|
|
4612
|
+
continue;
|
|
4613
|
+
edits.push({ start: r.start, end: r.end, marker: null });
|
|
4614
|
+
}
|
|
4615
|
+
markers.forEach((m, i) => edits.push({ start: m.start, end: m.end, marker: i }));
|
|
4616
|
+
edits.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
4617
|
+
const segments = [];
|
|
4618
|
+
let jsBuf = "";
|
|
4619
|
+
let pos = nodeStart;
|
|
4620
|
+
for (const edit of edits) {
|
|
4621
|
+
if (edit.start < pos)
|
|
4622
|
+
continue;
|
|
4623
|
+
jsBuf += fullText.slice(pos, edit.start);
|
|
4624
|
+
if (edit.marker !== null) {
|
|
4625
|
+
if (jsBuf)
|
|
4626
|
+
segments.push({ js: jsBuf });
|
|
4627
|
+
jsBuf = "";
|
|
4628
|
+
segments.push({ marker: edit.marker });
|
|
4629
|
+
}
|
|
4630
|
+
pos = edit.end;
|
|
4631
|
+
}
|
|
4632
|
+
if (pos < nodeEnd)
|
|
4633
|
+
jsBuf += fullText.slice(pos, nodeEnd);
|
|
4634
|
+
if (jsBuf)
|
|
4635
|
+
segments.push({ js: jsBuf });
|
|
4636
|
+
return segments;
|
|
4637
|
+
}
|
|
4564
4638
|
function mergeRanges(ranges) {
|
|
4565
4639
|
if (ranges.length === 0)
|
|
4566
4640
|
return [];
|
|
@@ -4706,10 +4780,11 @@ function findAngleBracketAfter(lastTypeArg, fullText) {
|
|
|
4706
4780
|
}
|
|
4707
4781
|
|
|
4708
4782
|
// src/analyzer-context.ts
|
|
4709
|
-
function createAnalyzerContext(sourceFile, filePath) {
|
|
4783
|
+
function createAnalyzerContext(sourceFile, filePath, acceptsCallbackBody) {
|
|
4710
4784
|
return {
|
|
4711
4785
|
sourceFile,
|
|
4712
4786
|
filePath,
|
|
4787
|
+
acceptsCallbackBody,
|
|
4713
4788
|
componentName: null,
|
|
4714
4789
|
componentNode: null,
|
|
4715
4790
|
hasDefaultExport: false,
|
|
@@ -4755,6 +4830,9 @@ function createAnalyzerContext(sourceFile, filePath) {
|
|
|
4755
4830
|
} catch {
|
|
4756
4831
|
ownSourceFile = undefined;
|
|
4757
4832
|
}
|
|
4833
|
+
if (process.env.BF_ASSERT_NO_JSX_IN_GETJS === "1" && this.errors.length === 0 && nodeContainsJsx(node)) {
|
|
4834
|
+
throw new Error("getJS() called on a JSX-bearing node — raw JSX must never be spliced " + "into emitted output. Carry mixed content as structured segments " + "(MapCallbackPreamble / FlatMapCallback) instead.");
|
|
4835
|
+
}
|
|
4758
4836
|
if (ownSourceFile && ownSourceFile !== sourceFile) {
|
|
4759
4837
|
return node.getText(ownSourceFile);
|
|
4760
4838
|
}
|
|
@@ -4762,6 +4840,11 @@ function createAnalyzerContext(sourceFile, filePath) {
|
|
|
4762
4840
|
}
|
|
4763
4841
|
};
|
|
4764
4842
|
}
|
|
4843
|
+
function nodeContainsJsx(node) {
|
|
4844
|
+
if (ts7.isJsxElement(node) || ts7.isJsxSelfClosingElement(node) || ts7.isJsxFragment(node))
|
|
4845
|
+
return true;
|
|
4846
|
+
return ts7.forEachChild(node, nodeContainsJsx) ?? false;
|
|
4847
|
+
}
|
|
4765
4848
|
function getSourceLocation(node, sourceFile, filePath) {
|
|
4766
4849
|
const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
4767
4850
|
const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
@@ -5564,7 +5647,7 @@ function createProgramForFile(source, filePath) {
|
|
|
5564
5647
|
return null;
|
|
5565
5648
|
}
|
|
5566
5649
|
}
|
|
5567
|
-
function analyzeComponent(source, filePath, targetComponentName, program) {
|
|
5650
|
+
function analyzeComponent(source, filePath, targetComponentName, program, acceptsCallbackBody) {
|
|
5568
5651
|
incrementCounter("filesAnalyzed");
|
|
5569
5652
|
const hadSharedProgram = program !== undefined;
|
|
5570
5653
|
const prescan = prescanReactiveFactoriesInSource(source, filePath);
|
|
@@ -5597,7 +5680,7 @@ function analyzeComponent(source, filePath, targetComponentName, program) {
|
|
|
5597
5680
|
checker = result.checker;
|
|
5598
5681
|
}
|
|
5599
5682
|
}
|
|
5600
|
-
const ctx = createAnalyzerContext(sourceFile, filePath);
|
|
5683
|
+
const ctx = createAnalyzerContext(sourceFile, filePath, acceptsCallbackBody);
|
|
5601
5684
|
ctx.checker = checker;
|
|
5602
5685
|
ctx.reactiveFactories = prescan.factories;
|
|
5603
5686
|
ctx.declinedReactiveFactories = prescan.declined;
|
|
@@ -6649,9 +6732,15 @@ function extractSingleJsxReturn(body) {
|
|
|
6649
6732
|
return null;
|
|
6650
6733
|
return jsxReturn;
|
|
6651
6734
|
}
|
|
6652
|
-
function
|
|
6735
|
+
function preambleUnsafe(preamble, branches, fallback) {
|
|
6736
|
+
if (preamble.length === 0)
|
|
6737
|
+
return false;
|
|
6738
|
+
return fallback === null || branches.some((b) => b.jsxReturn === null);
|
|
6739
|
+
}
|
|
6740
|
+
function extractMultiReturnJsxBranches(body, allowPreamble = false) {
|
|
6653
6741
|
const branches = [];
|
|
6654
6742
|
let fallback = null;
|
|
6743
|
+
const preamble = [];
|
|
6655
6744
|
const stmts = body.statements;
|
|
6656
6745
|
for (let i = 0;i < stmts.length; i++) {
|
|
6657
6746
|
const stmt = stmts[i];
|
|
@@ -6681,7 +6770,9 @@ function extractMultiReturnJsxBranches(body) {
|
|
|
6681
6770
|
}
|
|
6682
6771
|
if (branches.length === 0)
|
|
6683
6772
|
return null;
|
|
6684
|
-
|
|
6773
|
+
if (preambleUnsafe(preamble, branches, fallback))
|
|
6774
|
+
return null;
|
|
6775
|
+
return { branches, fallback, preamble };
|
|
6685
6776
|
}
|
|
6686
6777
|
break;
|
|
6687
6778
|
}
|
|
@@ -6696,23 +6787,38 @@ function extractMultiReturnJsxBranches(body) {
|
|
|
6696
6787
|
const hasDefault = stmt.caseBlock.clauses.some((c) => ts8.isDefaultClause(c));
|
|
6697
6788
|
if (!hasDefault)
|
|
6698
6789
|
return null;
|
|
6790
|
+
let pendingCases = [];
|
|
6699
6791
|
for (const clause of stmt.caseBlock.clauses) {
|
|
6792
|
+
if (ts8.isCaseClause(clause) && clause.statements.length === 0) {
|
|
6793
|
+
pendingCases.push(clause.expression);
|
|
6794
|
+
continue;
|
|
6795
|
+
}
|
|
6700
6796
|
const jsxReturn = findJsxReturnInCaseClause(clause);
|
|
6701
6797
|
const nullReturn = findNullReturnInCaseClause(clause);
|
|
6702
6798
|
if (!jsxReturn && !nullReturn)
|
|
6703
6799
|
return null;
|
|
6800
|
+
if (!caseClauseIsDirectReturn(clause))
|
|
6801
|
+
return null;
|
|
6704
6802
|
if (ts8.isCaseClause(clause)) {
|
|
6705
6803
|
branches.push({
|
|
6706
6804
|
condition: clause.expression,
|
|
6707
|
-
jsxReturn: jsxReturn ?? null
|
|
6805
|
+
jsxReturn: jsxReturn ?? null,
|
|
6806
|
+
extraCaseConditions: pendingCases.length > 0 ? pendingCases : undefined
|
|
6708
6807
|
});
|
|
6808
|
+
pendingCases = [];
|
|
6709
6809
|
} else {
|
|
6810
|
+
if (pendingCases.length > 0)
|
|
6811
|
+
return null;
|
|
6710
6812
|
fallback = jsxReturn ?? null;
|
|
6711
6813
|
}
|
|
6712
6814
|
}
|
|
6815
|
+
if (pendingCases.length > 0)
|
|
6816
|
+
return null;
|
|
6713
6817
|
if (branches.length === 0)
|
|
6714
6818
|
return null;
|
|
6715
|
-
|
|
6819
|
+
if (preambleUnsafe(preamble, branches, fallback))
|
|
6820
|
+
return null;
|
|
6821
|
+
return { branches, fallback, switchDiscriminant: stmt.expression, preamble };
|
|
6716
6822
|
}
|
|
6717
6823
|
if (ts8.isReturnStatement(stmt) && stmt.expression) {
|
|
6718
6824
|
const expr = unwrapJsxTransparent(stmt.expression);
|
|
@@ -6723,13 +6829,22 @@ function extractMultiReturnJsxBranches(body) {
|
|
|
6723
6829
|
}
|
|
6724
6830
|
continue;
|
|
6725
6831
|
}
|
|
6726
|
-
if (ts8.isVariableStatement(stmt))
|
|
6832
|
+
if (ts8.isVariableStatement(stmt)) {
|
|
6833
|
+
const declFlags = stmt.declarationList.flags;
|
|
6834
|
+
const isConstOrLet = (declFlags & ts8.NodeFlags.Const) !== 0 || (declFlags & ts8.NodeFlags.Let) !== 0;
|
|
6835
|
+
if (allowPreamble && isConstOrLet && branches.length === 0 && fallback === null) {
|
|
6836
|
+
preamble.push(stmt);
|
|
6837
|
+
continue;
|
|
6838
|
+
}
|
|
6727
6839
|
return null;
|
|
6840
|
+
}
|
|
6728
6841
|
return null;
|
|
6729
6842
|
}
|
|
6730
6843
|
if (branches.length === 0)
|
|
6731
6844
|
return null;
|
|
6732
|
-
|
|
6845
|
+
if (preambleUnsafe(preamble, branches, fallback))
|
|
6846
|
+
return null;
|
|
6847
|
+
return { branches, fallback, preamble };
|
|
6733
6848
|
}
|
|
6734
6849
|
function isDirectReturnBlock(node) {
|
|
6735
6850
|
if (ts8.isReturnStatement(node))
|
|
@@ -6739,9 +6854,9 @@ function isDirectReturnBlock(node) {
|
|
|
6739
6854
|
for (const stmt of node.statements) {
|
|
6740
6855
|
if (ts8.isReturnStatement(stmt)) {
|
|
6741
6856
|
returnCount++;
|
|
6742
|
-
|
|
6743
|
-
return false;
|
|
6857
|
+
continue;
|
|
6744
6858
|
}
|
|
6859
|
+
return false;
|
|
6745
6860
|
}
|
|
6746
6861
|
return returnCount === 1;
|
|
6747
6862
|
}
|
|
@@ -6772,6 +6887,24 @@ function findJsxReturnInCaseClause(clause) {
|
|
|
6772
6887
|
}
|
|
6773
6888
|
return null;
|
|
6774
6889
|
}
|
|
6890
|
+
function caseClauseIsDirectReturn(clause) {
|
|
6891
|
+
let returnCount = 0;
|
|
6892
|
+
let seenReturn = false;
|
|
6893
|
+
for (const stmt of clause.statements) {
|
|
6894
|
+
if (ts8.isReturnStatement(stmt)) {
|
|
6895
|
+
returnCount++;
|
|
6896
|
+
seenReturn = true;
|
|
6897
|
+
continue;
|
|
6898
|
+
}
|
|
6899
|
+
if (ts8.isBreakStatement(stmt)) {
|
|
6900
|
+
if (!seenReturn)
|
|
6901
|
+
return false;
|
|
6902
|
+
continue;
|
|
6903
|
+
}
|
|
6904
|
+
return false;
|
|
6905
|
+
}
|
|
6906
|
+
return returnCount === 1;
|
|
6907
|
+
}
|
|
6775
6908
|
function findNullReturnInCaseClause(clause) {
|
|
6776
6909
|
for (const stmt of clause.statements) {
|
|
6777
6910
|
if (ts8.isReturnStatement(stmt) && stmt.expression) {
|
|
@@ -8812,6 +8945,23 @@ var REACTIVE_BINDING_KINDS = new Set([
|
|
|
8812
8945
|
function isReactiveOrigin(origin) {
|
|
8813
8946
|
return origin.freeRefs?.some((r) => REACTIVE_BINDING_KINDS.has(r.kind)) ?? false;
|
|
8814
8947
|
}
|
|
8948
|
+
function tsxSourceText(raw) {
|
|
8949
|
+
return raw;
|
|
8950
|
+
}
|
|
8951
|
+
function preambleAnalysisText(p) {
|
|
8952
|
+
let out = "";
|
|
8953
|
+
for (const seg of p.segments)
|
|
8954
|
+
if (seg.kind === "js")
|
|
8955
|
+
out += seg.text;
|
|
8956
|
+
return out;
|
|
8957
|
+
}
|
|
8958
|
+
function preambleAnalysisTemplateText(p) {
|
|
8959
|
+
let out = "";
|
|
8960
|
+
for (const seg of p.segments)
|
|
8961
|
+
if (seg.kind === "js")
|
|
8962
|
+
out += seg.templateText ?? seg.text;
|
|
8963
|
+
return out;
|
|
8964
|
+
}
|
|
8815
8965
|
var AttrValueOf = {
|
|
8816
8966
|
literal(value) {
|
|
8817
8967
|
return { kind: "literal", value };
|
|
@@ -10051,8 +10201,13 @@ function attachParsedExpressions(node, analyzer, bound = EMPTY_BOUND) {
|
|
|
10051
10201
|
for (const child of nested.children)
|
|
10052
10202
|
attachParsedExpressions(child, analyzer, loopBound);
|
|
10053
10203
|
}
|
|
10054
|
-
for (const
|
|
10055
|
-
|
|
10204
|
+
for (const seg of node.flatMapCallback?.segments ?? []) {
|
|
10205
|
+
if (seg.kind === "jsx")
|
|
10206
|
+
attachParsedExpressions(seg.ir, analyzer, loopBound);
|
|
10207
|
+
}
|
|
10208
|
+
for (const seg of node.preamble?.segments ?? []) {
|
|
10209
|
+
if (seg.kind === "jsx")
|
|
10210
|
+
attachParsedExpressions(seg.ir, analyzer, loopBound);
|
|
10056
10211
|
}
|
|
10057
10212
|
break;
|
|
10058
10213
|
}
|
|
@@ -10687,74 +10842,78 @@ function transformMultiReturnJsxFunctionCall(callExpr, info, ctx) {
|
|
|
10687
10842
|
ctx.analyzer.getJS = substitutedGetJS;
|
|
10688
10843
|
try {
|
|
10689
10844
|
const loc = getSourceLocation(callExpr, ctx.sourceFile, ctx.filePath);
|
|
10690
|
-
|
|
10691
|
-
type: "expression",
|
|
10692
|
-
expr: "null",
|
|
10693
|
-
typeInfo: { kind: "primitive", raw: "null", primitive: "null" },
|
|
10694
|
-
reactive: false,
|
|
10695
|
-
slotId: null,
|
|
10696
|
-
loc,
|
|
10697
|
-
origin: { phase: "tick", scope: "template", effect: "pure", freeRefs: [] }
|
|
10698
|
-
};
|
|
10699
|
-
let result = info.fallback ? transformNode(info.fallback, ctx) ?? nullExpr : nullExpr;
|
|
10700
|
-
for (let i = info.branches.length - 1;i >= 0; i--) {
|
|
10701
|
-
const branch = info.branches[i];
|
|
10702
|
-
let conditionText;
|
|
10703
|
-
if (info.switchDiscriminant) {
|
|
10704
|
-
const discText = substitutedGetJS(info.switchDiscriminant);
|
|
10705
|
-
const caseText = substitutedGetJS(branch.condition);
|
|
10706
|
-
conditionText = `${discText} === ${caseText}`;
|
|
10707
|
-
} else {
|
|
10708
|
-
conditionText = substitutedGetJS(branch.condition);
|
|
10709
|
-
}
|
|
10710
|
-
const env = makeBindingEnv(ctx);
|
|
10711
|
-
const caseFreeRefs = resolveFreeRefs(branch.condition, env);
|
|
10712
|
-
const discFreeRefs = info.switchDiscriminant ? resolveFreeRefs(info.switchDiscriminant, env) : [];
|
|
10713
|
-
const conditionOrigin = {
|
|
10714
|
-
phase: "tick",
|
|
10715
|
-
scope: "template",
|
|
10716
|
-
effect: "pure",
|
|
10717
|
-
freeRefs: [...discFreeRefs, ...caseFreeRefs]
|
|
10718
|
-
};
|
|
10719
|
-
const reactive = isReactiveExpression(conditionText, ctx, branch.condition) || isReactiveOrigin(conditionOrigin);
|
|
10720
|
-
const loopParamReactive = !reactive && referencesLoopParam(conditionText, ctx);
|
|
10721
|
-
const callsReactive = exprCallsReactiveGetters(branch.condition, ctx) || (info.switchDiscriminant ? exprCallsReactiveGetters(info.switchDiscriminant, ctx) : false);
|
|
10722
|
-
const hasCalls = exprHasFunctionCalls(branch.condition) || (info.switchDiscriminant ? exprHasFunctionCalls(info.switchDiscriminant) : false);
|
|
10723
|
-
const needsSlot = reactive || loopParamReactive || callsReactive || hasCalls;
|
|
10724
|
-
const slotId = needsSlot ? generateSlotId(ctx) : null;
|
|
10725
|
-
const whenTrue = branch.jsxReturn ? transformNode(branch.jsxReturn, ctx) ?? nullExpr : nullExpr;
|
|
10726
|
-
let templateCondition;
|
|
10727
|
-
if (info.switchDiscriminant) {
|
|
10728
|
-
const discRewritten = rewriteBarePropRefs2(substitutedGetJS(info.switchDiscriminant), info.switchDiscriminant, ctx);
|
|
10729
|
-
const caseRewritten = rewriteBarePropRefs2(substitutedGetJS(branch.condition), branch.condition, ctx);
|
|
10730
|
-
const discPart = discRewritten ?? substitutedGetJS(info.switchDiscriminant);
|
|
10731
|
-
const casePart = caseRewritten ?? substitutedGetJS(branch.condition);
|
|
10732
|
-
templateCondition = `${discPart} === ${casePart}`;
|
|
10733
|
-
} else {
|
|
10734
|
-
templateCondition = rewriteBarePropRefs2(conditionText, branch.condition, ctx);
|
|
10735
|
-
}
|
|
10736
|
-
const conditional = {
|
|
10737
|
-
type: "conditional",
|
|
10738
|
-
condition: conditionText,
|
|
10739
|
-
templateCondition,
|
|
10740
|
-
conditionType: null,
|
|
10741
|
-
reactive,
|
|
10742
|
-
whenTrue,
|
|
10743
|
-
whenFalse: result,
|
|
10744
|
-
slotId,
|
|
10745
|
-
callsReactiveGetters: callsReactive || undefined,
|
|
10746
|
-
hasFunctionCalls: hasCalls || undefined,
|
|
10747
|
-
loc,
|
|
10748
|
-
origin: conditionOrigin
|
|
10749
|
-
};
|
|
10750
|
-
result = conditional;
|
|
10751
|
-
}
|
|
10752
|
-
return result;
|
|
10845
|
+
return foldMultiReturnBranches(info, ctx, loc, substitutedGetJS);
|
|
10753
10846
|
} finally {
|
|
10754
10847
|
ctx.getJS = originalCtxGetJS;
|
|
10755
10848
|
ctx.analyzer.getJS = originalAnalyzerGetJS;
|
|
10756
10849
|
}
|
|
10757
10850
|
}
|
|
10851
|
+
function foldMultiReturnBranches(info, ctx, loc, getText) {
|
|
10852
|
+
const nullExpr = {
|
|
10853
|
+
type: "expression",
|
|
10854
|
+
expr: "null",
|
|
10855
|
+
typeInfo: { kind: "primitive", raw: "null", primitive: "null" },
|
|
10856
|
+
reactive: false,
|
|
10857
|
+
slotId: null,
|
|
10858
|
+
loc,
|
|
10859
|
+
origin: { phase: "tick", scope: "template", effect: "pure", freeRefs: [] }
|
|
10860
|
+
};
|
|
10861
|
+
let result = info.fallback ? transformNode(info.fallback, ctx) ?? nullExpr : nullExpr;
|
|
10862
|
+
for (let i = info.branches.length - 1;i >= 0; i--) {
|
|
10863
|
+
const branch = info.branches[i];
|
|
10864
|
+
const caseConds = info.switchDiscriminant ? [branch.condition, ...branch.extraCaseConditions ?? []] : [branch.condition];
|
|
10865
|
+
let conditionText;
|
|
10866
|
+
if (info.switchDiscriminant) {
|
|
10867
|
+
const discText = getText(info.switchDiscriminant);
|
|
10868
|
+
conditionText = caseConds.map((c) => `(${discText}) === (${getText(c)})`).join(" || ");
|
|
10869
|
+
} else {
|
|
10870
|
+
conditionText = getText(branch.condition);
|
|
10871
|
+
}
|
|
10872
|
+
const env = makeBindingEnv(ctx);
|
|
10873
|
+
const caseFreeRefs = caseConds.flatMap((c) => resolveFreeRefs(c, env));
|
|
10874
|
+
const discFreeRefs = info.switchDiscriminant ? resolveFreeRefs(info.switchDiscriminant, env) : [];
|
|
10875
|
+
const conditionOrigin = {
|
|
10876
|
+
phase: "tick",
|
|
10877
|
+
scope: "template",
|
|
10878
|
+
effect: "pure",
|
|
10879
|
+
freeRefs: [...discFreeRefs, ...caseFreeRefs]
|
|
10880
|
+
};
|
|
10881
|
+
const reactive = isReactiveExpression(conditionText, ctx, branch.condition) || isReactiveOrigin(conditionOrigin);
|
|
10882
|
+
const loopParamReactive = !reactive && referencesLoopParam(conditionText, ctx);
|
|
10883
|
+
const callsReactive = caseConds.some((c) => exprCallsReactiveGetters(c, ctx)) || (info.switchDiscriminant ? exprCallsReactiveGetters(info.switchDiscriminant, ctx) : false);
|
|
10884
|
+
const hasCalls = caseConds.some((c) => exprHasFunctionCalls(c)) || (info.switchDiscriminant ? exprHasFunctionCalls(info.switchDiscriminant) : false);
|
|
10885
|
+
const needsSlot = reactive || loopParamReactive || callsReactive || hasCalls;
|
|
10886
|
+
const slotId = needsSlot ? generateSlotId(ctx) : null;
|
|
10887
|
+
const whenTrue = branch.jsxReturn ? transformNode(branch.jsxReturn, ctx) ?? nullExpr : nullExpr;
|
|
10888
|
+
let templateCondition;
|
|
10889
|
+
if (info.switchDiscriminant) {
|
|
10890
|
+
const discRewritten = rewriteBarePropRefs2(getText(info.switchDiscriminant), info.switchDiscriminant, ctx);
|
|
10891
|
+
const discPart = discRewritten ?? getText(info.switchDiscriminant);
|
|
10892
|
+
templateCondition = caseConds.map((c) => {
|
|
10893
|
+
const casePart = rewriteBarePropRefs2(getText(c), c, ctx) ?? getText(c);
|
|
10894
|
+
return `(${discPart}) === (${casePart})`;
|
|
10895
|
+
}).join(" || ");
|
|
10896
|
+
} else {
|
|
10897
|
+
templateCondition = rewriteBarePropRefs2(conditionText, branch.condition, ctx);
|
|
10898
|
+
}
|
|
10899
|
+
const conditional = {
|
|
10900
|
+
type: "conditional",
|
|
10901
|
+
condition: conditionText,
|
|
10902
|
+
templateCondition,
|
|
10903
|
+
conditionType: null,
|
|
10904
|
+
reactive,
|
|
10905
|
+
whenTrue,
|
|
10906
|
+
whenFalse: result,
|
|
10907
|
+
slotId,
|
|
10908
|
+
callsReactiveGetters: callsReactive || undefined,
|
|
10909
|
+
hasFunctionCalls: hasCalls || undefined,
|
|
10910
|
+
loc,
|
|
10911
|
+
origin: conditionOrigin
|
|
10912
|
+
};
|
|
10913
|
+
result = conditional;
|
|
10914
|
+
}
|
|
10915
|
+
return result;
|
|
10916
|
+
}
|
|
10758
10917
|
function transformConditional(node, ctx) {
|
|
10759
10918
|
const condition = ctx.getJS(node.condition);
|
|
10760
10919
|
const conditionOrigin = {
|
|
@@ -11605,9 +11764,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
11605
11764
|
let filterPredicate;
|
|
11606
11765
|
let sortComparator;
|
|
11607
11766
|
let chainOrder;
|
|
11608
|
-
let
|
|
11609
|
-
let templateMapPreamble;
|
|
11610
|
-
let typedMapPreamble;
|
|
11767
|
+
let preamble;
|
|
11611
11768
|
let iterationShape;
|
|
11612
11769
|
let objectIteration;
|
|
11613
11770
|
const setArray = (node2) => {
|
|
@@ -11637,7 +11794,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
11637
11794
|
const innerFilter = isFilterCall(sortInfo.array);
|
|
11638
11795
|
const sortExtraction = extractSortComparator(sortInfo.callback, sortInfo.method, ctx);
|
|
11639
11796
|
if (isClientOnly || !sortExtraction.result) {
|
|
11640
|
-
if (!isClientOnly && sortExtraction.unsupportedReason) {
|
|
11797
|
+
if (!isClientOnly && sortExtraction.unsupportedReason && !(ctx.analyzer.acceptsCallbackBody?.("sort") ?? false)) {
|
|
11641
11798
|
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(sortInfo.callback, ctx.sourceFile, ctx.filePath), {
|
|
11642
11799
|
message: `Expression cannot be compiled to marked template: ${sortExtraction.unsupportedReason}`,
|
|
11643
11800
|
suggestion: {
|
|
@@ -11652,7 +11809,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
11652
11809
|
chainOrder = "filter-sort";
|
|
11653
11810
|
const filterExtraction = extractFilterPredicate(innerFilter.callback, ctx);
|
|
11654
11811
|
if (isClientOnly || !filterExtraction.result) {
|
|
11655
|
-
if (!isClientOnly && filterExtraction.unsupportedReason) {
|
|
11812
|
+
if (!isClientOnly && filterExtraction.unsupportedReason && !(ctx.analyzer.acceptsCallbackBody?.("filter") ?? false)) {
|
|
11656
11813
|
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(innerFilter.callback, ctx.sourceFile, ctx.filePath), {
|
|
11657
11814
|
message: `Expression cannot be compiled to marked template: ${filterExtraction.unsupportedReason}`,
|
|
11658
11815
|
suggestion: {
|
|
@@ -11675,7 +11832,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
11675
11832
|
const innerSort = isSortCall(filterInfo.array);
|
|
11676
11833
|
const filterExtraction = extractFilterPredicate(filterInfo.callback, ctx);
|
|
11677
11834
|
if (isClientOnly || !filterExtraction.result) {
|
|
11678
|
-
if (!isClientOnly && filterExtraction.unsupportedReason) {
|
|
11835
|
+
if (!isClientOnly && filterExtraction.unsupportedReason && !(ctx.analyzer.acceptsCallbackBody?.("filter") ?? false)) {
|
|
11679
11836
|
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(filterInfo.callback, ctx.sourceFile, ctx.filePath), {
|
|
11680
11837
|
message: `Expression cannot be compiled to marked template: ${filterExtraction.unsupportedReason}`,
|
|
11681
11838
|
suggestion: {
|
|
@@ -11690,7 +11847,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
11690
11847
|
chainOrder = "sort-filter";
|
|
11691
11848
|
const sortExtraction = extractSortComparator(innerSort.callback, innerSort.method, ctx);
|
|
11692
11849
|
if (isClientOnly || !sortExtraction.result) {
|
|
11693
|
-
if (!isClientOnly && sortExtraction.unsupportedReason) {
|
|
11850
|
+
if (!isClientOnly && sortExtraction.unsupportedReason && !(ctx.analyzer.acceptsCallbackBody?.("sort") ?? false)) {
|
|
11694
11851
|
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(innerSort.callback, ctx.sourceFile, ctx.filePath), {
|
|
11695
11852
|
message: `Expression cannot be compiled to marked template: ${sortExtraction.unsupportedReason}`,
|
|
11696
11853
|
suggestion: {
|
|
@@ -11805,7 +11962,24 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
11805
11962
|
} else if (method === "flatMap" && ts11.isArrayLiteralExpression(body)) {
|
|
11806
11963
|
children = transformArrayLiteralChildren(body, ctx);
|
|
11807
11964
|
} else if (ts11.isBlock(body)) {
|
|
11808
|
-
const
|
|
11965
|
+
const multiReturn = method !== "flatMap" ? extractMultiReturnJsxBranches(body, true) : null;
|
|
11966
|
+
if (multiReturn && multiReturn.branches.length > 0) {
|
|
11967
|
+
const loc = getSourceLocation(body, ctx.sourceFile, ctx.filePath);
|
|
11968
|
+
children = [foldMultiReturnBranches(multiReturn, ctx, loc, ctx.getJS)];
|
|
11969
|
+
const pre = multiReturn.preamble ?? [];
|
|
11970
|
+
if (pre.length > 0) {
|
|
11971
|
+
preamble = preambleFromValueStatements(pre, ctx);
|
|
11972
|
+
if (!isClientOnly && !(ctx.analyzer.acceptsCallbackBody?.("map") ?? false)) {
|
|
11973
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, loc, {
|
|
11974
|
+
message: "A .map() callback body with a `const`/`let` preamble before its " + "branches cannot be lowered to a template: the loop-local binding " + "cannot be carried into a conditional branch on this backend.",
|
|
11975
|
+
suggestion: {
|
|
11976
|
+
message: "Add /* @client */ to evaluate this expression on the client only"
|
|
11977
|
+
}
|
|
11978
|
+
}));
|
|
11979
|
+
}
|
|
11980
|
+
}
|
|
11981
|
+
}
|
|
11982
|
+
const returnStmt = children.length === 0 ? body.statements.find((s) => ts11.isReturnStatement(s) && s.expression != null) : undefined;
|
|
11809
11983
|
if (returnStmt && returnStmt.expression) {
|
|
11810
11984
|
let returnExpr = returnStmt.expression;
|
|
11811
11985
|
while (ts11.isParenthesizedExpression(returnExpr)) {
|
|
@@ -11817,32 +11991,53 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
11817
11991
|
children = [transformed];
|
|
11818
11992
|
}
|
|
11819
11993
|
}
|
|
11820
|
-
|
|
11821
|
-
const templatePreambleStmts = [];
|
|
11822
|
-
const typedPreambleStmts = [];
|
|
11823
|
-
let hasTypeDiff = false;
|
|
11824
|
-
let hasTemplateDiff = false;
|
|
11994
|
+
let jsxPreambleStmt;
|
|
11825
11995
|
for (const stmt of body.statements) {
|
|
11826
11996
|
if (stmt === returnStmt)
|
|
11827
11997
|
break;
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
templatePreambleStmts.push(tjs.endsWith(";") ? tjs : tjs + ";");
|
|
11833
|
-
typedPreambleStmts.push(ts12.endsWith(";") ? ts12 : ts12 + ";");
|
|
11834
|
-
if (js !== ts12)
|
|
11835
|
-
hasTypeDiff = true;
|
|
11836
|
-
if (js !== tjs)
|
|
11837
|
-
hasTemplateDiff = true;
|
|
11998
|
+
if (containsJsxInExpression(stmt)) {
|
|
11999
|
+
jsxPreambleStmt = stmt;
|
|
12000
|
+
break;
|
|
12001
|
+
}
|
|
11838
12002
|
}
|
|
11839
|
-
if (
|
|
11840
|
-
|
|
11841
|
-
if (
|
|
11842
|
-
|
|
12003
|
+
if (jsxPreambleStmt) {
|
|
12004
|
+
const jsRuntime = isClientOnly || (ctx.analyzer.acceptsCallbackBody?.("map") ?? false);
|
|
12005
|
+
if (children.length === 0) {
|
|
12006
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(jsxPreambleStmt, ctx.sourceFile, ctx.filePath), {
|
|
12007
|
+
message: "A .map() callback that builds JSX in a statement before its " + "`return` must return a single JSX element that embeds the " + "built array — this return shape has no element root to host it.",
|
|
12008
|
+
suggestion: {
|
|
12009
|
+
message: "Wrap the result in one element root, e.g. `return <tr key={item.id}>{out}</tr>`."
|
|
12010
|
+
}
|
|
12011
|
+
}));
|
|
12012
|
+
} else if (!jsRuntime) {
|
|
12013
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(jsxPreambleStmt, ctx.sourceFile, ctx.filePath), {
|
|
12014
|
+
message: "A .map() callback that builds JSX in a statement before its " + "`return` (for example pushing elements into an array in a loop) " + "cannot be lowered to a template on this backend.",
|
|
12015
|
+
suggestion: {
|
|
12016
|
+
message: "Add /* @client */ to render this loop on the client only"
|
|
12017
|
+
}
|
|
12018
|
+
}));
|
|
12019
|
+
} else {
|
|
12020
|
+
const collected = buildPreambleSegments(body.statements, returnStmt, ctx);
|
|
12021
|
+
if (collected.refusalNode) {
|
|
12022
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(collected.refusalNode, ctx.sourceFile, ctx.filePath), {
|
|
12023
|
+
message: "A JSX element built in a .map() callback preamble cannot carry " + "event handlers, components, nested loops, or reactive expressions, " + "and cannot sit inside a template literal — the verbatim render " + "path builds it once, with no reactive wiring.",
|
|
12024
|
+
suggestion: {
|
|
12025
|
+
message: "Return the element directly (not through a preamble variable), " + "or add /* @client */ to render the loop on the client only."
|
|
12026
|
+
}
|
|
12027
|
+
}));
|
|
12028
|
+
} else {
|
|
12029
|
+
preamble = collected.preamble;
|
|
12030
|
+
}
|
|
11843
12031
|
}
|
|
11844
|
-
|
|
11845
|
-
|
|
12032
|
+
} else {
|
|
12033
|
+
const valueStmts = [];
|
|
12034
|
+
for (const stmt of body.statements) {
|
|
12035
|
+
if (stmt === returnStmt)
|
|
12036
|
+
break;
|
|
12037
|
+
valueStmts.push(stmt);
|
|
12038
|
+
}
|
|
12039
|
+
if (valueStmts.length > 0) {
|
|
12040
|
+
preamble = preambleFromValueStatements(valueStmts, ctx);
|
|
11846
12041
|
}
|
|
11847
12042
|
}
|
|
11848
12043
|
}
|
|
@@ -11871,6 +12066,31 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
11871
12066
|
const itemConditional = children.length > 0 ? loopBodyItemConditional(children) : null;
|
|
11872
12067
|
const bodyIsItemConditional = itemConditional !== null;
|
|
11873
12068
|
const key = bodyIsItemConditional ? extractItemConditionalKey(itemConditional) : children.length > 0 ? extractLoopKey(children[0]) : null;
|
|
12069
|
+
const declaredNameSet = preamble && preamble.declaredNames.length > 0 ? new Set(preamble.declaredNames) : undefined;
|
|
12070
|
+
if (key && declaredNameSet) {
|
|
12071
|
+
const keyRefs = extractFreeIdentifiersFromText(key);
|
|
12072
|
+
const usesLocal = [...keyRefs].some((r) => declaredNameSet.has(r));
|
|
12073
|
+
if (usesLocal) {
|
|
12074
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(node, ctx.sourceFile, ctx.filePath), {
|
|
12075
|
+
message: "A .map() loop key must be derivable from the loop item — it is " + "evaluated before the callback body runs, so it cannot reference a " + "value computed in the callback preamble.",
|
|
12076
|
+
suggestion: {
|
|
12077
|
+
message: "Derive the key directly from the loop item (e.g. key={item.id})."
|
|
12078
|
+
}
|
|
12079
|
+
}));
|
|
12080
|
+
}
|
|
12081
|
+
}
|
|
12082
|
+
if (preamble && preamble.builderNames.length > 0) {
|
|
12083
|
+
flagArrayChildExpressions(children, new Set(preamble.builderNames));
|
|
12084
|
+
}
|
|
12085
|
+
if (preamble && preamble.builderNames.length > 0 && children.length === 1 && children[0].type === "component") {
|
|
12086
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(node, ctx.sourceFile, ctx.filePath), {
|
|
12087
|
+
message: "A .map() callback that builds JSX in its preamble cannot return a " + "component: the built elements would reach the component as raw HTML " + "strings on the client but as JSX elements at SSR (a silent divergence).",
|
|
12088
|
+
suggestion: {
|
|
12089
|
+
message: "Return a plain element root that embeds the array, or move the " + "building logic inside the component."
|
|
12090
|
+
}
|
|
12091
|
+
}));
|
|
12092
|
+
preamble = undefined;
|
|
12093
|
+
}
|
|
11874
12094
|
let childComponent;
|
|
11875
12095
|
if (children.length === 1 && children[0].type === "component") {
|
|
11876
12096
|
const comp = children[0];
|
|
@@ -11919,11 +12139,9 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
11919
12139
|
objectIteration,
|
|
11920
12140
|
depth,
|
|
11921
12141
|
clientOnly: isClientOnly || undefined,
|
|
11922
|
-
|
|
11923
|
-
templateMapPreamble,
|
|
12142
|
+
preamble,
|
|
11924
12143
|
paramType,
|
|
11925
12144
|
indexType,
|
|
11926
|
-
typedMapPreamble,
|
|
11927
12145
|
paramBindings,
|
|
11928
12146
|
arrayFreeIdentifiers: extractFreeIdentifiersFromNode(arrayExpr),
|
|
11929
12147
|
flatMapCallback,
|
|
@@ -11959,47 +12177,206 @@ function containsJsx(node) {
|
|
|
11959
12177
|
function buildFlatMapCallback(callback, body, ctx) {
|
|
11960
12178
|
if (!containsJsx(body))
|
|
11961
12179
|
return;
|
|
11962
|
-
const
|
|
11963
|
-
const
|
|
11964
|
-
|
|
11965
|
-
const
|
|
11966
|
-
const bodyText = sourceText.slice(bodyStart, bodyEnd);
|
|
11967
|
-
const jsxNodes = [];
|
|
11968
|
-
function collectJsx(n) {
|
|
12180
|
+
const leafSpans = [];
|
|
12181
|
+
const leafIrs = [];
|
|
12182
|
+
let refusalNode;
|
|
12183
|
+
const collectJsx = (n, underTemplate) => {
|
|
11969
12184
|
if (ts11.isJsxElement(n) || ts11.isJsxSelfClosingElement(n) || ts11.isJsxFragment(n)) {
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
});
|
|
12185
|
+
if (underTemplate)
|
|
12186
|
+
refusalNode ??= n;
|
|
12187
|
+
leafSpans.push({ start: n.getStart(ctx.sourceFile), end: n.getEnd() });
|
|
12188
|
+
const ir = transformNode(n, ctx);
|
|
12189
|
+
leafIrs.push(ir ?? { type: "text", value: "", loc: getSourceLocation(n, ctx.sourceFile, ctx.filePath) });
|
|
11975
12190
|
return;
|
|
11976
12191
|
}
|
|
11977
|
-
n.
|
|
11978
|
-
|
|
11979
|
-
|
|
11980
|
-
|
|
12192
|
+
const inTemplate = underTemplate || ts11.isTemplateExpression(n) || ts11.isTaggedTemplateExpression(n);
|
|
12193
|
+
n.forEachChild((c) => collectJsx(c, inTemplate));
|
|
12194
|
+
};
|
|
12195
|
+
collectJsx(body, false);
|
|
12196
|
+
if (leafSpans.length === 0)
|
|
12197
|
+
return;
|
|
12198
|
+
if (refusalNode) {
|
|
12199
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(refusalNode, ctx.sourceFile, ctx.filePath), {
|
|
12200
|
+
message: "A JSX element inside a template literal in a .flatMap() callback " + "body cannot be compiled.",
|
|
12201
|
+
suggestion: { message: "Build the element outside the template literal." }
|
|
12202
|
+
}));
|
|
11981
12203
|
return;
|
|
11982
|
-
let compiledBody = "";
|
|
11983
|
-
let lastEnd = 0;
|
|
11984
|
-
for (let i = 0;i < jsxNodes.length; i++) {
|
|
11985
|
-
const { node, start, end } = jsxNodes[i];
|
|
11986
|
-
const placeholder = `__BF_JSX_${i}__`;
|
|
11987
|
-
compiledBody += bodyText.slice(lastEnd, start) + placeholder;
|
|
11988
|
-
lastEnd = end;
|
|
11989
|
-
const ir = transformNode(node, ctx);
|
|
11990
|
-
fragments.push({
|
|
11991
|
-
placeholder,
|
|
11992
|
-
ir: ir ?? { type: "text", value: "", loc: getSourceLocation(node, ctx.sourceFile, ctx.filePath) }
|
|
11993
|
-
});
|
|
11994
12204
|
}
|
|
11995
|
-
|
|
12205
|
+
const pieces = reconstructAsSegments(body, ctx.sourceFile, ctx.analyzer.typeExcludeRanges, leafSpans);
|
|
12206
|
+
const segments = pieces.map((piece) => {
|
|
12207
|
+
if ("marker" in piece)
|
|
12208
|
+
return { kind: "jsx", ir: leafIrs[piece.marker] };
|
|
12209
|
+
const tpl = rewriteBarePropRefs2(piece.js, body, ctx);
|
|
12210
|
+
return tpl !== undefined && tpl !== piece.js ? { kind: "js", text: piece.js, templateText: tpl } : { kind: "js", text: piece.js };
|
|
12211
|
+
});
|
|
11996
12212
|
const paramsText = callback.parameters.map((p) => p.getText(ctx.sourceFile)).join(", ");
|
|
11997
12213
|
return {
|
|
11998
12214
|
params: `(${paramsText})`,
|
|
11999
|
-
|
|
12000
|
-
|
|
12001
|
-
|
|
12002
|
-
|
|
12215
|
+
segments,
|
|
12216
|
+
rawBody: tsxSourceText(body.getText(ctx.sourceFile))
|
|
12217
|
+
};
|
|
12218
|
+
}
|
|
12219
|
+
function preambleFragmentNeedsWiring(ir) {
|
|
12220
|
+
switch (ir.type) {
|
|
12221
|
+
case "component":
|
|
12222
|
+
case "loop":
|
|
12223
|
+
return true;
|
|
12224
|
+
case "element":
|
|
12225
|
+
if (ir.events.length > 0)
|
|
12226
|
+
return true;
|
|
12227
|
+
if (ir.attrs.some((a) => a.name.startsWith("...")))
|
|
12228
|
+
return true;
|
|
12229
|
+
return ir.children.some(preambleFragmentNeedsWiring);
|
|
12230
|
+
case "expression":
|
|
12231
|
+
return ir.reactive === true;
|
|
12232
|
+
case "conditional":
|
|
12233
|
+
return preambleFragmentNeedsWiring(ir.whenTrue) || (ir.whenFalse ? preambleFragmentNeedsWiring(ir.whenFalse) : false);
|
|
12234
|
+
case "fragment":
|
|
12235
|
+
return ir.children.some(preambleFragmentNeedsWiring);
|
|
12236
|
+
default:
|
|
12237
|
+
return false;
|
|
12238
|
+
}
|
|
12239
|
+
}
|
|
12240
|
+
function flagArrayChildExpressions(nodes, declared) {
|
|
12241
|
+
for (const node of nodes) {
|
|
12242
|
+
switch (node.type) {
|
|
12243
|
+
case "expression": {
|
|
12244
|
+
const name = node.expr.trim();
|
|
12245
|
+
const refs = extractFreeIdentifiersFromText(node.expr);
|
|
12246
|
+
if (refs.size === 1 && refs.has(name) && declared.has(name)) {
|
|
12247
|
+
node.joinArrayChild = true;
|
|
12248
|
+
}
|
|
12249
|
+
break;
|
|
12250
|
+
}
|
|
12251
|
+
case "element":
|
|
12252
|
+
case "fragment":
|
|
12253
|
+
flagArrayChildExpressions(node.children, declared);
|
|
12254
|
+
break;
|
|
12255
|
+
case "conditional":
|
|
12256
|
+
flagArrayChildExpressions([node.whenTrue, ...node.whenFalse ? [node.whenFalse] : []], declared);
|
|
12257
|
+
break;
|
|
12258
|
+
}
|
|
12259
|
+
}
|
|
12260
|
+
}
|
|
12261
|
+
function collectBindingNames(name, out) {
|
|
12262
|
+
if (ts11.isIdentifier(name)) {
|
|
12263
|
+
out.add(name.text);
|
|
12264
|
+
return;
|
|
12265
|
+
}
|
|
12266
|
+
for (const el of name.elements) {
|
|
12267
|
+
if (ts11.isBindingElement(el))
|
|
12268
|
+
collectBindingNames(el.name, out);
|
|
12269
|
+
}
|
|
12270
|
+
}
|
|
12271
|
+
function collectPreambleDeclaredNames(stmt, out) {
|
|
12272
|
+
if (ts11.isVariableStatement(stmt)) {
|
|
12273
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
12274
|
+
collectBindingNames(decl.name, out);
|
|
12275
|
+
}
|
|
12276
|
+
} else if (ts11.isFunctionDeclaration(stmt) && stmt.name) {
|
|
12277
|
+
out.add(stmt.name.text);
|
|
12278
|
+
}
|
|
12279
|
+
}
|
|
12280
|
+
function preambleFromValueStatements(statements, ctx) {
|
|
12281
|
+
const segments = [];
|
|
12282
|
+
const typedParts = [];
|
|
12283
|
+
const declared = new Set;
|
|
12284
|
+
for (const stmt of statements) {
|
|
12285
|
+
collectPreambleDeclaredNames(stmt, declared);
|
|
12286
|
+
const js0 = ctx.getJS(stmt);
|
|
12287
|
+
const tjs0 = ctx.getTemplateJS(stmt);
|
|
12288
|
+
const raw0 = stmt.getText(ctx.sourceFile);
|
|
12289
|
+
const js = (js0.endsWith(";") ? js0 : js0 + ";") + " ";
|
|
12290
|
+
const tjs = (tjs0.endsWith(";") ? tjs0 : tjs0 + ";") + " ";
|
|
12291
|
+
typedParts.push(raw0.endsWith(";") ? raw0 : raw0 + ";");
|
|
12292
|
+
segments.push(tjs !== js ? { kind: "js", text: js, templateText: tjs } : { kind: "js", text: js });
|
|
12293
|
+
}
|
|
12294
|
+
return {
|
|
12295
|
+
segments: trimPreambleSegments(segments),
|
|
12296
|
+
ssrText: tsxSourceText(typedParts.join(" ")),
|
|
12297
|
+
declaredNames: [...declared],
|
|
12298
|
+
builderNames: []
|
|
12299
|
+
};
|
|
12300
|
+
}
|
|
12301
|
+
function trimPreambleSegments(segments) {
|
|
12302
|
+
const last = segments[segments.length - 1];
|
|
12303
|
+
if (last?.kind === "js") {
|
|
12304
|
+
const text = last.text.trimEnd();
|
|
12305
|
+
const templateText = last.templateText?.trimEnd();
|
|
12306
|
+
segments[segments.length - 1] = templateText !== undefined ? { kind: "js", text, templateText } : { kind: "js", text };
|
|
12307
|
+
}
|
|
12308
|
+
return segments;
|
|
12309
|
+
}
|
|
12310
|
+
function buildPreambleSegments(statements, returnStmt, ctx) {
|
|
12311
|
+
const segments = [];
|
|
12312
|
+
const typedParts = [];
|
|
12313
|
+
const declared = new Set;
|
|
12314
|
+
const builders = new Set;
|
|
12315
|
+
let refusalNode;
|
|
12316
|
+
const recordBuilderTarget = (leaf, stmt) => {
|
|
12317
|
+
for (let n = leaf.parent;n && n !== stmt.parent; n = n.parent) {
|
|
12318
|
+
if (ts11.isCallExpression(n) && ts11.isPropertyAccessExpression(n.expression) && (n.expression.name.text === "push" || n.expression.name.text === "unshift") && ts11.isIdentifier(n.expression.expression)) {
|
|
12319
|
+
builders.add(n.expression.expression.text);
|
|
12320
|
+
return;
|
|
12321
|
+
}
|
|
12322
|
+
if (ts11.isVariableDeclaration(n) && ts11.isIdentifier(n.name)) {
|
|
12323
|
+
builders.add(n.name.text);
|
|
12324
|
+
return;
|
|
12325
|
+
}
|
|
12326
|
+
}
|
|
12327
|
+
};
|
|
12328
|
+
for (const stmt of statements) {
|
|
12329
|
+
if (stmt === returnStmt)
|
|
12330
|
+
break;
|
|
12331
|
+
collectPreambleDeclaredNames(stmt, declared);
|
|
12332
|
+
const leafSpans = [];
|
|
12333
|
+
const leafIrs = [];
|
|
12334
|
+
const collect = (n, underTemplate) => {
|
|
12335
|
+
if (ts11.isJsxElement(n) || ts11.isJsxSelfClosingElement(n) || ts11.isJsxFragment(n)) {
|
|
12336
|
+
if (underTemplate)
|
|
12337
|
+
refusalNode ??= n;
|
|
12338
|
+
recordBuilderTarget(n, stmt);
|
|
12339
|
+
leafSpans.push({ start: n.getStart(ctx.sourceFile), end: n.getEnd() });
|
|
12340
|
+
const ir = transformNode(n, ctx);
|
|
12341
|
+
if (ir && preambleFragmentNeedsWiring(ir))
|
|
12342
|
+
refusalNode ??= n;
|
|
12343
|
+
leafIrs.push(ir ?? { type: "text", value: "", loc: getSourceLocation(n, ctx.sourceFile, ctx.filePath) });
|
|
12344
|
+
return;
|
|
12345
|
+
}
|
|
12346
|
+
const inTemplate = underTemplate || ts11.isTemplateExpression(n) || ts11.isTaggedTemplateExpression(n);
|
|
12347
|
+
n.forEachChild((c) => collect(c, inTemplate));
|
|
12348
|
+
};
|
|
12349
|
+
collect(stmt, false);
|
|
12350
|
+
const raw0 = stmt.getText(ctx.sourceFile);
|
|
12351
|
+
typedParts.push(raw0.endsWith(";") ? raw0 : raw0 + ";");
|
|
12352
|
+
if (leafSpans.length === 0) {
|
|
12353
|
+
const js0 = ctx.getJS(stmt);
|
|
12354
|
+
const tjs0 = ctx.getTemplateJS(stmt);
|
|
12355
|
+
const js = (js0.endsWith(";") ? js0 : js0 + ";") + " ";
|
|
12356
|
+
const tjs = (tjs0.endsWith(";") ? tjs0 : tjs0 + ";") + " ";
|
|
12357
|
+
segments.push(tjs !== js ? { kind: "js", text: js, templateText: tjs } : { kind: "js", text: js });
|
|
12358
|
+
continue;
|
|
12359
|
+
}
|
|
12360
|
+
const pieces = reconstructAsSegments(stmt, ctx.sourceFile, ctx.analyzer.typeExcludeRanges, leafSpans);
|
|
12361
|
+
for (const piece of pieces) {
|
|
12362
|
+
if ("marker" in piece) {
|
|
12363
|
+
segments.push({ kind: "jsx", ir: leafIrs[piece.marker] });
|
|
12364
|
+
} else {
|
|
12365
|
+
const tpl = rewriteBarePropRefs2(piece.js, stmt, ctx);
|
|
12366
|
+
segments.push(tpl !== undefined && tpl !== piece.js ? { kind: "js", text: piece.js, templateText: tpl } : { kind: "js", text: piece.js });
|
|
12367
|
+
}
|
|
12368
|
+
}
|
|
12369
|
+
const sep2 = raw0.endsWith(";") ? " " : "; ";
|
|
12370
|
+
segments.push({ kind: "js", text: sep2 });
|
|
12371
|
+
}
|
|
12372
|
+
return {
|
|
12373
|
+
preamble: {
|
|
12374
|
+
segments: trimPreambleSegments(segments),
|
|
12375
|
+
ssrText: tsxSourceText(typedParts.join(" ")),
|
|
12376
|
+
declaredNames: [...declared],
|
|
12377
|
+
builderNames: [...builders]
|
|
12378
|
+
},
|
|
12379
|
+
refusalNode
|
|
12003
12380
|
};
|
|
12004
12381
|
}
|
|
12005
12382
|
function collectNestedComponents(nodes) {
|
|
@@ -13642,7 +14019,7 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx, options)
|
|
|
13642
14019
|
objectIteration: n.objectIteration,
|
|
13643
14020
|
containerSlotId: scope.parentSlotId,
|
|
13644
14021
|
template,
|
|
13645
|
-
|
|
14022
|
+
preamble: n.preamble,
|
|
13646
14023
|
refsOuterParam: refsOuter,
|
|
13647
14024
|
childComponents,
|
|
13648
14025
|
insideConditional: !flat && scope.insideCond ? true : undefined,
|
|
@@ -13889,7 +14266,7 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
13889
14266
|
raw: l.sortComparator.raw
|
|
13890
14267
|
} : undefined,
|
|
13891
14268
|
chainOrder: l.chainOrder,
|
|
13892
|
-
|
|
14269
|
+
preamble: l.preamble
|
|
13893
14270
|
});
|
|
13894
14271
|
},
|
|
13895
14272
|
component: ({ node: c, descend, descendJsxChildren }) => {
|
|
@@ -14070,7 +14447,7 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
|
|
|
14070
14447
|
objectIteration: n.objectIteration,
|
|
14071
14448
|
template: childTemplate,
|
|
14072
14449
|
containerSlotId: containerSlot,
|
|
14073
|
-
|
|
14450
|
+
preamble: n.preamble,
|
|
14074
14451
|
nestedComponents: useElementReconciliation ? n.nestedComponents : undefined,
|
|
14075
14452
|
bindings: branchBindings,
|
|
14076
14453
|
innerLoops: useElementReconciliation ? innerLoopsCollected : undefined,
|
|
@@ -14346,8 +14723,8 @@ function buildReferencesGraph(ctx, irRoot) {
|
|
|
14346
14723
|
addExprEdges(ROOT_SOURCE, elem.filterPredicate.raw, "template-closure");
|
|
14347
14724
|
if (elem.sortComparator)
|
|
14348
14725
|
addExprEdges(ROOT_SOURCE, elem.sortComparator.raw, "template-closure");
|
|
14349
|
-
if (elem.
|
|
14350
|
-
addExprEdges(ROOT_SOURCE, elem.
|
|
14726
|
+
if (elem.preamble)
|
|
14727
|
+
addExprEdges(ROOT_SOURCE, preambleAnalysisText(elem.preamble), "template-closure");
|
|
14351
14728
|
for (const attr of elem.bindings.reactiveAttrs) {
|
|
14352
14729
|
addExprEdges(ROOT_SOURCE, attr.expression, "template-closure");
|
|
14353
14730
|
}
|
|
@@ -14457,8 +14834,8 @@ function buildReferencesGraph(ctx, irRoot) {
|
|
|
14457
14834
|
addExprEdges(ROOT_SOURCE, l.filterPredicate.raw, "template-closure");
|
|
14458
14835
|
if (l.sortComparator)
|
|
14459
14836
|
addExprEdges(ROOT_SOURCE, l.sortComparator.raw, "template-closure");
|
|
14460
|
-
if (l.
|
|
14461
|
-
addExprEdges(ROOT_SOURCE, l.
|
|
14837
|
+
if (l.preamble)
|
|
14838
|
+
addExprEdges(ROOT_SOURCE, preambleAnalysisText(l.preamble), "template-closure");
|
|
14462
14839
|
descend();
|
|
14463
14840
|
if (l.childComponent)
|
|
14464
14841
|
walkChildComponent(l.childComponent);
|
|
@@ -16567,7 +16944,7 @@ function nestedLoopReferencesIndex(inner, comps, events) {
|
|
|
16567
16944
|
if (exprRefs(a.expression, a.freeIdentifiers))
|
|
16568
16945
|
return true;
|
|
16569
16946
|
}
|
|
16570
|
-
if (inner.
|
|
16947
|
+
if (inner.preamble && extractFreeIdentifiersFromStatementText(preambleAnalysisText(inner.preamble)).has(index))
|
|
16571
16948
|
return true;
|
|
16572
16949
|
if (inner.template && extractFreeIdentifiersFromTemplateText(inner.template).has(index))
|
|
16573
16950
|
return true;
|
|
@@ -16722,6 +17099,11 @@ function emitComponentAndEventSetup(ls, indent, elVar, comps, events, loopParam,
|
|
|
16722
17099
|
}
|
|
16723
17100
|
|
|
16724
17101
|
// src/ir-to-client-js/plan/build-static-array-child-init.ts
|
|
17102
|
+
function staticPreludeStatements(preamble) {
|
|
17103
|
+
return preamble ? [renderPreamble(preamble, {
|
|
17104
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, undefined, undefined, true)
|
|
17105
|
+
})] : [];
|
|
17106
|
+
}
|
|
16725
17107
|
function buildStaticArrayChildInitsPlan(ctx) {
|
|
16726
17108
|
const plans = [];
|
|
16727
17109
|
for (const elem of ctx.loopElements) {
|
|
@@ -16759,7 +17141,7 @@ function buildSingleCompPlan(elem, childComponent) {
|
|
|
16759
17141
|
arrayExpr: elem.array,
|
|
16760
17142
|
param: elem.param,
|
|
16761
17143
|
indexParam: elem.index || "__idx",
|
|
16762
|
-
outerPreludeStatements: elem.
|
|
17144
|
+
outerPreludeStatements: staticPreludeStatements(elem.preamble),
|
|
16763
17145
|
propsExpr: buildStaticPropsExpr(props)
|
|
16764
17146
|
};
|
|
16765
17147
|
}
|
|
@@ -16774,7 +17156,7 @@ function buildOuterNestedPlan(elem, comp) {
|
|
|
16774
17156
|
param: elem.param,
|
|
16775
17157
|
indexParam,
|
|
16776
17158
|
offsetExpr: buildLoopChildIndexExpr(indexParam, elem.offset),
|
|
16777
|
-
outerPreludeStatements: elem.
|
|
17159
|
+
outerPreludeStatements: staticPreludeStatements(elem.preamble),
|
|
16778
17160
|
propsExpr: buildStaticPropsExpr(comp.props)
|
|
16779
17161
|
};
|
|
16780
17162
|
}
|
|
@@ -16793,13 +17175,13 @@ function buildInnerLoopNestedPlan(elem, innerLoop, innerComps) {
|
|
|
16793
17175
|
outerParam: elem.param,
|
|
16794
17176
|
outerIndexParam,
|
|
16795
17177
|
outerOffsetExpr: buildLoopChildIndexExpr(outerIndexParam, elem.offset),
|
|
16796
|
-
outerPreludeStatements: elem.
|
|
17178
|
+
outerPreludeStatements: staticPreludeStatements(elem.preamble),
|
|
16797
17179
|
innerContainerSlotId: innerLoop.containerSlotId ?? null,
|
|
16798
17180
|
innerArrayExpr: innerLoop.array,
|
|
16799
17181
|
innerParam: innerLoop.param,
|
|
16800
17182
|
innerIndexParam,
|
|
16801
17183
|
innerOffsetExpr: buildLoopChildIndexExpr(innerIndexParam, innerLoop.offset),
|
|
16802
|
-
innerPreludeStatements: innerLoop.
|
|
17184
|
+
innerPreludeStatements: staticPreludeStatements(innerLoop.preamble),
|
|
16803
17185
|
depth: innerLoop.depth,
|
|
16804
17186
|
comps
|
|
16805
17187
|
};
|
|
@@ -16816,11 +17198,11 @@ function buildComponentRootedInnerLoopPlan(elem, innerLoop, innerComps) {
|
|
|
16816
17198
|
outerArrayExpr: elem.array,
|
|
16817
17199
|
outerParam: elem.param,
|
|
16818
17200
|
outerIndexParam: elem.index,
|
|
16819
|
-
outerPreludeStatements: elem.
|
|
17201
|
+
outerPreludeStatements: staticPreludeStatements(elem.preamble),
|
|
16820
17202
|
innerArrayExpr: innerLoop.array,
|
|
16821
17203
|
innerParam: innerLoop.param,
|
|
16822
17204
|
innerIndexParam: innerLoop.index,
|
|
16823
|
-
innerPreludeStatements: innerLoop.
|
|
17205
|
+
innerPreludeStatements: staticPreludeStatements(innerLoop.preamble),
|
|
16824
17206
|
depth: innerLoop.depth,
|
|
16825
17207
|
comps
|
|
16826
17208
|
};
|
|
@@ -17389,7 +17771,7 @@ function buildInnerLoopsPlan(args) {
|
|
|
17389
17771
|
const containerExpr = inner.containerSlotId ? `qsa(${parentElVar}, '[bf="${inner.containerSlotId}"]')` : parentElVar;
|
|
17390
17772
|
const refsParent = !!outerLoopParam && (inner.arrayFreeIdentifiers?.has(outerLoopParam) ?? false);
|
|
17391
17773
|
const useReactive = refsParent && !!inner.template;
|
|
17392
|
-
const emit = useReactive ? buildReactiveEmit(inner, level, wrapOuter, uidSuffix) : buildStaticEmit(inner, level, uidSuffix);
|
|
17774
|
+
const emit = useReactive ? buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, outerLoopParamBindings) : buildStaticEmit(inner, level, uidSuffix);
|
|
17393
17775
|
const arrayExpr = useReactive ? wrapOuter(inner.array) : inner.array;
|
|
17394
17776
|
const childLevelsPlan = childLevels.length > 0 ? buildInnerLoopsPlan({
|
|
17395
17777
|
levels: childLevels,
|
|
@@ -17415,7 +17797,7 @@ function buildInnerLoopsPlan(args) {
|
|
|
17415
17797
|
}
|
|
17416
17798
|
return plan;
|
|
17417
17799
|
}
|
|
17418
|
-
function buildReactiveEmit(inner, level, wrapOuter, uidSuffix) {
|
|
17800
|
+
function buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, outerLoopParamBindings) {
|
|
17419
17801
|
const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings);
|
|
17420
17802
|
const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(inner.param, inner.paramBindings);
|
|
17421
17803
|
const wrappedKey = inner.key ? wrapLoopParamAsAccessor(inner.key, inner.param, inner.paramBindings) : null;
|
|
@@ -17467,8 +17849,16 @@ function buildReactiveEmit(inner, level, wrapOuter, uidSuffix) {
|
|
|
17467
17849
|
preludeStatements.push(indexAlias);
|
|
17468
17850
|
if (paramUnwrap)
|
|
17469
17851
|
preludeStatements.push(paramUnwrap);
|
|
17470
|
-
if (inner.
|
|
17471
|
-
|
|
17852
|
+
if (inner.preamble) {
|
|
17853
|
+
const leafLoopParams = outerLoopParam ? [
|
|
17854
|
+
{ param: outerLoopParam, bindings: outerLoopParamBindings },
|
|
17855
|
+
{ param: inner.param, bindings: inner.paramBindings }
|
|
17856
|
+
] : [{ param: inner.param, bindings: inner.paramBindings }];
|
|
17857
|
+
preludeStatements.push(renderPreamble(inner.preamble, {
|
|
17858
|
+
transformJs: (t) => wrapInner(wrapOuter(t)),
|
|
17859
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, leafLoopParams, undefined, true)
|
|
17860
|
+
}));
|
|
17861
|
+
}
|
|
17472
17862
|
const childRefs = buildChildRefBindings(inner.bindings.refs, inner.param, inner.paramBindings);
|
|
17473
17863
|
return {
|
|
17474
17864
|
mode: "reactive",
|
|
@@ -17490,8 +17880,11 @@ function buildStaticEmit(inner, level, uidSuffix) {
|
|
|
17490
17880
|
const indexAlias = nestedLoopIndexAlias(inner, `__innerIdx${uidSuffix}`, inner.param, level.comps, level.events);
|
|
17491
17881
|
if (indexAlias)
|
|
17492
17882
|
preludeStatements.push(indexAlias);
|
|
17493
|
-
if (inner.
|
|
17494
|
-
preludeStatements.push(inner.
|
|
17883
|
+
if (inner.preamble) {
|
|
17884
|
+
preludeStatements.push(renderPreamble(inner.preamble, {
|
|
17885
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, undefined, undefined, true)
|
|
17886
|
+
}));
|
|
17887
|
+
}
|
|
17495
17888
|
return {
|
|
17496
17889
|
mode: "static",
|
|
17497
17890
|
rawKey: inner.key ?? null,
|
|
@@ -17511,6 +17904,7 @@ function buildTopLevelCompositePlan(elem, profileComponentName) {
|
|
|
17511
17904
|
const outerCompsByDepth = nestedComps.filter((c) => !c.loopDepth || c.loopDepth === 0);
|
|
17512
17905
|
return {
|
|
17513
17906
|
kind: "composite",
|
|
17907
|
+
rowConstruction: "string-template",
|
|
17514
17908
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
17515
17909
|
markerId: elem.markerId,
|
|
17516
17910
|
arrayExpr: buildChainedArrayExpr(elem),
|
|
@@ -17518,7 +17912,10 @@ function buildTopLevelCompositePlan(elem, profileComponentName) {
|
|
|
17518
17912
|
paramHead,
|
|
17519
17913
|
paramUnwrap,
|
|
17520
17914
|
indexParam: elem.index || "__idx",
|
|
17521
|
-
mapPreambleWrapped: elem.
|
|
17915
|
+
mapPreambleWrapped: elem.preamble ? renderPreamble(elem.preamble, {
|
|
17916
|
+
transformJs: wrap,
|
|
17917
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined, true)
|
|
17918
|
+
}) : "",
|
|
17522
17919
|
template: elem.template,
|
|
17523
17920
|
outerComps: filterCondCompsOut(outerCompsByDepth, elem.bindings.conditionals),
|
|
17524
17921
|
outerEvents: elem.bindings.events.filter((ev) => ev.nestedLoops.length === 0),
|
|
@@ -17557,6 +17954,7 @@ function buildBranchCompositePlan(loop, cv, profileComponentName) {
|
|
|
17557
17954
|
const outerCompsByDepth = nestedComps.filter((c) => !c.loopDepth || c.loopDepth === 0);
|
|
17558
17955
|
return {
|
|
17559
17956
|
kind: "composite",
|
|
17957
|
+
rowConstruction: "string-template",
|
|
17560
17958
|
containerVar: `__loop_${cv}`,
|
|
17561
17959
|
markerId: loop.markerId,
|
|
17562
17960
|
arrayExpr: buildChainedArrayExpr(loop),
|
|
@@ -17564,7 +17962,10 @@ function buildBranchCompositePlan(loop, cv, profileComponentName) {
|
|
|
17564
17962
|
paramHead,
|
|
17565
17963
|
paramUnwrap,
|
|
17566
17964
|
indexParam: loop.index || "__idx",
|
|
17567
|
-
mapPreambleWrapped: loop.
|
|
17965
|
+
mapPreambleWrapped: loop.preamble ? renderPreamble(loop.preamble, {
|
|
17966
|
+
transformJs: wrap,
|
|
17967
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings }], undefined, true)
|
|
17968
|
+
}) : "",
|
|
17568
17969
|
template: loop.template,
|
|
17569
17970
|
outerComps: filterCondCompsOut(outerCompsByDepth, loop.bindings.conditionals),
|
|
17570
17971
|
outerEvents: childEvents.filter((ev) => ev.nestedLoops.length === 0),
|
|
@@ -17627,7 +18028,9 @@ function buildDynamicLoopDelegationPlan(elem, profileComponentName) {
|
|
|
17627
18028
|
paramBindings: elem.paramBindings,
|
|
17628
18029
|
key: elem.key,
|
|
17629
18030
|
index: elem.index,
|
|
17630
|
-
mapPreamble: elem.
|
|
18031
|
+
mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
|
|
18032
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined, true)
|
|
18033
|
+
}) : null
|
|
17631
18034
|
})
|
|
17632
18035
|
};
|
|
17633
18036
|
}
|
|
@@ -17643,7 +18046,9 @@ function buildBranchLoopDelegationPlan(loop, cv, profileComponentName) {
|
|
|
17643
18046
|
paramBindings: loop.paramBindings,
|
|
17644
18047
|
key: loop.key,
|
|
17645
18048
|
index: loop.index,
|
|
17646
|
-
mapPreamble: loop.
|
|
18049
|
+
mapPreamble: loop.preamble ? renderPreamble(loop.preamble, {
|
|
18050
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings }], undefined, true)
|
|
18051
|
+
}) : null
|
|
17647
18052
|
})
|
|
17648
18053
|
};
|
|
17649
18054
|
}
|
|
@@ -17657,7 +18062,9 @@ function buildStaticArrayDelegationPlan(elem, profileComponentName) {
|
|
|
17657
18062
|
kind: "static-index",
|
|
17658
18063
|
arrayExpr: buildChainedArrayExpr(elem),
|
|
17659
18064
|
param: elem.param,
|
|
17660
|
-
mapPreamble: elem.
|
|
18065
|
+
mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
|
|
18066
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined, true)
|
|
18067
|
+
}) : null,
|
|
17661
18068
|
offset: elem.offset ?? null,
|
|
17662
18069
|
indexParam: elem.index ?? null
|
|
17663
18070
|
}
|
|
@@ -17706,6 +18113,7 @@ function buildBranchLoopPlan(loop, profileComponentName) {
|
|
|
17706
18113
|
const hasReactiveEffects = loop.bindings.reactiveAttrs.length > 0 || loop.bindings.reactiveTexts.length > 0 || loop.bindings.conditionals.length > 0;
|
|
17707
18114
|
const plan = {
|
|
17708
18115
|
kind: "plain",
|
|
18116
|
+
rowConstruction: "string-template",
|
|
17709
18117
|
containerSlotId,
|
|
17710
18118
|
containerVar,
|
|
17711
18119
|
markerId: loop.markerId,
|
|
@@ -17714,7 +18122,10 @@ function buildBranchLoopPlan(loop, profileComponentName) {
|
|
|
17714
18122
|
paramHead,
|
|
17715
18123
|
paramUnwrap,
|
|
17716
18124
|
indexParam: loop.index || "__idx",
|
|
17717
|
-
mapPreambleWrapped: loop.
|
|
18125
|
+
mapPreambleWrapped: loop.preamble ? renderPreamble(loop.preamble, {
|
|
18126
|
+
transformJs: (t) => wrapLoopParamAsAccessor(t, loop.param, loop.paramBindings),
|
|
18127
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: loop.param, bindings: loop.paramBindings }], undefined, true)
|
|
18128
|
+
}) : "",
|
|
17718
18129
|
template: loop.template,
|
|
17719
18130
|
reactiveEffects: hasReactiveEffects ? buildReactiveEffectsPlan({
|
|
17720
18131
|
attrs: loop.bindings.reactiveAttrs,
|
|
@@ -18804,7 +19215,7 @@ function stringifyEventDelegation(lines, plan) {
|
|
|
18804
19215
|
for (const ev of evs) {
|
|
18805
19216
|
const childVar = varSlotId(ev.childSlotId);
|
|
18806
19217
|
lines.push(` const ${childVar}El = target.closest('[bf="${ev.childSlotId}"]')`);
|
|
18807
|
-
lines.push(` if (${childVar}El) {`);
|
|
19218
|
+
lines.push(` if (${childVar}El && ${containerVar}.contains(${childVar}El)) {`);
|
|
18808
19219
|
const handlerCall = withTurn(`(${ev.handler.trim()})(__bfEvt)`, profileComponentName, ev.childSlotId, ev.eventName);
|
|
18809
19220
|
switch (itemLookup.kind) {
|
|
18810
19221
|
case "keyed":
|
|
@@ -19130,6 +19541,7 @@ function buildComponentLoopPlan(elem, profileComponentName) {
|
|
|
19130
19541
|
const hasChildConds = elem.bindings.conditionals.length > 0;
|
|
19131
19542
|
return {
|
|
19132
19543
|
kind: "component",
|
|
19544
|
+
rowConstruction: "dom-ops",
|
|
19133
19545
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
19134
19546
|
markerId: elem.markerId,
|
|
19135
19547
|
arrayExpr: buildChainedArrayExpr(elem),
|
|
@@ -19177,6 +19589,7 @@ function buildPlainLoopPlan(elem, profileComponentName) {
|
|
|
19177
19589
|
const hasReactive2 = elem.bindings.reactiveAttrs.length > 0 || elem.bindings.reactiveTexts.length > 0 || elem.bindings.conditionals.length > 0;
|
|
19178
19590
|
return {
|
|
19179
19591
|
kind: "plain",
|
|
19592
|
+
rowConstruction: "string-template",
|
|
19180
19593
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
19181
19594
|
markerId: elem.markerId,
|
|
19182
19595
|
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : undefined,
|
|
@@ -19185,7 +19598,10 @@ function buildPlainLoopPlan(elem, profileComponentName) {
|
|
|
19185
19598
|
paramHead,
|
|
19186
19599
|
paramUnwrap,
|
|
19187
19600
|
indexParam: elem.index || "__idx",
|
|
19188
|
-
mapPreambleWrapped: elem.
|
|
19601
|
+
mapPreambleWrapped: elem.preamble ? renderPreamble(elem.preamble, {
|
|
19602
|
+
transformJs: wrap,
|
|
19603
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined, true)
|
|
19604
|
+
}) : "",
|
|
19189
19605
|
template: elem.template,
|
|
19190
19606
|
skeletonTemplate: elem.skeletonTemplate,
|
|
19191
19607
|
skeletonPaths: elem.skeletonPaths,
|
|
@@ -19212,6 +19628,7 @@ function buildStaticLoopPlan(elem, unsafeLocalNames, profileComponentName) {
|
|
|
19212
19628
|
const childIndexExpr = buildLoopChildIndexExpr(indexParam, elem.offset);
|
|
19213
19629
|
return {
|
|
19214
19630
|
kind: "static",
|
|
19631
|
+
rowConstruction: "string-template",
|
|
19215
19632
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
19216
19633
|
arrayExpr: elem.array,
|
|
19217
19634
|
param: elem.param,
|
|
@@ -19233,7 +19650,9 @@ function buildStaticLoopMaterialize(elem, unsafeLocalNames) {
|
|
|
19233
19650
|
return null;
|
|
19234
19651
|
return {
|
|
19235
19652
|
itemTemplate: elem.staticItemTemplate,
|
|
19236
|
-
mapPreamble: elem.
|
|
19653
|
+
mapPreamble: elem.preamble ? renderPreamble(elem.preamble, {
|
|
19654
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, [{ param: elem.param, bindings: elem.paramBindings }], undefined, true)
|
|
19655
|
+
}) : "",
|
|
19237
19656
|
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false
|
|
19238
19657
|
};
|
|
19239
19658
|
}
|
|
@@ -19262,6 +19681,7 @@ function emitLoopUpdates(lines, ctx, unsafeLocalNames) {
|
|
|
19262
19681
|
unsafeLocalNames,
|
|
19263
19682
|
profileComponentName: ctx.profile ? ctx.componentName : undefined
|
|
19264
19683
|
});
|
|
19684
|
+
internalInvariant(!(elem.preamble && elem.preamble.builderNames.length > 0 && plan.rowConstruction === "dom-ops"), `loop variant '${plan.kind}' declares dom-ops row construction but received a JSX-bearing preamble — add a Phase-1 refusal (or wire renderPreamble support) for this shape`);
|
|
19265
19685
|
stringifyLoop(lines, plan);
|
|
19266
19686
|
emitLoopEventDelegation(lines, elem, plan.kind, ctx.profile ? ctx.componentName : undefined);
|
|
19267
19687
|
}
|
|
@@ -20192,19 +20612,19 @@ function isJsxLike(expr) {
|
|
|
20192
20612
|
function collectArrowParamNames(arrow) {
|
|
20193
20613
|
const names = new Set;
|
|
20194
20614
|
for (const p of arrow.parameters)
|
|
20195
|
-
|
|
20615
|
+
collectBindingNames2(p.name, names);
|
|
20196
20616
|
return names;
|
|
20197
20617
|
}
|
|
20198
|
-
function
|
|
20618
|
+
function collectBindingNames2(name, out) {
|
|
20199
20619
|
const push = Array.isArray(out) ? (n) => out.push(n) : (n) => out.add(n);
|
|
20200
20620
|
if (ts16.isIdentifier(name)) {
|
|
20201
20621
|
push(name.text);
|
|
20202
20622
|
} else if (ts16.isObjectBindingPattern(name)) {
|
|
20203
|
-
name.elements.forEach((el) =>
|
|
20623
|
+
name.elements.forEach((el) => collectBindingNames2(el.name, out));
|
|
20204
20624
|
} else if (ts16.isArrayBindingPattern(name)) {
|
|
20205
20625
|
name.elements.forEach((el) => {
|
|
20206
20626
|
if (!ts16.isOmittedExpression(el))
|
|
20207
|
-
|
|
20627
|
+
collectBindingNames2(el.name, out);
|
|
20208
20628
|
});
|
|
20209
20629
|
}
|
|
20210
20630
|
}
|
|
@@ -20213,12 +20633,12 @@ function collectFreeIdentifiers(arrow) {
|
|
|
20213
20633
|
const bound = [];
|
|
20214
20634
|
for (const p of arrow.parameters) {
|
|
20215
20635
|
const names = [];
|
|
20216
|
-
|
|
20636
|
+
collectBindingNames2(p.name, names);
|
|
20217
20637
|
bound.push(...names);
|
|
20218
20638
|
}
|
|
20219
20639
|
function pushBindings(name) {
|
|
20220
20640
|
const names = [];
|
|
20221
|
-
|
|
20641
|
+
collectBindingNames2(name, names);
|
|
20222
20642
|
bound.push(...names);
|
|
20223
20643
|
return names;
|
|
20224
20644
|
}
|
|
@@ -20345,7 +20765,7 @@ function collectModuleScopeNames(sourceFile) {
|
|
|
20345
20765
|
names.add(stmt.name.text);
|
|
20346
20766
|
else if (ts16.isVariableStatement(stmt)) {
|
|
20347
20767
|
for (const decl of stmt.declarationList.declarations)
|
|
20348
|
-
|
|
20768
|
+
collectBindingNames2(decl.name, names);
|
|
20349
20769
|
} else if (ts16.isImportDeclaration(stmt) && stmt.importClause) {
|
|
20350
20770
|
const ic = stmt.importClause;
|
|
20351
20771
|
if (ic.name)
|
|
@@ -21364,8 +21784,13 @@ function walkNode(node, meta, bindings, matchers, errors, seen) {
|
|
|
21364
21784
|
for (const child of nested.children)
|
|
21365
21785
|
walkNode(child, meta, loopBindings, matchers, errors, seen);
|
|
21366
21786
|
}
|
|
21367
|
-
for (const
|
|
21368
|
-
|
|
21787
|
+
for (const seg of node.flatMapCallback?.segments ?? []) {
|
|
21788
|
+
if (seg.kind === "jsx")
|
|
21789
|
+
walkNode(seg.ir, meta, loopBindings, matchers, errors, seen);
|
|
21790
|
+
}
|
|
21791
|
+
for (const seg of node.preamble?.segments ?? []) {
|
|
21792
|
+
if (seg.kind === "jsx")
|
|
21793
|
+
walkNode(seg.ir, meta, loopBindings, matchers, errors, seen);
|
|
21369
21794
|
}
|
|
21370
21795
|
break;
|
|
21371
21796
|
}
|
|
@@ -21427,7 +21852,7 @@ function compileMultipleComponents(source, filePath, componentNames, options) {
|
|
|
21427
21852
|
const entries = [];
|
|
21428
21853
|
const program = options.program ?? (needsTypeBasedDetection(source) ? createProgramForFile(source, filePath)?.program : undefined);
|
|
21429
21854
|
for (const componentName of componentNames) {
|
|
21430
|
-
const ctx = analyzeComponent(source, filePath, componentName, program);
|
|
21855
|
+
const ctx = analyzeComponent(source, filePath, componentName, program, adapter.acceptsCallbackBody);
|
|
21431
21856
|
if (!ctx.jsxReturn) {
|
|
21432
21857
|
errors.push(...ctx.errors);
|
|
21433
21858
|
continue;
|
|
@@ -21736,7 +22161,7 @@ function compileJSX(source, filePath, options) {
|
|
|
21736
22161
|
if (componentNames.length > 1) {
|
|
21737
22162
|
return compileMultipleComponents(compileSource, filePath, componentNames, options);
|
|
21738
22163
|
}
|
|
21739
|
-
const ctx = analyzeComponent(compileSource, filePath, undefined, options.program);
|
|
22164
|
+
const ctx = analyzeComponent(compileSource, filePath, undefined, options.program, options.adapter.acceptsCallbackBody);
|
|
21740
22165
|
if (!ctx.jsxReturn) {
|
|
21741
22166
|
errors.push(...ctx.errors);
|
|
21742
22167
|
const exportedModuleSignals = ctx.signals.filter((s2) => s2.isModule && s2.isExported);
|
|
@@ -21936,6 +22361,7 @@ class BaseAdapter {
|
|
|
21936
22361
|
import { BF_SCOPE as BF_SCOPE4, BF_SLOT, BF_COND } from "@barefootjs/shared";
|
|
21937
22362
|
class JsxAdapter extends BaseAdapter {
|
|
21938
22363
|
componentName = "";
|
|
22364
|
+
acceptsCallbackBody = () => true;
|
|
21939
22365
|
formatImportSpecifiers(specifiers) {
|
|
21940
22366
|
const defaultSpec = specifiers.find((s) => s.isDefault);
|
|
21941
22367
|
const namespaceSpec = specifiers.find((s) => s.isNamespace);
|
|
@@ -22281,6 +22707,10 @@ export default ${this.componentName}` : "";
|
|
|
22281
22707
|
const indexParam = loop.index ? `, ${loop.index}` : "";
|
|
22282
22708
|
const children = this.renderChildren(loop.children);
|
|
22283
22709
|
const safeChildren = children.startsWith("{") ? `<>${children}</>` : children;
|
|
22710
|
+
const preamble = loop.preamble?.ssrText;
|
|
22711
|
+
if (preamble) {
|
|
22712
|
+
return `{${loop.array}.map((${loop.param}${indexParam}) => { ${preamble} return ${safeChildren} })}`;
|
|
22713
|
+
}
|
|
22284
22714
|
return `{${loop.array}.map((${loop.param}${indexParam}) => ${safeChildren})}`;
|
|
22285
22715
|
}
|
|
22286
22716
|
renderComponent(comp) {
|
|
@@ -22481,8 +22911,13 @@ function collectLoopBoundNames(ir) {
|
|
|
22481
22911
|
for (const child of nested.children)
|
|
22482
22912
|
visit3(child);
|
|
22483
22913
|
}
|
|
22484
|
-
for (const
|
|
22485
|
-
|
|
22914
|
+
for (const seg of node.flatMapCallback?.segments ?? []) {
|
|
22915
|
+
if (seg.kind === "jsx")
|
|
22916
|
+
visit3(seg.ir);
|
|
22917
|
+
}
|
|
22918
|
+
for (const seg of node.preamble?.segments ?? []) {
|
|
22919
|
+
if (seg.kind === "jsx")
|
|
22920
|
+
visit3(seg.ir);
|
|
22486
22921
|
}
|
|
22487
22922
|
break;
|
|
22488
22923
|
case "conditional":
|
|
@@ -23058,12 +23493,16 @@ function restNamesMisused(loop, names) {
|
|
|
23058
23493
|
check(l.array);
|
|
23059
23494
|
check(l.templateArray);
|
|
23060
23495
|
check(l.key);
|
|
23061
|
-
|
|
23062
|
-
|
|
23496
|
+
if (l.preamble) {
|
|
23497
|
+
check(preambleAnalysisText(l.preamble));
|
|
23498
|
+
check(preambleAnalysisTemplateText(l.preamble));
|
|
23499
|
+
}
|
|
23063
23500
|
if (l.flatMapCallback) {
|
|
23064
|
-
check(l.flatMapCallback
|
|
23065
|
-
|
|
23066
|
-
|
|
23501
|
+
check(preambleAnalysisText(l.flatMapCallback));
|
|
23502
|
+
for (const seg of l.flatMapCallback.segments) {
|
|
23503
|
+
if (seg.kind === "jsx")
|
|
23504
|
+
visit3(seg.ir);
|
|
23505
|
+
}
|
|
23067
23506
|
}
|
|
23068
23507
|
l.children.forEach(visit3);
|
|
23069
23508
|
};
|