@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
package/dist/cache.js CHANGED
@@ -2,6 +2,8 @@ import { createHash } from 'crypto';
2
2
  import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
3
3
  import { homedir } from 'os';
4
4
  import { join } from 'path';
5
+ // Version stamp for cache invalidation — changes when rules/analyzers change
6
+ const REVIEW_CACHE_VERSION = '3.2.0';
5
7
  export class ReviewCache {
6
8
  l1 = new Map();
7
9
  cacheDir;
@@ -66,6 +68,8 @@ export class ReviewCache {
66
68
  }
67
69
  export function computeCacheKey(fileContent, config, filePath) {
68
70
  const hash = createHash('sha256');
71
+ // Include version so cache auto-invalidates when kern-lang is upgraded
72
+ hash.update(REVIEW_CACHE_VERSION);
69
73
  hash.update(fileContent);
70
74
  hash.update(JSON.stringify(config));
71
75
  hash.update(filePath);
package/dist/cache.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,12 @@
9
9
  * A file imported by both server and client entry points is 'shared'.
10
10
  */
11
11
  import type { FileContext, GraphResult } from './types.js';
12
+ /**
13
+ * Check if a file starts with a `'use client'` directive.
14
+ * Exported so App Router rules can detect client-boundary files without
15
+ * going through the full graph pass. Results are memoized across calls.
16
+ */
17
+ export declare function hasUseClientDirective(filePath: string): boolean;
12
18
  /**
13
19
  * Build FileContext for every file in the import graph.
14
20
  * This is the main function — call it once after resolving the import graph,
@@ -46,7 +46,12 @@ function classifyEntryPoint(filePath) {
46
46
  }
47
47
  // ── 'use client' Detection ──────────────────────────────────────────────
48
48
  const useClientCache = new Map();
49
- function hasUseClientDirective(filePath) {
49
+ /**
50
+ * Check if a file starts with a `'use client'` directive.
51
+ * Exported so App Router rules can detect client-boundary files without
52
+ * going through the full graph pass. Results are memoized across calls.
53
+ */
54
+ export function hasUseClientDirective(filePath) {
50
55
  const cached = useClientCache.get(filePath);
51
56
  if (cached !== undefined)
52
57
  return cached;
@@ -1 +1 @@
1
- {"version":3,"file":"file-context.js","sourceRoot":"","sources":["../src/file-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAGlC,2EAA2E;AAE3E,2DAA2D;AAC3D,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IAErC,iCAAiC;IACjC,IAAI,oCAAoC,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACnE,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,gCAAgC,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,YAAY,CAAC;IAEtE,mCAAmC;IACnC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/C,IAAI,4BAA4B,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAAE,OAAO,QAAQ,CAAC;IACnF,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;IAED,0FAA0F;IAC1F,IAAI,4EAA4E,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE9G,8BAA8B;IAC9B,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9C,IAAI,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEjD,mBAAmB;IACnB,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE5E,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,2EAA2E;AAE3E,MAAM,cAAc,GAAG,IAAI,GAAG,EAAmB,CAAC;AAElD,SAAS,qBAAqB,CAAC,QAAgB;IAC7C,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,4BAA4B,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAC3E,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACrC,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACpC,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,2EAA2E;AAE3E;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAC7B,QAAgB,EAChB,OAA+B,EAC/B,QAAqB,EACrB,KAA2B,EAC3B,QAAqB;IAErB,IAAI,qBAAqB,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAEjD,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IAExC,yDAAyD;IACzD,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,qDAAqD;IACrD,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvB,MAAM,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,EAAE,CAC9C,sBAAsB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,CACrE,CAAC;IACF,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1B,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC5B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,2EAA2E;AAE3E,6EAA6E;AAC7E,SAAS,gBAAgB,CAAC,UAAkB,EAAE,OAA+B,EAAE,QAAqB;IAClG,IAAI,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAElD,8EAA8E;IAC9E,gEAAgE;IAChE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,KAAK,GAAa,CAAC,UAAU,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACxB,IAAI,UAA8B,CAAC;IAEnC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;QAE/B,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,EAAE;YAAE,SAAS;QAClB,KAAK,MAAM,QAAQ,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;YACrC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACpC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACtB,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,qCAAqC;YAEpE,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3B,UAAU,GAAG,QAAQ,CAAC;gBACtB,MAAM;YACR,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,UAAU;YAAE,MAAM;IACxB,CAAC;IAED,IAAI,CAAC,UAAU;QAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAErC,uDAAuD;IACvD,gFAAgF;IAChF,MAAM,KAAK,GAAa,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI;YAAE,MAAM;QACjB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,GAAG,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,2EAA2E;AAE3E;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAkB;IACpD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAuB,CAAC;IAClD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAE3C,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,wBAAwB;IACxB,MAAM,eAAe,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC3D,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrC,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,oCAAoC;IACpC,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAmB,CAAC;IAEvD,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,sBAAsB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACpG,MAAM,YAAY,GAAG,qBAAqB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,WAAW,GAAG,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAEjE,uCAAuC;QACvC,IAAI,QAAQ,GAAoB,SAAS,CAAC;QAE1C,IAAI,QAAQ,IAAI,YAAY,EAAE,CAAC;YAC7B,QAAQ,GAAG,QAAQ,CAAC;QACtB,CAAC;aAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;QACvD,CAAC;aAAM,CAAC;YACN,2DAA2D;YAC3D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAmB,CAAC;YAC/C,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBACrC,mDAAmD;gBACnD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACnC,IAAI,OAAO,IAAI,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,CAAC,EAAE,CAAC;oBAC5D,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;YAED,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBAC3B,QAAQ,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;YACjC,CAAC;iBAAM,IAAI,WAAW,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAChC,QAAQ,GAAG,QAAQ,CAAC,CAAC,kDAAkD;YACzE,CAAC;QACH,CAAC;QAED,0CAA0C;QAC1C,MAAM,gBAAgB,GAAa,EAAE,CAAC;QACtC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACrC,IAAI,KAAK,KAAK,EAAE,CAAC,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,CAAC,EAAE,CAAC;gBACtE,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE;YACtB,QAAQ;YACR,WAAW,EAAE,gBAAgB;YAC7B,WAAW;YACX,KAAK,EAAE,EAAE,CAAC,QAAQ;YAClB,UAAU,EAAE,EAAE,CAAC,UAAU;YACzB,gBAAgB,EAAE,QAAQ;YAC1B,qBAAqB,EAAE,YAAY;SACpC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,mEAAmE;AACnE,SAAS,QAAQ,CAAC,MAAc,EAAE,MAAc,EAAE,OAA+B,EAAE,OAAoB;IACrG,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAEpB,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IAEtB,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QAC7B,IAAI,GAAG,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QAChC,IAAI,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;IAC3D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB;IACnC,cAAc,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"file-context.js","sourceRoot":"","sources":["../src/file-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Accessibility rules (WCAG 2.1 high-signal subset).
3
+ *
4
+ * Mirrors eslint-plugin-jsx-a11y rule shapes but kept intentionally small —
5
+ * only the checks that have a clear right answer and low false-positive rate.
6
+ */
7
+ import type { ReviewFinding, RuleContext } from '../types.js';
8
+ declare function imgMissingAlt(ctx: RuleContext): ReviewFinding[];
9
+ export declare const a11yRules: (typeof imgMissingAlt)[];
10
+ export {};
@@ -0,0 +1,294 @@
1
+ /**
2
+ * Accessibility rules (WCAG 2.1 high-signal subset).
3
+ *
4
+ * Mirrors eslint-plugin-jsx-a11y rule shapes but kept intentionally small —
5
+ * only the checks that have a clear right answer and low false-positive rate.
6
+ */
7
+ import { Node, SyntaxKind } from 'ts-morph';
8
+ import { finding, insertAfterSpan } from './utils.js';
9
+ // ARIA 1.2 valid role values (subset covering the common ones).
10
+ // Source: https://www.w3.org/TR/wai-aria-1.2/#role_definitions
11
+ const VALID_ROLES = new Set([
12
+ // Landmark
13
+ 'banner',
14
+ 'complementary',
15
+ 'contentinfo',
16
+ 'form',
17
+ 'main',
18
+ 'navigation',
19
+ 'region',
20
+ 'search',
21
+ // Document structure
22
+ 'article',
23
+ 'cell',
24
+ 'columnheader',
25
+ 'definition',
26
+ 'directory',
27
+ 'document',
28
+ 'feed',
29
+ 'figure',
30
+ 'group',
31
+ 'heading',
32
+ 'img',
33
+ 'list',
34
+ 'listitem',
35
+ 'math',
36
+ 'none',
37
+ 'note',
38
+ 'presentation',
39
+ 'row',
40
+ 'rowgroup',
41
+ 'rowheader',
42
+ 'separator',
43
+ 'table',
44
+ 'term',
45
+ 'toolbar',
46
+ 'tooltip',
47
+ // Widget
48
+ 'alert',
49
+ 'alertdialog',
50
+ 'application',
51
+ 'button',
52
+ 'checkbox',
53
+ 'combobox',
54
+ 'dialog',
55
+ 'grid',
56
+ 'gridcell',
57
+ 'link',
58
+ 'log',
59
+ 'marquee',
60
+ 'menu',
61
+ 'menubar',
62
+ 'menuitem',
63
+ 'menuitemcheckbox',
64
+ 'menuitemradio',
65
+ 'option',
66
+ 'progressbar',
67
+ 'radio',
68
+ 'radiogroup',
69
+ 'scrollbar',
70
+ 'searchbox',
71
+ 'slider',
72
+ 'spinbutton',
73
+ 'status',
74
+ 'switch',
75
+ 'tab',
76
+ 'tablist',
77
+ 'tabpanel',
78
+ 'textbox',
79
+ 'timer',
80
+ 'tree',
81
+ 'treegrid',
82
+ 'treeitem',
83
+ // Live region
84
+ 'log',
85
+ 'marquee',
86
+ 'status',
87
+ 'timer',
88
+ ]);
89
+ const INTERACTIVE_EVENTS = new Set([
90
+ 'onClick',
91
+ 'onKeyDown',
92
+ 'onKeyUp',
93
+ 'onKeyPress',
94
+ 'onMouseDown',
95
+ 'onMouseUp',
96
+ 'onTouchStart',
97
+ 'onTouchEnd',
98
+ ]);
99
+ const NON_INTERACTIVE_ELEMENTS = new Set(['div', 'span', 'section', 'article', 'li', 'p', 'td', 'th']);
100
+ function getTagName(el) {
101
+ return el.getTagNameNode().getText();
102
+ }
103
+ function getAttr(el, name) {
104
+ for (const attr of el.getAttributes()) {
105
+ if (Node.isJsxAttribute(attr) && attr.getNameNode().getText() === name) {
106
+ return attr;
107
+ }
108
+ }
109
+ return undefined;
110
+ }
111
+ function hasAttr(el, name) {
112
+ return getAttr(el, name) !== undefined;
113
+ }
114
+ function hasAnyAttr(el, names) {
115
+ return names.some((n) => hasAttr(el, n));
116
+ }
117
+ function* iterJsxElements(ctx) {
118
+ for (const el of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement)) {
119
+ yield el;
120
+ }
121
+ for (const el of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)) {
122
+ yield el;
123
+ }
124
+ }
125
+ // ── Rule: img-missing-alt ────────────────────────────────────────────────
126
+ function imgMissingAlt(ctx) {
127
+ const findings = [];
128
+ for (const el of iterJsxElements(ctx)) {
129
+ const tag = getTagName(el);
130
+ // Cover both <img> and next/image <Image>
131
+ if (tag !== 'img' && tag !== 'Image')
132
+ continue;
133
+ // alt="" is valid (decorative image); only flag missing alt
134
+ if (hasAttr(el, 'alt'))
135
+ continue;
136
+ // Role presentation / none exempts from alt requirement
137
+ const role = getAttr(el, 'role')?.getInitializer();
138
+ if (role &&
139
+ Node.isStringLiteral(role) &&
140
+ (role.getLiteralValue() === 'presentation' || role.getLiteralValue() === 'none')) {
141
+ continue;
142
+ }
143
+ // aria-hidden="true" also exempts
144
+ if (hasAttr(el, 'aria-hidden'))
145
+ continue;
146
+ // Autofix: insert `alt=""` immediately after the tag name. `alt=""` is
147
+ // the correct default for images whose intent we can't determine — it
148
+ // marks them as decorative so screen readers skip them. The user can
149
+ // replace the empty string with real alt text later.
150
+ findings.push(finding('img-missing-alt', 'error', 'bug', `<${tag}> is missing an alt attribute — screen readers will skip or read the filename`, ctx.filePath, el.getStartLineNumber(), 1, {
151
+ suggestion: 'Add alt="description" for meaningful images, or alt="" for decorative images',
152
+ autofix: {
153
+ type: 'insert-after',
154
+ span: insertAfterSpan(el.getTagNameNode(), ctx.filePath),
155
+ replacement: ' alt=""',
156
+ description: 'Insert alt="" (safe default for unknown-intent images)',
157
+ },
158
+ }));
159
+ }
160
+ return findings;
161
+ }
162
+ // ── Rule: button-missing-name ────────────────────────────────────────────
163
+ function buttonMissingName(ctx) {
164
+ const findings = [];
165
+ for (const el of iterJsxElements(ctx)) {
166
+ const tag = getTagName(el);
167
+ if (tag !== 'button')
168
+ continue;
169
+ // Accept any form of accessible name
170
+ if (hasAnyAttr(el, ['aria-label', 'aria-labelledby', 'title']))
171
+ continue;
172
+ // Self-closing <button /> is always unnamed
173
+ if (Node.isJsxSelfClosingElement(el)) {
174
+ findings.push(finding('button-missing-name', 'error', 'bug', '<button /> has no accessible name — add text children, aria-label, or aria-labelledby', ctx.filePath, el.getStartLineNumber(), 1, { suggestion: 'Add text children, aria-label="Close", or reference a label with aria-labelledby' }));
175
+ continue;
176
+ }
177
+ // For <button>...</button> check children for text / svg with title
178
+ const parent = el.getParent();
179
+ if (!parent)
180
+ continue;
181
+ if (!Node.isJsxElement(parent))
182
+ continue;
183
+ const children = parent.getJsxChildren();
184
+ let hasTextLikeContent = false;
185
+ for (const child of children) {
186
+ if (Node.isJsxText(child) && child.getText().trim().length > 0) {
187
+ hasTextLikeContent = true;
188
+ break;
189
+ }
190
+ if (Node.isJsxExpression(child)) {
191
+ // Any expression child counts — we can't prove it's empty without eval
192
+ hasTextLikeContent = true;
193
+ break;
194
+ }
195
+ if (Node.isJsxElement(child) || Node.isJsxSelfClosingElement(child)) {
196
+ // Nested element might contain accessible content — don't flag
197
+ hasTextLikeContent = true;
198
+ break;
199
+ }
200
+ }
201
+ if (!hasTextLikeContent) {
202
+ findings.push(finding('button-missing-name', 'error', 'bug', '<button> has no accessible name — empty children and no aria-label', ctx.filePath, el.getStartLineNumber(), 1, { suggestion: 'Add text children, aria-label="Close", or reference a label with aria-labelledby' }));
203
+ }
204
+ }
205
+ return findings;
206
+ }
207
+ // ── Rule: label-missing-for ──────────────────────────────────────────────
208
+ function labelMissingFor(ctx) {
209
+ const findings = [];
210
+ for (const el of iterJsxElements(ctx)) {
211
+ const tag = getTagName(el);
212
+ if (tag !== 'label')
213
+ continue;
214
+ if (hasAnyAttr(el, ['htmlFor', 'for']))
215
+ continue;
216
+ // <label><input /></label> — nested control is fine
217
+ const parent = el.getParent();
218
+ if (parent && Node.isJsxElement(parent)) {
219
+ const hasNestedControl = parent.getDescendants().some((d) => {
220
+ if (!Node.isJsxSelfClosingElement(d) && !Node.isJsxOpeningElement(d))
221
+ return false;
222
+ const name = d.getTagNameNode().getText();
223
+ return name === 'input' || name === 'select' || name === 'textarea';
224
+ });
225
+ if (hasNestedControl)
226
+ continue;
227
+ }
228
+ findings.push(finding('label-missing-for', 'warning', 'bug', '<label> is not associated with a form control — add htmlFor={id} or nest the control inside the label', ctx.filePath, el.getStartLineNumber(), 1, { suggestion: 'Add htmlFor="input-id" matching the id prop of the associated input' }));
229
+ }
230
+ return findings;
231
+ }
232
+ // ── Rule: aria-invalid-role ──────────────────────────────────────────────
233
+ function ariaInvalidRole(ctx) {
234
+ const findings = [];
235
+ for (const el of iterJsxElements(ctx)) {
236
+ const roleAttr = getAttr(el, 'role');
237
+ if (!roleAttr)
238
+ continue;
239
+ const init = roleAttr.getInitializer();
240
+ if (!init || !Node.isStringLiteral(init))
241
+ continue; // Skip expression roles — can't statically validate
242
+ const value = init.getLiteralValue();
243
+ // role can be space-separated fallback list
244
+ const roles = value.split(/\s+/).filter(Boolean);
245
+ for (const r of roles) {
246
+ if (!VALID_ROLES.has(r)) {
247
+ findings.push(finding('aria-invalid-role', 'error', 'bug', `role="${r}" is not a valid ARIA role — assistive tech will ignore the element`, ctx.filePath, roleAttr.getStartLineNumber(), 1, {
248
+ suggestion: `Use a valid ARIA role from the WAI-ARIA 1.2 spec, or remove the role to use the element's implicit role`,
249
+ }));
250
+ break; // one finding per element
251
+ }
252
+ }
253
+ }
254
+ return findings;
255
+ }
256
+ // ── Rule: interactive-noninteractive ─────────────────────────────────────
257
+ function interactiveNonInteractive(ctx) {
258
+ const findings = [];
259
+ for (const el of iterJsxElements(ctx)) {
260
+ const tag = getTagName(el);
261
+ if (!NON_INTERACTIVE_ELEMENTS.has(tag))
262
+ continue;
263
+ let interactiveEvent;
264
+ for (const attr of el.getAttributes()) {
265
+ if (!Node.isJsxAttribute(attr))
266
+ continue;
267
+ const name = attr.getNameNode().getText();
268
+ if (INTERACTIVE_EVENTS.has(name)) {
269
+ interactiveEvent = name;
270
+ break;
271
+ }
272
+ }
273
+ if (!interactiveEvent)
274
+ continue;
275
+ // Exempt if role + tabIndex present
276
+ const hasRole = hasAttr(el, 'role');
277
+ const hasTabIndex = hasAttr(el, 'tabIndex') || hasAttr(el, 'tabindex');
278
+ if (hasRole && hasTabIndex)
279
+ continue;
280
+ findings.push(finding('interactive-noninteractive', 'warning', 'bug', `<${tag}> has ${interactiveEvent} but is not keyboard-accessible — keyboard users cannot focus or activate it`, ctx.filePath, el.getStartLineNumber(), 1, {
281
+ suggestion: `Use a <button> element, or add role="button" and tabIndex={0} plus an onKeyDown handler that maps Enter/Space to the same action`,
282
+ }));
283
+ }
284
+ return findings;
285
+ }
286
+ // ── Exported a11y rules ──────────────────────────────────────────────────
287
+ export const a11yRules = [
288
+ imgMissingAlt,
289
+ buttonMissingName,
290
+ labelMissingFor,
291
+ ariaInvalidRole,
292
+ interactiveNonInteractive,
293
+ ];
294
+ //# sourceMappingURL=a11y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y.js","sourceRoot":"","sources":["../../src/rules/a11y.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Async correctness — Wave 2 rules for common Promise/AbortController footguns.
3
+ * Deliberately narrow: each rule fires only on high-confidence patterns.
4
+ */
5
+ import type { ReviewFinding, RuleContext } from '../types.js';
6
+ declare function promiseAllErrorSwallow(ctx: RuleContext): ReviewFinding[];
7
+ export declare const asyncRules: (typeof promiseAllErrorSwallow)[];
8
+ export {};
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Async correctness — Wave 2 rules for common Promise/AbortController footguns.
3
+ * Deliberately narrow: each rule fires only on high-confidence patterns.
4
+ */
5
+ import { Node, SyntaxKind } from 'ts-morph';
6
+ import { finding, insertBeforeSpan } from './utils.js';
7
+ // ── Rule: promise-all-error-swallow ──────────────────────────────────────
8
+ // Promise.all([...]) without .catch and not inside a try/catch is a bug:
9
+ // a single rejection silently cancels the handler and the error is lost.
10
+ function promiseAllErrorSwallow(ctx) {
11
+ const findings = [];
12
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
13
+ const callee = call.getExpression().getText();
14
+ if (callee !== 'Promise.all' && callee !== 'Promise.allSettled')
15
+ continue;
16
+ // allSettled is safe by construction (never rejects)
17
+ if (callee === 'Promise.allSettled')
18
+ continue;
19
+ // Walk up to see if we're inside a try block
20
+ let inTry = false;
21
+ let chained = false;
22
+ let cur = call.getParent();
23
+ while (cur) {
24
+ if (Node.isTryStatement(cur)) {
25
+ inTry = true;
26
+ break;
27
+ }
28
+ // Function boundary — stop searching for try
29
+ if (Node.isFunctionDeclaration(cur) || Node.isArrowFunction(cur) || Node.isFunctionExpression(cur)) {
30
+ break;
31
+ }
32
+ cur = cur.getParent();
33
+ }
34
+ // Check for .catch chain: Promise.all(...).catch(...) / .then(..., onRejected)
35
+ const parent = call.getParent();
36
+ if (parent && Node.isPropertyAccessExpression(parent)) {
37
+ const method = parent.getName();
38
+ if (method === 'catch')
39
+ chained = true;
40
+ if (method === 'then') {
41
+ // Check if .then has a second argument (onRejected)
42
+ const thenCall = parent.getParent();
43
+ if (thenCall && Node.isCallExpression(thenCall) && thenCall.getArguments().length >= 2) {
44
+ chained = true;
45
+ }
46
+ else {
47
+ // See if there's a .catch further along
48
+ let chain = thenCall;
49
+ while (chain && Node.isCallExpression(chain)) {
50
+ const p = chain.getParent();
51
+ if (p && Node.isPropertyAccessExpression(p) && p.getName() === 'catch') {
52
+ chained = true;
53
+ break;
54
+ }
55
+ chain = p?.getParent();
56
+ }
57
+ }
58
+ }
59
+ }
60
+ // Check if awaited inside an async function — the caller may handle it
61
+ const awaited = parent && Node.isAwaitExpression(parent);
62
+ if (!inTry && !chained && !awaited) {
63
+ findings.push(finding('promise-all-error-swallow', 'warning', 'bug', 'Promise.all() called without .catch, try/catch, or await — a single rejection will be silently unhandled', ctx.filePath, call.getStartLineNumber(), 1, {
64
+ suggestion: 'Add .catch(err => ...), wrap in try/catch inside an async function, or use Promise.allSettled if per-promise failures are expected',
65
+ }));
66
+ }
67
+ }
68
+ return findings;
69
+ }
70
+ // ── Rule: abortcontroller-leak ───────────────────────────────────────────
71
+ // `new AbortController()` created inside useEffect without `.abort()` in
72
+ // the cleanup return. Classic memory leak + stale-response bug.
73
+ const EFFECT_HOOKS = new Set(['useEffect', 'useLayoutEffect']);
74
+ function abortControllerLeak(ctx) {
75
+ const findings = [];
76
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
77
+ const calleeText = call.getExpression().getText();
78
+ const calleeName = calleeText.includes('.') ? calleeText.split('.').pop() : calleeText;
79
+ if (!EFFECT_HOOKS.has(calleeName))
80
+ continue;
81
+ const args = call.getArguments();
82
+ if (args.length === 0)
83
+ continue;
84
+ const fnArg = args[0];
85
+ if (!Node.isArrowFunction(fnArg) && !Node.isFunctionExpression(fnArg))
86
+ continue;
87
+ const body = fnArg.getBody();
88
+ if (!body)
89
+ continue;
90
+ if (!Node.isBlock(body))
91
+ continue;
92
+ // Find `new AbortController()` created directly in the effect body
93
+ const controllers = [];
94
+ for (const newExpr of body.getDescendantsOfKind(SyntaxKind.NewExpression)) {
95
+ if (newExpr.getExpression().getText() !== 'AbortController')
96
+ continue;
97
+ // Walk up to find the variable declaration
98
+ let cur = newExpr.getParent();
99
+ while (cur && !Node.isVariableDeclaration(cur)) {
100
+ cur = cur.getParent();
101
+ }
102
+ if (cur && Node.isVariableDeclaration(cur)) {
103
+ const nameNode = cur.getNameNode();
104
+ if (Node.isIdentifier(nameNode)) {
105
+ controllers.push({ name: nameNode.getText(), line: newExpr.getStartLineNumber() });
106
+ }
107
+ }
108
+ }
109
+ if (controllers.length === 0)
110
+ continue;
111
+ // Check the cleanup function (the return value of the effect body)
112
+ let cleanupText = '';
113
+ let hasExistingReturn = false;
114
+ for (const stmt of body.getStatements()) {
115
+ if (Node.isReturnStatement(stmt)) {
116
+ hasExistingReturn = true;
117
+ const expr = stmt.getExpression();
118
+ if (expr && (Node.isArrowFunction(expr) || Node.isFunctionExpression(expr))) {
119
+ cleanupText = expr.getText();
120
+ }
121
+ break;
122
+ }
123
+ }
124
+ for (const ctrl of controllers) {
125
+ // Require both: the ref name appears in cleanup AND .abort() is called
126
+ const hasAbortCall = new RegExp(`\\b${ctrl.name}\\s*\\.\\s*abort\\s*\\(`).test(cleanupText);
127
+ if (!hasAbortCall) {
128
+ // Autofix: insert a cleanup return immediately before the closing brace
129
+ // of the effect body. Only safe when there is NO existing return — if
130
+ // one is there, the user already has a cleanup and we'd need to merge
131
+ // the abort into it, which is too risky for an automated transform.
132
+ const closingBrace = body.getLastChildByKind(SyntaxKind.CloseBraceToken);
133
+ const canAutofix = !hasExistingReturn && controllers.length === 1 && closingBrace != null;
134
+ findings.push(finding('abortcontroller-leak', 'warning', 'bug', `AbortController '${ctrl.name}' created in ${calleeName} but never aborted in cleanup — in-flight requests survive unmount and may overwrite newer state`, ctx.filePath, ctrl.line, 1, {
135
+ suggestion: `Return a cleanup function that calls ${ctrl.name}.abort(): return () => ${ctrl.name}.abort();`,
136
+ ...(canAutofix && closingBrace
137
+ ? {
138
+ autofix: {
139
+ type: 'insert-before',
140
+ span: insertBeforeSpan(closingBrace, ctx.filePath),
141
+ replacement: ` return () => ${ctrl.name}.abort();\n`,
142
+ description: `Insert cleanup return that aborts ${ctrl.name}`,
143
+ },
144
+ }
145
+ : {}),
146
+ }));
147
+ }
148
+ }
149
+ }
150
+ return findings;
151
+ }
152
+ // ── Exported Async Rules ─────────────────────────────────────────────────
153
+ export const asyncRules = [promiseAllErrorSwallow, abortControllerLeak];
154
+ //# sourceMappingURL=async.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async.js","sourceRoot":"","sources":["../../src/rules/async.ts"],"names":[],"mappings":"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"}
@@ -575,6 +575,34 @@ function handlerExtraction(ctx) {
575
575
  }
576
576
  return findings;
577
577
  }
578
+ // ── Rule 11: handler-size ────────────────────────────────────────────────
579
+ // Handler blocks exceeding a line threshold suggest extraction
580
+ const HANDLER_LINE_LIMIT = 30;
581
+ function handlerSize(ctx) {
582
+ const findings = [];
583
+ for (const r of ctx.inferred) {
584
+ // Check all node types that can contain handlers, not just fn
585
+ const handlers = (r.node.children || []).filter((c) => c.type === 'handler');
586
+ for (const handler of handlers) {
587
+ const code = handler.props?.code || '';
588
+ if (!code)
589
+ continue;
590
+ // Count non-empty, non-comment lines
591
+ const lines = code.split('\n').filter((l) => {
592
+ const trimmed = l.trim();
593
+ return trimmed.length > 0 && !trimmed.startsWith('//') && !trimmed.startsWith('#');
594
+ });
595
+ if (lines.length > HANDLER_LINE_LIMIT) {
596
+ const parentName = r.node.props?.name || r.node.type;
597
+ findings.push(finding('handler-size', 'warning', 'structure', `Handler in '${parentName}' is ${lines.length} lines (limit: ${HANDLER_LINE_LIMIT}). Extract logic into separate fn nodes for better structure and reviewability.`, ctx.filePath, handler.loc?.line ?? r.startLine, 1, {
598
+ nodeIds: [r.nodeId],
599
+ suggestion: `Split the handler into smaller fn nodes called from the handler. KERN's value is structure — keep handlers under ${HANDLER_LINE_LIMIT} lines.`,
600
+ }));
601
+ }
602
+ }
603
+ }
604
+ return findings;
605
+ }
578
606
  // ── Rule: memory-leak (from v1) ──────────────────────────────────────────
579
607
  // useEffect/watch/onMounted that creates subscriptions without cleanup
580
608
  const EFFECT_CALLEE_NAMES = new Set(['useEffect', 'watch', 'onMounted', 'watchEffect']);
@@ -849,6 +877,7 @@ export const baseRules = [
849
877
  cognitiveComplexity,
850
878
  templateAvailable,
851
879
  handlerExtraction,
880
+ handlerSize,
852
881
  memoryLeak,
853
882
  unhandledAsync,
854
883
  syncInAsync,