@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.
- package/config/angular.config.ts +468 -27
- package/config/guidelines.config.ts +130 -5
- package/config/nextjs.config.ts +284 -11
- package/config/react.config.ts +440 -16
- package/dist/config/angular.config.d.ts.map +1 -1
- package/dist/config/angular.config.js +468 -26
- package/dist/config/angular.config.js.map +1 -1
- package/dist/config/guidelines.config.d.ts.map +1 -1
- package/dist/config/guidelines.config.js +127 -5
- package/dist/config/guidelines.config.js.map +1 -1
- package/dist/config/nextjs.config.d.ts.map +1 -1
- package/dist/config/nextjs.config.js +284 -11
- package/dist/config/nextjs.config.js.map +1 -1
- package/dist/config/react.config.d.ts.map +1 -1
- package/dist/config/react.config.js +440 -16
- package/dist/config/react.config.js.map +1 -1
- package/dist/scripts/config-generators/ai-config-generator.d.ts.map +1 -1
- package/dist/scripts/config-generators/ai-config-generator.js +9 -71
- package/dist/scripts/config-generators/ai-config-generator.js.map +1 -1
- package/dist/scripts/config-generators/eslint-generator.d.ts.map +1 -1
- package/dist/scripts/config-generators/eslint-generator.js +517 -13
- package/dist/scripts/config-generators/eslint-generator.js.map +1 -1
- package/dist/scripts/config-generators/frameworks/angular.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/angular.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/angular.js +81 -0
- package/dist/scripts/config-generators/frameworks/angular.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/general.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/general.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/general.js +15 -0
- package/dist/scripts/config-generators/frameworks/general.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/index.d.ts +17 -0
- package/dist/scripts/config-generators/frameworks/index.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/index.js +28 -0
- package/dist/scripts/config-generators/frameworks/index.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/nextjs.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/nextjs.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/nextjs.js +115 -0
- package/dist/scripts/config-generators/frameworks/nextjs.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/node.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/node.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/node.js +19 -0
- package/dist/scripts/config-generators/frameworks/node.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/nuxt.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/nuxt.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/nuxt.js +18 -0
- package/dist/scripts/config-generators/frameworks/nuxt.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/react.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/react.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/react.js +117 -0
- package/dist/scripts/config-generators/frameworks/react.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/svelte.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/svelte.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/svelte.js +17 -0
- package/dist/scripts/config-generators/frameworks/svelte.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/vue.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/vue.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/vue.js +19 -0
- package/dist/scripts/config-generators/frameworks/vue.js.map +1 -0
- package/dist/scripts/utils/report-generator.js +17 -5
- package/dist/scripts/utils/report-generator.js.map +1 -1
- package/package.json +1 -1
- package/scripts/config-generators/ai-config-generator.ts +19 -78
- package/scripts/config-generators/eslint-generator.ts +511 -7
- package/scripts/config-generators/frameworks/angular.ts +78 -0
- package/scripts/config-generators/frameworks/general.ts +12 -0
- package/scripts/config-generators/frameworks/index.ts +17 -0
- package/scripts/config-generators/frameworks/nextjs.ts +112 -0
- package/scripts/config-generators/frameworks/node.ts +16 -0
- package/scripts/config-generators/frameworks/nuxt.ts +15 -0
- package/scripts/config-generators/frameworks/react.ts +114 -0
- package/scripts/config-generators/frameworks/svelte.ts +14 -0
- package/scripts/config-generators/frameworks/vue.ts +16 -0
- 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
|
-
//
|
|
153
|
-
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
//
|
|
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 ─────────────────────────────────────────────`,
|
|
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
|
-
//
|
|
348
|
-
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
// ──
|
|
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 ─────────────────────────────────────────────`,
|
|
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 ────────────────────────────────────────────────`,
|
|
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:
|