@barefootjs/test 0.26.3 → 0.26.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +192 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -196006,6 +196006,87 @@ function checkLoopKey(callback, ctx, isNested) {
|
|
|
196006
196006
|
return;
|
|
196007
196007
|
}
|
|
196008
196008
|
}
|
|
196009
|
+
function flatMapProjectionCall(body) {
|
|
196010
|
+
let expr;
|
|
196011
|
+
if (import_typescript11.default.isBlock(body)) {
|
|
196012
|
+
const real = body.statements;
|
|
196013
|
+
if (real.length !== 1 || !import_typescript11.default.isReturnStatement(real[0]) || !real[0].expression)
|
|
196014
|
+
return null;
|
|
196015
|
+
expr = real[0].expression;
|
|
196016
|
+
} else {
|
|
196017
|
+
expr = body;
|
|
196018
|
+
}
|
|
196019
|
+
while (import_typescript11.default.isParenthesizedExpression(expr))
|
|
196020
|
+
expr = expr.expression;
|
|
196021
|
+
if (!import_typescript11.default.isCallExpression(expr))
|
|
196022
|
+
return null;
|
|
196023
|
+
if (!getMapLikeMethod(expr))
|
|
196024
|
+
return null;
|
|
196025
|
+
const cb = expr.arguments[0];
|
|
196026
|
+
if (!cb || !import_typescript11.default.isArrowFunction(cb) && !import_typescript11.default.isFunctionExpression(cb))
|
|
196027
|
+
return null;
|
|
196028
|
+
for (const p of cb.parameters) {
|
|
196029
|
+
if (!import_typescript11.default.isIdentifier(p.name))
|
|
196030
|
+
return null;
|
|
196031
|
+
}
|
|
196032
|
+
let innerBody = cb.body;
|
|
196033
|
+
if (import_typescript11.default.isBlock(innerBody)) {
|
|
196034
|
+
const ret = innerBody.statements.find((s) => import_typescript11.default.isReturnStatement(s) && s.expression != null);
|
|
196035
|
+
if (innerBody.statements.length !== 1 || !ret?.expression)
|
|
196036
|
+
return null;
|
|
196037
|
+
innerBody = ret.expression;
|
|
196038
|
+
}
|
|
196039
|
+
while (import_typescript11.default.isParenthesizedExpression(innerBody))
|
|
196040
|
+
innerBody = innerBody.expression;
|
|
196041
|
+
const isElementish = (n) => {
|
|
196042
|
+
let m = n;
|
|
196043
|
+
while (import_typescript11.default.isParenthesizedExpression(m))
|
|
196044
|
+
m = m.expression;
|
|
196045
|
+
if (import_typescript11.default.isJsxElement(m) || import_typescript11.default.isJsxSelfClosingElement(m))
|
|
196046
|
+
return leafIsWirelessElement(m);
|
|
196047
|
+
if (import_typescript11.default.isConditionalExpression(m))
|
|
196048
|
+
return isElementish(m.whenTrue) && isElementish(m.whenFalse);
|
|
196049
|
+
return false;
|
|
196050
|
+
};
|
|
196051
|
+
if (!isElementish(innerBody))
|
|
196052
|
+
return null;
|
|
196053
|
+
return expr;
|
|
196054
|
+
}
|
|
196055
|
+
function leafIsWirelessElement(el) {
|
|
196056
|
+
let ok = true;
|
|
196057
|
+
const visit2 = (n) => {
|
|
196058
|
+
if (!ok)
|
|
196059
|
+
return;
|
|
196060
|
+
if (import_typescript11.default.isJsxOpeningElement(n) || import_typescript11.default.isJsxSelfClosingElement(n)) {
|
|
196061
|
+
const tagNode = n.tagName;
|
|
196062
|
+
const isIntrinsic = import_typescript11.default.isIdentifier(tagNode) ? !/^[A-Z]/.test(tagNode.text) : import_typescript11.default.isJsxNamespacedName(tagNode);
|
|
196063
|
+
if (!isIntrinsic) {
|
|
196064
|
+
ok = false;
|
|
196065
|
+
return;
|
|
196066
|
+
}
|
|
196067
|
+
for (const attr of n.attributes.properties) {
|
|
196068
|
+
if (import_typescript11.default.isJsxSpreadAttribute(attr)) {
|
|
196069
|
+
ok = false;
|
|
196070
|
+
return;
|
|
196071
|
+
}
|
|
196072
|
+
if (import_typescript11.default.isJsxAttribute(attr)) {
|
|
196073
|
+
const name = attr.name.getText();
|
|
196074
|
+
if (/^on[A-Z]/.test(name)) {
|
|
196075
|
+
ok = false;
|
|
196076
|
+
return;
|
|
196077
|
+
}
|
|
196078
|
+
}
|
|
196079
|
+
}
|
|
196080
|
+
}
|
|
196081
|
+
if (import_typescript11.default.isCallExpression(n) && getMapLikeMethod(n) && containsJsxInExpression(n)) {
|
|
196082
|
+
ok = false;
|
|
196083
|
+
return;
|
|
196084
|
+
}
|
|
196085
|
+
import_typescript11.default.forEachChild(n, visit2);
|
|
196086
|
+
};
|
|
196087
|
+
visit2(el);
|
|
196088
|
+
return ok;
|
|
196089
|
+
}
|
|
196009
196090
|
function loopBodyIsMultiRoot(children) {
|
|
196010
196091
|
const real = children.filter((c) => !(c.type === "text" && typeof c.value === "string" && !c.value.trim()));
|
|
196011
196092
|
if (real.length === 0)
|
|
@@ -196051,6 +196132,7 @@ function extractItemConditionalKey(cond) {
|
|
|
196051
196132
|
}
|
|
196052
196133
|
function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
196053
196134
|
const isNested = ctx.loopParams.size > 0;
|
|
196135
|
+
const diagCountAtEntry = ctx.analyzer.errors.length;
|
|
196054
196136
|
const depth = ctx.loopDepth;
|
|
196055
196137
|
const propAccess = node.expression;
|
|
196056
196138
|
const mapSource = propAccess.expression;
|
|
@@ -196337,12 +196419,34 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
196337
196419
|
}
|
|
196338
196420
|
}
|
|
196339
196421
|
}
|
|
196340
|
-
if (method === "flatMap" && children.length === 0) {
|
|
196422
|
+
if (method === "flatMap" && children.length === 0 && !flatMapProjectionCall(body)) {
|
|
196341
196423
|
flatMapCallback = buildFlatMapCallback(callback, body, ctx);
|
|
196342
196424
|
}
|
|
196343
196425
|
} else {
|
|
196344
196426
|
tryTransformRenderableBody(body);
|
|
196345
196427
|
}
|
|
196428
|
+
if (method === "flatMap" && children.length === 0 && !flatMapCallback) {
|
|
196429
|
+
const projection = flatMapProjectionCall(body);
|
|
196430
|
+
if (projection) {
|
|
196431
|
+
const transformed = transformJsxExpression(projection, ctx, isClientOnly);
|
|
196432
|
+
if (transformed && transformed.type === "loop") {
|
|
196433
|
+
children = [transformed];
|
|
196434
|
+
}
|
|
196435
|
+
}
|
|
196436
|
+
}
|
|
196437
|
+
if (method === "flatMap" && children.length === 0 && !flatMapCallback && !import_typescript11.default.isBlock(body)) {
|
|
196438
|
+
flatMapCallback = buildFlatMapCallback(callback, body, ctx);
|
|
196439
|
+
}
|
|
196440
|
+
if (flatMapCallback)
|
|
196441
|
+
preamble = undefined;
|
|
196442
|
+
if (flatMapCallback && !isClientOnly && !(ctx.analyzer.acceptsCallbackBody?.("flatMap") ?? false)) {
|
|
196443
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(body, ctx.sourceFile, ctx.filePath), {
|
|
196444
|
+
message: "A .flatMap() callback body with statements or a nested projection " + "cannot be lowered to a template on this backend.",
|
|
196445
|
+
suggestion: {
|
|
196446
|
+
message: "Add /* @client */ to render this loop on the client only"
|
|
196447
|
+
}
|
|
196448
|
+
}));
|
|
196449
|
+
}
|
|
196346
196450
|
if (paramBindings) {
|
|
196347
196451
|
for (const b of paramBindings)
|
|
196348
196452
|
ctx.loopParams.delete(b.name);
|
|
@@ -196354,6 +196458,16 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
196354
196458
|
ctx.loopDepth--;
|
|
196355
196459
|
}
|
|
196356
196460
|
if (children.length === 0 && !flatMapCallback) {
|
|
196461
|
+
const cb = node.arguments[0];
|
|
196462
|
+
const cbBody = cb && (import_typescript11.default.isArrowFunction(cb) || import_typescript11.default.isFunctionExpression(cb)) ? cb.body : undefined;
|
|
196463
|
+
if (cbBody && containsJsxInExpression(cbBody) && ctx.analyzer.errors.length === diagCountAtEntry) {
|
|
196464
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(cbBody, ctx.sourceFile, ctx.filePath), {
|
|
196465
|
+
message: `A .${method}() callback that builds JSX in this shape cannot be ` + "compiled — the JSX would leak verbatim into the client bundle. " + "Recognized bodies: a JSX element/fragment, a ternary or " + "&& / || / ?? expression, an array literal (flatMap), or a block " + "body whose return the compiler can lower.",
|
|
196466
|
+
suggestion: {
|
|
196467
|
+
message: "Restructure the callback to return the JSX element directly " + "(or via a block body with a plain `return`)."
|
|
196468
|
+
}
|
|
196469
|
+
}));
|
|
196470
|
+
}
|
|
196357
196471
|
return null;
|
|
196358
196472
|
}
|
|
196359
196473
|
if (import_typescript11.default.isArrowFunction(node.arguments[0]) && children.length > 0) {
|
|
@@ -196406,6 +196520,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
196406
196520
|
const hasCalls = exprHasFunctionCalls(arrayExpr);
|
|
196407
196521
|
const isDirectPropArray = method !== "flatMap" && isArrayExprDirectPropRef(arrayExpr, ctx);
|
|
196408
196522
|
const isStaticArray = !isSignalOrMemoArray(array, ctx) && !isDirectPropArray && !hasCalls && !objectIteration;
|
|
196523
|
+
const preambleRegions = preamble && !isStaticArray ? collectPreambleRegions(children, new Set(preamble.declaredNames), ctx) : undefined;
|
|
196409
196524
|
const nestedComponents = collectNestedComponents(children).filter((c) => c.name !== childComponent?.name);
|
|
196410
196525
|
return {
|
|
196411
196526
|
type: "loop",
|
|
@@ -196436,6 +196551,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
196436
196551
|
depth,
|
|
196437
196552
|
clientOnly: isClientOnly || undefined,
|
|
196438
196553
|
preamble,
|
|
196554
|
+
preambleRegions: preambleRegions && preambleRegions.length > 0 ? preambleRegions : undefined,
|
|
196439
196555
|
paramType,
|
|
196440
196556
|
indexType,
|
|
196441
196557
|
paramBindings,
|
|
@@ -196498,6 +196614,28 @@ function buildFlatMapCallback(callback, body, ctx) {
|
|
|
196498
196614
|
}));
|
|
196499
196615
|
return;
|
|
196500
196616
|
}
|
|
196617
|
+
for (const leafIr of leafIrs) {
|
|
196618
|
+
if (leafIr.type !== "element") {
|
|
196619
|
+
const loc = "loc" in leafIr && leafIr.loc ? leafIr.loc : getSourceLocation(body, ctx.sourceFile, ctx.filePath);
|
|
196620
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, loc, {
|
|
196621
|
+
message: "A JSX leaf produced by a .flatMap() callback must be a single " + "element — a fragment or non-element root cannot ride the keyed " + "descriptor path (each leaf hydrates and patches as one element).",
|
|
196622
|
+
suggestion: {
|
|
196623
|
+
message: "Wrap the leaf content in a single keyed element."
|
|
196624
|
+
}
|
|
196625
|
+
}));
|
|
196626
|
+
return;
|
|
196627
|
+
}
|
|
196628
|
+
if (flatMapLeafNeedsWiring(leafIr)) {
|
|
196629
|
+
const loc = "loc" in leafIr && leafIr.loc ? leafIr.loc : getSourceLocation(body, ctx.sourceFile, ctx.filePath);
|
|
196630
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, loc, {
|
|
196631
|
+
message: "A JSX element produced by a .flatMap() callback cannot carry " + "event handlers, components, nested loops, or spreads — the " + "leaf renders as a keyed HTML string with no per-element wiring.",
|
|
196632
|
+
suggestion: {
|
|
196633
|
+
message: "Restructure so the interactive element lives in a .map() body — " + "the descriptor path has no per-element wiring on any backend, " + "so /* @client */ does not lift this."
|
|
196634
|
+
}
|
|
196635
|
+
}));
|
|
196636
|
+
return;
|
|
196637
|
+
}
|
|
196638
|
+
}
|
|
196501
196639
|
const pieces = reconstructAsSegments(body, ctx.sourceFile, ctx.analyzer.typeExcludeRanges, leafSpans);
|
|
196502
196640
|
const segments = pieces.map((piece) => {
|
|
196503
196641
|
if ("marker" in piece)
|
|
@@ -196512,6 +196650,25 @@ function buildFlatMapCallback(callback, body, ctx) {
|
|
|
196512
196650
|
rawBody: tsxSourceText(body.getText(ctx.sourceFile))
|
|
196513
196651
|
};
|
|
196514
196652
|
}
|
|
196653
|
+
function flatMapLeafNeedsWiring(ir) {
|
|
196654
|
+
switch (ir.type) {
|
|
196655
|
+
case "component":
|
|
196656
|
+
case "loop":
|
|
196657
|
+
return true;
|
|
196658
|
+
case "element":
|
|
196659
|
+
if (ir.events.length > 0)
|
|
196660
|
+
return true;
|
|
196661
|
+
if (ir.attrs.some((a) => a.name.startsWith("...")))
|
|
196662
|
+
return true;
|
|
196663
|
+
return ir.children.some(flatMapLeafNeedsWiring);
|
|
196664
|
+
case "conditional":
|
|
196665
|
+
return flatMapLeafNeedsWiring(ir.whenTrue) || (ir.whenFalse ? flatMapLeafNeedsWiring(ir.whenFalse) : false);
|
|
196666
|
+
case "fragment":
|
|
196667
|
+
return ir.children.some(flatMapLeafNeedsWiring);
|
|
196668
|
+
default:
|
|
196669
|
+
return false;
|
|
196670
|
+
}
|
|
196671
|
+
}
|
|
196515
196672
|
function preambleFragmentNeedsWiring(ir) {
|
|
196516
196673
|
switch (ir.type) {
|
|
196517
196674
|
case "component":
|
|
@@ -196554,6 +196711,40 @@ function flagArrayChildExpressions(nodes, declared) {
|
|
|
196554
196711
|
}
|
|
196555
196712
|
}
|
|
196556
196713
|
}
|
|
196714
|
+
function collectPreambleRegions(nodes, declared, ctx) {
|
|
196715
|
+
const regions = [];
|
|
196716
|
+
const visit2 = (list) => {
|
|
196717
|
+
for (const node of list) {
|
|
196718
|
+
switch (node.type) {
|
|
196719
|
+
case "expression": {
|
|
196720
|
+
const refs = extractFreeIdentifiersFromText(node.expr);
|
|
196721
|
+
const usesPreambleLocal = [...refs].some((r) => declared.has(r));
|
|
196722
|
+
if (usesPreambleLocal) {
|
|
196723
|
+
if (!node.slotId)
|
|
196724
|
+
node.slotId = generateSlotId(ctx);
|
|
196725
|
+
node.preambleRegion = true;
|
|
196726
|
+
node.reactive = true;
|
|
196727
|
+
regions.push({
|
|
196728
|
+
slotId: node.slotId,
|
|
196729
|
+
expr: node.expr,
|
|
196730
|
+
joinArrayChild: node.joinArrayChild || undefined
|
|
196731
|
+
});
|
|
196732
|
+
}
|
|
196733
|
+
break;
|
|
196734
|
+
}
|
|
196735
|
+
case "element":
|
|
196736
|
+
case "fragment":
|
|
196737
|
+
visit2(node.children);
|
|
196738
|
+
break;
|
|
196739
|
+
case "conditional":
|
|
196740
|
+
visit2([node.whenTrue, ...node.whenFalse ? [node.whenFalse] : []]);
|
|
196741
|
+
break;
|
|
196742
|
+
}
|
|
196743
|
+
}
|
|
196744
|
+
};
|
|
196745
|
+
visit2(nodes);
|
|
196746
|
+
return regions;
|
|
196747
|
+
}
|
|
196557
196748
|
function collectBindingNames(name, out) {
|
|
196558
196749
|
if (import_typescript11.default.isIdentifier(name)) {
|
|
196559
196750
|
out.add(name.text);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/test",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.4",
|
|
4
4
|
"description": "Test utilities for BarefootJS - IR-based component testing without a browser",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"directory": "packages/test"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@barefootjs/jsx": "0.26.
|
|
42
|
+
"@barefootjs/jsx": "0.26.4"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"typescript": "^5.0.0"
|