@barefootjs/test 0.32.0 → 0.33.1
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 +128 -37
- package/dist/resolve-constants.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/resolve-constants.ts +3 -0
package/dist/index.js
CHANGED
|
@@ -187454,7 +187454,7 @@ function convertNode(node, raw) {
|
|
|
187454
187454
|
}
|
|
187455
187455
|
if (n === undefined || Number.isNaN(n)) {
|
|
187456
187456
|
const parsedDepth = convertNode(depthNode, raw);
|
|
187457
|
-
if (checkSupport(parsedDepth).supported) {
|
|
187457
|
+
if (checkSupport(parsedDepth, "rendered").supported) {
|
|
187458
187458
|
depthExpr = parsedDepth;
|
|
187459
187459
|
flatDepth = 1;
|
|
187460
187460
|
} else {
|
|
@@ -187548,10 +187548,12 @@ function convertNode(node, raw) {
|
|
|
187548
187548
|
const k = objectLiteralKeyName(prop.name);
|
|
187549
187549
|
if (k === null)
|
|
187550
187550
|
return { kind: "unsupported", raw, reason: `Unsupported syntax: ${import_typescript.default.SyntaxKind[node.kind]}` };
|
|
187551
|
-
properties.push({ key: k.key, keyKind: k.keyKind, shorthand: false, value: convertNode(prop.initializer, raw) });
|
|
187551
|
+
properties.push({ kind: "prop", key: k.key, keyKind: k.keyKind, shorthand: false, value: convertNode(prop.initializer, raw) });
|
|
187552
187552
|
} else if (import_typescript.default.isShorthandPropertyAssignment(prop)) {
|
|
187553
187553
|
const key = prop.name.text;
|
|
187554
|
-
properties.push({ key, keyKind: "identifier", shorthand: true, value: { kind: "identifier", name: key } });
|
|
187554
|
+
properties.push({ kind: "prop", key, keyKind: "identifier", shorthand: true, value: { kind: "identifier", name: key } });
|
|
187555
|
+
} else if (import_typescript.default.isSpreadAssignment(prop)) {
|
|
187556
|
+
properties.push({ kind: "spread", expr: convertNode(prop.expression, raw) });
|
|
187555
187557
|
} else {
|
|
187556
187558
|
return { kind: "unsupported", raw, reason: `Unsupported syntax: ${import_typescript.default.SyntaxKind[node.kind]}` };
|
|
187557
187559
|
}
|
|
@@ -188231,14 +188233,23 @@ function getUnaryOperatorString(op) {
|
|
|
188231
188233
|
}
|
|
188232
188234
|
}
|
|
188233
188235
|
function isSupported(expr) {
|
|
188234
|
-
return checkSupport(expr);
|
|
188236
|
+
return checkSupport(expr, "rendered");
|
|
188235
188237
|
}
|
|
188236
|
-
function checkSupport(expr) {
|
|
188238
|
+
function checkSupport(expr, pos) {
|
|
188237
188239
|
switch (expr.kind) {
|
|
188238
188240
|
case "unsupported":
|
|
188239
188241
|
return { supported: false, reason: expr.reason };
|
|
188240
|
-
case "object-literal":
|
|
188241
|
-
|
|
188242
|
+
case "object-literal": {
|
|
188243
|
+
if (pos !== "value") {
|
|
188244
|
+
return { supported: false, reason: "Unsupported syntax: ObjectLiteralExpression" };
|
|
188245
|
+
}
|
|
188246
|
+
for (const prop of expr.properties) {
|
|
188247
|
+
const propSupport = checkSupport(prop.kind === "spread" ? prop.expr : prop.value, pos);
|
|
188248
|
+
if (!propSupport.supported)
|
|
188249
|
+
return propSupport;
|
|
188250
|
+
}
|
|
188251
|
+
return { supported: true, level: "L2" };
|
|
188252
|
+
}
|
|
188242
188253
|
case "identifier":
|
|
188243
188254
|
return { supported: true, level: "L1" };
|
|
188244
188255
|
case "literal":
|
|
@@ -188248,7 +188259,7 @@ function checkSupport(expr) {
|
|
|
188248
188259
|
return { supported: false, reason: "Standalone arrow functions / regex literals are not supported" };
|
|
188249
188260
|
case "array-literal": {
|
|
188250
188261
|
for (const el of expr.elements) {
|
|
188251
|
-
const elSupport = checkSupport(el);
|
|
188262
|
+
const elSupport = checkSupport(el, pos);
|
|
188252
188263
|
if (!elSupport.supported)
|
|
188253
188264
|
return elSupport;
|
|
188254
188265
|
}
|
|
@@ -188261,16 +188272,16 @@ function checkSupport(expr) {
|
|
|
188261
188272
|
reason: `String.prototype.${expr.method} supports only a string pattern + string replacement (the regex form is deferred); use a string pattern or wrap the expression in /* @client */`
|
|
188262
188273
|
};
|
|
188263
188274
|
}
|
|
188264
|
-
const objSupport = checkSupport(expr.object);
|
|
188275
|
+
const objSupport = checkSupport(expr.object, pos);
|
|
188265
188276
|
if (!objSupport.supported)
|
|
188266
188277
|
return objSupport;
|
|
188267
188278
|
for (const arg of expr.args) {
|
|
188268
|
-
const argSupport = checkSupport(arg);
|
|
188279
|
+
const argSupport = checkSupport(arg, pos);
|
|
188269
188280
|
if (!argSupport.supported)
|
|
188270
188281
|
return argSupport;
|
|
188271
188282
|
}
|
|
188272
188283
|
if (expr.method === "flat" && expr.depthExpr) {
|
|
188273
|
-
const depthSupport = checkSupport(expr.depthExpr);
|
|
188284
|
+
const depthSupport = checkSupport(expr.depthExpr, pos);
|
|
188274
188285
|
if (!depthSupport.supported)
|
|
188275
188286
|
return depthSupport;
|
|
188276
188287
|
}
|
|
@@ -188279,10 +188290,10 @@ function checkSupport(expr) {
|
|
|
188279
188290
|
case "call": {
|
|
188280
188291
|
const cb = asCallbackMethodCall(expr);
|
|
188281
188292
|
if (cb) {
|
|
188282
|
-
const objSupport = checkSupport(cb.object);
|
|
188293
|
+
const objSupport = checkSupport(cb.object, pos);
|
|
188283
188294
|
if (!objSupport.supported)
|
|
188284
188295
|
return objSupport;
|
|
188285
|
-
const bodySupport = checkSupport(cb.arrow.body);
|
|
188296
|
+
const bodySupport = checkSupport(cb.arrow.body, pos);
|
|
188286
188297
|
if (!bodySupport.supported) {
|
|
188287
188298
|
return {
|
|
188288
188299
|
supported: false,
|
|
@@ -188291,13 +188302,13 @@ function checkSupport(expr) {
|
|
|
188291
188302
|
};
|
|
188292
188303
|
}
|
|
188293
188304
|
for (const rest of cb.args) {
|
|
188294
|
-
const restSupport = checkSupport(rest);
|
|
188305
|
+
const restSupport = checkSupport(rest, pos);
|
|
188295
188306
|
if (!restSupport.supported)
|
|
188296
188307
|
return restSupport;
|
|
188297
188308
|
}
|
|
188298
188309
|
return { supported: true, level: "L5" };
|
|
188299
188310
|
}
|
|
188300
|
-
const calleeSupport = checkSupport(expr.callee);
|
|
188311
|
+
const calleeSupport = checkSupport(expr.callee, pos);
|
|
188301
188312
|
if (!calleeSupport.supported) {
|
|
188302
188313
|
return calleeSupport;
|
|
188303
188314
|
}
|
|
@@ -188316,7 +188327,7 @@ function checkSupport(expr) {
|
|
|
188316
188327
|
return { supported: true, level: "L1" };
|
|
188317
188328
|
}
|
|
188318
188329
|
for (const arg of expr.args) {
|
|
188319
|
-
const argSupport = checkSupport(arg);
|
|
188330
|
+
const argSupport = checkSupport(arg, pos);
|
|
188320
188331
|
if (!argSupport.supported) {
|
|
188321
188332
|
return argSupport;
|
|
188322
188333
|
}
|
|
@@ -188324,7 +188335,7 @@ function checkSupport(expr) {
|
|
|
188324
188335
|
return { supported: true, level: "L2" };
|
|
188325
188336
|
}
|
|
188326
188337
|
case "member": {
|
|
188327
|
-
const objSupport = checkSupport(expr.object);
|
|
188338
|
+
const objSupport = checkSupport(expr.object, pos);
|
|
188328
188339
|
if (!objSupport.supported) {
|
|
188329
188340
|
return objSupport;
|
|
188330
188341
|
}
|
|
@@ -188334,19 +188345,19 @@ function checkSupport(expr) {
|
|
|
188334
188345
|
return { supported: true, level: "L2" };
|
|
188335
188346
|
}
|
|
188336
188347
|
case "index-access": {
|
|
188337
|
-
const objSupport = checkSupport(expr.object);
|
|
188348
|
+
const objSupport = checkSupport(expr.object, pos);
|
|
188338
188349
|
if (!objSupport.supported)
|
|
188339
188350
|
return objSupport;
|
|
188340
|
-
const indexSupport = checkSupport(expr.index);
|
|
188351
|
+
const indexSupport = checkSupport(expr.index, pos);
|
|
188341
188352
|
if (!indexSupport.supported)
|
|
188342
188353
|
return indexSupport;
|
|
188343
188354
|
return { supported: true, level: "L2" };
|
|
188344
188355
|
}
|
|
188345
188356
|
case "binary": {
|
|
188346
|
-
const leftSupport = checkSupport(expr.left);
|
|
188357
|
+
const leftSupport = checkSupport(expr.left, pos);
|
|
188347
188358
|
if (!leftSupport.supported)
|
|
188348
188359
|
return leftSupport;
|
|
188349
|
-
const rightSupport = checkSupport(expr.right);
|
|
188360
|
+
const rightSupport = checkSupport(expr.right, pos);
|
|
188350
188361
|
if (!rightSupport.supported)
|
|
188351
188362
|
return rightSupport;
|
|
188352
188363
|
if (["===", "==", "!==", "!=", ">", "<", ">=", "<="].includes(expr.op)) {
|
|
@@ -188358,7 +188369,7 @@ function checkSupport(expr) {
|
|
|
188358
188369
|
return { supported: false, reason: `Unknown operator: ${expr.op}` };
|
|
188359
188370
|
}
|
|
188360
188371
|
case "unary": {
|
|
188361
|
-
const argSupport = checkSupport(expr.argument);
|
|
188372
|
+
const argSupport = checkSupport(expr.argument, pos);
|
|
188362
188373
|
if (!argSupport.supported)
|
|
188363
188374
|
return argSupport;
|
|
188364
188375
|
if (expr.op === "!") {
|
|
@@ -188370,25 +188381,25 @@ function checkSupport(expr) {
|
|
|
188370
188381
|
return { supported: false, reason: `Unsupported unary operator: ${expr.op}` };
|
|
188371
188382
|
}
|
|
188372
188383
|
case "logical": {
|
|
188373
|
-
const leftSupport = checkSupport(expr.left);
|
|
188384
|
+
const leftSupport = checkSupport(expr.left, pos);
|
|
188374
188385
|
if (!leftSupport.supported)
|
|
188375
188386
|
return leftSupport;
|
|
188376
188387
|
if (expr.op === "??" && expr.right.kind === "object-literal" && expr.right.properties.length === 0) {
|
|
188377
188388
|
return { supported: true, level: "L4" };
|
|
188378
188389
|
}
|
|
188379
|
-
const rightSupport = checkSupport(expr.right);
|
|
188390
|
+
const rightSupport = checkSupport(expr.right, pos);
|
|
188380
188391
|
if (!rightSupport.supported)
|
|
188381
188392
|
return rightSupport;
|
|
188382
188393
|
return { supported: true, level: "L4" };
|
|
188383
188394
|
}
|
|
188384
188395
|
case "conditional": {
|
|
188385
|
-
const testSupport = checkSupport(expr.test);
|
|
188396
|
+
const testSupport = checkSupport(expr.test, pos);
|
|
188386
188397
|
if (!testSupport.supported)
|
|
188387
188398
|
return testSupport;
|
|
188388
|
-
const consSupport = checkSupport(expr.consequent);
|
|
188399
|
+
const consSupport = checkSupport(expr.consequent, pos);
|
|
188389
188400
|
if (!consSupport.supported)
|
|
188390
188401
|
return consSupport;
|
|
188391
|
-
const altSupport = checkSupport(expr.alternate);
|
|
188402
|
+
const altSupport = checkSupport(expr.alternate, pos);
|
|
188392
188403
|
if (!altSupport.supported)
|
|
188393
188404
|
return altSupport;
|
|
188394
188405
|
return { supported: true, level: "L4" };
|
|
@@ -188396,7 +188407,7 @@ function checkSupport(expr) {
|
|
|
188396
188407
|
case "template-literal": {
|
|
188397
188408
|
for (const part of expr.parts) {
|
|
188398
188409
|
if (part.type === "expression") {
|
|
188399
|
-
const partSupport = checkSupport(part.expr);
|
|
188410
|
+
const partSupport = checkSupport(part.expr, pos);
|
|
188400
188411
|
if (!partSupport.supported)
|
|
188401
188412
|
return partSupport;
|
|
188402
188413
|
}
|
|
@@ -188521,7 +188532,7 @@ function isPureInit(e, pureCallNames) {
|
|
|
188521
188532
|
case "array-literal":
|
|
188522
188533
|
return e.elements.every(pure);
|
|
188523
188534
|
case "object-literal":
|
|
188524
|
-
return e.properties.every((p) => pure(p.value));
|
|
188535
|
+
return e.properties.every((p) => pure(p.kind === "spread" ? p.expr : p.value));
|
|
188525
188536
|
case "call":
|
|
188526
188537
|
return e.callee.kind === "identifier" && e.args.length === 0 && pureCallNames !== undefined && pureCallNames.has(e.callee.name);
|
|
188527
188538
|
case "array-method":
|
|
@@ -188575,7 +188586,7 @@ function usesPerPath(name, expr) {
|
|
|
188575
188586
|
}
|
|
188576
188587
|
return add(walk(e.object), sum(e.args));
|
|
188577
188588
|
case "object-literal":
|
|
188578
|
-
return sum(e.properties.map((p) => p.value));
|
|
188589
|
+
return sum(e.properties.map((p) => p.kind === "spread" ? p.expr : p.value));
|
|
188579
188590
|
case "arrow":
|
|
188580
188591
|
return walk(e.body).max > 0 ? { min: 0, max: Number.POSITIVE_INFINITY } : { min: 0, max: 0 };
|
|
188581
188592
|
}
|
|
@@ -188641,7 +188652,7 @@ function inlineBinding(expr, name, value) {
|
|
|
188641
188652
|
case "object-literal":
|
|
188642
188653
|
return {
|
|
188643
188654
|
kind: "object-literal",
|
|
188644
|
-
properties: e.properties.map((p) => ({ ...p, value: walk(p.value, enclosing) })
|
|
188655
|
+
properties: e.properties.map((p) => p.kind === "spread" ? { ...p, expr: walk(p.expr, enclosing) } : { ...p, value: walk(p.value, enclosing) }),
|
|
188645
188656
|
raw: e.raw
|
|
188646
188657
|
};
|
|
188647
188658
|
case "literal":
|
|
@@ -190085,6 +190096,7 @@ var ErrorCodes = {
|
|
|
190085
190096
|
MISSING_KEY_IN_LIST: "BF023",
|
|
190086
190097
|
MISSING_KEY_IN_NESTED_LIST: "BF024",
|
|
190087
190098
|
UNSUPPORTED_DESTRUCTURE_REST: "BF025",
|
|
190099
|
+
RETURN_VALUE_NOT_JSX: "BF027",
|
|
190088
190100
|
PROPS_DESTRUCTURING: "BF043",
|
|
190089
190101
|
SIGNAL_GETTER_NOT_CALLED: "BF044",
|
|
190090
190102
|
JSX_IN_LOCAL_FUNCTION: "BF045",
|
|
@@ -190116,6 +190128,7 @@ var errorMessages = {
|
|
|
190116
190128
|
[ErrorCodes.MISSING_KEY_IN_LIST]: "Missing key attribute in list rendering. Add a key prop for efficient updates",
|
|
190117
190129
|
[ErrorCodes.MISSING_KEY_IN_NESTED_LIST]: "Nested .map() loop requires key attribute for event delegation. Add a key prop to elements in the inner loop",
|
|
190118
190130
|
[ErrorCodes.UNSUPPORTED_DESTRUCTURE_REST]: "Computed property key in .map() callback destructure is not supported. Rewrite the callback to destructure explicit bindings (e.g., `({ a, b }) => ...`) so the compiler can rewrite references to per-item signal accessors.",
|
|
190131
|
+
[ErrorCodes.RETURN_VALUE_NOT_JSX]: "Component's return value is not recognized as JSX — return the JSX expression directly instead of binding it to a local variable first.",
|
|
190119
190132
|
[ErrorCodes.PROPS_DESTRUCTURING]: "Props destructuring in function parameters breaks reactivity. Use props object directly.",
|
|
190120
190133
|
[ErrorCodes.SIGNAL_GETTER_NOT_CALLED]: "Signal/memo getter passed without calling it. Use getter() to read the value.",
|
|
190121
190134
|
[ErrorCodes.JSX_IN_LOCAL_FUNCTION]: "Local function returns JSX but cannot be inlined. Extract it as a top-level PascalCase component or use a single return statement.",
|
|
@@ -190685,6 +190698,14 @@ function visitComponentBody(node, ctx) {
|
|
|
190685
190698
|
}
|
|
190686
190699
|
}
|
|
190687
190700
|
if (isTopLevel && (import_typescript9.default.isTryStatement(node) || import_typescript9.default.isSwitchStatement(node) || import_typescript9.default.isForStatement(node) || import_typescript9.default.isForInStatement(node) || import_typescript9.default.isForOfStatement(node) || import_typescript9.default.isWhileStatement(node) || import_typescript9.default.isDoStatement(node) || import_typescript9.default.isThrowStatement(node) || import_typescript9.default.isBlock(node) && node.parent === ctx.componentBodyBlock)) {
|
|
190701
|
+
if (import_typescript9.default.isBlock(node)) {
|
|
190702
|
+
const returnedLocal = findBlockBodyReturnedJsxLocalName(node);
|
|
190703
|
+
if (returnedLocal) {
|
|
190704
|
+
ctx.errors.push(createError(ErrorCodes.RETURN_VALUE_NOT_JSX, getSourceLocation(node, ctx.sourceFile, ctx.filePath), {
|
|
190705
|
+
message: `Component '${ctx.componentName ?? "(unknown)"}' return value is not recognized ` + `as JSX — return the JSX expression directly instead of binding it to a local ` + `variable first (\`return ${returnedLocal}\` after \`const ${returnedLocal} = ` + `<jsx/>\` is not resolved at return position).`
|
|
190706
|
+
}));
|
|
190707
|
+
}
|
|
190708
|
+
}
|
|
190688
190709
|
collectInitStatement(node, ctx);
|
|
190689
190710
|
return;
|
|
190690
190711
|
}
|
|
@@ -190720,6 +190741,31 @@ function unwrapJsxTransparent(expr) {
|
|
|
190720
190741
|
}
|
|
190721
190742
|
return current;
|
|
190722
190743
|
}
|
|
190744
|
+
function findBlockBodyReturnedJsxLocalName(block) {
|
|
190745
|
+
const stmts = block.statements;
|
|
190746
|
+
const last = stmts[stmts.length - 1];
|
|
190747
|
+
if (!last || !import_typescript9.default.isReturnStatement(last) || !last.expression)
|
|
190748
|
+
return null;
|
|
190749
|
+
const returned = unwrapJsxTransparent(last.expression);
|
|
190750
|
+
if (!import_typescript9.default.isIdentifier(returned))
|
|
190751
|
+
return null;
|
|
190752
|
+
const name = returned.text;
|
|
190753
|
+
for (const stmt of stmts) {
|
|
190754
|
+
if (!import_typescript9.default.isVariableStatement(stmt))
|
|
190755
|
+
continue;
|
|
190756
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
190757
|
+
if (!import_typescript9.default.isIdentifier(decl.name) || decl.name.text !== name || !decl.initializer)
|
|
190758
|
+
continue;
|
|
190759
|
+
let init = decl.initializer;
|
|
190760
|
+
while (import_typescript9.default.isParenthesizedExpression(init))
|
|
190761
|
+
init = init.expression;
|
|
190762
|
+
if (import_typescript9.default.isJsxElement(init) || import_typescript9.default.isJsxSelfClosingElement(init) || import_typescript9.default.isJsxFragment(init) || initializerShapeContainsJsx(init) || isMapLikeCallWithJsx(init)) {
|
|
190763
|
+
return name;
|
|
190764
|
+
}
|
|
190765
|
+
}
|
|
190766
|
+
}
|
|
190767
|
+
return null;
|
|
190768
|
+
}
|
|
190723
190769
|
function extractJsxFromExpression(expr) {
|
|
190724
190770
|
const inner = unwrapJsxTransparent(expr);
|
|
190725
190771
|
if (import_typescript9.default.isJsxElement(inner) || import_typescript9.default.isJsxFragment(inner) || import_typescript9.default.isJsxSelfClosingElement(inner)) {
|
|
@@ -194362,6 +194408,8 @@ function matchToLocaleDateStringCall(callee, args, metadata) {
|
|
|
194362
194408
|
let tz = null;
|
|
194363
194409
|
const probeOptions = {};
|
|
194364
194410
|
for (const prop of options.properties) {
|
|
194411
|
+
if (prop.kind === "spread")
|
|
194412
|
+
return null;
|
|
194365
194413
|
if (prop.value.kind !== "literal" || prop.value.literalType !== "string")
|
|
194366
194414
|
return null;
|
|
194367
194415
|
const value = String(prop.value.value);
|
|
@@ -194934,8 +194982,14 @@ function buildIRRoot(analyzer) {
|
|
|
194934
194982
|
}
|
|
194935
194983
|
ctx.isRoot = false;
|
|
194936
194984
|
const ir = transformJsxExpression(jsxReturn, ctx);
|
|
194937
|
-
if (ir === null)
|
|
194985
|
+
if (ir === null) {
|
|
194986
|
+
if (import_typescript13.default.isIdentifier(jsxReturn) && (analyzer.jsxConstants.has(jsxReturn.text) || analyzer.inlineableJsxConsts.has(jsxReturn.text))) {
|
|
194987
|
+
analyzer.errors.push(createError(ErrorCodes.RETURN_VALUE_NOT_JSX, getSourceLocation(jsxReturn, analyzer.sourceFile, analyzer.filePath), {
|
|
194988
|
+
message: `Component '${analyzer.componentName ?? "(unknown)"}' return value is not recognized ` + `as JSX — return the JSX expression directly instead of binding it to a local variable ` + `first (\`return ${jsxReturn.text}\` after \`const ${jsxReturn.text} = <jsx/>\` is not ` + `resolved at return position).`
|
|
194989
|
+
}));
|
|
194990
|
+
}
|
|
194938
194991
|
return null;
|
|
194992
|
+
}
|
|
194939
194993
|
return wrapInScopeElement(ir);
|
|
194940
194994
|
}
|
|
194941
194995
|
function needsScopeWrapper(ir) {
|
|
@@ -196126,7 +196180,10 @@ function resolveCallbackMethodFunctionReferences(expr, analyzer, bound = EMPTY_B
|
|
|
196126
196180
|
...e.method === "flat" && e.depthExpr ? { depthExpr: visit2(e.depthExpr, bound2) } : {}
|
|
196127
196181
|
};
|
|
196128
196182
|
case "object-literal":
|
|
196129
|
-
return {
|
|
196183
|
+
return {
|
|
196184
|
+
...e,
|
|
196185
|
+
properties: e.properties.map((p) => p.kind === "spread" ? { ...p, expr: visit2(p.expr, bound2) } : { ...p, value: visit2(p.value, bound2) })
|
|
196186
|
+
};
|
|
196130
196187
|
case "arrow": {
|
|
196131
196188
|
const inner = e.params.length === 0 ? bound2 : new Set([...bound2, ...e.params]);
|
|
196132
196189
|
return { ...e, body: visit2(e.body, inner) };
|
|
@@ -198047,6 +198104,35 @@ function getStringValue(node) {
|
|
|
198047
198104
|
}
|
|
198048
198105
|
return null;
|
|
198049
198106
|
}
|
|
198107
|
+
function unwrapTransparentTsWrappers(node) {
|
|
198108
|
+
let n = node;
|
|
198109
|
+
while (import_typescript13.default.isParenthesizedExpression(n) || import_typescript13.default.isAsExpression(n) || import_typescript13.default.isSatisfiesExpression(n) || import_typescript13.default.isNonNullExpression(n)) {
|
|
198110
|
+
n = n.expression;
|
|
198111
|
+
}
|
|
198112
|
+
return n;
|
|
198113
|
+
}
|
|
198114
|
+
function expressionWrapsJsx(node) {
|
|
198115
|
+
const n = unwrapTransparentTsWrappers(node);
|
|
198116
|
+
if (import_typescript13.default.isJsxElement(n) || import_typescript13.default.isJsxSelfClosingElement(n) || import_typescript13.default.isJsxFragment(n))
|
|
198117
|
+
return true;
|
|
198118
|
+
if (import_typescript13.default.isConditionalExpression(n)) {
|
|
198119
|
+
return expressionWrapsJsx(n.whenTrue) || expressionWrapsJsx(n.whenFalse);
|
|
198120
|
+
}
|
|
198121
|
+
if (import_typescript13.default.isArrayLiteralExpression(n)) {
|
|
198122
|
+
return n.elements.some((el) => expressionWrapsJsx(import_typescript13.default.isSpreadElement(el) ? el.expression : el));
|
|
198123
|
+
}
|
|
198124
|
+
return false;
|
|
198125
|
+
}
|
|
198126
|
+
function reportNakedJsxWrapperProp(ctx, attr, propName, jsxExpr) {
|
|
198127
|
+
const shape = import_typescript13.default.isConditionalExpression(jsxExpr) ? "a ternary" : "an array literal";
|
|
198128
|
+
ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(attr, ctx.sourceFile, ctx.filePath), {
|
|
198129
|
+
message: `Prop '${propName}' is ${shape} wrapping JSX (${jsxExpr.getText(ctx.sourceFile)}). ` + `This shape is not compiled — only a JSX element/fragment given DIRECTLY as the prop value is.`,
|
|
198130
|
+
suggestion: {
|
|
198131
|
+
message: `Move the conditional/array out of the prop position: compute it in a local ` + `const and pass it as the component's children instead of a named prop ` + `(e.g. const ${propName} = ${jsxExpr.getText(ctx.sourceFile)}; <Comp>{${propName}}</Comp>). ` + `Wrapping the ternary/array in a fragment at the prop position ` + `(${propName}={<>{${jsxExpr.getText(ctx.sourceFile)}}</>}) is NOT a safe escape here: it compiles, ` + `but the child's own reactive prop getter receives the branch's HTML unbranded and re-escapes it as ` + `text on the child's very next reactive run, corrupting the DOM (a narrower gap #2651's door ` + `inventory left open — tracked separately).`,
|
|
198132
|
+
escape: [{ kind: "rewrite" }]
|
|
198133
|
+
}
|
|
198134
|
+
}));
|
|
198135
|
+
}
|
|
198050
198136
|
function processComponentProps(attributes, ctx) {
|
|
198051
198137
|
const props = [];
|
|
198052
198138
|
for (const attr of attributes.properties) {
|
|
@@ -198058,10 +198144,7 @@ function processComponentProps(attributes, ctx) {
|
|
|
198058
198144
|
continue;
|
|
198059
198145
|
const name = attr.name.getText(ctx.sourceFile);
|
|
198060
198146
|
if (attr.initializer && import_typescript13.default.isJsxExpression(attr.initializer) && attr.initializer.expression) {
|
|
198061
|
-
|
|
198062
|
-
while (import_typescript13.default.isParenthesizedExpression(jsxExpr)) {
|
|
198063
|
-
jsxExpr = jsxExpr.expression;
|
|
198064
|
-
}
|
|
198147
|
+
const jsxExpr = unwrapTransparentTsWrappers(attr.initializer.expression);
|
|
198065
198148
|
if (import_typescript13.default.isJsxElement(jsxExpr) || import_typescript13.default.isJsxSelfClosingElement(jsxExpr) || import_typescript13.default.isJsxFragment(jsxExpr)) {
|
|
198066
198149
|
const prevInsideComponentChildren = ctx.insideComponentChildren;
|
|
198067
198150
|
ctx.insideComponentChildren = true;
|
|
@@ -198076,6 +198159,10 @@ function processComponentProps(attributes, ctx) {
|
|
|
198076
198159
|
continue;
|
|
198077
198160
|
}
|
|
198078
198161
|
}
|
|
198162
|
+
if ((import_typescript13.default.isConditionalExpression(jsxExpr) || import_typescript13.default.isArrayLiteralExpression(jsxExpr)) && expressionWrapsJsx(jsxExpr)) {
|
|
198163
|
+
reportNakedJsxWrapperProp(ctx, attr, name, jsxExpr);
|
|
198164
|
+
continue;
|
|
198165
|
+
}
|
|
198079
198166
|
}
|
|
198080
198167
|
let value = getAttributeValue(attr, ctx);
|
|
198081
198168
|
if (value.kind === "template") {
|
|
@@ -199533,6 +199620,8 @@ function matchQueryHrefCall(callee, args, localNames) {
|
|
|
199533
199620
|
return null;
|
|
199534
199621
|
const triples = [];
|
|
199535
199622
|
for (const p of obj.properties) {
|
|
199623
|
+
if (p.kind === "spread")
|
|
199624
|
+
return null;
|
|
199536
199625
|
const v = p.value;
|
|
199537
199626
|
if (v.kind === "conditional" && isOmitBranch(v.alternate)) {
|
|
199538
199627
|
triples.push({ guard: v.test, key: p.key, value: v.consequent });
|
|
@@ -200241,6 +200330,8 @@ function resolveConstants(constants3) {
|
|
|
200241
200330
|
for (const c of constants3) {
|
|
200242
200331
|
if (c.parsed?.kind === "object-literal") {
|
|
200243
200332
|
for (const prop of c.parsed.properties) {
|
|
200333
|
+
if (prop.kind === "spread")
|
|
200334
|
+
continue;
|
|
200244
200335
|
const value2 = resolveObjectPropValue(prop.value, resolved);
|
|
200245
200336
|
if (value2 !== null)
|
|
200246
200337
|
resolved.set(`${c.name}.${prop.key}`, value2);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-constants.d.ts","sourceRoot":"","sources":["../src/resolve-constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,GAChG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"resolve-constants.d.ts","sourceRoot":"","sources":["../src/resolve-constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,GAChG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA6CrB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.1",
|
|
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.
|
|
42
|
+
"@barefootjs/jsx": "0.33.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"typescript": "^5.0.0"
|
package/src/resolve-constants.ts
CHANGED
|
@@ -39,6 +39,9 @@ export function resolveConstants(
|
|
|
39
39
|
// below (Copilot review, PR #2361).
|
|
40
40
|
if (c.parsed?.kind === 'object-literal') {
|
|
41
41
|
for (const prop of c.parsed.properties) {
|
|
42
|
+
// A spread (`{ ...t }`, #2696 Step 2) has no static `name.key`
|
|
43
|
+
// member path to expose — skip it, same as an unresolvable value.
|
|
44
|
+
if (prop.kind === 'spread') continue
|
|
42
45
|
const value = resolveObjectPropValue(prop.value, resolved)
|
|
43
46
|
if (value !== null) resolved.set(`${c.name}.${prop.key}`, value)
|
|
44
47
|
}
|