@hatem427/code-guard-ci 2.2.8 → 3.0.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 (73) hide show
  1. package/config/angular.config.ts +468 -27
  2. package/config/guidelines.config.ts +130 -5
  3. package/config/nextjs.config.ts +284 -11
  4. package/config/react.config.ts +440 -16
  5. package/dist/config/angular.config.d.ts.map +1 -1
  6. package/dist/config/angular.config.js +468 -26
  7. package/dist/config/angular.config.js.map +1 -1
  8. package/dist/config/guidelines.config.d.ts.map +1 -1
  9. package/dist/config/guidelines.config.js +127 -5
  10. package/dist/config/guidelines.config.js.map +1 -1
  11. package/dist/config/nextjs.config.d.ts.map +1 -1
  12. package/dist/config/nextjs.config.js +284 -11
  13. package/dist/config/nextjs.config.js.map +1 -1
  14. package/dist/config/react.config.d.ts.map +1 -1
  15. package/dist/config/react.config.js +440 -16
  16. package/dist/config/react.config.js.map +1 -1
  17. package/dist/scripts/config-generators/ai-config-generator.d.ts.map +1 -1
  18. package/dist/scripts/config-generators/ai-config-generator.js +9 -71
  19. package/dist/scripts/config-generators/ai-config-generator.js.map +1 -1
  20. package/dist/scripts/config-generators/eslint-generator.d.ts.map +1 -1
  21. package/dist/scripts/config-generators/eslint-generator.js +517 -13
  22. package/dist/scripts/config-generators/eslint-generator.js.map +1 -1
  23. package/dist/scripts/config-generators/frameworks/angular.d.ts +6 -0
  24. package/dist/scripts/config-generators/frameworks/angular.d.ts.map +1 -0
  25. package/dist/scripts/config-generators/frameworks/angular.js +81 -0
  26. package/dist/scripts/config-generators/frameworks/angular.js.map +1 -0
  27. package/dist/scripts/config-generators/frameworks/general.d.ts +6 -0
  28. package/dist/scripts/config-generators/frameworks/general.d.ts.map +1 -0
  29. package/dist/scripts/config-generators/frameworks/general.js +15 -0
  30. package/dist/scripts/config-generators/frameworks/general.js.map +1 -0
  31. package/dist/scripts/config-generators/frameworks/index.d.ts +17 -0
  32. package/dist/scripts/config-generators/frameworks/index.d.ts.map +1 -0
  33. package/dist/scripts/config-generators/frameworks/index.js +28 -0
  34. package/dist/scripts/config-generators/frameworks/index.js.map +1 -0
  35. package/dist/scripts/config-generators/frameworks/nextjs.d.ts +6 -0
  36. package/dist/scripts/config-generators/frameworks/nextjs.d.ts.map +1 -0
  37. package/dist/scripts/config-generators/frameworks/nextjs.js +115 -0
  38. package/dist/scripts/config-generators/frameworks/nextjs.js.map +1 -0
  39. package/dist/scripts/config-generators/frameworks/node.d.ts +6 -0
  40. package/dist/scripts/config-generators/frameworks/node.d.ts.map +1 -0
  41. package/dist/scripts/config-generators/frameworks/node.js +19 -0
  42. package/dist/scripts/config-generators/frameworks/node.js.map +1 -0
  43. package/dist/scripts/config-generators/frameworks/nuxt.d.ts +6 -0
  44. package/dist/scripts/config-generators/frameworks/nuxt.d.ts.map +1 -0
  45. package/dist/scripts/config-generators/frameworks/nuxt.js +18 -0
  46. package/dist/scripts/config-generators/frameworks/nuxt.js.map +1 -0
  47. package/dist/scripts/config-generators/frameworks/react.d.ts +6 -0
  48. package/dist/scripts/config-generators/frameworks/react.d.ts.map +1 -0
  49. package/dist/scripts/config-generators/frameworks/react.js +117 -0
  50. package/dist/scripts/config-generators/frameworks/react.js.map +1 -0
  51. package/dist/scripts/config-generators/frameworks/svelte.d.ts +6 -0
  52. package/dist/scripts/config-generators/frameworks/svelte.d.ts.map +1 -0
  53. package/dist/scripts/config-generators/frameworks/svelte.js +17 -0
  54. package/dist/scripts/config-generators/frameworks/svelte.js.map +1 -0
  55. package/dist/scripts/config-generators/frameworks/vue.d.ts +6 -0
  56. package/dist/scripts/config-generators/frameworks/vue.d.ts.map +1 -0
  57. package/dist/scripts/config-generators/frameworks/vue.js +19 -0
  58. package/dist/scripts/config-generators/frameworks/vue.js.map +1 -0
  59. package/dist/scripts/utils/report-generator.js +17 -5
  60. package/dist/scripts/utils/report-generator.js.map +1 -1
  61. package/package.json +1 -1
  62. package/scripts/config-generators/ai-config-generator.ts +19 -78
  63. package/scripts/config-generators/eslint-generator.ts +511 -7
  64. package/scripts/config-generators/frameworks/angular.ts +78 -0
  65. package/scripts/config-generators/frameworks/general.ts +12 -0
  66. package/scripts/config-generators/frameworks/index.ts +17 -0
  67. package/scripts/config-generators/frameworks/nextjs.ts +112 -0
  68. package/scripts/config-generators/frameworks/node.ts +16 -0
  69. package/scripts/config-generators/frameworks/nuxt.ts +15 -0
  70. package/scripts/config-generators/frameworks/react.ts +114 -0
  71. package/scripts/config-generators/frameworks/svelte.ts +14 -0
  72. package/scripts/config-generators/frameworks/vue.ts +16 -0
  73. package/scripts/utils/report-generator.ts +19 -5
@@ -149,25 +149,245 @@ ${configs.join(',\n\n')},
149
149
  }
150
150
  function buildNxCustomRules(project) {
151
151
  const rules = [];
152
- // ── Code Quality ──────────────────────────────────────────────────────
153
- rules.push(` // ── Code Quality ──────────────────────────────────────────────`, ` 'no-console': ['warn', { allow: ['warn', 'error', 'info'] }],`, ` 'no-debugger': 'error',`, ` 'no-alert': 'error',`, ` 'no-eval': 'error',`, ` 'no-implied-eval': 'error',`, ` 'no-new-func': 'error',`, ` 'prefer-const': 'error',`, ` 'no-var': 'error',`, ` 'eqeqeq': ['error', 'always'],`, ` 'curly': ['error', 'all'],`, ` 'no-throw-literal': 'error',`, ` 'prefer-template': 'warn',`, ` 'no-useless-concat': 'warn',`);
154
- // ── Import ordering ──────────────────────────────────────────────────
155
- rules.push(``, ` // ── Import Ordering ──────────────────────────────────────────`, ` 'import/order': ['error', {`, ` groups: [`, ` 'builtin',`, ` 'external',`, ` 'internal',`, ` ['parent', 'sibling'],`, ` 'index',`, ` 'type',`, ` ],`, ` pathGroups: [`, ` { pattern: '@/**', group: 'internal', position: 'before' },`, ` ],`, ` pathGroupsExcludedImportTypes: ['type'],`, ` 'newlines-between': 'always',`, ` alphabetize: { order: 'asc', caseInsensitive: true },`, ` }],`, ` 'import/no-duplicates': 'error',`, ` 'import/no-unresolved': 'off',`);
156
- // ── TypeScript overrides (Nx flat/typescript already sets defaults) ────
152
+ // ═══════════════════════════════════════════════════════════════════════
153
+ // CODE QUALITY RULES
154
+ // These rules catch common bugs, security holes, and bad practices
155
+ // that slip through code review. They run on every save/commit.
156
+ // ═══════════════════════════════════════════════════════════════════════
157
+ rules.push(` // ── Code Quality ──────────────────────────────────────────────`,
158
+ // ─────────────────────────────────────────
159
+ // RULE: no-console
160
+ // WHY: console.log left in production code clutters browser devtools,
161
+ // can leak sensitive data, and slows down rendering in loops.
162
+ // We allow warn/error/info for legitimate logging.
163
+ // HOW: Flags console.log() and console.debug() but allows
164
+ // console.warn(), console.error(), console.info().
165
+ // ─────────────────────────────────────────
166
+ ` 'no-console': ['warn', { allow: ['warn', 'error', 'info'] }],`,
167
+ // ─────────────────────────────────────────
168
+ // RULE: no-debugger
169
+ // WHY: debugger statements pause execution in the browser. If shipped
170
+ // to production, they freeze the app for end users.
171
+ // HOW: Errors on any `debugger;` statement in the codebase.
172
+ // ─────────────────────────────────────────
173
+ ` 'no-debugger': 'error',`,
174
+ // ─────────────────────────────────────────
175
+ // RULE: no-alert
176
+ // WHY: alert(), confirm(), prompt() block the main thread and create
177
+ // an ugly UX. Use toast notifications or modal components instead.
178
+ // HOW: Errors on any call to alert(), confirm(), or prompt().
179
+ // ─────────────────────────────────────────
180
+ ` 'no-alert': 'error',`,
181
+ // ─────────────────────────────────────────
182
+ // RULE: no-eval
183
+ // WHY: eval() executes arbitrary strings as code — the #1 XSS vector.
184
+ // Attackers can inject malicious code through user input.
185
+ // HOW: Errors on any eval() call in the codebase.
186
+ // ─────────────────────────────────────────
187
+ ` 'no-eval': 'error',`,
188
+ // ─────────────────────────────────────────
189
+ // RULE: no-implied-eval
190
+ // WHY: setTimeout("code", delay) and setInterval("code", delay) are
191
+ // hidden eval() calls with the same security risks.
192
+ // HOW: Errors on setTimeout/setInterval/execScript with string args.
193
+ // ─────────────────────────────────────────
194
+ ` 'no-implied-eval': 'error',`,
195
+ // ─────────────────────────────────────────
196
+ // RULE: no-new-func
197
+ // WHY: new Function("a", "b", "return a + b") is another form of
198
+ // eval() that creates functions from strings — same XSS risk.
199
+ // HOW: Errors on any `new Function(...)` expression.
200
+ // ─────────────────────────────────────────
201
+ ` 'no-new-func': 'error',`,
202
+ // ─────────────────────────────────────────
203
+ // RULE: prefer-const
204
+ // WHY: Variables that are never reassigned should use `const` to
205
+ // signal intent. Prevents accidental reassignment bugs.
206
+ // HOW: Errors when `let` is used but the variable is never reassigned.
207
+ // WRONG: let name = 'John'; (never reassigned)
208
+ // RIGHT: const name = 'John';
209
+ // ─────────────────────────────────────────
210
+ ` 'prefer-const': 'error',`,
211
+ // ─────────────────────────────────────────
212
+ // RULE: no-var
213
+ // WHY: `var` has function scope (not block scope), is hoisted, and
214
+ // causes subtle bugs. `let` and `const` are block-scoped and safer.
215
+ // HOW: Errors on any `var` declaration.
216
+ // WRONG: var count = 0;
217
+ // RIGHT: let count = 0; OR const count = 0;
218
+ // ─────────────────────────────────────────
219
+ ` 'no-var': 'error',`,
220
+ // ─────────────────────────────────────────
221
+ // RULE: eqeqeq
222
+ // WHY: `==` performs type coercion (e.g., 0 == '' is true, null == undefined
223
+ // is true). `===` compares value AND type — no surprises.
224
+ // HOW: Errors on `==` and `!=`, requires `===` and `!==`.
225
+ // WRONG: if (count == '0') → true (coercion!)
226
+ // RIGHT: if (count === 0) → type-safe comparison
227
+ // ─────────────────────────────────────────
228
+ ` 'eqeqeq': ['error', 'always'],`,
229
+ // ─────────────────────────────────────────
230
+ // RULE: curly
231
+ // WHY: Omitting braces in if/else/for/while causes bugs when adding
232
+ // new lines — the new line runs OUTSIDE the block unconditionally.
233
+ // HOW: Requires { } braces for ALL control flow statements.
234
+ // WRONG: if (done) return;
235
+ // RIGHT: if (done) { return; }
236
+ // ─────────────────────────────────────────
237
+ ` 'curly': ['error', 'all'],`,
238
+ // ─────────────────────────────────────────
239
+ // RULE: no-throw-literal
240
+ // WHY: throw 'error' loses the stack trace. throw new Error('message')
241
+ // captures the full call stack for debugging.
242
+ // HOW: Errors when throwing strings, numbers, or other non-Error objects.
243
+ // WRONG: throw 'Something went wrong';
244
+ // RIGHT: throw new Error('Something went wrong');
245
+ // ─────────────────────────────────────────
246
+ ` 'no-throw-literal': 'error',`,
247
+ // ─────────────────────────────────────────
248
+ // RULE: prefer-template
249
+ // WHY: Template literals (`Hello ${name}`) are more readable than
250
+ // string concatenation ('Hello ' + name) and support multiline.
251
+ // HOW: Warns on string concatenation that could use template literals.
252
+ // WRONG: 'Hello ' + name + ', welcome!'
253
+ // RIGHT: `Hello ${name}, welcome!`
254
+ // ─────────────────────────────────────────
255
+ ` 'prefer-template': 'warn',`,
256
+ // ─────────────────────────────────────────
257
+ // RULE: no-useless-concat
258
+ // WHY: 'a' + 'b' can just be 'ab'. Useless concatenation adds noise
259
+ // and suggests the developer forgot to add a variable.
260
+ // HOW: Warns on concatenation of two string literals.
261
+ // WRONG: const path = '/api/' + 'users';
262
+ // RIGHT: const path = '/api/users';
263
+ // ─────────────────────────────────────────
264
+ ` 'no-useless-concat': 'warn',`);
265
+ // ═══════════════════════════════════════════════════════════════════════
266
+ // IMPORT ORDERING RULES
267
+ // Consistent import order makes files scannable at a glance.
268
+ // Groups: Node builtins → npm packages → workspace → parent → sibling → types
269
+ // ═══════════════════════════════════════════════════════════════════════
270
+ rules.push(``, ` // ── Import Ordering ──────────────────────────────────────────`,
271
+ // ─────────────────────────────────────────
272
+ // RULE: import/order
273
+ // WHY: Without enforced ordering, imports become a random mess that's
274
+ // hard to scan. Grouping by source type (builtin → external →
275
+ // internal → relative → type) makes dependencies clear.
276
+ // HOW: Auto-sorts imports into groups with blank lines between them.
277
+ // Alphabetizes within each group. @/ paths are treated as internal.
278
+ // WRONG:
279
+ // import { UserService } from '../services/user';
280
+ // import { Component } from '@angular/core';
281
+ // import * as fs from 'fs';
282
+ // RIGHT:
283
+ // import * as fs from 'fs'; // builtin
284
+ //
285
+ // import { Component } from '@angular/core'; // external
286
+ //
287
+ // import { UserService } from '../services/user'; // parent
288
+ // ─────────────────────────────────────────
289
+ ` 'import/order': ['error', {`, ` groups: [`, ` 'builtin',`, ` 'external',`, ` 'internal',`, ` ['parent', 'sibling'],`, ` 'index',`, ` 'type',`, ` ],`, ` pathGroups: [`, ` { pattern: '@/**', group: 'internal', position: 'before' },`, ` ],`, ` pathGroupsExcludedImportTypes: ['type'],`, ` 'newlines-between': 'always',`, ` alphabetize: { order: 'asc', caseInsensitive: true },`, ` }],`,
290
+ // ─────────────────────────────────────────
291
+ // RULE: import/no-duplicates
292
+ // WHY: Importing from the same module twice wastes space and causes
293
+ // confusion. Merge them into a single import statement.
294
+ // HOW: Errors when two import statements reference the same module.
295
+ // WRONG:
296
+ // import { signal } from '@angular/core';
297
+ // import { computed } from '@angular/core';
298
+ // RIGHT:
299
+ // import { signal, computed } from '@angular/core';
300
+ // ─────────────────────────────────────────
301
+ ` 'import/no-duplicates': 'error',`,
302
+ // ─────────────────────────────────────────
303
+ // RULE: import/no-unresolved — OFF
304
+ // WHY: TypeScript already validates imports at compile time. Enabling
305
+ // this ESLint rule causes false positives with path aliases
306
+ // (@/...) and monorepo workspace references.
307
+ // ─────────────────────────────────────────
308
+ ` 'import/no-unresolved': 'off',`);
309
+ // ═══════════════════════════════════════════════════════════════════════
310
+ // TYPESCRIPT RULES
311
+ // These supplement TypeScript's built-in type checking with style and
312
+ // safety rules that tsc doesn't enforce (e.g., no `any`, unused vars).
313
+ // ═══════════════════════════════════════════════════════════════════════
157
314
  if (project.usesTypeScript) {
158
- rules.push(``, ` // ── TypeScript ─────────────────────────────────────────────`, ` '@typescript-eslint/no-explicit-any': 'error',`, ` '@typescript-eslint/no-unused-vars': ['error', {`, ` argsIgnorePattern: '^_',`, ` varsIgnorePattern: '^_',`, ` }],`, ` '@typescript-eslint/explicit-function-return-type': 'off',`, ` '@typescript-eslint/no-non-null-assertion': 'warn',`, ` '@typescript-eslint/consistent-type-imports': ['error', {`, ` prefer: 'type-imports',`, ` }],`);
315
+ rules.push(``, ` // ── TypeScript ─────────────────────────────────────────────`,
316
+ // ─────────────────────────────────────────
317
+ // RULE: @typescript-eslint/no-explicit-any
318
+ // WHY: `any` disables ALL type checking — it defeats the purpose of
319
+ // TypeScript. It hides bugs, breaks autocomplete, and makes
320
+ // refactoring impossible. Use `unknown` if the type is truly unknown.
321
+ // HOW: Errors on any explicit `any` type annotation.
322
+ // WRONG: function parse(data: any) { ... }
323
+ // RIGHT: function parse(data: unknown) { ... }
324
+ // RIGHT: function parse(data: Record<string, string>) { ... }
325
+ // ─────────────────────────────────────────
326
+ ` '@typescript-eslint/no-explicit-any': 'warn',`,
327
+ // ─────────────────────────────────────────
328
+ // RULE: @typescript-eslint/no-unused-vars
329
+ // WHY: Unused variables are dead code — they clutter the file and
330
+ // confuse readers. Variables prefixed with _ are intentionally
331
+ // ignored (e.g., _event in callbacks, _unused in destructuring).
332
+ // HOW: Errors on unused variables/args. Ignores names starting with _.
333
+ // WRONG: const result = getData(); (result never used)
334
+ // RIGHT: const _result = getData(); (intentionally unused)
335
+ // RIGHT: getData(); (no binding at all)
336
+ // ─────────────────────────────────────────
337
+ ` '@typescript-eslint/no-unused-vars': ['error', {`, ` argsIgnorePattern: '^_',`, ` varsIgnorePattern: '^_',`, ` }],`,
338
+ // ─────────────────────────────────────────
339
+ // RULE: @typescript-eslint/explicit-function-return-type — OFF
340
+ // WHY: TypeScript infers return types automatically in most cases.
341
+ // Forcing explicit return types adds noise without benefit.
342
+ // We rely on TypeScript's inference and strict mode instead.
343
+ // ─────────────────────────────────────────
344
+ ` '@typescript-eslint/explicit-function-return-type': 'off',`,
345
+ // ─────────────────────────────────────────
346
+ // RULE: @typescript-eslint/no-non-null-assertion
347
+ // WHY: The `!` operator (e.g., user!.name) tells TypeScript "trust me,
348
+ // this is not null" — but it's lying. If it IS null, you get a
349
+ // runtime crash. Use optional chaining (?.) or proper null checks.
350
+ // HOW: Warns on any `!` non-null assertion.
351
+ // WRONG: const name = user!.name;
352
+ // RIGHT: const name = user?.name ?? 'Unknown';
353
+ // ─────────────────────────────────────────
354
+ ` '@typescript-eslint/no-non-null-assertion': 'warn',`,
355
+ // ─────────────────────────────────────────
356
+ // RULE: @typescript-eslint/consistent-type-imports
357
+ // WHY: `import type { User }` is erased at compile time — it adds
358
+ // ZERO bytes to the bundle. Regular `import { User }` may keep
359
+ // the module in the bundle even if only the type is used.
360
+ // HOW: Errors when a type-only import uses the regular import syntax.
361
+ // WRONG: import { User } from './models'; (User is only a type)
362
+ // RIGHT: import type { User } from './models';
363
+ // ─────────────────────────────────────────
364
+ ` '@typescript-eslint/consistent-type-imports': ['error', {`, ` prefer: 'type-imports',`, ` }],`);
159
365
  }
160
366
  return rules.join('\n');
161
367
  }
162
368
  function buildNxFrameworkConfig(project) {
163
369
  switch (project.type) {
164
370
  case 'angular':
371
+ // ═══════════════════════════════════════════════════════════════════
372
+ // ANGULAR-SPECIFIC NX RULES
373
+ // These rules enforce Angular coding conventions on top of Nx's
374
+ // built-in Angular ESLint configs (which include template linting).
375
+ // ═══════════════════════════════════════════════════════════════════
165
376
  return ` // Angular-specific Nx configs
166
377
  ...nx.configs['flat/angular'],
167
378
  ...nx.configs['flat/angular-template'],
168
379
  {
169
380
  files: ['**/*.ts'],
170
381
  rules: {
382
+ // ─────────────────────────────────────────
383
+ // RULE: @angular-eslint/directive-selector
384
+ // WHY: Directives MUST use a consistent prefix (e.g., 'app') to avoid
385
+ // naming collisions with third-party libraries and native HTML.
386
+ // camelCase is the Angular convention for attribute selectors.
387
+ // HOW: Errors if a directive selector doesn't start with 'app'.
388
+ // WRONG: @Directive({ selector: '[highlight]' })
389
+ // RIGHT: @Directive({ selector: '[appHighlight]' })
390
+ // ─────────────────────────────────────────
171
391
  '@angular-eslint/directive-selector': [
172
392
  'error',
173
393
  {
@@ -176,6 +396,15 @@ function buildNxFrameworkConfig(project) {
176
396
  style: 'camelCase',
177
397
  },
178
398
  ],
399
+ // ─────────────────────────────────────────
400
+ // RULE: @angular-eslint/component-selector
401
+ // WHY: Components MUST use a consistent prefix (e.g., 'app-') to avoid
402
+ // collisions with HTML elements and third-party components.
403
+ // kebab-case is required for custom element names (Web Components spec).
404
+ // HOW: Errors if a component selector doesn't start with 'app-'.
405
+ // WRONG: @Component({ selector: 'user-card' })
406
+ // RIGHT: @Component({ selector: 'app-user-card' })
407
+ // ─────────────────────────────────────────
179
408
  '@angular-eslint/component-selector': [
180
409
  'error',
181
410
  {
@@ -184,6 +413,12 @@ function buildNxFrameworkConfig(project) {
184
413
  style: 'kebab-case',
185
414
  },
186
415
  ],
416
+ // ─────────────────────────────────────────
417
+ // RULE: @angular-eslint/prefer-standalone — OFF
418
+ // WHY: We turned this off because our Angular constitution already
419
+ // enforces standalone components through Code Guardian's custom
420
+ // rules (angular-standalone-components). No need to double-enforce.
421
+ // ─────────────────────────────────────────
187
422
  '@angular-eslint/prefer-standalone': 'off',
188
423
  },
189
424
  },
@@ -192,34 +427,144 @@ function buildNxFrameworkConfig(project) {
192
427
  rules: {},
193
428
  }`;
194
429
  case 'react':
430
+ // ═══════════════════════════════════════════════════════════════════
431
+ // REACT-SPECIFIC NX RULES
432
+ // These rules enforce React best practices, accessibility, and hook
433
+ // rules on top of Nx's built-in React ESLint configs.
434
+ // ═══════════════════════════════════════════════════════════════════
195
435
  return ` // React-specific Nx configs
196
436
  ...nx.configs['flat/react'],
197
437
  {
198
438
  files: ['**/*.tsx', '**/*.jsx'],
199
439
  rules: {
440
+ // ─────────────────────────────────────────
441
+ // RULE: react/react-in-jsx-scope — OFF
442
+ // WHY: React 17+ has automatic JSX runtime — you no longer need
443
+ // \`import React from 'react'\` at the top of every JSX file.
444
+ // This rule was important for React 16 but is now obsolete.
445
+ // ─────────────────────────────────────────
200
446
  'react/react-in-jsx-scope': 'off',
447
+
448
+ // ─────────────────────────────────────────
449
+ // RULE: react/prop-types — OFF
450
+ // WHY: We use TypeScript interfaces for prop validation, which is
451
+ // stricter than React PropTypes. PropTypes only validate at
452
+ // runtime; TypeScript catches errors at compile time.
453
+ // ─────────────────────────────────────────
201
454
  'react/prop-types': 'off',
455
+
456
+ // ─────────────────────────────────────────
457
+ // RULE: react/no-danger
458
+ // WHY: dangerouslySetInnerHTML injects raw HTML — if the content
459
+ // comes from user input, it's an XSS vulnerability. Every
460
+ // usage must be reviewed and sanitized (e.g., DOMPurify).
461
+ // HOW: Errors on any use of dangerouslySetInnerHTML.
462
+ // WRONG: <div dangerouslySetInnerHTML={{ __html: userContent }} />
463
+ // RIGHT: <div>{sanitize(userContent)}</div>
464
+ // ─────────────────────────────────────────
202
465
  'react/no-danger': 'error',
466
+
467
+ // ─────────────────────────────────────────
468
+ // RULE: react/no-array-index-key
469
+ // WHY: Using array index as key causes React to reuse wrong DOM nodes
470
+ // when items are reordered, deleted, or inserted — leading to
471
+ // stale state, wrong animations, and visual glitches.
472
+ // HOW: Warns when .map((item, index) => <X key={index} />) is used.
473
+ // WRONG: items.map((item, i) => <Card key={i} ... />)
474
+ // RIGHT: items.map(item => <Card key={item.id} ... />)
475
+ // ─────────────────────────────────────────
203
476
  'react/no-array-index-key': 'warn',
477
+
478
+ // ─────────────────────────────────────────
479
+ // RULE: react/self-closing-comp
480
+ // WHY: Components without children should self-close for cleaner JSX.
481
+ // It reduces visual noise and makes the template scannable.
482
+ // HOW: Errors on non-self-closing tags that have no children.
483
+ // WRONG: <UserAvatar></UserAvatar>
484
+ // RIGHT: <UserAvatar />
485
+ // ─────────────────────────────────────────
204
486
  'react/self-closing-comp': 'error',
487
+
488
+ // ─────────────────────────────────────────
489
+ // RULE: react/jsx-no-target-blank
490
+ // WHY: <a target="_blank"> without rel="noopener noreferrer" allows
491
+ // the opened page to access window.opener — a security risk
492
+ // (reverse tabnabbing attack).
493
+ // HOW: Errors on target="_blank" without rel="noopener noreferrer".
494
+ // WRONG: <a href="..." target="_blank">Link</a>
495
+ // RIGHT: <a href="..." target="_blank" rel="noopener noreferrer">Link</a>
496
+ // ─────────────────────────────────────────
205
497
  'react/jsx-no-target-blank': 'error',
498
+
499
+ // ─────────────────────────────────────────
500
+ // RULE: react-hooks/rules-of-hooks
501
+ // WHY: Hooks MUST be called at the top level (not inside conditions,
502
+ // loops, or nested functions). React uses call order to match
503
+ // hooks to state — conditional calls break this mapping.
504
+ // HOW: Errors if hooks are called inside if/for/while or nested funcs.
505
+ // WRONG: if (isLoggedIn) { const [user] = useState(null); }
506
+ // RIGHT: const [user] = useState(null); // Always at top level
507
+ // ─────────────────────────────────────────
206
508
  'react-hooks/rules-of-hooks': 'error',
509
+
510
+ // ─────────────────────────────────────────
511
+ // RULE: react-hooks/exhaustive-deps
512
+ // WHY: Missing dependencies in useEffect/useMemo/useCallback cause
513
+ // stale closures — the hook reads outdated values. This is the
514
+ // #1 source of "why doesn't my effect re-run?" bugs.
515
+ // HOW: Warns when a dependency is used inside the hook but missing
516
+ // from the dependency array.
517
+ // WRONG: useEffect(() => fetchUser(userId), []); // missing userId
518
+ // RIGHT: useEffect(() => fetchUser(userId), [userId]);
519
+ // ─────────────────────────────────────────
207
520
  'react-hooks/exhaustive-deps': 'warn',
521
+
522
+ // ─────────────────────────────────────────
523
+ // RULE: jsx-a11y/alt-text
524
+ // WHY: Images without alt text are invisible to screen readers.
525
+ // Alt text describes the image for blind users and shows as
526
+ // fallback when images fail to load.
527
+ // HOW: Errors on <img> without alt prop.
528
+ // WRONG: <img src="/logo.png" />
529
+ // RIGHT: <img src="/logo.png" alt="Company logo" />
530
+ // ─────────────────────────────────────────
208
531
  'jsx-a11y/alt-text': 'error',
532
+
533
+ // ─────────────────────────────────────────
534
+ // RULE: jsx-a11y/anchor-is-valid
535
+ // WHY: <a> tags without href (or with href="#") are not focusable by
536
+ // keyboard and don't announce as links to screen readers.
537
+ // Use <button> for actions, <Link> for navigation.
538
+ // HOW: Errors on <a> without valid href.
539
+ // WRONG: <a onClick={handleClick}>Click me</a>
540
+ // RIGHT: <button onClick={handleClick}>Click me</button>
541
+ // ─────────────────────────────────────────
209
542
  'jsx-a11y/anchor-is-valid': 'error',
210
543
  },
211
544
  }`;
212
545
  case 'nextjs':
546
+ // ═══════════════════════════════════════════════════════════════════
547
+ // NEXT.JS-SPECIFIC NX RULES
548
+ // Next.js inherits React rules. We only include the subset that
549
+ // applies to Next.js (e.g., no-array-index-key is less critical
550
+ // since Server Components don't have the same reorder issues).
551
+ // ═══════════════════════════════════════════════════════════════════
213
552
  return ` // Next.js + React Nx configs
214
553
  ...nx.configs['flat/react'],
215
554
  {
216
555
  files: ['**/*.tsx', '**/*.jsx'],
217
556
  rules: {
557
+ // react/react-in-jsx-scope — OFF: React 17+ automatic JSX runtime
218
558
  'react/react-in-jsx-scope': 'off',
559
+ // react/prop-types — OFF: TypeScript handles prop validation
219
560
  'react/prop-types': 'off',
561
+ // react/no-danger — ERROR: Prevents XSS via dangerouslySetInnerHTML
220
562
  'react/no-danger': 'error',
563
+ // react-hooks/rules-of-hooks — ERROR: Hooks must follow call order rules
221
564
  'react-hooks/rules-of-hooks': 'error',
565
+ // react-hooks/exhaustive-deps — WARN: Catch missing hook dependencies
222
566
  'react-hooks/exhaustive-deps': 'warn',
567
+ // jsx-a11y/alt-text — ERROR: All images must have alt text for accessibility
223
568
  'jsx-a11y/alt-text': 'error',
224
569
  },
225
570
  }`;
@@ -344,17 +689,78 @@ function getFilePatterns(project) {
344
689
  function buildMainConfigBlock(project, filePatterns) {
345
690
  const plugins = [`import: importPlugin`];
346
691
  const rules = [];
347
- // ── Code Quality ─────────────────────────────────────────────────────
348
- rules.push(` // ── Code Quality ──────────────────────────────────────────────`, ` 'no-console': ['warn', { allow: ['warn', 'error', 'info'] }],`, ` 'no-debugger': 'error',`, ` 'no-alert': 'error',`, ` 'no-eval': 'error',`, ` 'no-implied-eval': 'error',`, ` 'no-new-func': 'error',`, ` 'prefer-const': 'error',`, ` 'no-var': 'error',`, ` 'eqeqeq': ['error', 'always'],`, ` 'curly': ['error', 'all'],`, ` 'no-throw-literal': 'error',`, ` 'prefer-template': 'warn',`, ` 'no-useless-concat': 'warn',`);
349
- // ── Import ordering ──────────────────────────────────────────────────
350
- rules.push(``, ` // ── Import Ordering ──────────────────────────────────────────`, ` 'import/order': ['error', {`, ` groups: [`, ` 'builtin',`, ` 'external',`, ` 'internal',`, ` ['parent', 'sibling'],`, ` 'index',`, ` 'type',`, ` ],`, ` pathGroups: [`, ` { pattern: '@/**', group: 'internal', position: 'before' },`, ` ],`, ` pathGroupsExcludedImportTypes: ['type'],`, ` 'newlines-between': 'always',`, ` alphabetize: { order: 'asc', caseInsensitive: true },`, ` }],`, ` 'import/no-duplicates': 'error',`, ` 'import/no-unresolved': 'off',`);
351
- // ── TypeScript rules ──────────────────────────────────────────────────
692
+ // ═══════════════════════════════════════════════════════════════════════
693
+ // CODE QUALITY RULES (standalone projects)
694
+ // Same rules as Nx — see buildNxCustomRules() for full documentation.
695
+ // ═══════════════════════════════════════════════════════════════════════
696
+ rules.push(` // ── Code Quality ──────────────────────────────────────────────`,
697
+ // no-console: Allow warn/error/info, block log/debug in production
698
+ ` 'no-console': ['warn', { allow: ['warn', 'error', 'info'] }],`,
699
+ // no-debugger: Never ship debugger statements to production
700
+ ` 'no-debugger': 'error',`,
701
+ // no-alert: Use proper UI components instead of browser dialogs
702
+ ` 'no-alert': 'error',`,
703
+ // no-eval: Block eval() — the #1 XSS attack vector
704
+ ` 'no-eval': 'error',`,
705
+ // no-implied-eval: Block setTimeout("string") — hidden eval()
706
+ ` 'no-implied-eval': 'error',`,
707
+ // no-new-func: Block new Function("string") — another eval() variant
708
+ ` 'no-new-func': 'error',`,
709
+ // prefer-const: Use const when variable is never reassigned
710
+ ` 'prefer-const': 'error',`,
711
+ // no-var: Block var — use let/const for block scoping
712
+ ` 'no-var': 'error',`,
713
+ // eqeqeq: Require === instead of == to prevent type coercion bugs
714
+ ` 'eqeqeq': ['error', 'always'],`,
715
+ // curly: Require braces on all control flow to prevent scope bugs
716
+ ` 'curly': ['error', 'all'],`,
717
+ // no-throw-literal: throw Error objects, not strings (preserves stack trace)
718
+ ` 'no-throw-literal': 'error',`,
719
+ // prefer-template: Use template literals over string concatenation
720
+ ` 'prefer-template': 'warn',`,
721
+ // no-useless-concat: Don't concatenate two string literals needlessly
722
+ ` 'no-useless-concat': 'warn',`);
723
+ // ═══════════════════════════════════════════════════════════════════════
724
+ // IMPORT ORDERING (standalone projects)
725
+ // Same ordering as Nx — see buildNxCustomRules() for full documentation.
726
+ // ═══════════════════════════════════════════════════════════════════════
727
+ rules.push(``, ` // ── Import Ordering ──────────────────────────────────────────`,
728
+ // import/order: Enforce grouped, alphabetized imports with newlines
729
+ ` 'import/order': ['error', {`, ` groups: [`, ` 'builtin',`, ` 'external',`, ` 'internal',`, ` ['parent', 'sibling'],`, ` 'index',`, ` 'type',`, ` ],`, ` pathGroups: [`, ` { pattern: '@/**', group: 'internal', position: 'before' },`, ` ],`, ` pathGroupsExcludedImportTypes: ['type'],`, ` 'newlines-between': 'always',`, ` alphabetize: { order: 'asc', caseInsensitive: true },`, ` }],`,
730
+ // import/no-duplicates: Merge imports from the same module
731
+ ` 'import/no-duplicates': 'error',`,
732
+ // import/no-unresolved — OFF: TypeScript handles module resolution
733
+ ` 'import/no-unresolved': 'off',`);
734
+ // ═══════════════════════════════════════════════════════════════════════
735
+ // TYPESCRIPT RULES (standalone projects)
736
+ // Same rules as Nx — see buildNxCustomRules() for full documentation.
737
+ // ═══════════════════════════════════════════════════════════════════════
352
738
  if (project.usesTypeScript) {
353
- rules.push(``, ` // ── TypeScript ─────────────────────────────────────────────`, ` '@typescript-eslint/no-explicit-any': 'error',`, ` '@typescript-eslint/no-unused-vars': ['error', {`, ` argsIgnorePattern: '^_',`, ` varsIgnorePattern: '^_',`, ` }],`, ` '@typescript-eslint/explicit-function-return-type': 'off',`, ` '@typescript-eslint/no-non-null-assertion': 'warn',`, ` '@typescript-eslint/consistent-type-imports': ['error', {`, ` prefer: 'type-imports',`, ` }],`);
739
+ rules.push(``, ` // ── TypeScript ─────────────────────────────────────────────`,
740
+ // no-explicit-any: Ban `any` — use unknown, generics, or proper types
741
+ ` '@typescript-eslint/no-explicit-any': 'error',`,
742
+ // no-unused-vars: Remove dead variables, _ prefix = intentionally unused
743
+ ` '@typescript-eslint/no-unused-vars': ['error', {`, ` argsIgnorePattern: '^_',`, ` varsIgnorePattern: '^_',`, ` }],`,
744
+ // explicit-function-return-type — OFF: TypeScript infers return types
745
+ ` '@typescript-eslint/explicit-function-return-type': 'off',`,
746
+ // no-non-null-assertion: Warn on ! — use ?. or null checks instead
747
+ ` '@typescript-eslint/no-non-null-assertion': 'warn',`,
748
+ // consistent-type-imports: Use `import type` for type-only imports (zero bundle cost)
749
+ ` '@typescript-eslint/consistent-type-imports': ['error', {`, ` prefer: 'type-imports',`, ` }],`);
354
750
  }
355
751
  // ── Node.js specific ──────────────────────────────────────────────────
356
752
  if (project.type === 'node') {
357
- rules.push(``, ` // ── Node.js ────────────────────────────────────────────────`, ` 'no-process-exit': 'warn',`);
753
+ rules.push(``, ` // ── Node.js ────────────────────────────────────────────────`,
754
+ // ─────────────────────────────────────────
755
+ // RULE: no-process-exit
756
+ // WHY: process.exit() kills the process immediately without running
757
+ // cleanup handlers (graceful shutdown, DB disconnect, file flush).
758
+ // Use proper error handling and let the process exit naturally.
759
+ // HOW: Warns on any process.exit() call.
760
+ // WRONG: process.exit(1);
761
+ // RIGHT: throw new Error('Fatal error'); // Let error handler manage exit
762
+ // ─────────────────────────────────────────
763
+ ` 'no-process-exit': 'warn',`);
358
764
  }
359
765
  // ── Language options ──────────────────────────────────────────────────
360
766
  const globalsEntries = [];
@@ -395,6 +801,11 @@ ${rules.join('\n')}
395
801
  function buildFrameworkConfig(project) {
396
802
  switch (project.type) {
397
803
  case 'react':
804
+ // ═══════════════════════════════════════════════════════════════════
805
+ // REACT STANDALONE RULES
806
+ // Full React, Hooks, and Accessibility rules for non-Nx projects.
807
+ // See buildNxFrameworkConfig() for detailed per-rule documentation.
808
+ // ═══════════════════════════════════════════════════════════════════
398
809
  return ` // React-specific rules
399
810
  {
400
811
  files: ['**/*.{tsx,jsx}'],
@@ -407,19 +818,34 @@ function buildFrameworkConfig(project) {
407
818
  react: { version: 'detect' },
408
819
  },
409
820
  rules: {
821
+ // react/react-in-jsx-scope — OFF: React 17+ automatic JSX runtime
410
822
  'react/react-in-jsx-scope': 'off',
823
+ // react/prop-types — OFF: TypeScript handles prop type validation
411
824
  'react/prop-types': 'off',
825
+ // react/no-danger — ERROR: Block dangerouslySetInnerHTML (XSS risk)
412
826
  'react/no-danger': 'error',
827
+ // react/no-array-index-key — WARN: Array index as key causes reorder bugs
413
828
  'react/no-array-index-key': 'warn',
829
+ // react/self-closing-comp — ERROR: <Component /> not <Component></Component>
414
830
  'react/self-closing-comp': 'error',
831
+ // react/jsx-no-target-blank — ERROR: Prevent reverse tabnabbing attack
415
832
  'react/jsx-no-target-blank': 'error',
833
+ // react-hooks/rules-of-hooks — ERROR: Hooks must follow call order rules
416
834
  'react-hooks/rules-of-hooks': 'error',
835
+ // react-hooks/exhaustive-deps — WARN: Catch missing hook dependencies
417
836
  'react-hooks/exhaustive-deps': 'warn',
837
+ // jsx-a11y/alt-text — ERROR: All images must have alt text
418
838
  'jsx-a11y/alt-text': 'error',
839
+ // jsx-a11y/anchor-is-valid — ERROR: <a> must have valid href or use <button>
419
840
  'jsx-a11y/anchor-is-valid': 'error',
420
841
  },
421
842
  }`;
422
843
  case 'nextjs':
844
+ // ═══════════════════════════════════════════════════════════════════
845
+ // NEXT.JS STANDALONE RULES
846
+ // React rules + Next.js plugin (image optimization, link component,
847
+ // Google Font loading, script optimization).
848
+ // ═══════════════════════════════════════════════════════════════════
423
849
  return ` // Next.js + React rules
424
850
  {
425
851
  files: ['**/*.{tsx,jsx}'],
@@ -433,27 +859,63 @@ function buildFrameworkConfig(project) {
433
859
  react: { version: 'detect' },
434
860
  },
435
861
  rules: {
862
+ // react/react-in-jsx-scope — OFF: React 17+ automatic JSX runtime
436
863
  'react/react-in-jsx-scope': 'off',
864
+ // react/prop-types — OFF: TypeScript handles prop type validation
437
865
  'react/prop-types': 'off',
866
+ // react/no-danger — ERROR: Block dangerouslySetInnerHTML (XSS risk)
438
867
  'react/no-danger': 'error',
868
+ // react-hooks/rules-of-hooks — ERROR: Hooks must follow call order rules
439
869
  'react-hooks/rules-of-hooks': 'error',
870
+ // react-hooks/exhaustive-deps — WARN: Catch missing hook dependencies
440
871
  'react-hooks/exhaustive-deps': 'warn',
872
+ // jsx-a11y/alt-text — ERROR: All images must have alt text
441
873
  'jsx-a11y/alt-text': 'error',
874
+ // Next.js recommended: Use <Image>, <Link>, <Script> components
442
875
  ...nextPlugin.configs.recommended.rules,
876
+ // Core Web Vitals: Enforce performance best practices
443
877
  ...nextPlugin.configs['core-web-vitals'].rules,
444
878
  },
445
879
  }`;
446
880
  case 'angular':
881
+ // ═══════════════════════════════════════════════════════════════════
882
+ // ANGULAR STANDALONE RULES
883
+ // Minimal Angular-specific rules — most enforcement is done by
884
+ // @angular-eslint (configured separately) and Code Guardian's
885
+ // custom rules in angular.config.ts.
886
+ // ═══════════════════════════════════════════════════════════════════
447
887
  return ` // Angular-specific rules
448
888
  {
449
889
  files: ['**/*.ts'],
450
890
  rules: {
891
+ // ─────────────────────────────────────────
892
+ // RULE: @typescript-eslint/no-empty-function
893
+ // WHY: Empty functions are usually forgotten implementations or
894
+ // dead code. If intentionally empty (e.g., noop callback),
895
+ // add a comment explaining why.
896
+ // HOW: Warns on empty function bodies.
897
+ // WRONG: ngOnInit() {}
898
+ // RIGHT: ngOnInit() { /* Intentionally empty — inputs are static */ }
899
+ // ─────────────────────────────────────────
451
900
  '@typescript-eslint/no-empty-function': 'warn',
901
+
902
+ // ─────────────────────────────────────────
903
+ // RULE: @typescript-eslint/member-ordering
904
+ // WHY: Consistent class member ordering makes large Angular classes
905
+ // scannable. Convention: static → injected → signals/inputs →
906
+ // lifecycle → public methods → private methods.
907
+ // HOW: Warns when class members are in unexpected order.
908
+ // ─────────────────────────────────────────
452
909
  '@typescript-eslint/member-ordering': 'warn',
453
910
  },
454
911
  }`;
455
912
  case 'vue':
456
913
  case 'nuxt':
914
+ // ═══════════════════════════════════════════════════════════════════
915
+ // VUE / NUXT RULES
916
+ // Vue ESLint plugin handles template linting, component naming,
917
+ // and Vue-specific patterns.
918
+ // ═══════════════════════════════════════════════════════════════════
457
919
  return ` // Vue-specific rules
458
920
  ...vuePlugin.configs['flat/recommended'],
459
921
  {
@@ -465,13 +927,55 @@ function buildFrameworkConfig(project) {
465
927
  },` : ''}
466
928
  },
467
929
  rules: {
930
+ // ─────────────────────────────────────────
931
+ // RULE: vue/multi-word-component-names
932
+ // WHY: Single-word component names can conflict with current or
933
+ // future HTML element names (e.g., <Header>, <Footer>).
934
+ // Multi-word names (e.g., <AppHeader>) prevent collisions.
935
+ // HOW: Warns on single-word component names.
936
+ // WRONG: export default { name: 'Header' }
937
+ // RIGHT: export default { name: 'AppHeader' }
938
+ // ─────────────────────────────────────────
468
939
  'vue/multi-word-component-names': 'warn',
940
+
941
+ // ─────────────────────────────────────────
942
+ // RULE: vue/no-v-html
943
+ // WHY: v-html injects raw HTML — same XSS risk as React's
944
+ // dangerouslySetInnerHTML. All content must be sanitized.
945
+ // HOW: Errors on any v-html directive usage.
946
+ // WRONG: <div v-html="userContent" />
947
+ // RIGHT: <div>{{ sanitizedContent }}</div>
948
+ // ─────────────────────────────────────────
469
949
  'vue/no-v-html': 'error',
950
+
951
+ // ─────────────────────────────────────────
952
+ // RULE: vue/require-default-prop
953
+ // WHY: Props without defaults cause undefined values when the
954
+ // parent doesn't pass the prop. Defaults ensure components
955
+ // always have valid data to render.
956
+ // HOW: Warns on props without a default value.
957
+ // WRONG: props: { count: { type: Number } }
958
+ // RIGHT: props: { count: { type: Number, default: 0 } }
959
+ // ─────────────────────────────────────────
470
960
  'vue/require-default-prop': 'warn',
961
+
962
+ // ─────────────────────────────────────────
963
+ // RULE: vue/component-name-in-template-casing
964
+ // WHY: PascalCase component names in templates distinguish custom
965
+ // components from native HTML elements (e.g., <UserCard> vs
966
+ // <div>). This is the Vue style guide recommended convention.
967
+ // HOW: Errors on non-PascalCase component usage in templates.
968
+ // WRONG: <user-card />
969
+ // RIGHT: <UserCard />
970
+ // ─────────────────────────────────────────
471
971
  'vue/component-name-in-template-casing': ['error', 'PascalCase'],
472
972
  },
473
973
  }`;
474
974
  case 'svelte':
975
+ // ═══════════════════════════════════════════════════════════════════
976
+ // SVELTE RULES
977
+ // Uses the official eslint-plugin-svelte with recommended defaults.
978
+ // ═══════════════════════════════════════════════════════════════════
475
979
  return ` // Svelte-specific rules
476
980
  ...sveltePlugin.configs['flat/recommended']`;
477
981
  default: