@qvaroo/configs 1.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/README.md +391 -0
- package/eslint/index.js +222 -0
- package/eslint/node.js +126 -0
- package/eslint/react.js +159 -0
- package/eslint/rules/architecture.js +152 -0
- package/eslint/rules/code-quality.js +142 -0
- package/eslint/rules/naming-conventions.js +183 -0
- package/eslint/rules/spellcheck.js +263 -0
- package/index.d.ts +4 -0
- package/index.js +11 -0
- package/package.json +69 -0
- package/prettier/index.js +47 -0
- package/typescript/tsconfig.json +84 -0
- package/typescript/tsconfig.node.json +52 -0
- package/typescript/tsconfig.react.json +59 -0
package/eslint/node.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @qvaroo/configs - Node.js ESLint Configuration
|
|
3
|
+
*
|
|
4
|
+
* Extended configuration for Node.js/Express/NestJS backend projects.
|
|
5
|
+
* Includes all base rules plus Node-specific best practices.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const baseConfig = require('./index');
|
|
9
|
+
|
|
10
|
+
/** @type {import('eslint').Linter.Config} */
|
|
11
|
+
module.exports = {
|
|
12
|
+
...baseConfig,
|
|
13
|
+
|
|
14
|
+
env: {
|
|
15
|
+
node: true,
|
|
16
|
+
es2022: true,
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
plugins: [
|
|
20
|
+
...baseConfig.plugins,
|
|
21
|
+
'n', // eslint-plugin-n (Node.js)
|
|
22
|
+
'security',
|
|
23
|
+
],
|
|
24
|
+
|
|
25
|
+
extends: [
|
|
26
|
+
...baseConfig.extends.filter(ext => ext !== 'prettier'),
|
|
27
|
+
'plugin:n/recommended',
|
|
28
|
+
'plugin:security/recommended',
|
|
29
|
+
'prettier',
|
|
30
|
+
],
|
|
31
|
+
|
|
32
|
+
rules: {
|
|
33
|
+
...baseConfig.rules,
|
|
34
|
+
|
|
35
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
36
|
+
// NODE.JS SPECIFIC RULES
|
|
37
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
38
|
+
|
|
39
|
+
// Allow console in Node.js (for logging)
|
|
40
|
+
'no-console': 'off',
|
|
41
|
+
|
|
42
|
+
// Node.js best practices
|
|
43
|
+
'n/no-missing-import': 'error',
|
|
44
|
+
'n/no-missing-require': 'error',
|
|
45
|
+
'n/no-unpublished-import': 'off', // Dev dependencies in configs
|
|
46
|
+
'n/no-unpublished-require': 'off',
|
|
47
|
+
'n/no-unsupported-features/es-syntax': [
|
|
48
|
+
'error',
|
|
49
|
+
{ ignores: ['modules'] },
|
|
50
|
+
],
|
|
51
|
+
'n/no-extraneous-import': 'error',
|
|
52
|
+
'n/no-deprecated-api': 'error',
|
|
53
|
+
'n/prefer-global/buffer': ['error', 'always'],
|
|
54
|
+
'n/prefer-global/console': ['error', 'always'],
|
|
55
|
+
'n/prefer-global/process': ['error', 'always'],
|
|
56
|
+
'n/prefer-promises/dns': 'error',
|
|
57
|
+
'n/prefer-promises/fs': 'error',
|
|
58
|
+
|
|
59
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
60
|
+
// SECURITY RULES - Prevent common vulnerabilities
|
|
61
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
62
|
+
|
|
63
|
+
'security/detect-buffer-noassert': 'error',
|
|
64
|
+
'security/detect-child-process': 'warn',
|
|
65
|
+
'security/detect-disable-mustache-escape': 'error',
|
|
66
|
+
'security/detect-eval-with-expression': 'error',
|
|
67
|
+
'security/detect-new-buffer': 'error',
|
|
68
|
+
'security/detect-no-csrf-before-method-override': 'error',
|
|
69
|
+
'security/detect-non-literal-fs-filename': 'warn',
|
|
70
|
+
'security/detect-non-literal-regexp': 'warn',
|
|
71
|
+
'security/detect-non-literal-require': 'warn',
|
|
72
|
+
'security/detect-object-injection': 'warn',
|
|
73
|
+
'security/detect-possible-timing-attacks': 'warn',
|
|
74
|
+
'security/detect-pseudoRandomBytes': 'error',
|
|
75
|
+
'security/detect-unsafe-regex': 'error',
|
|
76
|
+
|
|
77
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
78
|
+
// ASYNC/AWAIT - Strict handling for backend
|
|
79
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
80
|
+
|
|
81
|
+
'@typescript-eslint/no-floating-promises': 'error',
|
|
82
|
+
'@typescript-eslint/promise-function-async': 'error',
|
|
83
|
+
'require-atomic-updates': 'error',
|
|
84
|
+
|
|
85
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
86
|
+
// ERROR HANDLING - Backend specific
|
|
87
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
88
|
+
|
|
89
|
+
'no-throw-literal': 'error',
|
|
90
|
+
'prefer-promise-reject-errors': 'error',
|
|
91
|
+
|
|
92
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
93
|
+
// IMPORT/EXPORT - CommonJS & ESM support
|
|
94
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
95
|
+
|
|
96
|
+
'unicorn/prefer-module': 'off', // Allow CommonJS in Node
|
|
97
|
+
'@typescript-eslint/no-require-imports': 'off', // Allow require()
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
overrides: [
|
|
101
|
+
...baseConfig.overrides,
|
|
102
|
+
// Controller files - allow larger methods for route handlers
|
|
103
|
+
{
|
|
104
|
+
files: ['**/*.controller.ts', '**/controllers/**/*.ts'],
|
|
105
|
+
rules: {
|
|
106
|
+
'max-lines-per-function': ['error', { max: 60 }],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
// Migration files
|
|
110
|
+
{
|
|
111
|
+
files: ['**/migrations/**/*.ts'],
|
|
112
|
+
rules: {
|
|
113
|
+
'@typescript-eslint/naming-convention': 'off',
|
|
114
|
+
'max-lines-per-function': 'off',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
// Seed files
|
|
118
|
+
{
|
|
119
|
+
files: ['**/seeds/**/*.ts', '**/seeders/**/*.ts'],
|
|
120
|
+
rules: {
|
|
121
|
+
'sonarjs/no-duplicate-string': 'off',
|
|
122
|
+
'no-magic-numbers': 'off',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
package/eslint/react.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @qvaroo/configs - React ESLint Configuration
|
|
3
|
+
*
|
|
4
|
+
* Extended configuration for React/Next.js projects.
|
|
5
|
+
* Includes all base rules plus React-specific best practices.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const baseConfig = require('./index');
|
|
9
|
+
|
|
10
|
+
/** @type {import('eslint').Linter.Config} */
|
|
11
|
+
module.exports = {
|
|
12
|
+
...baseConfig,
|
|
13
|
+
|
|
14
|
+
parserOptions: {
|
|
15
|
+
...baseConfig.parserOptions,
|
|
16
|
+
ecmaFeatures: {
|
|
17
|
+
jsx: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
plugins: [
|
|
22
|
+
...baseConfig.plugins,
|
|
23
|
+
'react',
|
|
24
|
+
'react-hooks',
|
|
25
|
+
'jsx-a11y',
|
|
26
|
+
],
|
|
27
|
+
|
|
28
|
+
extends: [
|
|
29
|
+
...baseConfig.extends.filter(ext => ext !== 'prettier'),
|
|
30
|
+
'plugin:react/recommended',
|
|
31
|
+
'plugin:react/jsx-runtime',
|
|
32
|
+
'plugin:react-hooks/recommended',
|
|
33
|
+
'plugin:jsx-a11y/strict',
|
|
34
|
+
'prettier',
|
|
35
|
+
],
|
|
36
|
+
|
|
37
|
+
settings: {
|
|
38
|
+
...baseConfig.settings,
|
|
39
|
+
react: {
|
|
40
|
+
version: 'detect',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
rules: {
|
|
45
|
+
...baseConfig.rules,
|
|
46
|
+
|
|
47
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
48
|
+
// REACT SPECIFIC RULES
|
|
49
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
50
|
+
|
|
51
|
+
// Component naming - PascalCase enforced
|
|
52
|
+
'react/jsx-pascal-case': ['error', { allowAllCaps: false }],
|
|
53
|
+
|
|
54
|
+
// Hooks rules - strict enforcement
|
|
55
|
+
'react-hooks/rules-of-hooks': 'error',
|
|
56
|
+
'react-hooks/exhaustive-deps': 'error',
|
|
57
|
+
|
|
58
|
+
// JSX best practices
|
|
59
|
+
'react/jsx-no-useless-fragment': 'error',
|
|
60
|
+
'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],
|
|
61
|
+
'react/jsx-boolean-value': ['error', 'never'],
|
|
62
|
+
'react/self-closing-comp': 'error',
|
|
63
|
+
'react/no-array-index-key': 'error',
|
|
64
|
+
'react/no-danger': 'error',
|
|
65
|
+
'react/no-deprecated': 'error',
|
|
66
|
+
'react/no-direct-mutation-state': 'error',
|
|
67
|
+
'react/no-find-dom-node': 'error',
|
|
68
|
+
'react/no-is-mounted': 'error',
|
|
69
|
+
'react/no-string-refs': 'error',
|
|
70
|
+
'react/no-unescaped-entities': 'error',
|
|
71
|
+
'react/no-unknown-property': 'error',
|
|
72
|
+
|
|
73
|
+
// Props and state
|
|
74
|
+
'react/prop-types': 'off', // Using TypeScript
|
|
75
|
+
'react/require-default-props': 'off', // Using TypeScript
|
|
76
|
+
'react/jsx-props-no-spreading': [
|
|
77
|
+
'error',
|
|
78
|
+
{
|
|
79
|
+
html: 'enforce',
|
|
80
|
+
custom: 'ignore',
|
|
81
|
+
explicitSpread: 'ignore',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
|
|
85
|
+
// Performance
|
|
86
|
+
'react/jsx-no-bind': [
|
|
87
|
+
'error',
|
|
88
|
+
{
|
|
89
|
+
ignoreDOMComponents: false,
|
|
90
|
+
ignoreRefs: true,
|
|
91
|
+
allowArrowFunctions: true,
|
|
92
|
+
allowFunctions: false,
|
|
93
|
+
allowBind: false,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
|
|
97
|
+
// Accessibility
|
|
98
|
+
'jsx-a11y/alt-text': 'error',
|
|
99
|
+
'jsx-a11y/anchor-has-content': 'error',
|
|
100
|
+
'jsx-a11y/anchor-is-valid': 'error',
|
|
101
|
+
'jsx-a11y/aria-props': 'error',
|
|
102
|
+
'jsx-a11y/aria-role': 'error',
|
|
103
|
+
'jsx-a11y/aria-unsupported-elements': 'error',
|
|
104
|
+
'jsx-a11y/click-events-have-key-events': 'error',
|
|
105
|
+
'jsx-a11y/heading-has-content': 'error',
|
|
106
|
+
'jsx-a11y/html-has-lang': 'error',
|
|
107
|
+
'jsx-a11y/iframe-has-title': 'error',
|
|
108
|
+
'jsx-a11y/img-redundant-alt': 'error',
|
|
109
|
+
'jsx-a11y/interactive-supports-focus': 'error',
|
|
110
|
+
'jsx-a11y/label-has-associated-control': 'error',
|
|
111
|
+
'jsx-a11y/lang': 'error',
|
|
112
|
+
'jsx-a11y/media-has-caption': 'error',
|
|
113
|
+
'jsx-a11y/mouse-events-have-key-events': 'error',
|
|
114
|
+
'jsx-a11y/no-access-key': 'error',
|
|
115
|
+
'jsx-a11y/no-autofocus': 'error',
|
|
116
|
+
'jsx-a11y/no-distracting-elements': 'error',
|
|
117
|
+
'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error',
|
|
118
|
+
'jsx-a11y/no-noninteractive-element-interactions': 'error',
|
|
119
|
+
'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error',
|
|
120
|
+
'jsx-a11y/no-redundant-roles': 'error',
|
|
121
|
+
'jsx-a11y/no-static-element-interactions': 'error',
|
|
122
|
+
'jsx-a11y/role-has-required-aria-props': 'error',
|
|
123
|
+
'jsx-a11y/role-supports-aria-props': 'error',
|
|
124
|
+
'jsx-a11y/scope': 'error',
|
|
125
|
+
'jsx-a11y/tabindex-no-positive': 'error',
|
|
126
|
+
|
|
127
|
+
// Allow relaxed naming for React components
|
|
128
|
+
'@typescript-eslint/naming-convention': [
|
|
129
|
+
'error',
|
|
130
|
+
// Keep all base rules
|
|
131
|
+
...require('./rules/naming-conventions')['@typescript-eslint/naming-convention'].slice(1),
|
|
132
|
+
|
|
133
|
+
// Add exception for React functional components
|
|
134
|
+
{
|
|
135
|
+
selector: 'variable',
|
|
136
|
+
modifiers: ['const', 'exported'],
|
|
137
|
+
format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
overrides: [
|
|
143
|
+
...baseConfig.overrides,
|
|
144
|
+
// Next.js pages/app router files
|
|
145
|
+
{
|
|
146
|
+
files: ['**/pages/**/*.tsx', '**/app/**/*.tsx'],
|
|
147
|
+
rules: {
|
|
148
|
+
'import/no-default-export': 'off',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
// Component files
|
|
152
|
+
{
|
|
153
|
+
files: ['**/*.tsx'],
|
|
154
|
+
rules: {
|
|
155
|
+
'max-lines-per-function': ['error', { max: 80 }], // Allow larger components
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Architecture Rules
|
|
3
|
+
*
|
|
4
|
+
* Enforces folder structure and separation of concerns:
|
|
5
|
+
* - /api, /components, /services, /events, /animations, /styles
|
|
6
|
+
* - Presentation-only views: No business logic or API calls
|
|
7
|
+
* - Boundary enforcement between layers
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
12
|
+
// BOUNDARY RULES - Folder separation enforcement
|
|
13
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
14
|
+
|
|
15
|
+
'boundaries/element-types': [
|
|
16
|
+
'error',
|
|
17
|
+
{
|
|
18
|
+
default: 'disallow',
|
|
19
|
+
rules: [
|
|
20
|
+
// Components can import from: styles, types, utils, hooks, components, animations
|
|
21
|
+
{
|
|
22
|
+
from: 'components',
|
|
23
|
+
allow: ['components', 'styles', 'types', 'utils', 'hooks', 'animations', 'constants'],
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
// Views can import from: components, styles, types, hooks (NO services, NO api)
|
|
27
|
+
{
|
|
28
|
+
from: 'views',
|
|
29
|
+
allow: ['components', 'styles', 'types', 'hooks', 'animations', 'constants'],
|
|
30
|
+
disallow: ['api', 'services'], // Presentation-only views
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Services can import from: api, types, utils, constants
|
|
34
|
+
{
|
|
35
|
+
from: 'services',
|
|
36
|
+
allow: ['api', 'types', 'utils', 'constants', 'services'],
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// API layer can only import from: types, utils, constants
|
|
40
|
+
{
|
|
41
|
+
from: 'api',
|
|
42
|
+
allow: ['types', 'utils', 'constants'],
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Hooks can import from: services, api, types, utils
|
|
46
|
+
{
|
|
47
|
+
from: 'hooks',
|
|
48
|
+
allow: ['services', 'api', 'types', 'utils', 'constants', 'hooks'],
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
// Events can import from: types, utils
|
|
52
|
+
{
|
|
53
|
+
from: 'events',
|
|
54
|
+
allow: ['types', 'utils', 'constants'],
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
// Animations can import from: types, utils, styles
|
|
58
|
+
{
|
|
59
|
+
from: 'animations',
|
|
60
|
+
allow: ['types', 'utils', 'styles', 'constants'],
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// Styles: standalone, no business imports
|
|
64
|
+
{
|
|
65
|
+
from: 'styles',
|
|
66
|
+
allow: ['styles', 'constants'],
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// Utils: standalone, no circular dependencies
|
|
70
|
+
{
|
|
71
|
+
from: 'utils',
|
|
72
|
+
allow: ['types', 'constants'],
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// Types: standalone
|
|
76
|
+
{
|
|
77
|
+
from: 'types',
|
|
78
|
+
allow: ['types'],
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// Constants: standalone
|
|
82
|
+
{
|
|
83
|
+
from: 'constants',
|
|
84
|
+
allow: ['types'],
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
|
|
90
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
91
|
+
// IMPORT BOUNDARIES - Prevent circular dependencies
|
|
92
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
93
|
+
|
|
94
|
+
'import/no-cycle': ['error', { maxDepth: 3 }],
|
|
95
|
+
|
|
96
|
+
'import/no-restricted-paths': [
|
|
97
|
+
'error',
|
|
98
|
+
{
|
|
99
|
+
zones: [
|
|
100
|
+
// Views cannot import directly from API
|
|
101
|
+
{
|
|
102
|
+
target: './src/views',
|
|
103
|
+
from: './src/api',
|
|
104
|
+
message: 'Views must not import from API directly. Use hooks or services instead.',
|
|
105
|
+
},
|
|
106
|
+
// Views cannot import directly from Services
|
|
107
|
+
{
|
|
108
|
+
target: './src/views',
|
|
109
|
+
from: './src/services',
|
|
110
|
+
message: 'Views must not import from services directly. Use hooks instead.',
|
|
111
|
+
},
|
|
112
|
+
// Components cannot import from views (prevent circular)
|
|
113
|
+
{
|
|
114
|
+
target: './src/components',
|
|
115
|
+
from: './src/views',
|
|
116
|
+
message: 'Components cannot import from views. This creates circular dependencies.',
|
|
117
|
+
},
|
|
118
|
+
// Styles cannot import business logic
|
|
119
|
+
{
|
|
120
|
+
target: './src/styles',
|
|
121
|
+
from: './src/api',
|
|
122
|
+
message: 'Styles must not contain business logic or API dependencies.',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
target: './src/styles',
|
|
126
|
+
from: './src/services',
|
|
127
|
+
message: 'Styles must not contain business logic or service dependencies.',
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
|
|
133
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
134
|
+
// FILE NAMING PATTERNS
|
|
135
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
136
|
+
|
|
137
|
+
// Enforce consistent file naming
|
|
138
|
+
'unicorn/filename-case': [
|
|
139
|
+
'error',
|
|
140
|
+
{
|
|
141
|
+
cases: {
|
|
142
|
+
kebabCase: true, // Regular files: my-component.ts
|
|
143
|
+
pascalCase: true, // React components: MyComponent.tsx
|
|
144
|
+
},
|
|
145
|
+
ignore: [
|
|
146
|
+
'^README\\.md$',
|
|
147
|
+
'^CHANGELOG\\.md$',
|
|
148
|
+
'^LICENSE$',
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Quality Rules
|
|
3
|
+
*
|
|
4
|
+
* Enforces strict quality limits:
|
|
5
|
+
* - Method length: max 40 lines → error
|
|
6
|
+
* - Class length: max 300 lines → error
|
|
7
|
+
* - Max nested if statements: 2 → error
|
|
8
|
+
* - No magic strings / hard-coded values → error
|
|
9
|
+
* - No empty catch blocks → error
|
|
10
|
+
* - Guard clauses enforcement
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
15
|
+
// METHOD & CLASS LENGTH LIMITS
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
17
|
+
|
|
18
|
+
// Maximum 40 lines per function/method
|
|
19
|
+
'max-lines-per-function': [
|
|
20
|
+
'error',
|
|
21
|
+
{
|
|
22
|
+
max: 40,
|
|
23
|
+
skipBlankLines: true,
|
|
24
|
+
skipComments: true,
|
|
25
|
+
IIFEs: true,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
|
|
29
|
+
// Maximum 300 lines per file (proxy for class length)
|
|
30
|
+
'max-lines': [
|
|
31
|
+
'error',
|
|
32
|
+
{
|
|
33
|
+
max: 300,
|
|
34
|
+
skipBlankLines: true,
|
|
35
|
+
skipComments: true,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
|
|
39
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
40
|
+
// NESTING DEPTH - Maximum 2 nested if statements
|
|
41
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
42
|
+
|
|
43
|
+
'max-depth': ['error', { max: 2 }],
|
|
44
|
+
|
|
45
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
46
|
+
// COMPLEXITY LIMITS - Encourage simpler code
|
|
47
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
48
|
+
|
|
49
|
+
// Cyclomatic complexity limit
|
|
50
|
+
complexity: ['error', { max: 10 }],
|
|
51
|
+
|
|
52
|
+
// Maximum statements per function
|
|
53
|
+
'max-statements': ['error', { max: 20 }],
|
|
54
|
+
|
|
55
|
+
// Maximum parameters
|
|
56
|
+
'max-params': ['error', { max: 4 }],
|
|
57
|
+
|
|
58
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
59
|
+
// MAGIC VALUES - No hard-coded strings/numbers
|
|
60
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
61
|
+
|
|
62
|
+
// No magic numbers (must use named constants)
|
|
63
|
+
'no-magic-numbers': [
|
|
64
|
+
'error',
|
|
65
|
+
{
|
|
66
|
+
ignore: [-1, 0, 1, 2, 100], // Common acceptable values
|
|
67
|
+
ignoreArrayIndexes: true,
|
|
68
|
+
ignoreDefaultValues: true,
|
|
69
|
+
enforceConst: true,
|
|
70
|
+
detectObjects: false,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
|
|
74
|
+
// SonarJS: Enforce no duplicate strings (magic strings)
|
|
75
|
+
'sonarjs/no-duplicate-string': [
|
|
76
|
+
'error',
|
|
77
|
+
{
|
|
78
|
+
threshold: 3,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
|
|
82
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
83
|
+
// GUARD CLAUSES - Early returns encouraged
|
|
84
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
85
|
+
|
|
86
|
+
// Prefer early returns (guard clauses)
|
|
87
|
+
'no-else-return': [
|
|
88
|
+
'error',
|
|
89
|
+
{
|
|
90
|
+
allowElseIf: false,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
|
|
94
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
95
|
+
// COGNITIVE COMPLEXITY - Readability enforcement
|
|
96
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
97
|
+
|
|
98
|
+
'sonarjs/cognitive-complexity': ['error', 15],
|
|
99
|
+
|
|
100
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
101
|
+
// CODE DUPLICATION - DRY principle
|
|
102
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
103
|
+
|
|
104
|
+
'sonarjs/no-identical-functions': 'error',
|
|
105
|
+
'sonarjs/no-all-duplicated-branches': 'error',
|
|
106
|
+
'sonarjs/no-duplicated-branches': 'error',
|
|
107
|
+
|
|
108
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
109
|
+
// ADDITIONAL QUALITY RULES
|
|
110
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
111
|
+
|
|
112
|
+
// No nested ternaries
|
|
113
|
+
'no-nested-ternary': 'error',
|
|
114
|
+
|
|
115
|
+
// Require default case in switch
|
|
116
|
+
'default-case': 'error',
|
|
117
|
+
'default-case-last': 'error',
|
|
118
|
+
|
|
119
|
+
// No fallthrough in switch (must be explicit)
|
|
120
|
+
'no-fallthrough': ['error', { commentPattern: 'falls?\\s?through' }],
|
|
121
|
+
|
|
122
|
+
// Enforce consistent return
|
|
123
|
+
'consistent-return': 'error',
|
|
124
|
+
|
|
125
|
+
// No unreachable code
|
|
126
|
+
'no-unreachable': 'error',
|
|
127
|
+
'no-unreachable-loop': 'error',
|
|
128
|
+
|
|
129
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
130
|
+
// ERROR HANDLING - Strict enforcement
|
|
131
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
132
|
+
|
|
133
|
+
// No empty blocks (including catch)
|
|
134
|
+
'no-empty': ['error', { allowEmptyCatch: false }],
|
|
135
|
+
|
|
136
|
+
// Prefer throwing Error objects
|
|
137
|
+
'@typescript-eslint/only-throw-error': 'error',
|
|
138
|
+
|
|
139
|
+
// Require await in async functions
|
|
140
|
+
'require-await': 'off', // Handled by TypeScript
|
|
141
|
+
'@typescript-eslint/require-await': 'error',
|
|
142
|
+
};
|