eslint-config-dzrv 2.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/LICENSE +22 -0
- package/README.md +262 -0
- package/package.json +69 -0
- package/src/index.js +446 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dzrv Services LLP
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# eslint-config-dzrv
|
|
2
|
+
|
|
3
|
+
A comprehensive, opinionated ESLint configuration for modern full-stack TypeScript projects. Supports React frontends, Node.js backends, and everything in between.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
✨ **ESLint v10** compatible (flat config format)
|
|
8
|
+
🎯 **Full-stack support** - Frontend (React) + Backend (Node.js)
|
|
9
|
+
📘 **TypeScript first** - Full TypeScript support with type-aware linting
|
|
10
|
+
🔒 **Security focused** - Built-in security rules
|
|
11
|
+
♿ **Accessibility** - JSX-A11Y rules for React components
|
|
12
|
+
🎨 **Prettier compatible** - No conflicts with Prettier formatting
|
|
13
|
+
📦 **Zero config** - Sensible defaults, customize as needed
|
|
14
|
+
🔧 **Modular** - Use only what you need
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install --save-dev eslint-config-dzrv eslint@^10 typescript
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Quick Start
|
|
25
|
+
|
|
26
|
+
Create an `eslint.config.js` file in your project root:
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
import fullstack from 'eslint-config-dzrv';
|
|
30
|
+
|
|
31
|
+
// Use the recommended fullstack preset
|
|
32
|
+
export default fullstack.configs['recommended-fullstack'];
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Available Presets
|
|
36
|
+
|
|
37
|
+
#### Recommended Presets (Most Common)
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
import fullstack from 'eslint-config-dzrv';
|
|
41
|
+
|
|
42
|
+
// Full-stack TypeScript + React + Node.js
|
|
43
|
+
export default fullstack.configs['recommended-fullstack'];
|
|
44
|
+
|
|
45
|
+
// Frontend only (React + TypeScript)
|
|
46
|
+
export default fullstack.configs['recommended-react-typescript'];
|
|
47
|
+
|
|
48
|
+
// Backend only (Node.js + TypeScript)
|
|
49
|
+
export default fullstack.configs['recommended-node-typescript'];
|
|
50
|
+
|
|
51
|
+
// Basic JavaScript (no frameworks)
|
|
52
|
+
export default fullstack.configs['recommended'];
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### Individual Configurations
|
|
56
|
+
|
|
57
|
+
For more control, compose your own configuration:
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
import fullstack from 'eslint-config-dzrv';
|
|
61
|
+
|
|
62
|
+
export default [
|
|
63
|
+
fullstack.configs.ignores,
|
|
64
|
+
fullstack.configs.base,
|
|
65
|
+
fullstack.configs.typescript,
|
|
66
|
+
fullstack.configs.react,
|
|
67
|
+
fullstack.configs.node,
|
|
68
|
+
fullstack.configs.prettier,
|
|
69
|
+
|
|
70
|
+
// Your custom overrides
|
|
71
|
+
{
|
|
72
|
+
rules: {
|
|
73
|
+
'no-console': 'off',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Configuration Examples
|
|
80
|
+
|
|
81
|
+
#### Monorepo with Frontend and Backend
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
import fullstack from 'eslint-config-dzrv';
|
|
85
|
+
|
|
86
|
+
export default [
|
|
87
|
+
...fullstack.configs['recommended-fullstack'],
|
|
88
|
+
|
|
89
|
+
// Backend-specific overrides
|
|
90
|
+
{
|
|
91
|
+
files: ['backend/**/*.ts', 'server/**/*.ts', 'api/**/*.ts'],
|
|
92
|
+
rules: {
|
|
93
|
+
'no-console': 'off',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
// Frontend-specific overrides
|
|
98
|
+
{
|
|
99
|
+
files: ['frontend/**/*.tsx', 'src/**/*.tsx', 'components/**/*.tsx'],
|
|
100
|
+
rules: {
|
|
101
|
+
'react/prop-types': 'off',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### React-only Project
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
import fullstack from 'eslint-config-dzrv';
|
|
111
|
+
|
|
112
|
+
export default [
|
|
113
|
+
...fullstack.configs['recommended-react-typescript'],
|
|
114
|
+
|
|
115
|
+
{
|
|
116
|
+
rules: {
|
|
117
|
+
'react-hooks/exhaustive-deps': 'error', // Stricter hook deps
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### Node.js API Server
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
import fullstack from 'eslint-config-dzrv';
|
|
127
|
+
|
|
128
|
+
export default [
|
|
129
|
+
...fullstack.configs['recommended-node-typescript'],
|
|
130
|
+
|
|
131
|
+
{
|
|
132
|
+
rules: {
|
|
133
|
+
'complexity': ['warn', 25], // Allow more complexity in API routes
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Configuration Modules
|
|
140
|
+
|
|
141
|
+
### Base Config
|
|
142
|
+
Core JavaScript/ES2021+ rules including:
|
|
143
|
+
- Import ordering and organization
|
|
144
|
+
- Unused import detection
|
|
145
|
+
- Security checks
|
|
146
|
+
- Best practices enforcement
|
|
147
|
+
- Code quality rules
|
|
148
|
+
|
|
149
|
+
### TypeScript Config
|
|
150
|
+
TypeScript-specific rules:
|
|
151
|
+
- Type-safe linting
|
|
152
|
+
- Consistent type imports
|
|
153
|
+
- Promise handling
|
|
154
|
+
- Type inference optimization
|
|
155
|
+
|
|
156
|
+
### Node.js Config
|
|
157
|
+
Backend-specific rules:
|
|
158
|
+
- Node.js API best practices
|
|
159
|
+
- Promise handling
|
|
160
|
+
- File system operations
|
|
161
|
+
- Process and buffer globals
|
|
162
|
+
|
|
163
|
+
### React Config
|
|
164
|
+
Frontend React rules:
|
|
165
|
+
- React 17+ (no React import needed)
|
|
166
|
+
- Hooks linting
|
|
167
|
+
- JSX best practices
|
|
168
|
+
- Accessibility (a11y) checks
|
|
169
|
+
|
|
170
|
+
### Prettier Config
|
|
171
|
+
Disables all rules that conflict with Prettier formatting.
|
|
172
|
+
|
|
173
|
+
## Rules Philosophy
|
|
174
|
+
|
|
175
|
+
### Error vs Warning
|
|
176
|
+
- **Errors**: Security issues, bugs, breaking code
|
|
177
|
+
- **Warnings**: Code quality, maintainability, conventions
|
|
178
|
+
|
|
179
|
+
### Complexity Rules
|
|
180
|
+
- Base: `complexity: 15` (general code)
|
|
181
|
+
- Node: `complexity: 20` (backend routes can be complex)
|
|
182
|
+
- React: Uses base settings
|
|
183
|
+
|
|
184
|
+
### Console Usage
|
|
185
|
+
- **Frontend**: `warn` (should use proper logging)
|
|
186
|
+
- **Backend**: `off` (console is acceptable)
|
|
187
|
+
|
|
188
|
+
## TypeScript Support
|
|
189
|
+
|
|
190
|
+
This config requires TypeScript projects to have a `tsconfig.json`. The config uses `projectService: true` for type-aware linting.
|
|
191
|
+
|
|
192
|
+
### Recommended tsconfig.json
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"compilerOptions": {
|
|
197
|
+
"target": "ES2022",
|
|
198
|
+
"module": "ESNext",
|
|
199
|
+
"moduleResolution": "bundler",
|
|
200
|
+
"strict": true,
|
|
201
|
+
"esModuleInterop": true,
|
|
202
|
+
"skipLibCheck": true,
|
|
203
|
+
"forceConsistentCasingInFileNames": true
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Peer Dependencies
|
|
209
|
+
|
|
210
|
+
- `eslint` ^10.0.0
|
|
211
|
+
- `typescript` >=5.0.0 <6.1.0 (optional; required only for TypeScript projects)
|
|
212
|
+
|
|
213
|
+
## Migration from ESLint v8
|
|
214
|
+
|
|
215
|
+
ESLint v9 uses the new flat config format. Key changes:
|
|
216
|
+
|
|
217
|
+
1. **Config file**: `.eslintrc.*` → `eslint.config.js`
|
|
218
|
+
2. **Format**: JSON/YAML → JavaScript (ES modules)
|
|
219
|
+
3. **Extends**: String references → Direct imports
|
|
220
|
+
|
|
221
|
+
### Before (v8)
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"extends": ["some-config"],
|
|
225
|
+
"rules": {}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### After (v9)
|
|
230
|
+
```javascript
|
|
231
|
+
import someConfig from 'some-config';
|
|
232
|
+
|
|
233
|
+
export default [
|
|
234
|
+
someConfig,
|
|
235
|
+
{
|
|
236
|
+
rules: {},
|
|
237
|
+
},
|
|
238
|
+
];
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Contributing
|
|
242
|
+
|
|
243
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
MIT © [Your Name]
|
|
248
|
+
|
|
249
|
+
## Acknowledgments
|
|
250
|
+
|
|
251
|
+
Inspired by:
|
|
252
|
+
- [@eslint/js](https://www.npmjs.com/package/@eslint/js)
|
|
253
|
+
- [eslint-config-airbnb](https://www.npmjs.com/package/eslint-config-airbnb)
|
|
254
|
+
- [eslint-config-prettier](https://www.npmjs.com/package/eslint-config-prettier)
|
|
255
|
+
- [eslint-config-dzrv](https://github.com/Dzrv-Digital/eslint-config-dzrv)
|
|
256
|
+
|
|
257
|
+
## Related Packages
|
|
258
|
+
|
|
259
|
+
- [Prettier](https://prettier.io/) - Code formatter
|
|
260
|
+
- [TypeScript ESLint](https://typescript-eslint.io/) - TypeScript support
|
|
261
|
+
- [ESLint](https://eslint.org/) - Pluggable linting utility
|
|
262
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eslint-config-dzrv",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "A comprehensive ESLint configuration for full-stack TypeScript projects (React + Node.js)",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"eslint",
|
|
12
|
+
"eslintconfig",
|
|
13
|
+
"config",
|
|
14
|
+
"typescript",
|
|
15
|
+
"react",
|
|
16
|
+
"node",
|
|
17
|
+
"fullstack",
|
|
18
|
+
"javascript",
|
|
19
|
+
"code-quality"
|
|
20
|
+
],
|
|
21
|
+
"author": "Dzrv Services LLP",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/arcaauth/eslint-config-dzrv.git"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"src",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"eslint": "^10.0.0",
|
|
34
|
+
"typescript": ">=5.0.0 <6.1.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependenciesMeta": {
|
|
37
|
+
"typescript": {
|
|
38
|
+
"optional": true
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@arcaauth/eslint-plugin-jsx-a11y": "^6.10.2",
|
|
43
|
+
"@arcaauth/eslint-plugin-react": "^7.37.5",
|
|
44
|
+
"@eslint/js": "^10.0.0",
|
|
45
|
+
"@typescript-eslint/eslint-plugin": "^8.61.0",
|
|
46
|
+
"@typescript-eslint/parser": "^8.61.0",
|
|
47
|
+
"eslint-config-prettier": "^10.1.8",
|
|
48
|
+
"eslint-import-resolver-typescript": "^4.4.5",
|
|
49
|
+
"eslint-plugin-import-x": "^4.16.2",
|
|
50
|
+
"eslint-plugin-n": "^18.1.0",
|
|
51
|
+
"eslint-plugin-promise": "^7.3.0",
|
|
52
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
53
|
+
"eslint-plugin-security": "^4.0.1",
|
|
54
|
+
"eslint-plugin-unused-imports": "^4.4.1",
|
|
55
|
+
"globals": "^16.0.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"eslint": "^10.5.0",
|
|
59
|
+
"typescript": "^6.0.3"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"test": "cd test && npx eslint .",
|
|
63
|
+
"test:validate": "node test/test-config.test.js",
|
|
64
|
+
"validate": "node -e \"import('./src/index.js').then(m => console.log('✅ Config loaded successfully!', Object.keys(m.configs).join(', '))).catch(e => { console.error('❌ Error:', e.message); process.exit(1); })\""
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": "^20.19.0 || ^22.13.0 || >=24"
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
// src/index.js
|
|
2
|
+
import js from '@eslint/js';
|
|
3
|
+
import tseslint from '@typescript-eslint/eslint-plugin';
|
|
4
|
+
import tsparser from '@typescript-eslint/parser';
|
|
5
|
+
import reactPlugin from '@arcaauth/eslint-plugin-react';
|
|
6
|
+
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
|
7
|
+
import importPlugin from 'eslint-plugin-import-x';
|
|
8
|
+
import jsxA11yPlugin from '@arcaauth/eslint-plugin-jsx-a11y';
|
|
9
|
+
import nodePlugin from 'eslint-plugin-n';
|
|
10
|
+
import promisePlugin from 'eslint-plugin-promise';
|
|
11
|
+
import prettierConfig from 'eslint-config-prettier';
|
|
12
|
+
import unusedImportsPlugin from 'eslint-plugin-unused-imports';
|
|
13
|
+
import securityPlugin from 'eslint-plugin-security';
|
|
14
|
+
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript';
|
|
15
|
+
import globals from 'globals';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Base configuration for all JavaScript/TypeScript projects
|
|
19
|
+
*/
|
|
20
|
+
const baseConfig = {
|
|
21
|
+
name: 'fullstack/base',
|
|
22
|
+
languageOptions: {
|
|
23
|
+
ecmaVersion: 'latest',
|
|
24
|
+
sourceType: 'module',
|
|
25
|
+
globals: {
|
|
26
|
+
...globals.es2021,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
plugins: {
|
|
30
|
+
import: importPlugin,
|
|
31
|
+
'unused-imports': unusedImportsPlugin,
|
|
32
|
+
security: securityPlugin,
|
|
33
|
+
},
|
|
34
|
+
rules: {
|
|
35
|
+
...js.configs.recommended.rules,
|
|
36
|
+
|
|
37
|
+
// Import/Export rules
|
|
38
|
+
'import/order': [
|
|
39
|
+
'error',
|
|
40
|
+
{
|
|
41
|
+
groups: [
|
|
42
|
+
'builtin',
|
|
43
|
+
'external',
|
|
44
|
+
'internal',
|
|
45
|
+
'parent',
|
|
46
|
+
'sibling',
|
|
47
|
+
'index',
|
|
48
|
+
],
|
|
49
|
+
'newlines-between': 'always',
|
|
50
|
+
alphabetize: {
|
|
51
|
+
order: 'asc',
|
|
52
|
+
caseInsensitive: true,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
'import/no-unresolved': 'off', // TypeScript/Node resolution handles this
|
|
57
|
+
'import/no-cycle': 'error',
|
|
58
|
+
'import/no-self-import': 'error',
|
|
59
|
+
'import/no-useless-path-segments': 'error',
|
|
60
|
+
'import/newline-after-import': 'error',
|
|
61
|
+
'import/no-duplicates': 'error',
|
|
62
|
+
|
|
63
|
+
// Unused imports
|
|
64
|
+
'unused-imports/no-unused-imports': 'error',
|
|
65
|
+
'unused-imports/no-unused-vars': [
|
|
66
|
+
'warn',
|
|
67
|
+
{
|
|
68
|
+
vars: 'all',
|
|
69
|
+
varsIgnorePattern: '^_',
|
|
70
|
+
args: 'after-used',
|
|
71
|
+
argsIgnorePattern: '^_',
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
|
|
75
|
+
// Security rules
|
|
76
|
+
'security/detect-object-injection': 'warn',
|
|
77
|
+
'security/detect-non-literal-regexp': 'warn',
|
|
78
|
+
'security/detect-unsafe-regex': 'error',
|
|
79
|
+
'security/detect-buffer-noassert': 'error',
|
|
80
|
+
'security/detect-child-process': 'warn',
|
|
81
|
+
'security/detect-disable-mustache-escape': 'error',
|
|
82
|
+
'security/detect-eval-with-expression': 'error',
|
|
83
|
+
'security/detect-no-csrf-before-method-override': 'error',
|
|
84
|
+
'security/detect-non-literal-fs-filename': 'warn',
|
|
85
|
+
'security/detect-non-literal-require': 'warn',
|
|
86
|
+
'security/detect-possible-timing-attacks': 'warn',
|
|
87
|
+
'security/detect-pseudoRandomBytes': 'error',
|
|
88
|
+
|
|
89
|
+
// Best practices
|
|
90
|
+
'no-console': ['warn', { allow: ['warn', 'error', 'info'] }],
|
|
91
|
+
'no-debugger': 'error',
|
|
92
|
+
'no-alert': 'warn',
|
|
93
|
+
'no-var': 'error',
|
|
94
|
+
'prefer-const': 'error',
|
|
95
|
+
'prefer-arrow-callback': 'error',
|
|
96
|
+
'arrow-body-style': ['error', 'as-needed'],
|
|
97
|
+
'object-shorthand': 'error',
|
|
98
|
+
'prefer-template': 'error',
|
|
99
|
+
'template-curly-spacing': ['error', 'never'],
|
|
100
|
+
'prefer-destructuring': [
|
|
101
|
+
'error',
|
|
102
|
+
{
|
|
103
|
+
array: false,
|
|
104
|
+
object: true,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
enforceForRenamedProperties: false,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
|
|
111
|
+
// Code quality
|
|
112
|
+
'complexity': ['warn', 15],
|
|
113
|
+
'max-depth': ['warn', 4],
|
|
114
|
+
'max-lines': ['warn', 500],
|
|
115
|
+
'max-lines-per-function': 'off', // Too restrictive for full-stack
|
|
116
|
+
'max-params': ['warn', 5],
|
|
117
|
+
'no-duplicate-imports': 'error',
|
|
118
|
+
'no-unreachable': 'error',
|
|
119
|
+
'no-unused-expressions': 'error',
|
|
120
|
+
'consistent-return': 'error',
|
|
121
|
+
'default-case': 'warn',
|
|
122
|
+
'eqeqeq': ['error', 'always', { null: 'ignore' }],
|
|
123
|
+
'guard-for-in': 'error',
|
|
124
|
+
'no-else-return': ['error', { allowElseIf: false }],
|
|
125
|
+
'no-empty-function': ['error', { allow: ['arrowFunctions'] }],
|
|
126
|
+
'no-magic-numbers': 'off', // Too restrictive for real-world code
|
|
127
|
+
'no-return-assign': 'error',
|
|
128
|
+
'no-throw-literal': 'error',
|
|
129
|
+
'prefer-promise-reject-errors': 'error',
|
|
130
|
+
'require-await': 'error',
|
|
131
|
+
'no-nested-ternary': 'warn',
|
|
132
|
+
'no-unneeded-ternary': 'error',
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* TypeScript configuration for both frontend and backend
|
|
138
|
+
*/
|
|
139
|
+
const typescriptConfig = {
|
|
140
|
+
name: 'fullstack/typescript',
|
|
141
|
+
files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'],
|
|
142
|
+
languageOptions: {
|
|
143
|
+
parser: tsparser,
|
|
144
|
+
parserOptions: {
|
|
145
|
+
ecmaVersion: 'latest',
|
|
146
|
+
sourceType: 'module',
|
|
147
|
+
ecmaFeatures: {
|
|
148
|
+
jsx: true,
|
|
149
|
+
},
|
|
150
|
+
projectService: true,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
plugins: {
|
|
154
|
+
'@typescript-eslint': tseslint,
|
|
155
|
+
import: importPlugin,
|
|
156
|
+
'unused-imports': unusedImportsPlugin,
|
|
157
|
+
},
|
|
158
|
+
rules: {
|
|
159
|
+
// Disable base rules that are covered by TypeScript
|
|
160
|
+
'no-unused-vars': 'off',
|
|
161
|
+
'no-undef': 'off',
|
|
162
|
+
'no-redeclare': 'off',
|
|
163
|
+
'no-use-before-define': 'off',
|
|
164
|
+
'no-shadow': 'off',
|
|
165
|
+
'no-duplicate-imports': 'off', // TypeScript handles this better
|
|
166
|
+
|
|
167
|
+
// TypeScript-specific rules
|
|
168
|
+
'@typescript-eslint/no-unused-vars': 'off', // handled by unused-imports
|
|
169
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
170
|
+
'@typescript-eslint/no-non-null-assertion': 'warn',
|
|
171
|
+
'@typescript-eslint/prefer-optional-chain': 'error',
|
|
172
|
+
'@typescript-eslint/prefer-nullish-coalescing': 'error',
|
|
173
|
+
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
|
|
174
|
+
'@typescript-eslint/no-inferrable-types': 'error',
|
|
175
|
+
'@typescript-eslint/consistent-type-imports': [
|
|
176
|
+
'error',
|
|
177
|
+
{
|
|
178
|
+
prefer: 'type-imports',
|
|
179
|
+
fixStyle: 'inline-type-imports'
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
'@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
|
|
183
|
+
'@typescript-eslint/array-type': ['error', { default: 'array' }],
|
|
184
|
+
'@typescript-eslint/prefer-for-of': 'error',
|
|
185
|
+
'@typescript-eslint/prefer-includes': 'error',
|
|
186
|
+
'@typescript-eslint/prefer-string-starts-ends-with': 'error',
|
|
187
|
+
'@typescript-eslint/no-confusing-void-expression': 'error',
|
|
188
|
+
'@typescript-eslint/no-duplicate-enum-values': 'error',
|
|
189
|
+
'@typescript-eslint/no-meaningless-void-operator': 'error',
|
|
190
|
+
'@typescript-eslint/no-mixed-enums': 'error',
|
|
191
|
+
'@typescript-eslint/no-redundant-type-constituents': 'error',
|
|
192
|
+
'@typescript-eslint/no-useless-empty-export': 'error',
|
|
193
|
+
'@typescript-eslint/prefer-reduce-type-parameter': 'error',
|
|
194
|
+
'@typescript-eslint/no-floating-promises': 'error',
|
|
195
|
+
'@typescript-eslint/await-thenable': 'error',
|
|
196
|
+
'@typescript-eslint/no-misused-promises': 'error',
|
|
197
|
+
'@typescript-eslint/no-shadow': 'error',
|
|
198
|
+
|
|
199
|
+
// Import rules for TypeScript - Relaxed due to TS handling
|
|
200
|
+
'import/no-unresolved': 'off', // TypeScript handles this
|
|
201
|
+
'import/extensions': 'off', // TypeScript handles this
|
|
202
|
+
},
|
|
203
|
+
settings: {
|
|
204
|
+
// eslint-plugin-import-x reads resolver/parser settings under the
|
|
205
|
+
// `import-x/*` namespace regardless of the alias plugin key. The legacy
|
|
206
|
+
// `import/resolver` key is silently ignored, which disables no-cycle.
|
|
207
|
+
'import-x/resolver-next': [
|
|
208
|
+
createTypeScriptImportResolver({
|
|
209
|
+
alwaysTryTypes: true,
|
|
210
|
+
project: './tsconfig.json',
|
|
211
|
+
}),
|
|
212
|
+
],
|
|
213
|
+
// no-cycle needs a parser for the target .ts/.tsx files, otherwise the
|
|
214
|
+
// dependency crawler never traverses them and the rule silently never fires.
|
|
215
|
+
'import-x/parsers': {
|
|
216
|
+
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Node.js backend configuration
|
|
223
|
+
*/
|
|
224
|
+
const nodeConfig = {
|
|
225
|
+
name: 'fullstack/node',
|
|
226
|
+
files: ['**/*.js', '**/*.ts', '**/*.mjs', '**/*.cjs'],
|
|
227
|
+
languageOptions: {
|
|
228
|
+
globals: {
|
|
229
|
+
...globals.node,
|
|
230
|
+
...globals.es2021,
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
plugins: {
|
|
234
|
+
n: nodePlugin,
|
|
235
|
+
promise: promisePlugin,
|
|
236
|
+
},
|
|
237
|
+
rules: {
|
|
238
|
+
// Node.js specific rules
|
|
239
|
+
'n/no-deprecated-api': 'error',
|
|
240
|
+
'n/no-exports-assign': 'error',
|
|
241
|
+
'n/no-missing-import': 'off', // TypeScript handles this
|
|
242
|
+
'n/no-missing-require': 'off', // TypeScript handles this
|
|
243
|
+
'n/no-unpublished-import': 'off', // Too restrictive
|
|
244
|
+
'n/no-unpublished-require': 'off', // Too restrictive
|
|
245
|
+
'n/no-unsupported-features/es-syntax': 'off', // We use modern syntax
|
|
246
|
+
'n/no-process-exit': 'warn',
|
|
247
|
+
'n/prefer-global/buffer': ['error', 'always'],
|
|
248
|
+
'n/prefer-global/console': ['error', 'always'],
|
|
249
|
+
'n/prefer-global/process': ['error', 'always'],
|
|
250
|
+
'n/prefer-promises/fs': 'error',
|
|
251
|
+
'n/prefer-promises/dns': 'error',
|
|
252
|
+
|
|
253
|
+
// Promise rules
|
|
254
|
+
'promise/always-return': 'error',
|
|
255
|
+
'promise/no-return-wrap': 'error',
|
|
256
|
+
'promise/param-names': 'error',
|
|
257
|
+
'promise/catch-or-return': 'error',
|
|
258
|
+
'promise/no-nesting': 'warn',
|
|
259
|
+
'promise/no-promise-in-callback': 'warn',
|
|
260
|
+
'promise/no-callback-in-promise': 'warn',
|
|
261
|
+
'promise/avoid-new': 'off',
|
|
262
|
+
'promise/no-return-in-finally': 'error',
|
|
263
|
+
|
|
264
|
+
// Relax some rules for backend
|
|
265
|
+
'no-console': 'off', // Console is acceptable in Node.js
|
|
266
|
+
'complexity': ['warn', 20], // Backend routes can be more complex
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* React frontend configuration
|
|
272
|
+
*/
|
|
273
|
+
const reactConfig = {
|
|
274
|
+
name: 'fullstack/react',
|
|
275
|
+
files: ['**/*.jsx', '**/*.tsx'],
|
|
276
|
+
languageOptions: {
|
|
277
|
+
globals: {
|
|
278
|
+
...globals.browser,
|
|
279
|
+
},
|
|
280
|
+
parserOptions: {
|
|
281
|
+
ecmaFeatures: {
|
|
282
|
+
jsx: true,
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
plugins: {
|
|
287
|
+
react: reactPlugin,
|
|
288
|
+
'react-hooks': reactHooksPlugin,
|
|
289
|
+
'jsx-a11y': jsxA11yPlugin,
|
|
290
|
+
},
|
|
291
|
+
rules: {
|
|
292
|
+
// React rules
|
|
293
|
+
'react/jsx-uses-react': 'off', // Not needed in React 17+
|
|
294
|
+
'react/react-in-jsx-scope': 'off', // Not needed in React 17+
|
|
295
|
+
'react/jsx-uses-vars': 'error',
|
|
296
|
+
'react/jsx-key': ['error', { checkFragmentShorthand: true }],
|
|
297
|
+
'react/jsx-no-duplicate-props': 'error',
|
|
298
|
+
'react/jsx-no-target-blank': 'error',
|
|
299
|
+
'react/jsx-no-undef': 'error',
|
|
300
|
+
'react/jsx-pascal-case': 'error',
|
|
301
|
+
'react/jsx-curly-brace-presence': [
|
|
302
|
+
'error',
|
|
303
|
+
{ props: 'never', children: 'never' },
|
|
304
|
+
],
|
|
305
|
+
'react/jsx-boolean-value': ['error', 'never'],
|
|
306
|
+
'react/jsx-fragments': ['error', 'syntax'],
|
|
307
|
+
'react/no-array-index-key': 'warn',
|
|
308
|
+
'react/no-children-prop': 'error',
|
|
309
|
+
'react/no-danger-with-children': 'error',
|
|
310
|
+
'react/no-deprecated': 'error',
|
|
311
|
+
'react/no-direct-mutation-state': 'error',
|
|
312
|
+
'react/no-find-dom-node': 'error',
|
|
313
|
+
'react/no-is-mounted': 'error',
|
|
314
|
+
'react/no-render-return-value': 'error',
|
|
315
|
+
'react/no-string-refs': 'error',
|
|
316
|
+
'react/no-unescaped-entities': 'error',
|
|
317
|
+
'react/no-unknown-property': 'error',
|
|
318
|
+
'react/no-unused-prop-types': 'warn',
|
|
319
|
+
'react/no-unused-state': 'warn',
|
|
320
|
+
'react/prefer-stateless-function': 'warn',
|
|
321
|
+
'react/require-render-return': 'error',
|
|
322
|
+
'react/self-closing-comp': 'error',
|
|
323
|
+
'react/void-dom-elements-no-children': 'error',
|
|
324
|
+
|
|
325
|
+
// React Hooks rules
|
|
326
|
+
'react-hooks/rules-of-hooks': 'error',
|
|
327
|
+
'react-hooks/exhaustive-deps': 'warn',
|
|
328
|
+
|
|
329
|
+
// Accessibility rules
|
|
330
|
+
'jsx-a11y/alt-text': 'error',
|
|
331
|
+
'jsx-a11y/anchor-has-content': 'error',
|
|
332
|
+
'jsx-a11y/anchor-is-valid': 'error',
|
|
333
|
+
'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
|
|
334
|
+
'jsx-a11y/aria-props': 'error',
|
|
335
|
+
'jsx-a11y/aria-proptypes': 'error',
|
|
336
|
+
'jsx-a11y/aria-role': 'error',
|
|
337
|
+
'jsx-a11y/aria-unsupported-elements': 'error',
|
|
338
|
+
'jsx-a11y/click-events-have-key-events': 'warn',
|
|
339
|
+
'jsx-a11y/heading-has-content': 'error',
|
|
340
|
+
'jsx-a11y/img-redundant-alt': 'error',
|
|
341
|
+
'jsx-a11y/interactive-supports-focus': 'warn',
|
|
342
|
+
'jsx-a11y/label-has-associated-control': 'error',
|
|
343
|
+
'jsx-a11y/no-access-key': 'error',
|
|
344
|
+
'jsx-a11y/no-autofocus': 'warn',
|
|
345
|
+
'jsx-a11y/no-distracting-elements': 'error',
|
|
346
|
+
'jsx-a11y/no-redundant-roles': 'error',
|
|
347
|
+
'jsx-a11y/role-has-required-aria-props': 'error',
|
|
348
|
+
'jsx-a11y/role-supports-aria-props': 'error',
|
|
349
|
+
'jsx-a11y/scope': 'error',
|
|
350
|
+
},
|
|
351
|
+
settings: {
|
|
352
|
+
react: {
|
|
353
|
+
version: 'detect',
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Prettier configuration to disable conflicting rules
|
|
360
|
+
*/
|
|
361
|
+
const prettierCompat = {
|
|
362
|
+
name: 'fullstack/prettier',
|
|
363
|
+
rules: {
|
|
364
|
+
...prettierConfig.rules,
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Ignore patterns common to most projects
|
|
370
|
+
*/
|
|
371
|
+
const ignoresConfig = {
|
|
372
|
+
name: 'fullstack/ignores',
|
|
373
|
+
ignores: [
|
|
374
|
+
'**/node_modules/**',
|
|
375
|
+
'**/dist/**',
|
|
376
|
+
'**/build/**',
|
|
377
|
+
'**/.next/**',
|
|
378
|
+
'**/coverage/**',
|
|
379
|
+
'**/.cache/**',
|
|
380
|
+
'**/public/build/**',
|
|
381
|
+
'**/*.min.js',
|
|
382
|
+
],
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Export configurations
|
|
387
|
+
*/
|
|
388
|
+
export const configs = {
|
|
389
|
+
base: baseConfig,
|
|
390
|
+
typescript: typescriptConfig,
|
|
391
|
+
node: nodeConfig,
|
|
392
|
+
react: reactConfig,
|
|
393
|
+
prettier: prettierCompat,
|
|
394
|
+
ignores: ignoresConfig,
|
|
395
|
+
|
|
396
|
+
// Recommended preset combinations
|
|
397
|
+
recommended: [ignoresConfig, baseConfig, prettierCompat],
|
|
398
|
+
|
|
399
|
+
'recommended-typescript': [
|
|
400
|
+
ignoresConfig,
|
|
401
|
+
baseConfig,
|
|
402
|
+
typescriptConfig,
|
|
403
|
+
prettierCompat,
|
|
404
|
+
],
|
|
405
|
+
|
|
406
|
+
'recommended-node': [
|
|
407
|
+
ignoresConfig,
|
|
408
|
+
baseConfig,
|
|
409
|
+
nodeConfig,
|
|
410
|
+
prettierCompat,
|
|
411
|
+
],
|
|
412
|
+
|
|
413
|
+
'recommended-node-typescript': [
|
|
414
|
+
ignoresConfig,
|
|
415
|
+
baseConfig,
|
|
416
|
+
typescriptConfig,
|
|
417
|
+
nodeConfig,
|
|
418
|
+
prettierCompat,
|
|
419
|
+
],
|
|
420
|
+
|
|
421
|
+
'recommended-react': [
|
|
422
|
+
ignoresConfig,
|
|
423
|
+
baseConfig,
|
|
424
|
+
reactConfig,
|
|
425
|
+
prettierCompat,
|
|
426
|
+
],
|
|
427
|
+
|
|
428
|
+
'recommended-react-typescript': [
|
|
429
|
+
ignoresConfig,
|
|
430
|
+
baseConfig,
|
|
431
|
+
typescriptConfig,
|
|
432
|
+
reactConfig,
|
|
433
|
+
prettierCompat,
|
|
434
|
+
],
|
|
435
|
+
|
|
436
|
+
'recommended-fullstack': [
|
|
437
|
+
ignoresConfig,
|
|
438
|
+
baseConfig,
|
|
439
|
+
typescriptConfig,
|
|
440
|
+
nodeConfig,
|
|
441
|
+
reactConfig,
|
|
442
|
+
prettierCompat,
|
|
443
|
+
],
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
export default configs;
|