@kununu/eslint-config 6.0.0-beta.8 ā 6.0.1
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 +2 -63
- package/eslint.config.mjs +223 -324
- package/package.json +6 -9
- package/MIGRATION_NOTES.md +0 -181
- package/index.js +0 -345
- package/test.mjs +0 -57
package/README.md
CHANGED
|
@@ -14,19 +14,11 @@ Add @kununu/eslint-config npm package as dev dependency to your project:
|
|
|
14
14
|
npm install --save-dev @kununu/eslint-config
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
### TypeScript Projects
|
|
18
|
-
|
|
19
|
-
If you're linting TypeScript files, you'll also need to install TypeScript:
|
|
20
|
-
|
|
21
|
-
```console
|
|
22
|
-
npm install --save-dev typescript
|
|
23
|
-
```
|
|
24
|
-
|
|
25
17
|
## š» Usage
|
|
26
18
|
|
|
27
|
-
### ESLint v9 (Flat Config)
|
|
19
|
+
### ESLint v9 (Flat Config)
|
|
28
20
|
|
|
29
|
-
Create an `eslint.config.
|
|
21
|
+
Create an `eslint.config.cjs` file:
|
|
30
22
|
|
|
31
23
|
```javascript
|
|
32
24
|
import kununuConfig from '@kununu/eslint-config';
|
|
@@ -37,59 +29,6 @@ export default [
|
|
|
37
29
|
];
|
|
38
30
|
```
|
|
39
31
|
|
|
40
|
-
### ESLint v8 (Legacy Format)
|
|
41
|
-
|
|
42
|
-
If you're still using ESLint v8, use version 5.x of this package:
|
|
43
|
-
|
|
44
|
-
```console
|
|
45
|
-
npm install --save-dev @kununu/eslint-config@5
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Then create a `.eslintrc.js` file:
|
|
49
|
-
|
|
50
|
-
```javascript
|
|
51
|
-
module.exports = {
|
|
52
|
-
extends: '@kununu/eslint-config'
|
|
53
|
-
};
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## š§ Configuration Details
|
|
57
|
-
|
|
58
|
-
This config includes rules for:
|
|
59
|
-
|
|
60
|
-
- **JavaScript/JSX**: Babel parser with React support
|
|
61
|
-
- **TypeScript/TSX**: TypeScript ESLint v8 with strict typing
|
|
62
|
-
- **React**: React 18+ with hooks support
|
|
63
|
-
- **Testing**: Jest, Jest DOM, and Testing Library
|
|
64
|
-
- **Code Quality**: Import ordering, Lodash optimization, granular selectors
|
|
65
|
-
- **Formatting**: Prettier integration with consistent style
|
|
66
|
-
|
|
67
|
-
### Included Plugins
|
|
68
|
-
|
|
69
|
-
- `@babel/eslint-plugin` - Babel-specific rules
|
|
70
|
-
- `eslint-plugin-react` - React-specific linting rules
|
|
71
|
-
- `eslint-plugin-react-hooks` - Rules for React Hooks
|
|
72
|
-
- `eslint-plugin-jsx-a11y` - Accessibility rules for JSX
|
|
73
|
-
- `eslint-plugin-import` - Import/export syntax validation
|
|
74
|
-
- `eslint-plugin-lodash` - Lodash optimization
|
|
75
|
-
- `eslint-plugin-prettier` - Prettier integration
|
|
76
|
-
- `@typescript-eslint` - TypeScript support
|
|
77
|
-
- `eslint-plugin-testing-library` - Testing Library best practices
|
|
78
|
-
- `eslint-plugin-jest-dom` - Jest DOM matchers
|
|
79
|
-
- `eslint-plugin-sort-destructure-keys` - Destructuring key sorting
|
|
80
|
-
- `eslint-plugin-granular-selectors` - Zustand/Redux selector validation
|
|
81
|
-
- `eslint-plugin-perfectionist` - Universal sorting (interfaces, enums, objects, imports, etc.)
|
|
82
|
-
|
|
83
|
-
## š Key Rules
|
|
84
|
-
|
|
85
|
-
- Prefer early returns over logical expressions in return statements
|
|
86
|
-
- Alphabetically sorted imports with newlines between groups
|
|
87
|
-
- React components with sorted props and defaultProps
|
|
88
|
-
- TypeScript interfaces with sorted keys
|
|
89
|
-
- 2-space indentation
|
|
90
|
-
- Single quotes, trailing commas
|
|
91
|
-
- No semicolons (via Prettier)
|
|
92
|
-
|
|
93
32
|
See [docs](https://eslint.org/docs/user-guide/getting-started) to find more detailed information on ESLint configuration and usage.
|
|
94
33
|
|
|
95
34
|
## ā”ļø Plugins
|
package/eslint.config.mjs
CHANGED
|
@@ -1,371 +1,270 @@
|
|
|
1
1
|
import babelParser from '@babel/eslint-parser';
|
|
2
2
|
import babelPlugin from '@babel/eslint-plugin';
|
|
3
3
|
import js from '@eslint/js';
|
|
4
|
+
import stylistic from '@stylistic/eslint-plugin';
|
|
4
5
|
import typescriptParser from '@typescript-eslint/parser';
|
|
5
6
|
import eslintConfigPrettier from 'eslint-config-prettier/flat';
|
|
6
7
|
import granularSelectorsPlugin from 'eslint-plugin-granular-selectors';
|
|
7
|
-
import
|
|
8
|
+
import pluginJest from 'eslint-plugin-jest';
|
|
8
9
|
import jestDomPlugin from 'eslint-plugin-jest-dom';
|
|
9
10
|
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
|
|
10
11
|
import lodashPlugin from 'eslint-plugin-lodash';
|
|
11
|
-
import
|
|
12
|
+
import perfectionistPlugin from 'eslint-plugin-perfectionist';
|
|
12
13
|
import reactPlugin from 'eslint-plugin-react';
|
|
13
14
|
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
|
14
|
-
import
|
|
15
|
+
import sonarjs from 'eslint-plugin-sonarjs';
|
|
15
16
|
import testingLibraryPlugin from 'eslint-plugin-testing-library';
|
|
16
|
-
import
|
|
17
|
+
import {defineConfig, globalIgnores} from 'eslint/config';
|
|
18
|
+
import tseslint from 'typescript-eslint';
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
'
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
'jestsetup.js',
|
|
46
|
-
'mockBff/*',
|
|
47
|
-
'next.config.js',
|
|
48
|
-
],
|
|
49
|
-
}],
|
|
50
|
-
'import/no-useless-path-segments': ['error', {
|
|
51
|
-
'noUselessIndex': true,
|
|
52
|
-
}],
|
|
53
|
-
'import/order': [
|
|
54
|
-
'error',
|
|
55
|
-
{
|
|
56
|
-
'alphabetize': {
|
|
57
|
-
'caseInsensitive': true,
|
|
58
|
-
'order': 'asc'
|
|
59
|
-
},
|
|
60
|
-
'groups': [
|
|
61
|
-
'builtin',
|
|
62
|
-
'external',
|
|
63
|
-
'internal',
|
|
64
|
-
'parent',
|
|
65
|
-
'sibling',
|
|
66
|
-
'index'
|
|
67
|
-
],
|
|
68
|
-
'newlines-between': 'always',
|
|
69
|
-
'pathGroups': [
|
|
70
|
-
{
|
|
71
|
-
'group': 'builtin',
|
|
72
|
-
'pattern': 'react',
|
|
73
|
-
'position': 'before'
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
'group': 'external',
|
|
77
|
-
'pattern': '@kununu/**',
|
|
78
|
-
'position': 'after'
|
|
79
|
-
}
|
|
80
|
-
],
|
|
81
|
-
'pathGroupsExcludedImportTypes': ['react']
|
|
82
|
-
}
|
|
83
|
-
],
|
|
84
|
-
'indent': 'off',
|
|
85
|
-
'jsx-a11y/anchor-is-valid': ['error', {
|
|
86
|
-
components: ['Link'],
|
|
87
|
-
specialLink: ['to'],
|
|
88
|
-
}],
|
|
89
|
-
'jsx-a11y/label-has-for': ['error', {
|
|
90
|
-
'required': 'id',
|
|
91
|
-
}],
|
|
92
|
-
'lodash/import-scope': [2, 'method'],
|
|
93
|
-
'max-len': 'off',
|
|
94
|
-
'no-confusing-arrow': 'off',
|
|
95
|
-
'no-multiple-empty-lines': ['error', {'max': 1, 'maxEOF': 1}],
|
|
96
|
-
'no-param-reassign': ['error', {props: false}],
|
|
97
|
-
'no-prototype-builtins': 'off',
|
|
98
|
-
'no-restricted-exports': 'off',
|
|
99
|
-
'no-restricted-syntax': ['error',
|
|
100
|
-
{
|
|
101
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.',
|
|
102
|
-
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > JSXElement'
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.',
|
|
106
|
-
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > JSXFragment'
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.',
|
|
110
|
-
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > CallExpression'
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.',
|
|
114
|
-
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > ObjectExpression'
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.',
|
|
118
|
-
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > ArrayExpression'
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.',
|
|
122
|
-
selector: 'ReturnStatement > SequenceExpression LogicalExpression[operator="&&"]'
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.',
|
|
126
|
-
selector: 'ReturnStatement > ParenthesizedExpression > SequenceExpression LogicalExpression[operator="&&"]'
|
|
127
|
-
}
|
|
128
|
-
],
|
|
129
|
-
'no-underscore-dangle': ['error', {'allow': ['__NEXT_DATA__', '__NEXT_REDUX_STORE__']}],
|
|
130
|
-
'no-use-before-define': 'off',
|
|
131
|
-
'object-curly-spacing': 'off',
|
|
132
|
-
'padding-line-between-statements': ['error', {
|
|
133
|
-
'blankLine': 'always', 'next': '*', 'prev': ['const', 'let', 'var'],
|
|
134
|
-
}, {
|
|
135
|
-
'blankLine': 'any', 'next': ['const', 'let', 'var'], 'prev': ['const', 'let', 'var'],
|
|
136
|
-
}],
|
|
137
|
-
'prefer-promise-reject-errors': 'off',
|
|
138
|
-
'react-hooks/exhaustive-deps': 'warn',
|
|
139
|
-
'react-hooks/rules-of-hooks': 'error',
|
|
140
|
-
'react/function-component-definition': 'off',
|
|
141
|
-
'react/jsx-props-no-spreading': ['error', {
|
|
142
|
-
'custom': 'ignore',
|
|
143
|
-
'html': 'enforce',
|
|
144
|
-
}],
|
|
145
|
-
'react/jsx-sort-props': ['error', {
|
|
146
|
-
'ignoreCase': true
|
|
147
|
-
}],
|
|
148
|
-
'react/no-direct-mutation-state': 'error',
|
|
149
|
-
'react/react-in-jsx-scope': 'off',
|
|
150
|
-
'react/require-default-props': 0,
|
|
151
|
-
'react/sort-default-props': ['error', {
|
|
152
|
-
'ignoreCase': true
|
|
153
|
-
}],
|
|
154
|
-
'react/sort-prop-types': ['error', {
|
|
155
|
-
'ignoreCase': true
|
|
156
|
-
}],
|
|
157
|
-
'react/state-in-constructor': 'off',
|
|
158
|
-
'react/static-property-placement': ['error', 'property assignment'],
|
|
159
|
-
'sort-destructure-keys/sort-destructure-keys': [2, {'caseSensitive': false}],
|
|
160
|
-
'sort-keys': ['error', 'asc', {'caseSensitive': false, 'natural': false}],
|
|
161
|
-
'testing-library/prefer-screen-queries': 'off',
|
|
162
|
-
'testing-library/render-result-naming-convention': 'off'
|
|
163
|
-
};
|
|
20
|
+
export default defineConfig([
|
|
21
|
+
eslintConfigPrettier,
|
|
22
|
+
js.configs.recommended,
|
|
23
|
+
jsxA11yPlugin.flatConfigs.recommended,
|
|
24
|
+
perfectionistPlugin.configs['recommended-natural'],
|
|
25
|
+
reactHooksPlugin.configs.flat.recommended,
|
|
26
|
+
reactPlugin.configs.flat.recommended,
|
|
27
|
+
reactPlugin.configs.flat['jsx-runtime'],
|
|
28
|
+
sonarjs.configs.recommended,
|
|
29
|
+
stylistic.configs.recommended,
|
|
30
|
+
testingLibraryPlugin.configs['flat/dom'],
|
|
31
|
+
tseslint.configs.recommended,
|
|
32
|
+
|
|
33
|
+
globalIgnores([
|
|
34
|
+
'**/.next/',
|
|
35
|
+
'**/__mocks__/',
|
|
36
|
+
'**/__snapshots__/',
|
|
37
|
+
'**/build/',
|
|
38
|
+
'**/coverage/',
|
|
39
|
+
'**/dist/',
|
|
40
|
+
'**/node_modules/',
|
|
41
|
+
'**/out/',
|
|
42
|
+
'.git/',
|
|
43
|
+
'jest.config.js',
|
|
44
|
+
'newrelic.js',
|
|
45
|
+
'next.config.js',
|
|
46
|
+
]),
|
|
164
47
|
|
|
165
|
-
export default [
|
|
166
|
-
// Global ignores
|
|
167
48
|
{
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
49
|
+
files: ['**/*.ts', '**/*.tsx'],
|
|
50
|
+
languageOptions: {
|
|
51
|
+
parser: typescriptParser,
|
|
52
|
+
parserOptions: {
|
|
53
|
+
projectService: true,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
175
56
|
},
|
|
176
57
|
|
|
177
|
-
// Base JavaScript/JSX configuration
|
|
178
58
|
{
|
|
179
59
|
files: ['**/*.js', '**/*.jsx'],
|
|
180
60
|
languageOptions: {
|
|
181
|
-
globals: {
|
|
182
|
-
__dirname: 'readonly',
|
|
183
|
-
__filename: 'readonly',
|
|
184
|
-
afterAll: 'readonly',
|
|
185
|
-
afterEach: 'readonly',
|
|
186
|
-
beforeAll: 'readonly',
|
|
187
|
-
beforeEach: 'readonly',
|
|
188
|
-
Buffer: 'readonly',
|
|
189
|
-
console: 'readonly',
|
|
190
|
-
describe: 'readonly',
|
|
191
|
-
document: 'readonly',
|
|
192
|
-
expect: 'readonly',
|
|
193
|
-
exports: 'readonly',
|
|
194
|
-
fetch: 'readonly',
|
|
195
|
-
it: 'readonly',
|
|
196
|
-
jest: 'readonly',
|
|
197
|
-
localStorage: 'readonly',
|
|
198
|
-
module: 'readonly',
|
|
199
|
-
navigator: 'readonly',
|
|
200
|
-
process: 'readonly',
|
|
201
|
-
require: 'readonly',
|
|
202
|
-
sessionStorage: 'readonly',
|
|
203
|
-
test: 'readonly',
|
|
204
|
-
window: 'readonly',
|
|
205
|
-
},
|
|
206
61
|
parser: babelParser,
|
|
207
62
|
parserOptions: {
|
|
208
|
-
babelOptions: {
|
|
209
|
-
presets: ['@babel/preset-react'],
|
|
210
|
-
},
|
|
211
|
-
ecmaFeatures: {
|
|
212
|
-
jsx: true,
|
|
213
|
-
},
|
|
214
|
-
ecmaVersion: 'latest',
|
|
215
63
|
requireConfigFile: false,
|
|
216
|
-
sourceType: 'module',
|
|
217
64
|
},
|
|
218
65
|
},
|
|
219
66
|
plugins: {
|
|
220
|
-
|
|
221
|
-
'granular-selectors': granularSelectorsPlugin,
|
|
222
|
-
'import': importPlugin,
|
|
223
|
-
'jest-dom': jestDomPlugin,
|
|
224
|
-
'jsx-a11y': jsxA11yPlugin,
|
|
225
|
-
'lodash': lodashPlugin,
|
|
226
|
-
'react': reactPlugin,
|
|
227
|
-
'react-hooks': reactHooksPlugin,
|
|
228
|
-
'sort-destructure-keys': sortDestructureKeysPlugin,
|
|
67
|
+
babel: babelPlugin,
|
|
229
68
|
},
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
{
|
|
72
|
+
files: [
|
|
73
|
+
['**/*.spec.*'],
|
|
74
|
+
],
|
|
75
|
+
...jestDomPlugin.configs['flat/recommended'],
|
|
76
|
+
languageOptions: {
|
|
77
|
+
globals: pluginJest.environments.globals.globals,
|
|
233
78
|
},
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
react: {
|
|
241
|
-
version: 'detect',
|
|
242
|
-
},
|
|
79
|
+
plugins: {jest: pluginJest},
|
|
80
|
+
rules: {
|
|
81
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
82
|
+
'react/display-name': 'off',
|
|
243
83
|
},
|
|
244
84
|
},
|
|
245
85
|
|
|
246
|
-
// TypeScript configuration
|
|
247
86
|
{
|
|
248
|
-
files: ['**/*.ts', '**/*.tsx'],
|
|
249
87
|
languageOptions: {
|
|
250
|
-
globals: {
|
|
251
|
-
__dirname: 'readonly',
|
|
252
|
-
__filename: 'readonly',
|
|
253
|
-
afterAll: 'readonly',
|
|
254
|
-
afterEach: 'readonly',
|
|
255
|
-
beforeAll: 'readonly',
|
|
256
|
-
beforeEach: 'readonly',
|
|
257
|
-
Buffer: 'readonly',
|
|
258
|
-
console: 'readonly',
|
|
259
|
-
describe: 'readonly',
|
|
260
|
-
document: 'readonly',
|
|
261
|
-
expect: 'readonly',
|
|
262
|
-
exports: 'readonly',
|
|
263
|
-
fetch: 'readonly',
|
|
264
|
-
it: 'readonly',
|
|
265
|
-
jest: 'readonly',
|
|
266
|
-
localStorage: 'readonly',
|
|
267
|
-
module: 'readonly',
|
|
268
|
-
navigator: 'readonly',
|
|
269
|
-
process: 'readonly',
|
|
270
|
-
require: 'readonly',
|
|
271
|
-
sessionStorage: 'readonly',
|
|
272
|
-
test: 'readonly',
|
|
273
|
-
window: 'readonly',
|
|
274
|
-
},
|
|
275
|
-
parser: typescriptParser,
|
|
276
88
|
parserOptions: {
|
|
277
89
|
ecmaFeatures: {
|
|
278
90
|
jsx: true,
|
|
279
91
|
},
|
|
280
|
-
ecmaVersion: 'latest',
|
|
281
|
-
sourceType: 'module',
|
|
282
92
|
},
|
|
283
93
|
},
|
|
94
|
+
linterOptions: {
|
|
95
|
+
reportUnusedDisableDirectives: 'error',
|
|
96
|
+
},
|
|
284
97
|
plugins: {
|
|
285
|
-
'@babel': babelPlugin,
|
|
286
|
-
'@typescript-eslint': typescriptEslint.plugin,
|
|
287
98
|
'granular-selectors': granularSelectorsPlugin,
|
|
288
|
-
|
|
289
|
-
'jest-dom': jestDomPlugin,
|
|
290
|
-
'jsx-a11y': jsxA11yPlugin,
|
|
291
|
-
'lodash': lodashPlugin,
|
|
292
|
-
'react': reactPlugin,
|
|
293
|
-
'react-hooks': reactHooksPlugin,
|
|
294
|
-
'sort-destructure-keys': sortDestructureKeysPlugin,
|
|
99
|
+
lodash: lodashPlugin,
|
|
295
100
|
},
|
|
296
101
|
rules: {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
'@
|
|
302
|
-
'@
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
102
|
+
// Prevent empty lines in arrays
|
|
103
|
+
'@stylistic/array-bracket-newline': ['error', 'consistent'],
|
|
104
|
+
'@stylistic/array-bracket-spacing': ['error', 'never'],
|
|
105
|
+
'@stylistic/arrow-parens': ['error', 'as-needed'],
|
|
106
|
+
'@stylistic/brace-style': ['error', '1tbs'],
|
|
107
|
+
'@stylistic/member-delimiter-style': ['error', {
|
|
108
|
+
multiline: {
|
|
109
|
+
delimiter: 'semi',
|
|
110
|
+
requireLast: true,
|
|
111
|
+
},
|
|
112
|
+
overrides: {
|
|
113
|
+
interface: {
|
|
114
|
+
multiline: {
|
|
115
|
+
delimiter: 'semi',
|
|
116
|
+
requireLast: true,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
singleline: {
|
|
121
|
+
delimiter: 'semi',
|
|
122
|
+
requireLast: false,
|
|
123
|
+
},
|
|
310
124
|
}],
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
125
|
+
// Prevent multiple consecutive empty lines but allow single ones
|
|
126
|
+
'@stylistic/no-multiple-empty-lines': ['error', {
|
|
127
|
+
max: 1,
|
|
128
|
+
maxBOF: 0,
|
|
129
|
+
maxEOF: 0,
|
|
130
|
+
}],
|
|
131
|
+
// For object destructuring patterns
|
|
132
|
+
'@stylistic/object-curly-newline': ['error', {consistent: true}],
|
|
133
|
+
// Prevent empty lines inside object literals and destructuring
|
|
134
|
+
'@stylistic/object-curly-spacing': ['error', 'never'],
|
|
135
|
+
'@stylistic/operator-linebreak': ['error', 'after', {overrides: {'|': 'before'}}],
|
|
136
|
+
'@stylistic/padded-blocks': ['error', 'never'],
|
|
137
|
+
'@stylistic/padding-line-between-statements': ['error',
|
|
138
|
+
// Allow any spacing between imports (to allow grouping and other import rules)
|
|
139
|
+
{blankLine: 'any', next: 'import', prev: 'import'},
|
|
140
|
+
// Always require blank line after imports when followed by non-imports
|
|
141
|
+
{blankLine: 'always', next: '*', prev: 'import'},
|
|
142
|
+
// But allow imports to be followed by imports without forcing blank line
|
|
143
|
+
{blankLine: 'any', next: 'import', prev: 'import'},
|
|
144
|
+
// Allow any spacing between variable declarations (before and between)
|
|
145
|
+
{blankLine: 'any', next: ['const', 'let', 'var'], prev: '*'},
|
|
146
|
+
// Always require blank line after variable declarations when followed by non-variables
|
|
147
|
+
{blankLine: 'always', next: '*', prev: ['const', 'let', 'var']},
|
|
148
|
+
// But allow variables to be followed by variables without forcing blank line (override above)
|
|
149
|
+
{blankLine: 'any', next: ['const', 'let', 'var'], prev: ['const', 'let', 'var']},
|
|
150
|
+
// Always require blank line before return statements
|
|
151
|
+
{blankLine: 'always', next: 'return', prev: '*'},
|
|
152
|
+
// Always require blank line before case and default statements
|
|
153
|
+
{blankLine: 'always', next: '*', prev: ['case', 'default']},
|
|
154
|
+
// Allow blank lines between JSX elements (expression statements)
|
|
155
|
+
{blankLine: 'any', next: 'expression', prev: 'expression'}],
|
|
156
|
+
'@stylistic/quote-props': ['error', 'as-needed'],
|
|
157
|
+
'@stylistic/semi': ['error', 'always'],
|
|
158
|
+
'@stylistic/switch-colon-spacing': 'error',
|
|
159
|
+
'@typescript-eslint/no-use-before-define': 'error',
|
|
160
|
+
'@typescript-eslint/no-var-requires': 'error',
|
|
161
|
+
'granular-selectors/granular-selectors': ['error', {
|
|
162
|
+
include: ['use.*Selector.*', 'use.*Store.*'],
|
|
163
|
+
}],
|
|
164
|
+
'lodash/import-scope': [2, 'method'],
|
|
165
|
+
'no-console': 'warn',
|
|
166
|
+
'no-param-reassign': ['error', {props: false}],
|
|
167
|
+
'no-restricted-imports': [
|
|
168
|
+
'error',
|
|
169
|
+
{
|
|
170
|
+
paths: [
|
|
171
|
+
{
|
|
172
|
+
importNames: ['default'],
|
|
173
|
+
message: '\n We want to avoid importing react directly. Please import individual hooks and types from the react module instead.',
|
|
174
|
+
name: 'react',
|
|
175
|
+
},
|
|
176
|
+
],
|
|
317
177
|
},
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
178
|
+
],
|
|
179
|
+
'no-use-before-define': 'off',
|
|
180
|
+
'perfectionist/sort-imports': [
|
|
181
|
+
'error',
|
|
182
|
+
{
|
|
183
|
+
customGroups: [
|
|
184
|
+
{
|
|
185
|
+
elementNamePattern: ['^react$'],
|
|
186
|
+
groupName: 'react',
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
elementNamePattern: [
|
|
190
|
+
'^actions/.+',
|
|
191
|
+
'^client/.+',
|
|
192
|
+
'^components/.+',
|
|
193
|
+
'^contexts/.+',
|
|
194
|
+
'^genericTypes/.+',
|
|
195
|
+
'^hooks/.+',
|
|
196
|
+
'^images/.+',
|
|
197
|
+
'^mocks/.+',
|
|
198
|
+
'^pages/.+',
|
|
199
|
+
'^server/.+',
|
|
200
|
+
'^slices/.+',
|
|
201
|
+
'^src/.+',
|
|
202
|
+
'^state/.+',
|
|
203
|
+
'^tracking/.+',
|
|
204
|
+
'^types/.+',
|
|
205
|
+
'^utils/.+',
|
|
206
|
+
],
|
|
207
|
+
groupName: 'alias',
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
groups: [
|
|
211
|
+
'react',
|
|
212
|
+
'type-import',
|
|
213
|
+
['type-builtin', 'type-external'],
|
|
214
|
+
'type-internal',
|
|
215
|
+
'type-parent',
|
|
216
|
+
'type-sibling',
|
|
217
|
+
'type-index',
|
|
218
|
+
['value-builtin', 'value-external'],
|
|
219
|
+
'value-internal',
|
|
220
|
+
'alias',
|
|
221
|
+
'value-parent',
|
|
222
|
+
'value-sibling',
|
|
223
|
+
'value-index',
|
|
224
|
+
'ts-equals-import',
|
|
225
|
+
'unknown',
|
|
226
|
+
],
|
|
227
|
+
internalPattern: ['^~/.+', '^@kununu/.+'],
|
|
228
|
+
type: 'natural',
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
'perfectionist/sort-modules': ['error', {type: 'usage'}],
|
|
232
|
+
'perfectionist/sort-union-types': ['error', {
|
|
233
|
+
groups: [
|
|
234
|
+
'conditional',
|
|
235
|
+
'function',
|
|
236
|
+
'import',
|
|
237
|
+
'intersection',
|
|
238
|
+
'keyword',
|
|
239
|
+
'literal',
|
|
240
|
+
'named',
|
|
241
|
+
'object',
|
|
242
|
+
'operator',
|
|
243
|
+
'tuple',
|
|
244
|
+
'union',
|
|
245
|
+
'nullish',
|
|
246
|
+
],
|
|
247
|
+
}],
|
|
248
|
+
'prefer-template': 'error',
|
|
249
|
+
'react-hooks/exhaustive-deps': 'warn',
|
|
250
|
+
'react-hooks/rules-of-hooks': 'error',
|
|
251
|
+
'react/jsx-closing-bracket-location': ['error', 'tag-aligned'],
|
|
252
|
+
// More restrictive JSX rules that are auto-fixable
|
|
253
|
+
'react/jsx-curly-spacing': ['error', 'never'],
|
|
254
|
+
'react/jsx-first-prop-new-line': ['error', 'multiline-multiprop'],
|
|
255
|
+
// Control JSX prop formatting more precisely
|
|
256
|
+
'react/jsx-max-props-per-line': ['error', {maximum: 1, when: 'multiline'}],
|
|
257
|
+
// Keep the non-auto-fixable rule to detect the issue
|
|
258
|
+
'react/jsx-props-no-multi-spaces': 'error',
|
|
259
|
+
// Use jsx-tag-spacing for what it can auto-fix
|
|
260
|
+
'react/jsx-tag-spacing': ['error', {
|
|
261
|
+
afterOpening: 'never',
|
|
262
|
+
beforeClosing: 'never',
|
|
263
|
+
beforeSelfClosing: 'always',
|
|
264
|
+
closingSlash: 'never',
|
|
265
|
+
}],
|
|
266
|
+
'sonarjs/deprecation': ['warn'],
|
|
267
|
+
'sonarjs/todo-tag': ['warn'],
|
|
355
268
|
},
|
|
356
269
|
},
|
|
357
|
-
|
|
358
|
-
// Reducer files
|
|
359
|
-
{
|
|
360
|
-
files: [
|
|
361
|
-
'**/reducers/**/*.js',
|
|
362
|
-
'**/reducers/**/*.ts'
|
|
363
|
-
],
|
|
364
|
-
rules: {
|
|
365
|
-
'default-param-last': 'off'
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
|
|
369
|
-
perfectionist.configs['recommended-natural'],
|
|
370
|
-
eslintConfigPrettier,
|
|
371
|
-
];
|
|
270
|
+
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kununu/eslint-config",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "kununu's ESLint config",
|
|
5
5
|
"main": "eslint.config.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -30,27 +30,24 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://github.com/kununu/eslint-config#readme",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@babel/core": "7.
|
|
33
|
+
"@babel/core": "7.29.0",
|
|
34
34
|
"@babel/eslint-parser": "7.28.6",
|
|
35
35
|
"@babel/eslint-plugin": "7.27.1",
|
|
36
|
-
"@eslint/compat": "2.0.1",
|
|
37
36
|
"@eslint/js": "9.39.2",
|
|
38
|
-
"@
|
|
37
|
+
"@stylistic/eslint-plugin": "5.7.1",
|
|
39
38
|
"@typescript-eslint/parser": "8.54.0",
|
|
40
39
|
"eslint": "9.39.2",
|
|
41
40
|
"eslint-config-prettier": "10.1.8",
|
|
42
|
-
"eslint-import-resolver-alias": "1.1.2",
|
|
43
41
|
"eslint-plugin-granular-selectors": "1.4.0",
|
|
44
|
-
"eslint-plugin-
|
|
42
|
+
"eslint-plugin-jest": "29.12.2",
|
|
45
43
|
"eslint-plugin-jest-dom": "5.5.0",
|
|
46
44
|
"eslint-plugin-jsx-a11y": "6.10.2",
|
|
47
45
|
"eslint-plugin-lodash": "8.0.0",
|
|
48
|
-
"eslint-plugin-
|
|
46
|
+
"eslint-plugin-perfectionist": "5.4.0",
|
|
49
47
|
"eslint-plugin-react": "7.37.5",
|
|
50
48
|
"eslint-plugin-react-hooks": "7.0.1",
|
|
51
|
-
"eslint-plugin-
|
|
49
|
+
"eslint-plugin-sonarjs": "3.0.6",
|
|
52
50
|
"eslint-plugin-testing-library": "7.15.4",
|
|
53
|
-
"eslint-plugin-perfectionist": "5.4.0",
|
|
54
51
|
"prettier": "3.8.1",
|
|
55
52
|
"typescript-eslint": "8.54.0"
|
|
56
53
|
},
|
package/MIGRATION_NOTES.md
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
# ESLint Config v6.0.0 Migration Notes
|
|
2
|
-
|
|
3
|
-
This document outlines the breaking changes and migration steps from v5.x to v6.0.0.
|
|
4
|
-
|
|
5
|
-
## Major Version Updates
|
|
6
|
-
|
|
7
|
-
### 1. ESLint v9 (9.17.0 ā 9.39.2)
|
|
8
|
-
|
|
9
|
-
- **Change**: Minor version update within ESLint 9
|
|
10
|
-
- **Impact**: No breaking changes, improvements and bug fixes
|
|
11
|
-
- **Action**: None required
|
|
12
|
-
|
|
13
|
-
### 2. @eslint/js (9.17.0 ā 9.39.2)
|
|
14
|
-
|
|
15
|
-
- **Change**: Minor version update
|
|
16
|
-
- **Impact**: Updated recommended rules
|
|
17
|
-
- **Action**: None required
|
|
18
|
-
|
|
19
|
-
### 3. @eslint/compat (1.2.4 ā 2.0.1) ā ļø MAJOR
|
|
20
|
-
|
|
21
|
-
- **Change**: API improvements for flat config compatibility
|
|
22
|
-
- **Impact**: Better compatibility layer for legacy plugins
|
|
23
|
-
- **Action**: None required - internal API improvements
|
|
24
|
-
|
|
25
|
-
### 4. TypeScript ESLint v8 (7.18.0 ā 8.54.0) ā ļø MAJOR
|
|
26
|
-
|
|
27
|
-
**Breaking Changes:**
|
|
28
|
-
|
|
29
|
-
- New unified package structure via `typescript-eslint`
|
|
30
|
-
- Plugin and parser now accessed through unified exports
|
|
31
|
-
- Some rule names and behaviors changed
|
|
32
|
-
|
|
33
|
-
**Migration Actions Taken:**
|
|
34
|
-
|
|
35
|
-
- Added `typescript-eslint` package (v8.54.0)
|
|
36
|
-
- Updated imports from `@typescript-eslint/eslint-plugin` to `typescript-eslint`
|
|
37
|
-
- Changed `typescriptPlugin` to `typescriptEslint.plugin`
|
|
38
|
-
- Updated config access from `typescriptPlugin.configs.*` to `typescriptEslint.configs.*`
|
|
39
|
-
- Kept separate `@typescript-eslint/parser` and `@typescript-eslint/eslint-plugin` for compatibility
|
|
40
|
-
|
|
41
|
-
**Key Changes:**
|
|
42
|
-
|
|
43
|
-
```javascript
|
|
44
|
-
// Old (v7)
|
|
45
|
-
const typescriptPlugin = require('@typescript-eslint/eslint-plugin');
|
|
46
|
-
plugins: { '@typescript-eslint': typescriptPlugin }
|
|
47
|
-
rules: { ...typescriptPlugin.configs.recommended.rules }
|
|
48
|
-
|
|
49
|
-
// New (v8)
|
|
50
|
-
const typescriptEslint = require('typescript-eslint');
|
|
51
|
-
plugins: { '@typescript-eslint': typescriptEslint.plugin }
|
|
52
|
-
rules: { ...typescriptEslint.configs.recommended.rules }
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### 5. eslint-plugin-lodash (7.4.0 ā 8.0.0) ā ļø MAJOR
|
|
56
|
-
|
|
57
|
-
- **Change**: ESLint 9 compatibility updates
|
|
58
|
-
- **Impact**: Improved flat config support
|
|
59
|
-
- **Action**: None required - API compatible
|
|
60
|
-
|
|
61
|
-
### 6. eslint-plugin-react-hooks (4.6.2 ā 7.0.1) ā ļø MAJOR
|
|
62
|
-
|
|
63
|
-
**Breaking Changes:**
|
|
64
|
-
|
|
65
|
-
- Requires ESLint 9+ (already met)
|
|
66
|
-
- Stricter exhaustive-deps checking
|
|
67
|
-
- Better support for React 19
|
|
68
|
-
|
|
69
|
-
**Migration Actions:**
|
|
70
|
-
|
|
71
|
-
- No code changes required
|
|
72
|
-
- Existing rules remain compatible
|
|
73
|
-
- May surface additional dependency warnings (intended behavior)
|
|
74
|
-
|
|
75
|
-
**Current Rules:**
|
|
76
|
-
|
|
77
|
-
```javascript
|
|
78
|
-
'react-hooks/rules-of-hooks': 'error',
|
|
79
|
-
'react-hooks/exhaustive-deps': 'warn',
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Package.json Updates
|
|
83
|
-
|
|
84
|
-
All dependencies updated to latest versions:
|
|
85
|
-
|
|
86
|
-
```json
|
|
87
|
-
{
|
|
88
|
-
"@eslint/compat": "2.0.1", // 1.2.4 ā 2.0.1
|
|
89
|
-
"@eslint/js": "9.39.2", // 9.17.0 ā 9.39.2
|
|
90
|
-
"@typescript-eslint/eslint-plugin": "8.54.0", // 7.18.0 ā 8.54.0
|
|
91
|
-
"@typescript-eslint/parser": "8.54.0", // 7.18.0 ā 8.54.0
|
|
92
|
-
"eslint": "9.39.2", // 9.17.0 ā 9.39.2
|
|
93
|
-
"eslint-plugin-lodash": "8.0.0", // 7.4.0 ā 8.0.0
|
|
94
|
-
"eslint-plugin-react-hooks": "7.0.1", // 4.6.2 ā 7.0.1
|
|
95
|
-
"eslint-plugin-perfectionist": "5.4.0", // REPLACES eslint-plugin-typescript-sort-keys
|
|
96
|
-
"typescript-eslint": "8.54.0" // NEW PACKAGE
|
|
97
|
-
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**Removed Dependencies**
|
|
101
|
-
|
|
102
|
-
**Airbnb Configs Removed:**
|
|
103
|
-
|
|
104
|
-
- `eslint-config-airbnb` - Not compatible with ESLint 9
|
|
105
|
-
- `eslint-config-airbnb-typescript` - Not compatible with ESLint 9
|
|
106
|
-
|
|
107
|
-
**Reason**: These packages haven't been updated to support ESLint 9's flat config format.
|
|
108
|
-
The rules they provided are already covered by our custom rule set and the recommended
|
|
109
|
-
configs from individual plugins (React, JSX A11y, Import, etc.). The flat config format
|
|
110
|
-
doesn't use `extends` in the traditional way, so we've incorporated the rules directly.
|
|
111
|
-
|
|
112
|
-
**Replaced Dependencies**
|
|
113
|
-
|
|
114
|
-
**eslint-plugin-typescript-sort-keys ā eslint-plugin-perfectionist:**
|
|
115
|
-
|
|
116
|
-
- `eslint-plugin-typescript-sort-keys` - Doesn't support ESLint 9
|
|
117
|
-
- Replaced with `eslint-plugin-perfectionist` v5.4.0 (ESLint 9 compatible)
|
|
118
|
-
- Perfectionist provides the same interface/enum sorting functionality plus additional sorting capabilities
|
|
119
|
-
- Rules migrated:
|
|
120
|
-
- `typescript-sort-keys/interface` ā `perfectionist/sort-interfaces`
|
|
121
|
-
- `typescript-sort-keys/string-enum` ā `perfectionist/sort-enums`
|
|
122
|
-
|
|
123
|
-
## Configuration Format
|
|
124
|
-
|
|
125
|
-
The configuration now uses ESLint v9 flat config format (`eslint.config.mjs`):
|
|
126
|
-
|
|
127
|
-
- **ESM format** (import/export) instead of CommonJS (require/module.exports)
|
|
128
|
-
- Array-based configuration instead of object
|
|
129
|
-
- `languageOptions` instead of `env`, `parser`, `parserOptions`
|
|
130
|
-
- Explicit `globals` definitions
|
|
131
|
-
- Direct plugin imports as objects
|
|
132
|
-
- File-based configuration overrides using `files` property
|
|
133
|
-
|
|
134
|
-
**Note**: The configuration file is now `eslint.config.mjs` (ESM) because some plugins
|
|
135
|
-
(like `eslint-plugin-perfectionist`) are ESM-only. Your consuming projects should use
|
|
136
|
-
ESM format for their ESLint configs as well.
|
|
137
|
-
|
|
138
|
-
## Testing & Validation
|
|
139
|
-
|
|
140
|
-
After updating, ensure:
|
|
141
|
-
|
|
142
|
-
1. **Install dependencies:**
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
npm install --legacy-peer-deps
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
**Note**: The `--legacy-peer-deps` flag is required because some plugins
|
|
149
|
-
(`eslint-plugin-typescript-sort-keys`) haven't updated their peer dependencies
|
|
150
|
-
to support ESLint 9 yet, even though they work correctly with it.
|
|
151
|
-
|
|
152
|
-
2. **Test in consuming projects:**
|
|
153
|
-
- Update import: `extends: ['@kununu/eslint-config']` (if using old format)
|
|
154
|
-
- Or import config: `const kununuConfig = require('@kununu/eslint-config')`
|
|
155
|
-
- You may also need `--legacy-peer-deps` in consuming projects
|
|
156
|
-
|
|
157
|
-
3. **Check for new warnings:**
|
|
158
|
-
- TypeScript rules may be stricter
|
|
159
|
-
- React Hooks exhaustive-deps may surface more warnings
|
|
160
|
-
- Review and address any new lint errors
|
|
161
|
-
|
|
162
|
-
4. **Verify compatibility:**
|
|
163
|
-
- Test with JavaScript files
|
|
164
|
-
- Test with TypeScript files
|
|
165
|
-
- Test with React components
|
|
166
|
-
- Test with test files (*.spec.*, *.test.*)
|
|
167
|
-
|
|
168
|
-
## Rollback Plan
|
|
169
|
-
|
|
170
|
-
If issues occur, you can rollback to v5.x:
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
npm install @kununu/eslint-config@5.11.7
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
And revert to using `index.js` configuration format.
|
|
177
|
-
|
|
178
|
-
## Support
|
|
179
|
-
|
|
180
|
-
For issues or questions, please file an issue at:
|
|
181
|
-
<https://github.com/kununu/eslint-config/issues>
|
package/index.js
DELETED
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
// we need to have the baseRules to have the same on the typescript override
|
|
2
|
-
// because it has extends the eslint does not take into consideration the ones from the base
|
|
3
|
-
const baseRules = {
|
|
4
|
-
'import/no-extraneous-dependencies': ['error', {
|
|
5
|
-
devDependencies: [
|
|
6
|
-
'**/*.spec.js',
|
|
7
|
-
'**/*.spec.jsx',
|
|
8
|
-
'**/*.test.js',
|
|
9
|
-
'**/*.test.jsx',
|
|
10
|
-
'**/stories.jsx',
|
|
11
|
-
'*/test-*/*.js',
|
|
12
|
-
'*/test-*/*.jsx',
|
|
13
|
-
'config/**/*.js',
|
|
14
|
-
'jest.setup.js',
|
|
15
|
-
'jestsetup.js',
|
|
16
|
-
'mockBff/*',
|
|
17
|
-
'next.config.js',
|
|
18
|
-
],
|
|
19
|
-
}],
|
|
20
|
-
'max-len': 'off', // Sometimes longer lines are more readable (Airbnb rule change)
|
|
21
|
-
'no-param-reassign': ['error', {props: false}],
|
|
22
|
-
'no-prototype-builtins': 'off', // Objects aren't created that don't extend from Object.prototype (Airbnb rule change)
|
|
23
|
-
'object-curly-spacing': 'off', // Disabled in favor of @babel/object-curly-spacing in order to avoid false positives with ECMAScript modules (Airbnb rule change)
|
|
24
|
-
'no-use-before-define': 'off',
|
|
25
|
-
'sort-destructure-keys/sort-destructure-keys': [2, {'caseSensitive': false}],
|
|
26
|
-
|
|
27
|
-
// https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules
|
|
28
|
-
'react/no-direct-mutation-state': 'error', // Use .setState() always (Airbnb rule change)
|
|
29
|
-
|
|
30
|
-
// https://github.com/babel/babel/tree/main/eslint/babel-eslint-plugin#rules
|
|
31
|
-
'@babel/object-curly-spacing': 'error', // No spaces in single-line objects to make nested objects like {a: {b: 'c'}} look more sane (Airbnb rule change)
|
|
32
|
-
|
|
33
|
-
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/
|
|
34
|
-
'import/order': ['error', { // Make import sort order an error (Airbnb rule change)
|
|
35
|
-
'newlines-between': 'always',
|
|
36
|
-
groups: [
|
|
37
|
-
'builtin', // import fs from 'fs';
|
|
38
|
-
'external', // import chalk from 'chalk';
|
|
39
|
-
'internal', // import foo from 'src/foo';
|
|
40
|
-
'parent', // import qux from '../qux';
|
|
41
|
-
'sibling', // import bar from './bar';
|
|
42
|
-
'index', // import main from './';
|
|
43
|
-
],
|
|
44
|
-
}],
|
|
45
|
-
|
|
46
|
-
'import/no-useless-path-segments': ['error', {
|
|
47
|
-
'noUselessIndex': true,
|
|
48
|
-
}],
|
|
49
|
-
|
|
50
|
-
// https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-is-valid.md#rule-details
|
|
51
|
-
// allow `Link` to have `to` and not the mandatory `href`
|
|
52
|
-
'jsx-a11y/anchor-is-valid': ['error', {
|
|
53
|
-
components: ['Link'],
|
|
54
|
-
specialLink: ['to'],
|
|
55
|
-
}],
|
|
56
|
-
|
|
57
|
-
// https://eslint.org/docs/rules/no-confusing-arrow
|
|
58
|
-
// turn off to prevent conflict with
|
|
59
|
-
// https://eslint.org/docs/rules/arrow-body-style
|
|
60
|
-
'no-confusing-arrow': 'off',
|
|
61
|
-
|
|
62
|
-
// https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/label-has-for.md
|
|
63
|
-
// 'label' tags need 'htmlFor' prop, but nesting is not required
|
|
64
|
-
'jsx-a11y/label-has-for': ['error', {
|
|
65
|
-
'required': 'id',
|
|
66
|
-
}],
|
|
67
|
-
|
|
68
|
-
// https://eslint.org/docs/rules/padding-line-between-statements
|
|
69
|
-
// enforce empty lines after variable declarations
|
|
70
|
-
'padding-line-between-statements': ['error', {
|
|
71
|
-
'blankLine': 'always', 'prev': ['const', 'let', 'var'], 'next': '*',
|
|
72
|
-
}, {
|
|
73
|
-
'blankLine': 'any', 'prev': ['const', 'let', 'var'], 'next': ['const', 'let', 'var'],
|
|
74
|
-
}],
|
|
75
|
-
|
|
76
|
-
// https://www.npmjs.com/package/eslint-plugin-react-hooks
|
|
77
|
-
// enforces the rules of react-hooks (call at top level and only from functional components; checks dependencies)
|
|
78
|
-
'react-hooks/rules-of-hooks': 'error',
|
|
79
|
-
'react-hooks/exhaustive-deps': 'warn',
|
|
80
|
-
|
|
81
|
-
// https://eslint.org/docs/rules/no-underscore-dangle
|
|
82
|
-
// no underscores at either the beginning or end of an identifier
|
|
83
|
-
'no-underscore-dangle': ['error', {'allow': ['__NEXT_DATA__', '__NEXT_REDUX_STORE__']}],
|
|
84
|
-
|
|
85
|
-
'no-multiple-empty-lines': ['error', {'max': 1, 'maxEOF': 1}],
|
|
86
|
-
|
|
87
|
-
// https://eslint.org/docs/rules/eol-last
|
|
88
|
-
// require newline at the end of files
|
|
89
|
-
'eol-last': ['error', 'always'],
|
|
90
|
-
|
|
91
|
-
// https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/sort-default-props.md
|
|
92
|
-
// enforce defaultProps declarations alphabetical sorting
|
|
93
|
-
'react/sort-default-props': ['error', {
|
|
94
|
-
'ignoreCase': true
|
|
95
|
-
}],
|
|
96
|
-
|
|
97
|
-
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md
|
|
98
|
-
// enforce props alphabetical sorting
|
|
99
|
-
'react/jsx-sort-props': ['error', {
|
|
100
|
-
'ignoreCase': true
|
|
101
|
-
}],
|
|
102
|
-
|
|
103
|
-
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-prop-types.md
|
|
104
|
-
// enforce propTypes declarations alphabetical sorting
|
|
105
|
-
'react/sort-prop-types': ['error', {
|
|
106
|
-
'ignoreCase': true
|
|
107
|
-
}],
|
|
108
|
-
|
|
109
|
-
// https://eslint.org/docs/rules/sort-keys
|
|
110
|
-
// require object keys to be sorted
|
|
111
|
-
'sort-keys': ['error', 'asc', {'caseSensitive': false, 'natural': false}],
|
|
112
|
-
|
|
113
|
-
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/static-property-placement.md
|
|
114
|
-
// enforces where React component static properties should be positioned
|
|
115
|
-
'react/static-property-placement': ['error', 'property assignment'],
|
|
116
|
-
|
|
117
|
-
// https://eslint.org/docs/rules/indent
|
|
118
|
-
// enforces a consistent 2 spaces indentation style
|
|
119
|
-
'indent': ['error', 2, {
|
|
120
|
-
'SwitchCase': 1
|
|
121
|
-
}],
|
|
122
|
-
|
|
123
|
-
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/state-in-constructor.md
|
|
124
|
-
// enforces the state initialization style to be either in a constructor or with a class property
|
|
125
|
-
'react/state-in-constructor': 'off',
|
|
126
|
-
|
|
127
|
-
// https://eslint.org/docs/rules/arrow-parens
|
|
128
|
-
// enforces no braces where they can be omitted
|
|
129
|
-
'arrow-parens': ['error', 'as-needed'],
|
|
130
|
-
|
|
131
|
-
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md
|
|
132
|
-
'import/extensions': ['error', 'ignorePackages', {
|
|
133
|
-
'js': 'never',
|
|
134
|
-
'jsx': 'never',
|
|
135
|
-
'ts': 'never',
|
|
136
|
-
'tsx': 'never',
|
|
137
|
-
'scss': 'ignorePackages',
|
|
138
|
-
'json': 'always'
|
|
139
|
-
}],
|
|
140
|
-
|
|
141
|
-
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-props-no-spreading.md
|
|
142
|
-
// disallow spread on html tags directly but allows it on React components
|
|
143
|
-
'react/jsx-props-no-spreading': ['error', {
|
|
144
|
-
'html': 'enforce',
|
|
145
|
-
'custom': 'ignore',
|
|
146
|
-
}],
|
|
147
|
-
|
|
148
|
-
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/function-component-definition.md
|
|
149
|
-
'react/function-component-definition': 'off',
|
|
150
|
-
'react/react-in-jsx-scope': 'off',
|
|
151
|
-
'prefer-promise-reject-errors': 'off',
|
|
152
|
-
'no-restricted-exports': 'off',
|
|
153
|
-
|
|
154
|
-
'testing-library/prefer-screen-queries': 'off',
|
|
155
|
-
'testing-library/render-result-naming-convention': 'off',
|
|
156
|
-
|
|
157
|
-
'indent': 'off',
|
|
158
|
-
|
|
159
|
-
'prettier/prettier': [
|
|
160
|
-
'error',
|
|
161
|
-
{
|
|
162
|
-
'arrowParens': 'avoid',
|
|
163
|
-
'bracketSpacing': false,
|
|
164
|
-
'semi': true,
|
|
165
|
-
'singleQuote': true,
|
|
166
|
-
'trailingComma': 'all',
|
|
167
|
-
},
|
|
168
|
-
],
|
|
169
|
-
|
|
170
|
-
'import/order': [
|
|
171
|
-
'error',
|
|
172
|
-
{
|
|
173
|
-
'groups': [
|
|
174
|
-
'builtin',
|
|
175
|
-
'external',
|
|
176
|
-
'internal',
|
|
177
|
-
'parent',
|
|
178
|
-
'sibling',
|
|
179
|
-
'index'
|
|
180
|
-
],
|
|
181
|
-
'newlines-between': 'always',
|
|
182
|
-
'alphabetize': {
|
|
183
|
-
'order': 'asc',
|
|
184
|
-
'caseInsensitive': true
|
|
185
|
-
},
|
|
186
|
-
'pathGroups': [
|
|
187
|
-
{
|
|
188
|
-
'pattern': 'react',
|
|
189
|
-
'group': 'builtin',
|
|
190
|
-
'position': 'before'
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
'pattern': '@kununu/**',
|
|
194
|
-
'group': 'external',
|
|
195
|
-
'position': 'after'
|
|
196
|
-
}
|
|
197
|
-
],
|
|
198
|
-
'pathGroupsExcludedImportTypes': ['react']
|
|
199
|
-
}
|
|
200
|
-
],
|
|
201
|
-
|
|
202
|
-
'lodash/import-scope': [2, 'method'],
|
|
203
|
-
|
|
204
|
-
// https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-proptypes-and-defaultprops
|
|
205
|
-
'react/require-default-props': 0,
|
|
206
|
-
|
|
207
|
-
'no-restricted-syntax': ['error',
|
|
208
|
-
// Target JSX elements specifically
|
|
209
|
-
{
|
|
210
|
-
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > JSXElement',
|
|
211
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
|
|
212
|
-
},
|
|
213
|
-
// Target JSX fragments
|
|
214
|
-
{
|
|
215
|
-
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > JSXFragment',
|
|
216
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
|
|
217
|
-
},
|
|
218
|
-
// Target function calls (which could be component invocations)
|
|
219
|
-
{
|
|
220
|
-
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > CallExpression',
|
|
221
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
|
|
222
|
-
},
|
|
223
|
-
// Target object expressions (which could be props)
|
|
224
|
-
{
|
|
225
|
-
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > ObjectExpression',
|
|
226
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
|
|
227
|
-
},
|
|
228
|
-
// Target array expressions
|
|
229
|
-
{
|
|
230
|
-
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > ArrayExpression',
|
|
231
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
|
|
232
|
-
},
|
|
233
|
-
// Keep the sequence expression rules
|
|
234
|
-
{
|
|
235
|
-
selector: 'ReturnStatement > SequenceExpression LogicalExpression[operator="&&"]',
|
|
236
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
selector: 'ReturnStatement > ParenthesizedExpression > SequenceExpression LogicalExpression[operator="&&"]',
|
|
240
|
-
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
|
|
241
|
-
}
|
|
242
|
-
],
|
|
243
|
-
|
|
244
|
-
'granular-selectors/granular-selectors': ['error', {
|
|
245
|
-
// Array of patterns to include for selector function detection
|
|
246
|
-
include: ['use.*Selector.*', 'use.*Store.*']
|
|
247
|
-
}]
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
module.exports = {
|
|
251
|
-
extends: [
|
|
252
|
-
'airbnb', // Many strict rules for ECMAScript and React
|
|
253
|
-
'airbnb/hooks',
|
|
254
|
-
'plugin:import/errors',
|
|
255
|
-
'plugin:jest-dom/recommended',
|
|
256
|
-
'plugin:prettier/recommended',
|
|
257
|
-
'plugin:react-hooks/recommended',
|
|
258
|
-
],
|
|
259
|
-
|
|
260
|
-
parser: '@babel/eslint-parser',
|
|
261
|
-
|
|
262
|
-
plugins: [
|
|
263
|
-
'@babel',
|
|
264
|
-
'lodash',
|
|
265
|
-
'sort-destructure-keys',
|
|
266
|
-
'granular-selectors'
|
|
267
|
-
],
|
|
268
|
-
|
|
269
|
-
env: {
|
|
270
|
-
browser: true,
|
|
271
|
-
jest: true,
|
|
272
|
-
node: true,
|
|
273
|
-
es6: true,
|
|
274
|
-
},
|
|
275
|
-
|
|
276
|
-
rules: baseRules,
|
|
277
|
-
|
|
278
|
-
overrides: [{
|
|
279
|
-
files: [
|
|
280
|
-
'**/__tests__/**/*.[jt]s?(x)',
|
|
281
|
-
'**/?(*.)+(spec|test).[jt]s?(x)'
|
|
282
|
-
],
|
|
283
|
-
extends: [
|
|
284
|
-
'plugin:testing-library/react'
|
|
285
|
-
],
|
|
286
|
-
}, {
|
|
287
|
-
files: [
|
|
288
|
-
'**/*.ts',
|
|
289
|
-
'**/*.tsx',
|
|
290
|
-
],
|
|
291
|
-
extends: [
|
|
292
|
-
'airbnb', // Many strict rules for ECMAScript and React
|
|
293
|
-
'airbnb-typescript',
|
|
294
|
-
'airbnb/hooks',
|
|
295
|
-
'plugin:@typescript-eslint/recommended',
|
|
296
|
-
'plugin:@typescript-eslint/stylistic',
|
|
297
|
-
'plugin:import/errors',
|
|
298
|
-
'plugin:jest-dom/recommended',
|
|
299
|
-
'plugin:prettier/recommended',
|
|
300
|
-
'plugin:react-hooks/recommended',
|
|
301
|
-
],
|
|
302
|
-
parser: '@typescript-eslint/parser',
|
|
303
|
-
plugins: [
|
|
304
|
-
'typescript-sort-keys'
|
|
305
|
-
],
|
|
306
|
-
rules: {
|
|
307
|
-
...baseRules,
|
|
308
|
-
'@typescript-eslint/indent': 'off',
|
|
309
|
-
'@typescript-eslint/no-use-before-define': ['error'],
|
|
310
|
-
'@typescript-eslint/no-var-requires': 'off',
|
|
311
|
-
'react/prop-types': 'off',
|
|
312
|
-
'typescript-sort-keys/interface': ['error', 'asc', {'caseSensitive': false, 'natural': false, requiredFirst: true}],
|
|
313
|
-
'typescript-sort-keys/string-enum': ['error', 'asc', {'caseSensitive': false, 'natural': false}],
|
|
314
|
-
'import/no-extraneous-dependencies': ['error', {
|
|
315
|
-
devDependencies: [
|
|
316
|
-
'**/*.spec.ts',
|
|
317
|
-
'**/*.spec.tsx',
|
|
318
|
-
'**/stories.tsx',
|
|
319
|
-
],
|
|
320
|
-
}],
|
|
321
|
-
},
|
|
322
|
-
overrides: [
|
|
323
|
-
{
|
|
324
|
-
files: ['**/stories.tsx', '*.spec.ts', '*.spec.tsx'],
|
|
325
|
-
rules: {
|
|
326
|
-
'@typescript-eslint/no-explicit-any': 'off',
|
|
327
|
-
},
|
|
328
|
-
}
|
|
329
|
-
],
|
|
330
|
-
}, {
|
|
331
|
-
files: ['*.spec.js', '*.spec.jsx', '*.spec.ts', '*.spec.tsx'],
|
|
332
|
-
rules: {
|
|
333
|
-
'global-require': 'off',
|
|
334
|
-
'jsx-a11y/anchor-is-valid': 'off',
|
|
335
|
-
},
|
|
336
|
-
}, {
|
|
337
|
-
files: [
|
|
338
|
-
'**/reducers/**/*.js',
|
|
339
|
-
'**/reducers/**/*.ts'
|
|
340
|
-
],
|
|
341
|
-
rules: {
|
|
342
|
-
'default-param-last': 'off'
|
|
343
|
-
}
|
|
344
|
-
}],
|
|
345
|
-
};
|
package/test.mjs
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Simple test to verify the ESLint config loads and exports correctly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import config from './eslint.config.mjs';
|
|
8
|
-
|
|
9
|
-
console.log('Testing @kununu/eslint-config...\n');
|
|
10
|
-
|
|
11
|
-
// Test 1: Config is an array
|
|
12
|
-
if (!Array.isArray(config)) {
|
|
13
|
-
console.error('ā Config should be an array');
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
console.log('ā Config is an array');
|
|
17
|
-
|
|
18
|
-
// Test 2: Has expected number of config objects
|
|
19
|
-
if (config.length < 5) {
|
|
20
|
-
console.error('ā Config should have at least 5 config objects');
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
console.log(`ā Config has ${config.length} config objects`);
|
|
24
|
-
|
|
25
|
-
// Test 3: Has ignores
|
|
26
|
-
const hasIgnores = config.some(c => c.ignores);
|
|
27
|
-
if (!hasIgnores) {
|
|
28
|
-
console.error('ā Config should have ignores');
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
console.log('ā Config has ignores');
|
|
32
|
-
|
|
33
|
-
// Test 4: Has TypeScript config
|
|
34
|
-
const hasTsConfig = config.some(c => c.files && c.files.some(f => f.includes('*.ts')));
|
|
35
|
-
if (!hasTsConfig) {
|
|
36
|
-
console.error('ā Config should have TypeScript config');
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
console.log('ā Config has TypeScript support');
|
|
40
|
-
|
|
41
|
-
// Test 5: Has perfectionist plugin
|
|
42
|
-
const hasPerfectionist = config.some(c => c.plugins && c.plugins.perfectionist);
|
|
43
|
-
if (!hasPerfectionist) {
|
|
44
|
-
console.error('ā Config should have perfectionist plugin');
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
console.log('ā Config has perfectionist plugin');
|
|
48
|
-
|
|
49
|
-
// Test 6: Has React plugin
|
|
50
|
-
const hasReact = config.some(c => c.plugins && c.plugins.react);
|
|
51
|
-
if (!hasReact) {
|
|
52
|
-
console.error('ā Config should have React plugin');
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
console.log('ā Config has React plugin');
|
|
56
|
-
|
|
57
|
-
console.log('\nā All tests passed!');
|