@vibecodeqa/cli 0.38.0 → 0.38.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.
@@ -3,6 +3,30 @@
3
3
  * This runner catches patterns beyond what the plugin covers. */
4
4
  import { getProductionFiles, readDeps } from "../fs-utils.js";
5
5
  import { gradeFromScore } from "../types.js";
6
+ /** True when the `.map()` callback starting at line `i` actually returns JSX.
7
+ * `after` is the line text from `.map(` onward, right-trimmed. Distinguishes JSX
8
+ * returns from data maps (`=> ({...})`, `=> fn(...)`), TS generics, and comparisons. */
9
+ function mapCallbackReturnsJsx(after, lines, i) {
10
+ if (/=>\s*<[A-Za-z]/.test(after))
11
+ return true; // inline: => <Tag
12
+ if (/=>\s*\($/.test(after)) {
13
+ // multiline arrow body: => ( followed by JSX on the next non-empty line
14
+ return /^<[A-Za-z]/.test((lines[i + 1] || "").trim());
15
+ }
16
+ if (/=>\s*\{$/.test(after)) {
17
+ // block body: JSX iff it returns `<Tag` or `(` then `<Tag`
18
+ for (let j = i; j < Math.min(i + 12, lines.length); j++) {
19
+ const lt = (lines[j] || "").trim();
20
+ if (/return\s*<[A-Za-z]/.test(lt))
21
+ return true;
22
+ if (/return\s*\($/.test(lt))
23
+ return /^<[A-Za-z]/.test((lines[j + 1] || "").trim());
24
+ if (j > i && /^return\b/.test(lt))
25
+ return false; // returns a non-JSX value
26
+ }
27
+ }
28
+ return false;
29
+ }
6
30
  export function runReact(cwd, stack) {
7
31
  const start = Date.now();
8
32
  if (stack.framework !== "react") {
@@ -68,11 +92,13 @@ export function runReact(cwd, stack) {
68
92
  rule: "conditional-hook",
69
93
  });
70
94
  }
71
- // 2. Missing key in .map() returning JSX
72
- if (/\.map\s*\(/.test(trimmed)) {
73
- // Look ahead for JSX return without key
74
- const mapBlock = lines.slice(i, Math.min(i + 10, lines.length)).join("\n");
75
- if (/<\w/.test(mapBlock) && !mapBlock.includes("key=") && !mapBlock.includes("key:")) {
95
+ // 2. Missing key in .map() returning JSX. Only flag genuine JSX returns —
96
+ // not data maps, TS generics, or comparisons (see mapCallbackReturnsJsx).
97
+ const mapIdx = trimmed.indexOf(".map(");
98
+ if (mapIdx !== -1 && mapCallbackReturnsJsx(trimmed.slice(mapIdx).trimEnd(), lines, i)) {
99
+ // Inspect just the JSX head for a key enough to cover the opening element.
100
+ const head = lines.slice(i, Math.min(i + 8, lines.length)).join("\n");
101
+ if (!head.includes("key=") && !head.includes("key:")) {
76
102
  missingKeys++;
77
103
  issues.push({ severity: "error", message: "JSX in .map() without key prop", file: f.path, line: i + 1, rule: "missing-key" });
78
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecodeqa/cli",
3
- "version": "0.38.0",
3
+ "version": "0.38.1",
4
4
  "description": "Code health scanner for the AI coding era. 25 checks, zero config, full report.",
5
5
  "type": "module",
6
6
  "bin": {