@kernlang/review 3.1.9 → 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.
- package/dist/cache.js +4 -0
- package/dist/cache.js.map +1 -1
- package/dist/file-context.d.ts +6 -0
- package/dist/file-context.js +6 -1
- package/dist/file-context.js.map +1 -1
- package/dist/rules/a11y.d.ts +10 -0
- package/dist/rules/a11y.js +294 -0
- package/dist/rules/a11y.js.map +1 -0
- package/dist/rules/async.d.ts +8 -0
- package/dist/rules/async.js +154 -0
- package/dist/rules/async.js.map +1 -0
- package/dist/rules/index.d.ts +12 -0
- package/dist/rules/index.js +283 -4
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/ink.js +41 -0
- package/dist/rules/ink.js.map +1 -1
- package/dist/rules/kern-source.js +94 -14
- package/dist/rules/kern-source.js.map +1 -1
- package/dist/rules/nextjs-app-router.d.ts +11 -0
- package/dist/rules/nextjs-app-router.js +277 -0
- package/dist/rules/nextjs-app-router.js.map +1 -0
- package/dist/rules/nextjs.js +77 -1
- package/dist/rules/nextjs.js.map +1 -1
- package/dist/rules/perf.d.ts +11 -0
- package/dist/rules/perf.js +131 -0
- package/dist/rules/perf.js.map +1 -0
- package/dist/rules/react-composition.d.ts +12 -0
- package/dist/rules/react-composition.js +360 -0
- package/dist/rules/react-composition.js.map +1 -0
- package/dist/rules/react-hooks.d.ts +11 -0
- package/dist/rules/react-hooks.js +380 -0
- package/dist/rules/react-hooks.js.map +1 -0
- package/dist/rules/security-v5.d.ts +11 -0
- package/dist/rules/security-v5.js +200 -0
- package/dist/rules/security-v5.js.map +1 -0
- package/dist/rules/utils.d.ts +16 -0
- package/dist/rules/utils.js +46 -0
- package/dist/rules/utils.js.map +1 -1
- package/dist/taint-ast.js +32 -6
- package/dist/taint-ast.js.map +1 -1
- package/dist/taint-findings.js +3 -0
- package/dist/taint-findings.js.map +1 -1
- package/dist/taint-types.d.ts +2 -2
- package/dist/taint-types.js +38 -4
- package/dist/taint-types.js.map +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/dist/rules/nextjs.js
CHANGED
|
@@ -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
|
-
|
|
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',
|
package/dist/rules/nextjs.js.map
CHANGED
|
@@ -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":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,SAAS,iBAAiB,CAAC,QAAgB;IACzC,oEAAoE;IACpE,OAAO,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;AACvE,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,GAAgB;IACnC,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IAC9C,yBAAyB;IACzB,IAAI,wBAAwB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACjG,gDAAgD;IAChD,IAAI,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9F,IAAI,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClG,kBAAkB;IAClB,IAAI,gEAAgE,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACjG,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4EAA4E;AAC5E,iEAAiE;AACjE,4EAA4E;AAE5E,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC;IAC3B,UAAU;IACV,WAAW;IACX,QAAQ;IACR,aAAa;IACb,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,iBAAiB;IACjB,eAAe;IACf,kBAAkB;IAClB,qBAAqB;IACrB,sBAAsB;CACvB,CAAC,CAAC;AAEH,SAAS,UAAU,CAAC,GAAgB;IAClC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,kFAAkF;IAClF,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAEhD,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,iBAAiB,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEjD,mFAAmF;IACnF,IAAI,GAAG,CAAC,WAAW,EAAE,gBAAgB;QAAE,OAAO,QAAQ,CAAC;IACvD,IAAI,GAAG,CAAC,WAAW,EAAE,QAAQ,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE5D,qEAAqE;IACrE,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;IAC7E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAClC,IAAI,QAA4B,CAAC;QAEjC,6BAA6B;QAC7B,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,UAAU,EAAE,CAAC;YAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YAC5B,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,QAAQ,GAAG,IAAI,CAAC;QAC9C,CAAC;QACD,uCAAuC;aAClC,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,wBAAwB,EAAE,CAAC;YAChE,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,wBAAwB,CAAC,CAAC;YAC9D,IAAI,IAAI,EAAE,CAAC;gBACT,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC5B,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,QAAQ,GAAG,IAAI,CAAC;YAC9C,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ;YAAE,SAAS;QAExB,iFAAiF;QACjF,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAChC,IACE,MAAM;YACN,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,kBAAkB;gBACjD,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,6BAA6B;gBAC7D,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,YAAY,CAAC;YAE/C,SAAS;QAEX,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACvC,QAAQ,CAAC,IAAI,CACX,OAAO,CACL,aAAa,EACb,OAAO,EACP,KAAK,EACL,IAAI,QAAQ,uFAAuF,EACnG,GAAG,CAAC,QAAQ,EACZ,IAAI,EACJ,CAAC,EACD,EAAE,UAAU,EAAE,yCAAyC,EAAE,CAC1D,CACF,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,4EAA4E;AAC5E,2EAA2E;AAE3E,SAAS,iBAAiB,CAAC,GAAgB;IACzC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,kFAAkF;IAClF,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEvC,iFAAiF;IACjF,iFAAiF;IACjF,+DAA+D;IAC/D,IAAI,GAAG,CAAC,WAAW,EAAE,QAAQ,KAAK,KAAK,IAAI,GAAG,CAAC,WAAW,EAAE,QAAQ,KAAK,YAAY;QAAE,OAAO,QAAQ,CAAC;IAEvG,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IAE9C,mFAAmF;IACnF,MAAM,UAAU,GAA4B,EAAE,CAAC;IAC/C,MAAM,aAAa,GAAG,6DAA6D,CAAC;IACpF,IAAI,SAAS,CAAC;IACd,OAAO,CAAC,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC3D,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACvD,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,QAAQ,GAAG,QAAQ,CAAC;QACxB,KAAK,IAAI,CAAC,GAAG,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAChD,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,KAAK,EAAE,CAAC;YACjC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,KAAK,EAAE,CAAC;YACjC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACxB,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;oBAChB,QAAQ,GAAG,CAAC,CAAC;oBACb,MAAM;gBACR,CAAC;gBACD,KAAK,EAAE,CAAC;YACV,CAAC;YACD,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,KAAK,EAAE,CAAC;QACnC,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,aAAa,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC;IAEzF,MAAM,gBAAgB,GAAG;QACvB,EAAE,OAAO,EAAE,wBAAwB,EAAE,IAAI,EAAE,YAAY,EAAE;QACzD,EAAE,OAAO,EAAE,2BAA2B,EAAE,IAAI,EAAE,eAAe,EAAE;QAC/D,EAAE,OAAO,EAAE,yBAAyB,EAAE,IAAI,EAAE,YAAY,EAAE;QAC1D,EAAE,OAAO,EAAE,iCAAiC,EAAE,IAAI,EAAE,qBAAqB,EAAE;KAC5E,CAAC;IAEF,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,gBAAgB,EAAE,CAAC;QACjD,IAAI,KAAK,CAAC;QACV,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACjD,qEAAqE;YACrE,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC;gBAAE,SAAS;YAEzC,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;YACnE,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;YACtD,IAAI,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC;gBAAE,SAAS;YAEhD,QAAQ,CAAC,IAAI,CACX,OAAO,CACL,oBAAoB,EACpB,SAAS,EACT,KAAK,EACL,GAAG,IAAI,+EAA+E,EACtF,GAAG,CAAC,QAAQ,EACZ,IAAI,EACJ,CAAC,EACD,EAAE,UAAU,EAAE,oEAAoE,EAAE,CACrF,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,4EAA4E;AAC5E,0EAA0E;AAC1E,iEAAiE;AAEjE,qFAAqF;AACrF,SAAS,mBAAmB,CAAC,GAAgB;IAC3C,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,EAAE,UAAU,IAAI,EAAE,CAAC;IACrD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC;IAClD,MAAM,eAAe,GAAa,EAAE,CAAC;IAErC,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,cAAc,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,MAAM,EAAE,CAAC;YACX,2EAA2E;YAC3E,IAAI,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAC;gBAC9D,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;aAAM,CAAC;YACN,kEAAkE;YAClE,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBAC3C,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC;oBAChC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,uBAAuB;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAgB;IACxC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,2EAA2E;IAC3E,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEvC,4EAA4E;IAC5E,iDAAiD;IACjD,IAAI,GAAG,CAAC,WAAW,EAAE,gBAAgB;QAAE,OAAO,QAAQ,CAAC;IACvD,IAAI,GAAG,CAAC,WAAW,EAAE,QAAQ,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE5D,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IAE9C,IAAI,iBAAiB,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEjD,uEAAuE;IACvE,qEAAqE;IACrE,gEAAgE;IAChE,6DAA6D;IAC7D,IAAI,QAAQ,GAAiC,SAAS,CAAC;IACvD,IAAI,QAAQ,GAAsB,SAAS,CAAC;IAC5C,IAAI,mBAAuC,CAAC;IAE5C,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QACpB,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC;QAC9C,MAAM,eAAe,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;QAEjD,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,QAAQ,GAAG,OAAO,CAAC;YACnB,QAAQ,GAAG,KAAK,CAAC;YACjB,mBAAmB,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,CAAC;aAAM,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,QAAQ,GAAG,SAAS,CAAC;QACvB,CAAC;aAAM,IAAI,GAAG,CAAC,WAAW,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YACvC,kFAAkF;YAClF,QAAQ,GAAG,SAAS,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,MAAM,CAAC;QACpB,CAAC;IACH,CAAC;IACD,wEAAwE;IAExE,MAAM,aAAa,GAAG;QACpB,SAAS;QACT,UAAU;QACV,UAAU;QACV,WAAW;QACX,SAAS;QACT,cAAc;QACd,cAAc;QACd,SAAS;QACT,QAAQ;QACR,SAAS;QACT,cAAc;QACd,YAAY;QACZ,UAAU;QACV,QAAQ;KACT,CAAC;IAEF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAEhC,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,OAAO,MAAM,EAAE,GAAG,CAAC,CAAC;QACnD,IAAI,KAAK,CAAC;QACV,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/C,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,SAAS;YACjC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACnB,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;YAEnE,IAAI,OAAe,CAAC;YACpB,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;gBACzB,OAAO,GAAG,+BAA+B,OAAO,2CAA2C,mBAAmB,GAAG,CAAC;YACpH,CAAC;iBAAM,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpE,OAAO,GAAG,kCAAkC,OAAO,yDAAyD,CAAC;YAC/G,CAAC;iBAAM,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;gBAC1D,OAAO,GAAG,IAAI,OAAO,wDAAwD,CAAC;YAChF,CAAC;iBAAM,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;gBAC3B,OAAO,GAAG,kCAAkC,OAAO,iCAAiC,CAAC;YACvF,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,IAAI,OAAO,sDAAsD,CAAC;YAC9E,CAAC;YAED,QAAQ,CAAC,IAAI,CACX,OAAO,CAAC,oBAAoB,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE;gBAChF,UAAU,EAAE,2EAA2E;gBACvF,OAAO,EAAE;oBACP,IAAI,EAAE,eAAe;oBACrB,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;oBAC9E,WAAW,EAAE,mBAAmB;oBAChC,WAAW,EAAE,gCAAgC;iBAC9C;aACF,CAAC,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,4EAA4E;AAE5E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,CAAC,CAAC"}
|
|
@@ -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 {};
|