@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.
- package/dist/runners/react.js +31 -5
- package/package.json +1 -1
package/dist/runners/react.js
CHANGED
|
@@ -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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
}
|