@barefootjs/test 0.33.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.
Files changed (2) hide show
  1. package/dist/index.js +76 -5
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -190096,6 +190096,7 @@ var ErrorCodes = {
190096
190096
  MISSING_KEY_IN_LIST: "BF023",
190097
190097
  MISSING_KEY_IN_NESTED_LIST: "BF024",
190098
190098
  UNSUPPORTED_DESTRUCTURE_REST: "BF025",
190099
+ RETURN_VALUE_NOT_JSX: "BF027",
190099
190100
  PROPS_DESTRUCTURING: "BF043",
190100
190101
  SIGNAL_GETTER_NOT_CALLED: "BF044",
190101
190102
  JSX_IN_LOCAL_FUNCTION: "BF045",
@@ -190127,6 +190128,7 @@ var errorMessages = {
190127
190128
  [ErrorCodes.MISSING_KEY_IN_LIST]: "Missing key attribute in list rendering. Add a key prop for efficient updates",
190128
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",
190129
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.",
190130
190132
  [ErrorCodes.PROPS_DESTRUCTURING]: "Props destructuring in function parameters breaks reactivity. Use props object directly.",
190131
190133
  [ErrorCodes.SIGNAL_GETTER_NOT_CALLED]: "Signal/memo getter passed without calling it. Use getter() to read the value.",
190132
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.",
@@ -190696,6 +190698,14 @@ function visitComponentBody(node, ctx) {
190696
190698
  }
190697
190699
  }
190698
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
+ }
190699
190709
  collectInitStatement(node, ctx);
190700
190710
  return;
190701
190711
  }
@@ -190731,6 +190741,31 @@ function unwrapJsxTransparent(expr) {
190731
190741
  }
190732
190742
  return current;
190733
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
+ }
190734
190769
  function extractJsxFromExpression(expr) {
190735
190770
  const inner = unwrapJsxTransparent(expr);
190736
190771
  if (import_typescript9.default.isJsxElement(inner) || import_typescript9.default.isJsxFragment(inner) || import_typescript9.default.isJsxSelfClosingElement(inner)) {
@@ -194947,8 +194982,14 @@ function buildIRRoot(analyzer) {
194947
194982
  }
194948
194983
  ctx.isRoot = false;
194949
194984
  const ir = transformJsxExpression(jsxReturn, ctx);
194950
- 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
+ }
194951
194991
  return null;
194992
+ }
194952
194993
  return wrapInScopeElement(ir);
194953
194994
  }
194954
194995
  function needsScopeWrapper(ir) {
@@ -198063,6 +198104,35 @@ function getStringValue(node) {
198063
198104
  }
198064
198105
  return null;
198065
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
+ }
198066
198136
  function processComponentProps(attributes, ctx) {
198067
198137
  const props = [];
198068
198138
  for (const attr of attributes.properties) {
@@ -198074,10 +198144,7 @@ function processComponentProps(attributes, ctx) {
198074
198144
  continue;
198075
198145
  const name = attr.name.getText(ctx.sourceFile);
198076
198146
  if (attr.initializer && import_typescript13.default.isJsxExpression(attr.initializer) && attr.initializer.expression) {
198077
- let jsxExpr = attr.initializer.expression;
198078
- while (import_typescript13.default.isParenthesizedExpression(jsxExpr)) {
198079
- jsxExpr = jsxExpr.expression;
198080
- }
198147
+ const jsxExpr = unwrapTransparentTsWrappers(attr.initializer.expression);
198081
198148
  if (import_typescript13.default.isJsxElement(jsxExpr) || import_typescript13.default.isJsxSelfClosingElement(jsxExpr) || import_typescript13.default.isJsxFragment(jsxExpr)) {
198082
198149
  const prevInsideComponentChildren = ctx.insideComponentChildren;
198083
198150
  ctx.insideComponentChildren = true;
@@ -198092,6 +198159,10 @@ function processComponentProps(attributes, ctx) {
198092
198159
  continue;
198093
198160
  }
198094
198161
  }
198162
+ if ((import_typescript13.default.isConditionalExpression(jsxExpr) || import_typescript13.default.isArrayLiteralExpression(jsxExpr)) && expressionWrapsJsx(jsxExpr)) {
198163
+ reportNakedJsxWrapperProp(ctx, attr, name, jsxExpr);
198164
+ continue;
198165
+ }
198095
198166
  }
198096
198167
  let value = getAttributeValue(attr, ctx);
198097
198168
  if (value.kind === "template") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/test",
3
- "version": "0.33.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.33.0"
42
+ "@barefootjs/jsx": "0.33.1"
43
43
  },
44
44
  "devDependencies": {
45
45
  "typescript": "^5.0.0"