@hatem427/code-guard-ci 3.2.0 → 3.4.0

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 (86) hide show
  1. package/config/angular.config.ts +29 -708
  2. package/config/guidelines.config.ts +5 -130
  3. package/config/nextjs.config.ts +27 -511
  4. package/config/react.config.ts +19 -614
  5. package/dist/config/angular.config.d.ts +5 -8
  6. package/dist/config/angular.config.d.ts.map +1 -1
  7. package/dist/config/angular.config.js +28 -666
  8. package/dist/config/angular.config.js.map +1 -1
  9. package/dist/config/guidelines.config.d.ts.map +1 -1
  10. package/dist/config/guidelines.config.js +5 -127
  11. package/dist/config/guidelines.config.js.map +1 -1
  12. package/dist/config/nextjs.config.d.ts +7 -9
  13. package/dist/config/nextjs.config.d.ts.map +1 -1
  14. package/dist/config/nextjs.config.js +26 -472
  15. package/dist/config/nextjs.config.js.map +1 -1
  16. package/dist/config/react.config.d.ts +4 -5
  17. package/dist/config/react.config.d.ts.map +1 -1
  18. package/dist/config/react.config.js +19 -586
  19. package/dist/config/react.config.js.map +1 -1
  20. package/dist/scripts/auto-fix.d.ts +0 -5
  21. package/dist/scripts/auto-fix.d.ts.map +1 -1
  22. package/dist/scripts/auto-fix.js +0 -5
  23. package/dist/scripts/auto-fix.js.map +1 -1
  24. package/dist/scripts/cli.js +209 -390
  25. package/dist/scripts/cli.js.map +1 -1
  26. package/dist/scripts/config-generators/ai-config-generator.d.ts.map +1 -1
  27. package/dist/scripts/config-generators/ai-config-generator.js +71 -15
  28. package/dist/scripts/config-generators/ai-config-generator.js.map +1 -1
  29. package/dist/scripts/config-generators/eslint-generator.d.ts.map +1 -1
  30. package/dist/scripts/config-generators/eslint-generator.js +13 -625
  31. package/dist/scripts/config-generators/eslint-generator.js.map +1 -1
  32. package/dist/scripts/config-generators/index.d.ts +0 -1
  33. package/dist/scripts/config-generators/index.d.ts.map +1 -1
  34. package/dist/scripts/config-generators/index.js +1 -5
  35. package/dist/scripts/config-generators/index.js.map +1 -1
  36. package/dist/scripts/config-generators/typescript-generator.d.ts.map +1 -1
  37. package/dist/scripts/config-generators/typescript-generator.js +0 -33
  38. package/dist/scripts/config-generators/typescript-generator.js.map +1 -1
  39. package/dist/scripts/config-generators/vscode-generator.d.ts.map +1 -1
  40. package/dist/scripts/config-generators/vscode-generator.js +28 -171
  41. package/dist/scripts/config-generators/vscode-generator.js.map +1 -1
  42. package/dist/scripts/generate-pr-checklist.d.ts +0 -5
  43. package/dist/scripts/generate-pr-checklist.d.ts.map +1 -1
  44. package/dist/scripts/generate-pr-checklist.js +1 -6
  45. package/dist/scripts/generate-pr-checklist.js.map +1 -1
  46. package/dist/scripts/postinstall.js +0 -38
  47. package/dist/scripts/postinstall.js.map +1 -1
  48. package/dist/scripts/precommit-check.d.ts +0 -5
  49. package/dist/scripts/precommit-check.d.ts.map +1 -1
  50. package/dist/scripts/precommit-check.js +137 -121
  51. package/dist/scripts/precommit-check.js.map +1 -1
  52. package/dist/scripts/utils/naming-validator.d.ts.map +1 -1
  53. package/dist/scripts/utils/naming-validator.js +2 -96
  54. package/dist/scripts/utils/naming-validator.js.map +1 -1
  55. package/dist/scripts/utils/project-detector.d.ts +9 -12
  56. package/dist/scripts/utils/project-detector.d.ts.map +1 -1
  57. package/dist/scripts/utils/project-detector.js +11 -63
  58. package/dist/scripts/utils/project-detector.js.map +1 -1
  59. package/dist/scripts/utils/report-generator.js +5 -17
  60. package/dist/scripts/utils/report-generator.js.map +1 -1
  61. package/dist/scripts/utils/structure-validator.d.ts.map +1 -1
  62. package/dist/scripts/utils/structure-validator.js +0 -50
  63. package/dist/scripts/utils/structure-validator.js.map +1 -1
  64. package/package.json +1 -12
  65. package/scripts/auto-fix.ts +0 -5
  66. package/scripts/cli.ts +224 -424
  67. package/scripts/config-generators/ai-config-generator.ts +78 -28
  68. package/scripts/config-generators/eslint-generator.ts +7 -621
  69. package/scripts/config-generators/index.ts +0 -1
  70. package/scripts/config-generators/typescript-generator.ts +0 -36
  71. package/scripts/config-generators/vscode-generator.ts +40 -178
  72. package/scripts/generate-pr-checklist.ts +1 -6
  73. package/scripts/postinstall.ts +0 -38
  74. package/scripts/precommit-check.ts +143 -224
  75. package/scripts/utils/naming-validator.ts +2 -104
  76. package/scripts/utils/project-detector.ts +11 -78
  77. package/scripts/utils/report-generator.ts +5 -19
  78. package/scripts/utils/structure-validator.ts +0 -54
  79. package/config/fastify.config.ts +0 -326
  80. package/config/hono.config.ts +0 -331
  81. package/config/nestjs.config.ts +0 -500
  82. package/config/python.config.ts +0 -512
  83. package/templates/feature-doc-api.md +0 -101
  84. package/templates/feature-doc-backend.md +0 -114
  85. package/templates/feature-doc-service.md +0 -113
  86. package/templates/feature-doc-ui.md +0 -91
@@ -1,282 +1,20 @@
1
1
  "use strict";
2
2
  /**
3
3
  * ============================================================================
4
- * react.config.ts — React-specific coding rules (v19+)
4
+ * react.config.ts — React-specific coding rules
5
5
  * ============================================================================
6
6
  *
7
7
  * Registers rules that only apply to React projects:
8
- * - React 19 hooks (use(), useActionState(), useTransition(), useOptimistic())
9
- * - Server Components patterns
8
+ * - Hook usage patterns
10
9
  * - Props/state typing
11
- * - Performance optimization
12
- * - Security best practices
10
+ * - Component patterns
11
+ * - Performance anti-patterns
13
12
  */
14
13
  Object.defineProperty(exports, "__esModule", { value: true });
15
14
  exports.reactRules = void 0;
16
15
  const guidelines_config_1 = require("./guidelines.config");
17
16
  const reactRules = [
18
- // ── NEW: React 19+ Features ────────────────────────────────────────────
19
- // ─────────────────────────────────────────
20
- // RULE: react-use-hook
21
- // ROLE: Recommend React 19 use() hook for promise/context unwrapping
22
- // PURPOSE: use() replaces .then() chains and useContext() with a
23
- // single API that works in both Server and Client Components.
24
- // It suspends the component until the promise resolves.
25
- // EXAMPLE:
26
- // WRONG:
27
- // const data = await fetchData().then(res => res.json());
28
- // const theme = useContext(ThemeContext);
29
- // RIGHT:
30
- // import { use } from 'react';
31
- // const data = use(fetchData());
32
- // const theme = use(ThemeContext);
33
- // ─────────────────────────────────────────
34
- {
35
- id: 'react-use-hook',
36
- label: 'Use use() hook for unwrapping promises and context',
37
- description: 'React 19 introduces use() hook to unwrap promises directly in components and access context in Server Components. Example: const data = use(fetchData())',
38
- severity: 'warning',
39
- fileExtensions: ['tsx', 'jsx'],
40
- pattern: null,
41
- customCheck: (file) => {
42
- const violations = [];
43
- if (/\.then\s*\(|async\s*\(\s*\).*=>.*=>/.test(file.content) &&
44
- !file.content.includes('use(')) {
45
- violations.push({
46
- line: null,
47
- message: 'Found promise handling. Use React 19 use() hook to unwrap promises directly.',
48
- });
49
- }
50
- return violations;
51
- },
52
- applicableTo: ['react', 'nextjs'],
53
- category: 'React 19',
54
- },
55
- // ─────────────────────────────────────────
56
- // RULE: react-use-action-state
57
- // ROLE: Enforce React 19 form handling pattern
58
- // PURPOSE: useActionState() eliminates manual pending/error state
59
- // management for forms. It handles submission state, errors,
60
- // and progressive enhancement automatically.
61
- // EXAMPLE:
62
- // WRONG:
63
- // const [pending, setPending] = useState(false);
64
- // const handleSubmit = async (e) => {
65
- // setPending(true);
66
- // await submitForm();
67
- // setPending(false);
68
- // };
69
- // RIGHT:
70
- // const [state, formAction, isPending] = useActionState(submit, initialState);
71
- // <form action={formAction}>
72
- // <button disabled={isPending}>{isPending ? 'Loading...' : 'Submit'}</button>
73
- // </form>
74
- // ─────────────────────────────────────────
75
- {
76
- id: 'react-use-action-state',
77
- label: 'Use useActionState() for form handling',
78
- description: 'React 19 useActionState() replaces form submission logic. It handles pending states, actions, and errors automatically.',
79
- severity: 'warning',
80
- fileExtensions: ['tsx', 'jsx'],
81
- pattern: null,
82
- customCheck: (file) => {
83
- const violations = [];
84
- if (/use\(["']use client['"], \{.*\}|<form\s+onSubmit|useState.*pending|useState.*loading/.test(file.content) &&
85
- !file.content.includes('useActionState') &&
86
- file.content.includes("'use client'")) {
87
- violations.push({
88
- line: null,
89
- message: 'Form with manual state management detected. Use useActionState() for simpler form handling in React 19.',
90
- });
91
- }
92
- return violations;
93
- },
94
- applicableTo: ['react', 'nextjs'],
95
- category: 'React 19',
96
- },
97
- // ─────────────────────────────────────────
98
- // RULE: react-use-transition
99
- // ROLE: Recommend non-blocking UI updates for expensive operations
100
- // PURPOSE: useTransition() marks state updates as low-priority so the
101
- // UI stays responsive during expensive re-renders (filtering,
102
- // sorting, searching large datasets).
103
- // EXAMPLE:
104
- // WRONG:
105
- // const [query, setQuery] = useState('');
106
- // const handleChange = (e) => setQuery(e.target.value);
107
- // // UI freezes during expensive filtering
108
- // RIGHT:
109
- // const [isPending, startTransition] = useTransition();
110
- // const handleChange = (e) => {
111
- // startTransition(() => setQuery(e.target.value));
112
- // };
113
- // // UI stays responsive, shows pending state
114
- // ─────────────────────────────────────────
115
- {
116
- id: 'react-use-transition',
117
- label: 'Use useTransition() for non-blocking updates',
118
- description: 'Use useTransition() to mark updates as non-blocking (lower priority). Good for search boxes, filters, and heavy computations.',
119
- severity: 'info',
120
- fileExtensions: ['tsx', 'jsx'],
121
- pattern: null,
122
- customCheck: (file) => {
123
- const violations = [];
124
- if (/filter|search|sort|debounce/.test(file.content) &&
125
- /useState\s*\(/.test(file.content) &&
126
- !file.content.includes('useTransition')) {
127
- violations.push({
128
- line: null,
129
- message: 'Found filter/search logic. Use useTransition() to keep UI responsive during expensive updates.',
130
- });
131
- }
132
- return violations;
133
- },
134
- applicableTo: ['react', 'nextjs'],
135
- category: 'React 19',
136
- },
137
- // ─────────────────────────────────────────
138
- // RULE: react-use-optimistic
139
- // ROLE: Recommend optimistic UI updates for async actions
140
- // PURPOSE: useOptimistic() shows instant feedback to the user while
141
- // the server processes the request. If the server fails, the
142
- // state automatically rolls back.
143
- // EXAMPLE:
144
- // WRONG:
145
- // const handleLike = async () => {
146
- // await api.like(postId); // User waits...
147
- // refetch();
148
- // };
149
- // RIGHT:
150
- // const [optimisticLikes, addOptimistic] = useOptimistic(
151
- // likes,
152
- // (state, newLike) => [...state, newLike]
153
- // );
154
- // const handleLike = async () => {
155
- // addOptimistic(newLike); // Instant feedback
156
- // await api.like(postId);
157
- // };
158
- // ─────────────────────────────────────────
159
- {
160
- id: 'react-use-optimistic',
161
- label: 'Use useOptimistic() for optimistic updates',
162
- description: 'React 19 useOptimistic() enables optimistic UI updates before server confirmation. Great for forms and instant feedback.',
163
- severity: 'info',
164
- fileExtensions: ['tsx', 'jsx'],
165
- pattern: null,
166
- customCheck: (file) => {
167
- const violations = [];
168
- const hasAsyncHandler = /const\s+handle\w+\s*=\s*async\s*\(|async\s+function\s+handle/i.test(file.content);
169
- const hasOptimistic = /useOptimistic/i.test(file.content);
170
- if (hasAsyncHandler && !hasOptimistic) {
171
- for (let i = 0; i < file.lines.length; i++) {
172
- if (/const\s+handle\w+\s*=\s*async|async\s+function\s+handle/i.test(file.lines[i])) {
173
- violations.push({
174
- line: i + 1,
175
- message: `Async form handler found. Use useOptimistic() for instant UI feedback.`,
176
- });
177
- break;
178
- }
179
- }
180
- }
181
- return violations;
182
- },
183
- applicableTo: ['react', 'nextjs'],
184
- category: 'React 19',
185
- },
186
- // ── Server Components (Next.js 13+, React Canary) ────────────────────
187
- // ─────────────────────────────────────────
188
- // RULE: react-server-functions
189
- // ROLE: Recommend "use server" directive for secure server logic
190
- // PURPOSE: Server Functions run entirely on the server, enabling
191
- // direct database access, secret handling, and mutations
192
- // without creating API routes.
193
- // EXAMPLE:
194
- // WRONG:
195
- // // pages/api/user.ts (API route approach)
196
- // export async function POST(req) {
197
- // const data = await req.json();
198
- // await db.users.create(data);
199
- // }
200
- // RIGHT:
201
- // // actions/user.ts
202
- // 'use server';
203
- // export async function createUser(formData: FormData) {
204
- // await db.users.create(Object.fromEntries(formData));
205
- // revalidatePath('/users');
206
- // }
207
- // ─────────────────────────────────────────
208
- {
209
- id: 'react-server-functions',
210
- label: 'Use Server Functions with "use server" directive',
211
- description: 'Define async functions with "use server" to run on the server. Enables secure database access and secret handling without API routes.',
212
- severity: 'info',
213
- fileExtensions: ['ts', 'tsx'],
214
- pattern: null,
215
- applicableTo: ['nextjs'],
216
- category: 'React Server Functions',
217
- },
218
- // ─────────────────────────────────────────
219
- // RULE: react-server-client-boundary
220
- // ROLE: Validate "use client" boundary placement
221
- // PURPOSE: Unnecessarily marking components as "use client" prevents
222
- // server-side rendering optimizations and increases the
223
- // JavaScript bundle sent to the browser.
224
- // EXAMPLE:
225
- // WRONG:
226
- // 'use client'; // No hooks, no interactivity!
227
- // export default function StaticCard({ title }) {
228
- // return <div>{title}</div>;
229
- // }
230
- // RIGHT:
231
- // // No directive needed — Server Component by default
232
- // export default function StaticCard({ title }) {
233
- // return <div>{title}</div>;
234
- // }
235
- // ─────────────────────────────────────────
236
- {
237
- id: 'react-server-client-boundary',
238
- label: 'Verify "use client" boundaries',
239
- description: 'Interleave Server and Client Components properly. Only mark Client Components with "use client" to minimize JavaScript.',
240
- severity: 'warning',
241
- fileExtensions: ['tsx', 'jsx', 'ts'],
242
- pattern: null,
243
- customCheck: (file) => {
244
- const violations = [];
245
- const hasUseClient = file.content.includes("'use client'") || file.content.includes('"use client"');
246
- const hasHooks = /use(State|Effect|Context|Reducer|Callback|Memo|Ref|Transition|OptimisticOptimistic)\s*\(/.test(file.content);
247
- const hasExport = /export\s+(default\s+)?function|export\s+(default\s+)?const/.test(file.content);
248
- if (hasUseClient && !hasHooks && hasExport) {
249
- violations.push({
250
- line: 1,
251
- message: '"use client" found but no hooks detected. This component might work as a Server Component for better performance.',
252
- });
253
- }
254
- return violations;
255
- },
256
- applicableTo: ['nextjs'],
257
- category: 'React Server Components',
258
- },
259
17
  // ── Hooks ──────────────────────────────────────────────────────────────
260
- // ─────────────────────────────────────────
261
- // RULE: react-no-useeffect-no-deps
262
- // ROLE: Block useEffect without dependency array
263
- // PURPOSE: Missing dependency array causes useEffect to run after
264
- // EVERY render, leading to infinite loops, stale closures,
265
- // and severe performance issues.
266
- // EXAMPLE:
267
- // WRONG:
268
- // useEffect(() => {
269
- // fetchData();
270
- // }); // Runs on every render!
271
- // RIGHT:
272
- // useEffect(() => {
273
- // fetchData();
274
- // }, []); // Runs once on mount
275
- // // With dependencies:
276
- // useEffect(() => {
277
- // fetchData(id);
278
- // }, [id]); // Runs when id changes
279
- // ─────────────────────────────────────────
280
18
  {
281
19
  id: 'react-no-useeffect-no-deps',
282
20
  label: 'useEffect must have dependency array',
@@ -289,7 +27,9 @@ const reactRules = [
289
27
  const lines = file.lines;
290
28
  for (let i = 0; i < lines.length; i++) {
291
29
  const line = lines[i];
30
+ // Detect useEffect( without a closing bracket on subsequent lines containing dependency array
292
31
  if (/useEffect\s*\(\s*\(\s*\)\s*=>\s*\{/.test(line)) {
32
+ // Look ahead for the closing of useEffect — find , [] or , [deps])
293
33
  let foundDeps = false;
294
34
  let parenDepth = 0;
295
35
  for (let j = i; j < Math.min(i + 50, lines.length); j++) {
@@ -299,6 +39,7 @@ const reactRules = [
299
39
  if (ch === ')')
300
40
  parenDepth--;
301
41
  }
42
+ // Check if the dependency array is present
302
43
  if (/\]\s*\)\s*;?\s*$/.test(lines[j]) || /,\s*\[/.test(lines[j])) {
303
44
  foundDeps = true;
304
45
  break;
@@ -319,18 +60,6 @@ const reactRules = [
319
60
  applicableTo: ['react', 'nextjs'],
320
61
  category: 'React Hooks',
321
62
  },
322
- // ─────────────────────────────────────────
323
- // RULE: react-no-inline-styles
324
- // ROLE: Block inline style objects in JSX
325
- // PURPOSE: Inline style={{ }} creates a new object on every render,
326
- // preventing React's reconciliation from skipping unchanged
327
- // elements. Use Tailwind or CSS modules instead.
328
- // EXAMPLE:
329
- // WRONG:
330
- // <div style={{ color: 'red', fontSize: '1rem' }}>Hello</div>
331
- // RIGHT:
332
- // <div className="text-red-500 text-base">Hello</div>
333
- // ─────────────────────────────────────────
334
63
  {
335
64
  id: 'react-no-inline-styles',
336
65
  label: 'No inline style objects',
@@ -341,21 +70,6 @@ const reactRules = [
341
70
  applicableTo: ['react', 'nextjs'],
342
71
  category: 'Styling',
343
72
  },
344
- // ─────────────────────────────────────────
345
- // RULE: react-no-direct-dom
346
- // ROLE: Block direct DOM manipulation in React components
347
- // PURPOSE: React maintains a virtual DOM — direct DOM manipulation
348
- // bypasses React's reconciliation and causes state
349
- // inconsistencies, hydration errors, and bugs.
350
- // EXAMPLE:
351
- // WRONG:
352
- // document.getElementById('input').value = 'hello';
353
- // document.querySelector('.modal').classList.add('open');
354
- // RIGHT:
355
- // const inputRef = useRef<HTMLInputElement>(null);
356
- // const [isOpen, setIsOpen] = useState(false);
357
- // inputRef.current!.value = 'hello';
358
- // ─────────────────────────────────────────
359
73
  {
360
74
  id: 'react-no-direct-dom',
361
75
  label: 'No direct DOM manipulation',
@@ -367,63 +81,16 @@ const reactRules = [
367
81
  category: 'React Patterns',
368
82
  },
369
83
  // ── Component patterns ─────────────────────────────────────────────────
370
- // ─────────────────────────────────────────
371
- // RULE: react-no-react-fc
372
- // ROLE: Block deprecated React.FC type
373
- // PURPOSE: React.FC implicitly includes children, has issues with
374
- // generics, and doesn't work well with defaultProps. Direct
375
- // prop typing is cleaner and more explicit.
376
- // EXAMPLE:
377
- // WRONG:
378
- // const UserCard: React.FC<Props> = ({ name }) => { ... };
379
- // RIGHT:
380
- // function UserCard({ name }: Props): React.ReactNode { ... }
381
- // // Or:
382
- // const UserCard = ({ name }: Props) => { ... };
383
- // ─────────────────────────────────────────
384
84
  {
385
- id: 'react-no-react-fc',
386
- label: 'Do not use React.FC',
387
- description: 'React.FC is deprecated. Type props parameter directly: export default function MyComponent(props: Props) { ... } or use const style with explicit return type.',
388
- severity: 'warning',
85
+ id: 'react-prefer-fc-typing',
86
+ label: 'Type component props explicitly',
87
+ description: 'Define a Props interface/type and use it. Avoid React.FC type props parameter directly.',
88
+ severity: 'info',
389
89
  fileExtensions: ['tsx'],
390
90
  pattern: /React\.FC/g,
391
91
  applicableTo: ['react', 'nextjs'],
392
92
  category: 'TypeScript',
393
93
  },
394
- // ─────────────────────────────────────────
395
- // RULE: react-explicit-return-types
396
- // ROLE: Recommend explicit return types for component functions
397
- // PURPOSE: Explicit return types catch accidental non-JSX returns,
398
- // prevent undefined leaks, and improve code documentation.
399
- // EXAMPLE:
400
- // WRONG:
401
- // export function UserCard(props: Props) { ... }
402
- // RIGHT:
403
- // export function UserCard(props: Props): React.ReactNode { ... }
404
- // ─────────────────────────────────────────
405
- {
406
- id: 'react-explicit-return-types',
407
- label: 'Add explicit return types for components',
408
- description: 'Export function components with explicit return type: (props: Props): React.ReactNode => { ... } for better type safety.',
409
- severity: 'info',
410
- fileExtensions: ['tsx', 'jsx'],
411
- pattern: /export\s+(?:default\s+)?function\s+\w+\s*\(\s*props/g,
412
- applicableTo: ['react', 'nextjs'],
413
- category: 'TypeScript',
414
- },
415
- // ─────────────────────────────────────────
416
- // RULE: react-no-prop-spreading
417
- // ROLE: Discourage prop spreading for API clarity
418
- // PURPOSE: Spreading props ({...props}) hides the component API,
419
- // passes unintended props, and breaks TypeScript strictness.
420
- // Explicitly passing required props is safer and self-documenting.
421
- // EXAMPLE:
422
- // WRONG:
423
- // <Button {...props} />
424
- // RIGHT:
425
- // <Button onClick={props.onClick} disabled={props.disabled} label={props.label} />
426
- // ─────────────────────────────────────────
427
94
  {
428
95
  id: 'react-no-prop-spreading',
429
96
  label: 'Avoid prop spreading ({...props})',
@@ -437,20 +104,6 @@ const reactRules = [
437
104
  // ── Security Rules ─────────────────────────────────────────────────────
438
105
  // NOTE: 'react-no-dangerously-set-html' removed — covered by ESLint 'react/no-danger' rule
439
106
  // NOTE: 'no-eval' removed — covered by ESLint 'no-eval' rule
440
- // ─────────────────────────────────────────
441
- // RULE: no-hardcoded-credentials
442
- // ROLE: Block hardcoded secrets in source code
443
- // PURPOSE: Hardcoded API keys, passwords, and tokens get committed to
444
- // git, leaked in public repos, and exposed in client bundles.
445
- // Always use environment variables (.env).
446
- // EXAMPLE:
447
- // WRONG:
448
- // const apiKey = 'sk-1234567890abcdef';
449
- // const password = 'admin123';
450
- // RIGHT:
451
- // const apiKey = process.env.API_KEY;
452
- // const password = process.env.ADMIN_PASSWORD;
453
- // ─────────────────────────────────────────
454
107
  {
455
108
  id: 'no-hardcoded-credentials',
456
109
  label: 'No hardcoded credentials',
@@ -485,20 +138,6 @@ const reactRules = [
485
138
  applicableTo: ['react', 'nextjs', 'angular'],
486
139
  category: 'Security',
487
140
  },
488
- // ─────────────────────────────────────────
489
- // RULE: require-https
490
- // ROLE: Block insecure HTTP URLs in source code
491
- // PURPOSE: HTTP transmits data in plain text — passwords, tokens, and
492
- // user data can be intercepted. HTTPS is mandatory for all
493
- // production API calls and external resources.
494
- // EXAMPLE:
495
- // WRONG:
496
- // fetch('http://api.example.com/users');
497
- // RIGHT:
498
- // fetch('https://api.example.com/users');
499
- // // localhost is exempt:
500
- // fetch('http://localhost:3000/api');
501
- // ─────────────────────────────────────────
502
141
  {
503
142
  id: 'require-https',
504
143
  label: 'Require HTTPS URLs',
@@ -510,46 +149,20 @@ const reactRules = [
510
149
  category: 'Security',
511
150
  },
512
151
  // ── Performance Rules ──────────────────────────────────────────────────
513
- // ─────────────────────────────────────────
514
- // RULE: react-no-anonymous-functions-in-render
515
- // ROLE: Block anonymous functions in JSX event handlers
516
- // PURPOSE: Arrow functions in props create a new function reference on
517
- // every render, defeating React.memo and causing child
518
- // components to re-render unnecessarily.
519
- // EXAMPLE:
520
- // WRONG:
521
- // <Button onClick={() => handleClick(id)} />
522
- // RIGHT:
523
- // const handleButtonClick = useCallback(() => handleClick(id), [id]);
524
- // <Button onClick={handleButtonClick} />
525
- // ─────────────────────────────────────────
526
152
  {
527
153
  id: 'react-no-anonymous-functions-in-render',
528
- label: 'No anonymous functions in JSX props',
529
- description: 'Anonymous functions in JSX props cause unnecessary re-renders. Use useCallback, named functions, or extract to constants.',
154
+ label: 'No anonymous functions in JSX',
155
+ description: 'Anonymous functions in JSX props cause unnecessary re-renders. Use useCallback or define functions outside render.',
530
156
  severity: 'warning',
531
157
  fileExtensions: ['tsx', 'jsx'],
532
- pattern: /onClick\s*=\s*\{\s*\(\s*\)\s*=>|onSubmit\s*=\s*\{\s*\(\s*\)\s*=>/g,
158
+ pattern: /onClick\s*=\s*\{\s*\(\s*\)\s*=>/g,
533
159
  applicableTo: ['react', 'nextjs'],
534
160
  category: 'Performance',
535
161
  },
536
- // ─────────────────────────────────────────
537
- // RULE: react-missing-key-prop
538
- // ROLE: Block list rendering without key props
539
- // PURPOSE: React uses key to identify which items changed, were added,
540
- // or removed. Missing keys cause UI bugs and poor performance.
541
- // Array index as key causes issues with reordering.
542
- // EXAMPLE:
543
- // WRONG:
544
- // items.map(item => <Card title={item.name} />);
545
- // items.map((item, i) => <Card key={i} title={item.name} />);
546
- // RIGHT:
547
- // items.map(item => <Card key={item.id} title={item.name} />);
548
- // ─────────────────────────────────────────
549
162
  {
550
163
  id: 'react-missing-key-prop',
551
164
  label: 'Missing key prop in list',
552
- description: 'When rendering lists with .map(), always provide a unique key prop. Never use array index as key.',
165
+ description: 'When rendering lists with .map(), always provide a unique key prop for optimal reconciliation.',
553
166
  severity: 'error',
554
167
  fileExtensions: ['tsx', 'jsx'],
555
168
  pattern: null,
@@ -558,7 +171,9 @@ const reactRules = [
558
171
  const lines = file.lines;
559
172
  for (let i = 0; i < lines.length; i++) {
560
173
  const line = lines[i];
174
+ // Detect .map( with JSX but no key prop nearby
561
175
  if (/\.\s*map\s*\(/.test(line)) {
176
+ // Look ahead for JSX opening tag
562
177
  let foundKey = false;
563
178
  for (let j = i; j < Math.min(i + 10, lines.length); j++) {
564
179
  if (/key\s*=/.test(lines[j])) {
@@ -566,6 +181,7 @@ const reactRules = [
566
181
  break;
567
182
  }
568
183
  }
184
+ // Check if there's JSX in the map
569
185
  let hasJsx = false;
570
186
  for (let j = i; j < Math.min(i + 10, lines.length); j++) {
571
187
  if (/<[A-Z]/.test(lines[j]) || /<[a-z]/.test(lines[j])) {
@@ -576,7 +192,7 @@ const reactRules = [
576
192
  if (hasJsx && !foundKey) {
577
193
  violations.push({
578
194
  line: i + 1,
579
- message: 'List items rendered with .map() must have a key prop. Never use index as key.',
195
+ message: 'List items rendered with .map() must have a key prop',
580
196
  });
581
197
  }
582
198
  }
@@ -586,20 +202,6 @@ const reactRules = [
586
202
  applicableTo: ['react', 'nextjs'],
587
203
  category: 'Performance',
588
204
  },
589
- // ─────────────────────────────────────────
590
- // RULE: no-large-bundle-imports
591
- // ROLE: Block large library wildcard imports
592
- // PURPOSE: Importing entire lodash (~70KB) or moment (~290KB) when
593
- // you only need one function wastes bandwidth. Use specific
594
- // imports for tree-shaking support.
595
- // EXAMPLE:
596
- // WRONG:
597
- // import _ from 'lodash';
598
- // import moment from 'moment';
599
- // RIGHT:
600
- // import get from 'lodash/get';
601
- // import { format } from 'date-fns';
602
- // ─────────────────────────────────────────
603
205
  {
604
206
  id: 'no-large-bundle-imports',
605
207
  label: 'Avoid large library imports',
@@ -612,19 +214,6 @@ const reactRules = [
612
214
  },
613
215
  // NOTE: 'require-alt-text' removed — covered by ESLint 'jsx-a11y/alt-text' rule
614
216
  // ── Accessibility Rules ────────────────────────────────────────────────
615
- // ─────────────────────────────────────────
616
- // RULE: require-button-type
617
- // ROLE: Enforce type attribute on all button elements
618
- // PURPOSE: Buttons default to type="submit", which triggers form
619
- // submission unexpectedly. Always specify type explicitly
620
- // to prevent accidental form submissions.
621
- // EXAMPLE:
622
- // WRONG:
623
- // <button onClick={handleClick}>Click me</button>
624
- // RIGHT:
625
- // <button type="button" onClick={handleClick}>Click me</button>
626
- // <button type="submit">Submit</button>
627
- // ─────────────────────────────────────────
628
217
  {
629
218
  id: 'require-button-type',
630
219
  label: 'Buttons must have type attribute',
@@ -650,21 +239,6 @@ const reactRules = [
650
239
  category: 'Accessibility',
651
240
  },
652
241
  // ── Additional Performance Rules ───────────────────────────────────────
653
- // ─────────────────────────────────────────
654
- // RULE: react-prefer-lazy-loading
655
- // ROLE: Recommend code splitting for route components
656
- // PURPOSE: Eagerly importing all page components increases the initial
657
- // bundle size. React.lazy() + Suspense splits code by route,
658
- // loading pages only when navigated to.
659
- // EXAMPLE:
660
- // WRONG:
661
- // import Dashboard from '../pages/Dashboard';
662
- // import Settings from '../pages/Settings';
663
- // RIGHT:
664
- // const Dashboard = lazy(() => import('../pages/Dashboard'));
665
- // const Settings = lazy(() => import('../pages/Settings'));
666
- // <Suspense fallback={<Spinner />}><Dashboard /></Suspense>
667
- // ─────────────────────────────────────────
668
242
  {
669
243
  id: 'react-prefer-lazy-loading',
670
244
  label: 'Consider lazy loading for routes',
@@ -675,22 +249,6 @@ const reactRules = [
675
249
  applicableTo: ['react', 'nextjs'],
676
250
  category: 'Performance',
677
251
  },
678
- // ─────────────────────────────────────────
679
- // RULE: react-memo-expensive-components
680
- // ROLE: Recommend React.memo for large render trees
681
- // PURPOSE: Components with expensive render logic (>500 chars of JSX)
682
- // should be wrapped in React.memo to skip re-renders when
683
- // props haven't changed.
684
- // EXAMPLE:
685
- // WRONG:
686
- // export function DataTable({ rows }) {
687
- // // ... 500+ lines of complex rendering
688
- // }
689
- // RIGHT:
690
- // export const DataTable = memo(function DataTable({ rows }) {
691
- // // ... 500+ lines of complex rendering
692
- // });
693
- // ─────────────────────────────────────────
694
252
  {
695
253
  id: 'react-memo-expensive-components',
696
254
  label: 'Consider React.memo for expensive renders',
@@ -701,21 +259,6 @@ const reactRules = [
701
259
  applicableTo: ['react', 'nextjs'],
702
260
  category: 'Performance',
703
261
  },
704
- // ─────────────────────────────────────────
705
- // RULE: no-object-array-in-deps
706
- // ROLE: Block unstable references in hook dependency arrays
707
- // PURPOSE: Objects and arrays create new references on every render.
708
- // Putting them in dependency arrays triggers hooks on every
709
- // render, defeating the purpose of the dependency array.
710
- // EXAMPLE:
711
- // WRONG:
712
- // useEffect(() => { ... }, [{ id: 1 }]);
713
- // useMemo(() => { ... }, [[1, 2, 3]]);
714
- // RIGHT:
715
- // useEffect(() => { ... }, [id]);
716
- // const configRef = useRef(config); // stable reference
717
- // useEffect(() => { ... }, [configRef]);
718
- // ─────────────────────────────────────────
719
262
  {
720
263
  id: 'no-object-array-in-deps',
721
264
  label: 'Avoid objects/arrays in dependency arrays',
@@ -727,18 +270,6 @@ const reactRules = [
727
270
  category: 'Performance',
728
271
  },
729
272
  // ── Additional Best Practice Rules ────────────────────────────────────
730
- // ─────────────────────────────────────────
731
- // RULE: react-use-fragments
732
- // ROLE: Recommend React fragments over wrapper divs
733
- // PURPOSE: Unnecessary wrapper <div>s add DOM nodes, break CSS layouts
734
- // (flexbox, grid), increase memory usage, and cause
735
- // accessibility issues with screen readers.
736
- // EXAMPLE:
737
- // WRONG:
738
- // return (<div><Header /><Content /></div>);
739
- // RIGHT:
740
- // return (<><Header /><Content /></>);
741
- // ─────────────────────────────────────────
742
273
  {
743
274
  id: 'react-use-fragments',
744
275
  label: 'Use React fragments instead of divs',
@@ -749,18 +280,6 @@ const reactRules = [
749
280
  applicableTo: ['react', 'nextjs'],
750
281
  category: 'Best Practices',
751
282
  },
752
- // ─────────────────────────────────────────
753
- // RULE: prefer-optional-chaining
754
- // ROLE: Recommend modern null-safe access patterns
755
- // PURPOSE: Chained && checks are verbose and error-prone. Optional
756
- // chaining (?.) is built into the language, more readable,
757
- // and handles null/undefined safely.
758
- // EXAMPLE:
759
- // WRONG:
760
- // user && user.profile && user.profile.name
761
- // RIGHT:
762
- // user?.profile?.name
763
- // ─────────────────────────────────────────
764
283
  {
765
284
  id: 'prefer-optional-chaining',
766
285
  label: 'Use optional chaining',
@@ -771,18 +290,6 @@ const reactRules = [
771
290
  applicableTo: [],
772
291
  category: 'Modern Syntax',
773
292
  },
774
- // ─────────────────────────────────────────
775
- // RULE: prefer-nullish-coalescing
776
- // ROLE: Recommend ?? over || for default values
777
- // PURPOSE: || treats 0, '', and false as falsy and replaces them.
778
- // ?? only replaces null/undefined, preserving valid falsy
779
- // values like 0 (count) and '' (empty string).
780
- // EXAMPLE:
781
- // WRONG:
782
- // const count = data.count || 10; // 0 becomes 10!
783
- // RIGHT:
784
- // const count = data.count ?? 10; // 0 stays 0
785
- // ─────────────────────────────────────────
786
293
  {
787
294
  id: 'prefer-nullish-coalescing',
788
295
  label: 'Use nullish coalescing (??)',
@@ -793,24 +300,6 @@ const reactRules = [
793
300
  applicableTo: [],
794
301
  category: 'Modern Syntax',
795
302
  },
796
- // ─────────────────────────────────────────
797
- // RULE: react-error-boundary-usage
798
- // ROLE: Recommend error boundaries for crash prevention
799
- // PURPOSE: Without error boundaries, a single thrown error crashes
800
- // the entire React app. Error boundaries catch render errors
801
- // and display fallback UI instead.
802
- // EXAMPLE:
803
- // WRONG:
804
- // <App>
805
- // <BuggyComponent /> {/* Crash kills entire app */}
806
- // </App>
807
- // RIGHT:
808
- // <App>
809
- // <ErrorBoundary fallback={<ErrorPage />}>
810
- // <BuggyComponent /> {/* Crash is contained */}
811
- // </ErrorBoundary>
812
- // </App>
813
- // ─────────────────────────────────────────
814
303
  {
815
304
  id: 'react-error-boundary-usage',
816
305
  label: 'Consider error boundaries',
@@ -821,19 +310,6 @@ const reactRules = [
821
310
  applicableTo: ['react', 'nextjs'],
822
311
  category: 'Error Handling',
823
312
  },
824
- // ─────────────────────────────────────────
825
- // RULE: no-sync-external-calls
826
- // ROLE: Block synchronous external API calls
827
- // PURPOSE: Synchronous fetch/XHR blocks the main thread, freezing
828
- // the UI completely. Always use async/await for API calls,
829
- // file operations, and external services.
830
- // EXAMPLE:
831
- // WRONG:
832
- // const data = fetch('/api/data'); // No await!
833
- // RIGHT:
834
- // const data = await fetch('/api/data');
835
- // const json = await data.json();
836
- // ─────────────────────────────────────────
837
313
  {
838
314
  id: 'no-sync-external-calls',
839
315
  label: 'Avoid synchronous external calls',
@@ -844,18 +320,6 @@ const reactRules = [
844
320
  applicableTo: [],
845
321
  category: 'Async Patterns',
846
322
  },
847
- // ─────────────────────────────────────────
848
- // RULE: react-prefer-controlled-inputs
849
- // ROLE: Recommend controlled components over uncontrolled
850
- // PURPOSE: Controlled components (value + onChange) keep React as the
851
- // single source of truth. Uncontrolled (defaultValue + ref)
852
- // creates two sources of truth and makes validation harder.
853
- // EXAMPLE:
854
- // WRONG:
855
- // <input defaultValue="hello" ref={inputRef} />
856
- // RIGHT:
857
- // <input value={name} onChange={(e) => setName(e.target.value)} />
858
- // ─────────────────────────────────────────
859
323
  {
860
324
  id: 'react-prefer-controlled-inputs',
861
325
  label: 'Prefer controlled components',
@@ -867,23 +331,6 @@ const reactRules = [
867
331
  category: 'Forms',
868
332
  },
869
333
  // NOTE: 'no-index-as-key' removed — covered by ESLint 'react/no-array-index-key' rule
870
- // ─────────────────────────────────────────
871
- // RULE: react-cleanup-effects
872
- // ROLE: Enforce cleanup functions in side-effect hooks
873
- // PURPOSE: useEffect with subscriptions, timers, or event listeners
874
- // must return a cleanup function. Without it, listeners
875
- // accumulate on every re-render, causing memory leaks.
876
- // EXAMPLE:
877
- // WRONG:
878
- // useEffect(() => {
879
- // window.addEventListener('resize', handleResize);
880
- // }, []);
881
- // RIGHT:
882
- // useEffect(() => {
883
- // window.addEventListener('resize', handleResize);
884
- // return () => window.removeEventListener('resize', handleResize);
885
- // }, []);
886
- // ─────────────────────────────────────────
887
334
  {
888
335
  id: 'react-cleanup-effects',
889
336
  label: 'Cleanup side effects',
@@ -894,20 +341,6 @@ const reactRules = [
894
341
  applicableTo: ['react', 'nextjs'],
895
342
  category: 'Memory Management',
896
343
  },
897
- // ─────────────────────────────────────────
898
- // RULE: prefer-const-assertion
899
- // ROLE: Recommend const assertions for literal types
900
- // PURPOSE: "as const" narrows types to literal values and makes
901
- // objects/arrays readonly, improving type safety and
902
- // enabling discriminated unions.
903
- // EXAMPLE:
904
- // WRONG:
905
- // const ROLES: string[] = ['admin', 'user', 'viewer'];
906
- // // Type: string[] — loses literal type info
907
- // RIGHT:
908
- // const ROLES = ['admin', 'user', 'viewer'] as const;
909
- // // Type: readonly ['admin', 'user', 'viewer']
910
- // ─────────────────────────────────────────
911
344
  {
912
345
  id: 'prefer-const-assertion',
913
346
  label: 'Use const assertions for literal types',