@kernlang/review 3.1.8 → 3.2.3

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 (50) hide show
  1. package/dist/cache.js +4 -0
  2. package/dist/cache.js.map +1 -1
  3. package/dist/file-context.d.ts +6 -0
  4. package/dist/file-context.js +6 -1
  5. package/dist/file-context.js.map +1 -1
  6. package/dist/rules/a11y.d.ts +10 -0
  7. package/dist/rules/a11y.js +294 -0
  8. package/dist/rules/a11y.js.map +1 -0
  9. package/dist/rules/async.d.ts +8 -0
  10. package/dist/rules/async.js +154 -0
  11. package/dist/rules/async.js.map +1 -0
  12. package/dist/rules/base.js +29 -0
  13. package/dist/rules/base.js.map +1 -1
  14. package/dist/rules/index.d.ts +12 -0
  15. package/dist/rules/index.js +283 -4
  16. package/dist/rules/index.js.map +1 -1
  17. package/dist/rules/ink.js +41 -0
  18. package/dist/rules/ink.js.map +1 -1
  19. package/dist/rules/kern-source.js +94 -14
  20. package/dist/rules/kern-source.js.map +1 -1
  21. package/dist/rules/nextjs-app-router.d.ts +11 -0
  22. package/dist/rules/nextjs-app-router.js +277 -0
  23. package/dist/rules/nextjs-app-router.js.map +1 -0
  24. package/dist/rules/nextjs.js +77 -1
  25. package/dist/rules/nextjs.js.map +1 -1
  26. package/dist/rules/perf.d.ts +11 -0
  27. package/dist/rules/perf.js +131 -0
  28. package/dist/rules/perf.js.map +1 -0
  29. package/dist/rules/react-composition.d.ts +12 -0
  30. package/dist/rules/react-composition.js +360 -0
  31. package/dist/rules/react-composition.js.map +1 -0
  32. package/dist/rules/react-hooks.d.ts +11 -0
  33. package/dist/rules/react-hooks.js +380 -0
  34. package/dist/rules/react-hooks.js.map +1 -0
  35. package/dist/rules/security-v5.d.ts +11 -0
  36. package/dist/rules/security-v5.js +200 -0
  37. package/dist/rules/security-v5.js.map +1 -0
  38. package/dist/rules/utils.d.ts +16 -0
  39. package/dist/rules/utils.js +46 -0
  40. package/dist/rules/utils.js.map +1 -1
  41. package/dist/taint-ast.js +32 -6
  42. package/dist/taint-ast.js.map +1 -1
  43. package/dist/taint-findings.js +3 -0
  44. package/dist/taint-findings.js.map +1 -1
  45. package/dist/taint-types.d.ts +2 -2
  46. package/dist/taint-types.js +38 -4
  47. package/dist/taint-types.js.map +1 -1
  48. package/dist/types.d.ts +20 -0
  49. package/dist/types.js.map +1 -1
  50. package/package.json +2 -2
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Next.js App Router review rules — active when target = nextjs, on top of nextjsRules.
3
+ *
4
+ * Focus: directive placement, client/server boundary correctness, server actions.
5
+ * These rules require import-graph awareness — they gracefully no-op when run
6
+ * in single-file mode (no ctx.fileContext).
7
+ */
8
+ import { basename } from 'path';
9
+ import { Node, SyntaxKind } from 'ts-morph';
10
+ import { finding } from './utils.js';
11
+ // ── Helpers ──────────────────────────────────────────────────────────────
12
+ const CLIENT_HOOKS = new Set([
13
+ 'useState',
14
+ 'useEffect',
15
+ 'useRef',
16
+ 'useCallback',
17
+ 'useMemo',
18
+ 'useReducer',
19
+ 'useContext',
20
+ 'useLayoutEffect',
21
+ 'useTransition',
22
+ 'useDeferredValue',
23
+ 'useImperativeHandle',
24
+ 'useSyncExternalStore',
25
+ ]);
26
+ const CLIENT_EVENT_HANDLERS = new Set([
27
+ 'onClick',
28
+ 'onChange',
29
+ 'onSubmit',
30
+ 'onKeyDown',
31
+ 'onKeyUp',
32
+ 'onMouseEnter',
33
+ 'onMouseLeave',
34
+ 'onFocus',
35
+ 'onBlur',
36
+ 'onInput',
37
+ 'onTouchStart',
38
+ 'onTouchEnd',
39
+ 'onScroll',
40
+ 'onDrag',
41
+ ]);
42
+ const BROWSER_GLOBALS = /\b(window|document|localStorage|sessionStorage|navigator|history|location)\b/;
43
+ function hasClientDirective(fullText) {
44
+ return /^['"]use client['"];?\s*$/m.test(fullText.substring(0, 200));
45
+ }
46
+ function hasServerDirective(fullText) {
47
+ return /^['"]use server['"];?\s*$/m.test(fullText.substring(0, 200));
48
+ }
49
+ /** Does this file itself use any client-only API (hooks, browser globals, event handlers)? */
50
+ function fileUsesClientApi(ctx) {
51
+ const fullText = ctx.sourceFile.getFullText();
52
+ if (BROWSER_GLOBALS.test(fullText))
53
+ return true;
54
+ // JSX event handlers
55
+ for (const attr of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.JsxAttribute)) {
56
+ const name = attr.getNameNode().getText();
57
+ if (CLIENT_EVENT_HANDLERS.has(name))
58
+ return true;
59
+ }
60
+ // Hook calls
61
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
62
+ const expr = call.getExpression();
63
+ if (expr.getKind() === SyntaxKind.Identifier) {
64
+ if (CLIENT_HOOKS.has(expr.getText()))
65
+ return true;
66
+ }
67
+ else if (expr.getKind() === SyntaxKind.PropertyAccessExpression) {
68
+ const prop = expr.asKind(SyntaxKind.PropertyAccessExpression);
69
+ if (prop && CLIENT_HOOKS.has(prop.getName()))
70
+ return true;
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+ // ── Rule: use-client-drilled-too-high ────────────────────────────────────
76
+ // File has 'use client' but doesn't actually use any client API itself.
77
+ // Its children do. Moving the directive down would preserve RSC benefits.
78
+ function useClientDrilledTooHigh(ctx) {
79
+ if (ctx.fileRole !== 'runtime')
80
+ return [];
81
+ const fullText = ctx.sourceFile.getFullText();
82
+ if (!hasClientDirective(fullText))
83
+ return [];
84
+ if (fileUsesClientApi(ctx))
85
+ return [];
86
+ // The file marks itself 'use client' but uses no client APIs. This is likely
87
+ // a parent wrapper that drilled the directive too high. Signal is strongest
88
+ // when the file has child imports that DO use client APIs — but we can't
89
+ // cheaply check that without the full fileContextMap. Fire as a warning
90
+ // either way; severity bumps to error when we can prove a child needs it.
91
+ let severity = 'warning';
92
+ let detail = 'File has "use client" but uses no hooks, event handlers, or browser APIs itself.';
93
+ const fileContextMap = ctx.config?.fileContextMap;
94
+ if (fileContextMap) {
95
+ // If at least one imported child has its own 'use client' or needs one, this is a drilled directive.
96
+ const gfImports = [...fileContextMap.entries()]
97
+ .filter(([, v]) => v.importedBy.includes(ctx.filePath))
98
+ .map(([k]) => k);
99
+ if (gfImports.length > 0) {
100
+ severity = 'warning';
101
+ detail += ` Imported children: ${gfImports
102
+ .slice(0, 3)
103
+ .map((p) => basename(p))
104
+ .join(', ')}${gfImports.length > 3 ? '…' : ''}.`;
105
+ }
106
+ }
107
+ const line = 1;
108
+ return [
109
+ finding('use-client-drilled-too-high', severity, 'pattern', `'use client' directive is drilled too high — ${detail} Move it to the leaf component that actually uses client APIs to preserve Server Component benefits.`, ctx.filePath, line, 1, {
110
+ suggestion: 'Remove the top-level "use client" and add it to only the child component(s) that use hooks or browser APIs',
111
+ }),
112
+ ];
113
+ }
114
+ // ── Rule: server-api-in-client ───────────────────────────────────────────
115
+ // Client Component imports or calls server-only APIs:
116
+ // - next/headers (cookies(), headers(), draftMode())
117
+ // - server-only (explicit guard package)
118
+ // These will fail at build or runtime.
119
+ const SERVER_API_CALLS = new Set(['cookies', 'headers', 'draftMode']);
120
+ function serverApiInClient(ctx) {
121
+ if (ctx.fileRole !== 'runtime')
122
+ return [];
123
+ const fullText = ctx.sourceFile.getFullText();
124
+ const isClient = hasClientDirective(fullText) || ctx.fileContext?.isClientBoundary === true;
125
+ if (!isClient)
126
+ return [];
127
+ const findings = [];
128
+ // Import check: `from 'next/headers'` or `from 'server-only'`
129
+ for (const imp of ctx.sourceFile.getImportDeclarations()) {
130
+ const mod = imp.getModuleSpecifierValue();
131
+ if (mod === 'next/headers' || mod === 'server-only') {
132
+ findings.push(finding('server-api-in-client', 'error', 'bug', `Client Component imports '${mod}' — this will fail at build time. Server-only APIs cannot run in a client boundary.`, ctx.filePath, imp.getStartLineNumber(), 1, {
133
+ suggestion: `Move this logic to a Server Component or a server action, or drop the 'use client' directive if this file does not need it`,
134
+ }));
135
+ }
136
+ }
137
+ // Call check: cookies()/headers()/draftMode() invocation in client code
138
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
139
+ const expr = call.getExpression();
140
+ if (expr.getKind() !== SyntaxKind.Identifier)
141
+ continue;
142
+ const name = expr.getText();
143
+ if (!SERVER_API_CALLS.has(name))
144
+ continue;
145
+ // Only flag when imported from 'next/headers' — avoid false positives on
146
+ // user-defined functions of the same name. We already flagged the import above,
147
+ // so only emit the call-site finding if the import actually came from next/headers.
148
+ const fromNextHeaders = ctx.sourceFile
149
+ .getImportDeclarations()
150
+ .some((imp) => imp.getModuleSpecifierValue() === 'next/headers' && imp.getNamedImports().some((ni) => ni.getName() === name));
151
+ if (!fromNextHeaders)
152
+ continue;
153
+ findings.push(finding('server-api-in-client', 'error', 'bug', `'${name}()' called in Client Component — next/headers APIs are server-only and will throw at runtime`, ctx.filePath, call.getStartLineNumber(), 1, { suggestion: `Call '${name}()' in a Server Component or server action, then pass the result as a prop` }));
154
+ }
155
+ return findings;
156
+ }
157
+ // ── Rule: server-action-unvalidated-input ────────────────────────────────
158
+ // Server action (file or function marked 'use server') receives args and
159
+ // uses them without passing through a validator (.parse, .safeParse, zod,
160
+ // yup, joi, a schema, or a typeof/instanceof guard).
161
+ // Validator detection is intentionally strict: we require the call to look
162
+ // like it originates from a known schema library, not just ANY .parse(). A
163
+ // naive /\.parse\(/ test would accept `JSON.parse(str)` or `path.parse(p)`
164
+ // as "validation" and suppress the rule. Instead, we require BOTH a known
165
+ // library reference AND a validating method call in the same body.
166
+ const SCHEMA_LIBRARY_PATTERNS = [
167
+ /\bz\.\w+/, // zod
168
+ /\byup\.\w+/,
169
+ /\bjoi\.\w+/,
170
+ /\b(from\s+['"]zod['"]|from\s+['"]yup['"]|from\s+['"]joi['"]|from\s+['"]valibot['"]|from\s+['"]@?superstruct['"])/,
171
+ ];
172
+ const SCHEMA_METHOD_PATTERNS = [
173
+ /\.safeParse\s*\(/,
174
+ /\bz\.(object|string|number|boolean|array|enum|union|literal|tuple)\s*\(/,
175
+ /\bparse\s*\(/, // bare parse — only counted alongside a library reference (see hasValidatorUsage)
176
+ ];
177
+ const NAIVE_VALIDATOR_PATTERNS = [/\.validate(Sync)?\s*\(/, /\.assert\s*\(/, /\bassert\s*\(/];
178
+ function hasValidatorUsage(bodyText, importsText) {
179
+ // Strong signal: schema library import or reference PLUS a schema method call
180
+ const hasLib = SCHEMA_LIBRARY_PATTERNS.some((p) => p.test(importsText)) || SCHEMA_LIBRARY_PATTERNS.some((p) => p.test(bodyText));
181
+ const hasSchemaMethod = SCHEMA_METHOD_PATTERNS.some((p) => p.test(bodyText));
182
+ if (hasLib && hasSchemaMethod)
183
+ return true;
184
+ // Weaker but still reasonable: explicit .validate()/.assert() call
185
+ if (NAIVE_VALIDATOR_PATTERNS.some((p) => p.test(bodyText)))
186
+ return true;
187
+ return false;
188
+ }
189
+ /** Check that at least ONE of the function's params is referenced in the body. */
190
+ function anyParamIsReferenced(paramNames, bodyText) {
191
+ for (const name of paramNames) {
192
+ if (!name)
193
+ continue;
194
+ if (new RegExp(`\\b${name}\\b`).test(bodyText))
195
+ return name;
196
+ }
197
+ return undefined;
198
+ }
199
+ function getImportsText(ctx) {
200
+ return ctx.sourceFile
201
+ .getImportDeclarations()
202
+ .map((d) => d.getText())
203
+ .join('\n');
204
+ }
205
+ function serverActionUnvalidatedInput(ctx) {
206
+ if (ctx.fileRole !== 'runtime')
207
+ return [];
208
+ const fullText = ctx.sourceFile.getFullText();
209
+ const fileIsServerAction = hasServerDirective(fullText);
210
+ const findings = [];
211
+ const importsText = getImportsText(ctx);
212
+ // Iterate exported async functions
213
+ for (const fn of ctx.sourceFile.getFunctions()) {
214
+ if (!fn.isExported() || !fn.isAsync())
215
+ continue;
216
+ const params = fn.getParameters();
217
+ if (params.length === 0)
218
+ continue;
219
+ const body = fn.getBody();
220
+ if (!body)
221
+ continue;
222
+ const bodyText = body.getText();
223
+ // Function-level 'use server' directive (inside the function body) OR file-level
224
+ const fnIsServerAction = fileIsServerAction || /['"]use server['"]/.test(bodyText.substring(0, 100));
225
+ if (!fnIsServerAction)
226
+ continue;
227
+ if (hasValidatorUsage(bodyText, importsText))
228
+ continue;
229
+ // Check ALL params, not just the first — Next server actions use
230
+ // `(prevState, formData)` when wired to useActionState, so formData is
231
+ // often params[1], not params[0].
232
+ const paramNames = params.map((p) => p.getName());
233
+ const refParam = anyParamIsReferenced(paramNames, bodyText);
234
+ if (!refParam)
235
+ continue;
236
+ findings.push(finding('server-action-unvalidated-input', 'warning', 'bug', `Server action '${fn.getName() || '<anon>'}' uses parameter '${refParam}' without validation — server actions receive untrusted client input`, ctx.filePath, fn.getStartLineNumber(), 1, {
237
+ suggestion: 'Validate input with a schema (zod.parse / yup.validate / joi.validate) before using. Type annotations are NOT enforced at runtime.',
238
+ }));
239
+ }
240
+ // Also handle arrow functions assigned to exported consts
241
+ for (const stmt of ctx.sourceFile.getVariableStatements()) {
242
+ if (!stmt.isExported())
243
+ continue;
244
+ for (const decl of stmt.getDeclarations()) {
245
+ const init = decl.getInitializer();
246
+ if (!init)
247
+ continue;
248
+ if (!Node.isArrowFunction(init) && !Node.isFunctionExpression(init))
249
+ continue;
250
+ if (!init.isAsync?.())
251
+ continue;
252
+ const params = init.getParameters();
253
+ if (params.length === 0)
254
+ continue;
255
+ const body = init.getBody();
256
+ if (!body)
257
+ continue;
258
+ const bodyText = body.getText();
259
+ const fnIsServerAction = fileIsServerAction || /['"]use server['"]/.test(bodyText.substring(0, 100));
260
+ if (!fnIsServerAction)
261
+ continue;
262
+ if (hasValidatorUsage(bodyText, importsText))
263
+ continue;
264
+ const paramNames = params.map((p) => p.getName());
265
+ const refParam = anyParamIsReferenced(paramNames, bodyText);
266
+ if (!refParam)
267
+ continue;
268
+ findings.push(finding('server-action-unvalidated-input', 'warning', 'bug', `Server action '${decl.getName()}' uses parameter '${refParam}' without validation`, ctx.filePath, decl.getStartLineNumber(), 1, {
269
+ suggestion: 'Validate input with a schema (zod.parse / yup.validate / joi.validate) before using. Type annotations are NOT enforced at runtime.',
270
+ }));
271
+ }
272
+ }
273
+ return findings;
274
+ }
275
+ // ── Exported App Router Rules ────────────────────────────────────────────
276
+ export const nextjsAppRouterRules = [useClientDrilledTooHigh, serverApiInClient, serverActionUnvalidatedInput];
277
+ //# sourceMappingURL=nextjs-app-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nextjs-app-router.js","sourceRoot":"","sources":["../../src/rules/nextjs-app-router.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,8 @@
3
3
  *
4
4
  * Catches Server Component / App Router pitfalls.
5
5
  */
6
+ import { readFileSync } from 'fs';
7
+ import { basename } from 'path';
6
8
  import { SyntaxKind } from 'ts-morph';
7
9
  import { finding } from './utils.js';
8
10
  function isClientComponent(fullText) {
@@ -154,6 +156,37 @@ function hydrationMismatch(ctx) {
154
156
  }
155
157
  // ── Rule 23: missing-use-client ──────────────────────────────────────────
156
158
  // Event handlers (onClick, onChange, etc.) without 'use client' directive
159
+ // Import-graph-aware: severity depends on who imports this file.
160
+ /** Find importers that are NOT within a client boundary (i.e. server components). */
161
+ function findServerImporters(ctx) {
162
+ const importedBy = ctx.fileContext?.importedBy || [];
163
+ if (importedBy.length === 0)
164
+ return [];
165
+ const fileContextMap = ctx.config?.fileContextMap;
166
+ const serverImporters = [];
167
+ for (const imp of importedBy) {
168
+ const impCtx = fileContextMap?.get(imp);
169
+ if (impCtx) {
170
+ // Use computed boundary — accounts for transitive 'use client' propagation
171
+ if (!impCtx.isClientBoundary && !impCtx.hasUseClientDirective) {
172
+ serverImporters.push(imp);
173
+ }
174
+ }
175
+ else {
176
+ // Fallback: read file directly (no graph entry for this importer)
177
+ try {
178
+ const content = readFileSync(imp, 'utf-8');
179
+ if (!isClientComponent(content)) {
180
+ serverImporters.push(imp);
181
+ }
182
+ }
183
+ catch {
184
+ /* unreadable — skip */
185
+ }
186
+ }
187
+ }
188
+ return serverImporters;
189
+ }
157
190
  function missingUseClient(ctx) {
158
191
  const findings = [];
159
192
  // Gate: skip non-React files — only JSX files can have event handler props
@@ -168,6 +201,33 @@ function missingUseClient(ctx) {
168
201
  const fullText = ctx.sourceFile.getFullText();
169
202
  if (isClientComponent(fullText))
170
203
  return findings;
204
+ // ── Import-graph-aware severity ─────────────────────────────────────
205
+ // error → imported from a server component (will break at runtime)
206
+ // warning → only client importers, or no import graph available
207
+ // info → file has no importers (dead code or entry point)
208
+ let severity = 'warning';
209
+ let category = 'pattern';
210
+ let serverImporterNames;
211
+ if (ctx.fileContext) {
212
+ const importedBy = ctx.fileContext.importedBy;
213
+ const serverImporters = findServerImporters(ctx);
214
+ if (serverImporters.length > 0) {
215
+ severity = 'error';
216
+ category = 'bug';
217
+ serverImporterNames = serverImporters.map((p) => basename(p)).join(', ');
218
+ }
219
+ else if (importedBy.length > 0) {
220
+ severity = 'warning';
221
+ }
222
+ else if (ctx.fileContext.depth === 0) {
223
+ // Entry point (page.tsx, layout.tsx) — Next.js loads directly as server component
224
+ severity = 'warning';
225
+ }
226
+ else {
227
+ severity = 'info';
228
+ }
229
+ }
230
+ // No fileContext → keep default 'warning' (single-file review fallback)
171
231
  const eventHandlers = [
172
232
  'onClick',
173
233
  'onChange',
@@ -193,7 +253,23 @@ function missingUseClient(ctx) {
193
253
  continue;
194
254
  found.add(handler);
195
255
  const line = fullText.substring(0, match.index).split('\n').length;
196
- findings.push(finding('missing-use-client', 'warning', 'pattern', `'${handler}' in Server Component — needs 'use client' directive`, ctx.filePath, line, 1, {
256
+ let message;
257
+ if (severity === 'error') {
258
+ message = `Missing 'use client' — uses ${handler} and is imported from server component (${serverImporterNames})`;
259
+ }
260
+ else if (ctx.fileContext && ctx.fileContext.importedBy.length > 0) {
261
+ message = `Consider adding 'use client' — ${handler} used here, all importers are already client components`;
262
+ }
263
+ else if (ctx.fileContext && ctx.fileContext.depth === 0) {
264
+ message = `'${handler}' in server entry point — needs 'use client' directive`;
265
+ }
266
+ else if (ctx.fileContext) {
267
+ message = `Consider adding 'use client' — ${handler} used but file has no importers`;
268
+ }
269
+ else {
270
+ message = `'${handler}' in Server Component — needs 'use client' directive`;
271
+ }
272
+ findings.push(finding('missing-use-client', severity, category, message, ctx.filePath, line, 1, {
197
273
  suggestion: "Add 'use client' at the top of the file, or extract to a Client Component",
198
274
  autofix: {
199
275
  type: 'insert-before',
@@ -1 +1 @@
1
- {"version":3,"file":"nextjs.js","sourceRoot":"","sources":["../../src/rules/nextjs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nextjs.js","sourceRoot":"","sources":["../../src/rules/nextjs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Performance rules — Wave 3 breadth additions.
3
+ *
4
+ * All three rules are heuristics; they ship with `precision: 'medium'` so
5
+ * kern-sight can hide them by default and let users opt in after the first
6
+ * noise-budget pass.
7
+ */
8
+ import type { ReviewFinding, RuleContext } from '../types.js';
9
+ declare function imageNoLazy(ctx: RuleContext): ReviewFinding[];
10
+ export declare const perfRules: (typeof imageNoLazy)[];
11
+ export {};
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Performance rules — Wave 3 breadth additions.
3
+ *
4
+ * All three rules are heuristics; they ship with `precision: 'medium'` so
5
+ * kern-sight can hide them by default and let users opt in after the first
6
+ * noise-budget pass.
7
+ */
8
+ import { Node, SyntaxKind } from 'ts-morph';
9
+ import { finding, insertAfterSpan } from './utils.js';
10
+ // ── Rule: image-no-lazy ──────────────────────────────────────────────────
11
+ // <img> without loading="lazy". next/image is exempt (it lazy-loads by default).
12
+ function imageNoLazy(ctx) {
13
+ const findings = [];
14
+ const jsxElements = [
15
+ ...ctx.sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement),
16
+ ...ctx.sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),
17
+ ];
18
+ for (const el of jsxElements) {
19
+ const tag = el.getTagNameNode().getText();
20
+ if (tag !== 'img')
21
+ continue;
22
+ let hasLoading = false;
23
+ let hasPriority = false;
24
+ for (const attr of el.getAttributes()) {
25
+ if (!Node.isJsxAttribute(attr))
26
+ continue;
27
+ const name = attr.getNameNode().getText();
28
+ if (name === 'loading')
29
+ hasLoading = true;
30
+ if (name === 'fetchPriority' || name === 'fetchpriority')
31
+ hasPriority = true;
32
+ }
33
+ if (hasLoading)
34
+ continue;
35
+ // Above-the-fold images often use fetchPriority="high" — don't nag
36
+ if (hasPriority)
37
+ continue;
38
+ findings.push(finding('image-no-lazy', 'info', 'pattern', '<img> without loading="lazy" — consider lazy loading below-the-fold images or switching to next/image', ctx.filePath, el.getStartLineNumber(), 1, {
39
+ suggestion: 'Add loading="lazy" (and optionally decoding="async") or use next/image which lazy-loads by default',
40
+ autofix: {
41
+ type: 'insert-after',
42
+ span: insertAfterSpan(el.getTagNameNode(), ctx.filePath),
43
+ replacement: ' loading="lazy"',
44
+ description: 'Insert loading="lazy" attribute',
45
+ },
46
+ }));
47
+ }
48
+ return findings;
49
+ }
50
+ // ── Rule: heavy-computation-in-render ────────────────────────────────────
51
+ // Inline .sort(), .filter().map(), .reduce() chains directly in JSX without
52
+ // a useMemo wrap. Fires only when the chain has at least 2 operations to
53
+ // reduce noise.
54
+ const EXPENSIVE_METHODS = new Set(['sort', 'filter', 'reduce', 'flatMap', 'reverse']);
55
+ function heavyComputationInRender(ctx) {
56
+ const findings = [];
57
+ // Walk JSX expression braces — computations that land directly in the tree
58
+ for (const jsxExpr of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.JsxExpression)) {
59
+ const inner = jsxExpr.getExpression();
60
+ if (!inner)
61
+ continue;
62
+ // Count chained expensive operations
63
+ let expensiveCount = 0;
64
+ let cur = inner;
65
+ while (cur) {
66
+ if (Node.isCallExpression(cur)) {
67
+ const callee = cur.getExpression();
68
+ if (Node.isPropertyAccessExpression(callee)) {
69
+ if (EXPENSIVE_METHODS.has(callee.getName()))
70
+ expensiveCount++;
71
+ cur = callee.getExpression();
72
+ continue;
73
+ }
74
+ }
75
+ if (Node.isPropertyAccessExpression(cur)) {
76
+ cur = cur.getExpression();
77
+ continue;
78
+ }
79
+ break;
80
+ }
81
+ if (expensiveCount < 2)
82
+ continue;
83
+ findings.push(finding('heavy-computation-in-render', 'info', 'pattern', `Chained expensive array operations (${expensiveCount} of sort/filter/reduce/flatMap/reverse) inline in JSX — this reruns on every render`, ctx.filePath, jsxExpr.getStartLineNumber(), 1, {
84
+ suggestion: 'Wrap the computation in useMemo with the correct dependencies, or move it out of the render path entirely',
85
+ }));
86
+ }
87
+ return findings;
88
+ }
89
+ // ── Rule: large-list-no-virtualization ───────────────────────────────────
90
+ // Heuristic: `.map(...)` in JSX over an identifier whose name suggests a
91
+ // collection (items/rows/data/list/entries/results) when the component has
92
+ // no import from react-window / react-virtual / virtuoso. Very noisy for
93
+ // small lists — ships as `info` and `precision: 'experimental'`.
94
+ const LIST_LIKE_NAMES = new Set(['items', 'rows', 'data', 'list', 'entries', 'results', 'records', 'elements']);
95
+ const VIRTUAL_LIBS = [/react-window/, /react-virtual/, /virtuoso/, /react-virtualized/, /@tanstack\/react-virtual/];
96
+ function largeListNoVirtualization(ctx) {
97
+ const findings = [];
98
+ // Early skip: does this file already import a virtualization library?
99
+ for (const imp of ctx.sourceFile.getImportDeclarations()) {
100
+ const mod = imp.getModuleSpecifierValue();
101
+ if (VIRTUAL_LIBS.some((r) => r.test(mod)))
102
+ return findings;
103
+ }
104
+ for (const jsxExpr of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.JsxExpression)) {
105
+ const inner = jsxExpr.getExpression();
106
+ if (!inner || !Node.isCallExpression(inner))
107
+ continue;
108
+ const callee = inner.getExpression();
109
+ if (!Node.isPropertyAccessExpression(callee))
110
+ continue;
111
+ if (callee.getName() !== 'map')
112
+ continue;
113
+ // Only fire when the callee root is a plain identifier with a list-like name.
114
+ let root = callee.getExpression();
115
+ while (Node.isPropertyAccessExpression(root)) {
116
+ root = root.getExpression();
117
+ }
118
+ if (!Node.isIdentifier(root))
119
+ continue;
120
+ const name = root.getText();
121
+ if (!LIST_LIKE_NAMES.has(name))
122
+ continue;
123
+ findings.push(finding('large-list-no-virtualization', 'info', 'pattern', `Rendering '${name}.map(...)' inline — if ${name} can grow large, consider react-window or @tanstack/react-virtual to avoid rendering off-screen rows`, ctx.filePath, jsxExpr.getStartLineNumber(), 1, {
124
+ suggestion: `Wrap the list in a virtualized container if ${name}.length is unbounded. Skip this rule for static/small collections.`,
125
+ }));
126
+ }
127
+ return findings;
128
+ }
129
+ // ── Exported perf rules ──────────────────────────────────────────────────
130
+ export const perfRules = [imageNoLazy, heavyComputationInRender, largeListNoVirtualization];
131
+ //# sourceMappingURL=perf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf.js","sourceRoot":"","sources":["../../src/rules/perf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * React composition rules — catch prop-drilling and "parent rerenders child
3
+ * that doesn't depend on parent state" antipatterns.
4
+ *
5
+ * These rules push toward the `children` prop pattern, which preserves
6
+ * element identity across parent renders and lets React skip reconciliation
7
+ * of unchanged subtrees.
8
+ */
9
+ import type { ReviewFinding, RuleContext } from '../types.js';
10
+ declare function childrenNotUsed(ctx: RuleContext): ReviewFinding[];
11
+ export declare const reactCompositionRules: (typeof childrenNotUsed)[];
12
+ export {};