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.
Files changed (4) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +262 -0
  3. package/package.json +69 -0
  4. 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;