@allthings/eslint-config 3.7.0 → 3.8.0-alpha.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 CHANGED
@@ -48,6 +48,26 @@ export default [
48
48
  ]
49
49
  ```
50
50
 
51
+ ### Functional-style Node.js projects
52
+
53
+ Opt-in strict-functional variant of the Node config. On top of everything in `/node`, it forbids `let`, classes, `this`, and all object/array mutation via [`eslint-plugin-functional`](https://github.com/eslint-functional/eslint-plugin-functional), and re-enables `unicorn/no-array-reverse` + `unicorn/no-array-sort` for consistency. Existing codebases will see a lot of errors on first run — this is intentional and aligns with the functional-programming style.
54
+
55
+ ```js
56
+ import allthingsFunctionalConfig from '@allthings/eslint-config/functional'
57
+
58
+ export default [
59
+ ...allthingsFunctionalConfig,
60
+ {
61
+ languageOptions: {
62
+ parserOptions: {
63
+ project: './tsconfig.json',
64
+ tsconfigRootDir: import.meta.dirname,
65
+ },
66
+ },
67
+ },
68
+ ]
69
+ ```
70
+
51
71
  ## Development
52
72
 
53
73
  Run `yarn link` in the project folder
@@ -57,7 +77,7 @@ Run `yarn link @allthings/eslint-config` in the project that you want to test it
57
77
  After you finish run in your project `yarn unlink @allthings/eslint-config` and then `yarn install --force`
58
78
  to restore the initial state of dependencies
59
79
 
60
- Or you could release a dev npm version with `yarn deploy:dev`. Remember to update the version in package.json
80
+ If `yarn link` isn't enough and you need a real published artifact, see [Development release](#development-release) below.
61
81
 
62
82
  ### Testing the config
63
83
 
@@ -95,7 +115,7 @@ See semantic-releases [docs](https://github.com/semantic-release/semantic-releas
95
115
  git push --force origin HEAD:beta # or alpha / next
96
116
  ```
97
117
 
98
- The pipeline will automatically run `semantic-release`, which detects the branch name, bumps the version with the appropriate pre-release tag, and publishes it to npm under the matching dist-tag. Check [Actions page](https://github.com/allthings/eslint-config/actions) for the release logs.
118
+ The pipeline will automatically run `semantic-release`, which detects the branch name, bumps the version with the appropriate pre-release tag, and publishes it to npm under the matching dist-tag. Check [Actions page](https://github.com/allthings/eslint-config-allthings/actions) for the release logs.
99
119
 
100
120
  3. **Install the pre-release** in another project:
101
121
 
package/base.js ADDED
@@ -0,0 +1,309 @@
1
+ import stylisticPlugin from '@stylistic/eslint-plugin'
2
+ import jestPlugin from 'eslint-plugin-jest'
3
+ import perfectionistPlugin from 'eslint-plugin-perfectionist'
4
+ import preferArrowPlugin from 'eslint-plugin-prefer-arrow'
5
+ import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort'
6
+ import globals from 'globals'
7
+
8
+ export const baseGlobals = {
9
+ ...globals.node,
10
+ ...jestPlugin.environments.globals.globals,
11
+ }
12
+
13
+ export const baseParserOptions = {
14
+ ecmaVersion: 2021,
15
+ project: 'tsconfig.json',
16
+ sourceType: 'module',
17
+ warnOnUnsupportedTypeScriptVersion: false,
18
+ }
19
+
20
+ export const baseSettings = {
21
+ 'import/parsers': {
22
+ '@typescript-eslint/parser': ['.ts', '.mts', '.cts', '.tsx', '.d.ts'],
23
+ },
24
+ 'import/resolver': {
25
+ node: { extensions: ['.js', '.jsx', '.ts', '.tsx'] },
26
+ typescript: { alwaysTryTypes: true },
27
+ },
28
+ }
29
+
30
+ export const basePlugins = {
31
+ '@stylistic': stylisticPlugin,
32
+ jest: jestPlugin,
33
+ perfectionist: perfectionistPlugin,
34
+ 'prefer-arrow': preferArrowPlugin,
35
+ 'simple-import-sort': simpleImportSortPlugin,
36
+ }
37
+
38
+ export const baseRules = {
39
+ '@stylistic/padding-line-between-statements': [
40
+ 'error',
41
+ {
42
+ blankLine: 'always',
43
+ next: 'return',
44
+ prev: '*',
45
+ },
46
+ ],
47
+ '@stylistic/spaced-comment': [
48
+ 'error',
49
+ 'always',
50
+ {
51
+ markers: ['/'],
52
+ },
53
+ ],
54
+ '@typescript-eslint/adjacent-overload-signatures': 'error',
55
+ '@typescript-eslint/array-type': [
56
+ 'error',
57
+ {
58
+ default: 'array',
59
+ },
60
+ ],
61
+ '@typescript-eslint/consistent-type-assertions': 'error',
62
+ '@typescript-eslint/consistent-type-definitions': 'off',
63
+ '@typescript-eslint/explicit-function-return-type': 'off',
64
+ '@typescript-eslint/explicit-module-boundary-types': [
65
+ 'warn', // TODO: strict later
66
+ {
67
+ allowArgumentsExplicitlyTypedAsAny: true,
68
+ allowDirectConstAssertionInArrowFunctions: true,
69
+ allowHigherOrderFunctions: false,
70
+ allowTypedFunctionExpressions: false,
71
+ },
72
+ ],
73
+ '@typescript-eslint/naming-convention': [
74
+ 'error',
75
+ {
76
+ custom: {
77
+ match: true,
78
+ regex: '^I[A-Z]',
79
+ },
80
+ format: ['PascalCase'],
81
+ selector: 'interface',
82
+ },
83
+ ],
84
+ '@typescript-eslint/no-base-to-string': 'warn',
85
+ '@typescript-eslint/no-dynamic-delete': 'error',
86
+ // need for tests' mockups
87
+ '@typescript-eslint/no-empty-function': 'warn',
88
+ '@typescript-eslint/no-empty-object-type': 'error',
89
+ '@typescript-eslint/no-explicit-any': 'warn',
90
+ // turn on later
91
+ '@typescript-eslint/no-floating-promises': 'warn',
92
+ '@typescript-eslint/no-for-in-array': 'error',
93
+ '@typescript-eslint/no-inferrable-types': 'error',
94
+ '@typescript-eslint/no-misused-new': 'error',
95
+ '@typescript-eslint/no-namespace': 'error',
96
+ '@typescript-eslint/no-redundant-type-constituents': 'warn',
97
+ '@typescript-eslint/no-require-imports': 'error',
98
+ '@typescript-eslint/no-shadow': [
99
+ 'error',
100
+ {
101
+ hoist: 'all',
102
+ },
103
+ ],
104
+ '@typescript-eslint/no-this-alias': 'error',
105
+ '@typescript-eslint/no-unsafe-argument': 'warn',
106
+ '@typescript-eslint/no-unsafe-assignment': 'warn',
107
+ '@typescript-eslint/no-unsafe-call': 'warn',
108
+ '@typescript-eslint/no-unsafe-enum-comparison': 'off',
109
+ '@typescript-eslint/no-unsafe-member-access': 'off',
110
+ '@typescript-eslint/no-unsafe-return': 'warn',
111
+ '@typescript-eslint/no-unused-expressions': 'error',
112
+ '@typescript-eslint/no-use-before-define': 'error',
113
+ '@typescript-eslint/prefer-for-of': 'error',
114
+ '@typescript-eslint/prefer-function-type': 'error',
115
+ '@typescript-eslint/prefer-namespace-keyword': 'error',
116
+ '@typescript-eslint/prefer-nullish-coalescing': 'off',
117
+ '@typescript-eslint/prefer-promise-reject-errors': 'warn',
118
+ '@typescript-eslint/restrict-template-expressions': 'warn',
119
+ '@typescript-eslint/triple-slash-reference': [
120
+ 'error',
121
+ {
122
+ lib: 'always',
123
+ path: 'always',
124
+ types: 'prefer-import',
125
+ },
126
+ ],
127
+ '@typescript-eslint/unified-signatures': 'error',
128
+ 'arrow-body-style': ['error', 'as-needed'],
129
+ complexity: [
130
+ 'error',
131
+ {
132
+ max: 15,
133
+ },
134
+ ],
135
+ 'constructor-super': 'error',
136
+ eqeqeq: ['error', 'smart'],
137
+ 'guard-for-in': 'error',
138
+ 'id-denylist': [
139
+ 'error',
140
+ 'any',
141
+ 'Number',
142
+ 'number',
143
+ 'String',
144
+ 'string',
145
+ 'Boolean',
146
+ 'boolean',
147
+ 'Undefined',
148
+ 'undefined',
149
+ ],
150
+ 'id-match': 'error',
151
+ 'import/no-anonymous-default-export': [
152
+ 'error',
153
+ {
154
+ allowAnonymousClass: false,
155
+ allowAnonymousFunction: false,
156
+ allowArray: false,
157
+ allowArrowFunction: true,
158
+ allowCallExpression: true,
159
+ // The true value here is for backward compatibility
160
+ allowLiteral: false,
161
+ allowObject: true,
162
+ },
163
+ ],
164
+ 'import/no-extraneous-dependencies': [
165
+ 'error',
166
+ {
167
+ devDependencies: true,
168
+ },
169
+ ],
170
+ 'jest/no-disabled-tests': 'warn',
171
+ 'jest/no-focused-tests': 'error',
172
+ 'jest/no-identical-title': 'error',
173
+ 'jest/valid-expect': 'error',
174
+ 'max-classes-per-file': ['error', 1],
175
+ 'no-bitwise': 'error',
176
+ 'no-caller': 'error',
177
+ 'no-cond-assign': 'error',
178
+ 'no-console': 'error',
179
+ 'no-debugger': 'error',
180
+ 'no-duplicate-case': 'error',
181
+ 'no-duplicate-imports': 'error',
182
+ 'no-empty': 'error',
183
+ 'no-eval': 'error',
184
+ 'no-extra-bind': 'error',
185
+ 'no-multiple-empty-lines': 'error',
186
+ 'no-new-func': 'error',
187
+ 'no-new-wrappers': 'error',
188
+ 'no-param-reassign': 'error',
189
+ 'no-redeclare': 'error',
190
+ 'no-sequences': 'error',
191
+ 'no-sparse-arrays': 'error',
192
+ 'no-template-curly-in-string': 'error',
193
+ 'no-throw-literal': 'error',
194
+ 'no-trailing-spaces': 'error',
195
+ 'no-undef-init': 'error',
196
+ 'no-unsafe-finally': 'error',
197
+ 'no-unused-labels': 'error',
198
+ 'no-var': 'error',
199
+ 'object-shorthand': 'error',
200
+ 'one-var': ['error', 'never'],
201
+ // perfectionist — replaces eslint-plugin-typescript-sort-keys
202
+ // (string-enum equivalent: sort-enums with default sortByValue: 'ifNumericEnum')
203
+ 'perfectionist/sort-enums': 'error',
204
+ 'perfectionist/sort-heritage-clauses': 'error',
205
+ 'perfectionist/sort-interfaces': 'error',
206
+ 'perfectionist/sort-object-types': 'error',
207
+ 'prefer-arrow/prefer-arrow-functions': 'warn',
208
+ 'prefer-const': 'error',
209
+ 'prefer-object-spread': 'error',
210
+ 'prefer-template': 'error',
211
+ radix: 'error',
212
+ 'simple-import-sort/exports': 'error',
213
+ 'simple-import-sort/imports': 'error',
214
+ 'sort-keys': 'error',
215
+ 'unicorn/better-regex': 'off',
216
+ 'unicorn/consistent-existence-index-check': 'off',
217
+ 'unicorn/expiring-todo-comments': 'off',
218
+ 'unicorn/filename-case': [
219
+ 'warn',
220
+ {
221
+ cases: {
222
+ camelCase: true,
223
+ pascalCase: true,
224
+ },
225
+ },
226
+ ],
227
+ 'unicorn/import-style': 'warn',
228
+ 'unicorn/no-anonymous-default-export': 'off',
229
+ 'unicorn/no-array-reduce': 'off',
230
+ // .reverse() mutates in place — semantic + perf cost to force .toReversed()
231
+ 'unicorn/no-array-reverse': 'off',
232
+ // .sort() mutates in place — same story as no-array-reverse
233
+ 'unicorn/no-array-sort': 'off',
234
+ 'unicorn/no-await-expression-member': 'warn',
235
+ 'unicorn/no-console-spaces': 'off',
236
+ 'unicorn/no-document-cookie': 'warn',
237
+ 'unicorn/no-empty-file': 'off',
238
+ 'unicorn/no-hex-escape': 'warn',
239
+ 'unicorn/no-invalid-remove-event-listener': 'warn',
240
+ 'unicorn/no-named-default': 'warn',
241
+ 'unicorn/no-nested-ternary': 'off',
242
+ // because we have a lot of logic depends on null
243
+ 'unicorn/no-null': 'warn',
244
+ 'unicorn/no-object-as-default-parameter': 'warn',
245
+ 'unicorn/no-single-promise-in-promise-methods': 'warn',
246
+ 'unicorn/no-unreadable-array-destructuring': 'off',
247
+ 'unicorn/no-unused-properties': 'warn',
248
+ 'unicorn/no-zero-fractions': 'off',
249
+ 'unicorn/prefer-class-fields': 'warn',
250
+ 'unicorn/prefer-export-from': 'off',
251
+ 'unicorn/prefer-global-this': 'warn',
252
+ 'unicorn/prefer-includes': 'off',
253
+ 'unicorn/prefer-math-min-max': 'warn',
254
+ 'unicorn/prefer-module': 'warn',
255
+ 'unicorn/prefer-node-protocol': 'off',
256
+ 'unicorn/prefer-optional-catch-binding': 'off',
257
+ 'unicorn/prefer-prototype-methods': 'off',
258
+ 'unicorn/prefer-query-selector': 'off',
259
+ 'unicorn/prefer-regexp-test': 'off',
260
+ 'unicorn/prefer-set-has': 'off',
261
+ 'unicorn/prefer-set-size': 'off',
262
+ // opinionated condition ordering — too noisy
263
+ 'unicorn/prefer-simple-condition-first': 'off',
264
+ 'unicorn/prefer-string-raw': 'off',
265
+ 'unicorn/prefer-string-replace-all': 'off',
266
+ 'unicorn/prefer-structured-clone': 'warn',
267
+ 'unicorn/prevent-abbreviations': [
268
+ 'error',
269
+ {
270
+ allowList: {
271
+ params: true,
272
+ props: true,
273
+ utils: true,
274
+ },
275
+ replacements: {
276
+ params: {
277
+ properties: false,
278
+ },
279
+ props: {
280
+ properties: false,
281
+ },
282
+ utils: {
283
+ utilities: false,
284
+ },
285
+ },
286
+ },
287
+ ],
288
+ 'unicorn/relative-url-style': 'off',
289
+ 'unicorn/require-module-attributes': 'warn',
290
+ 'unicorn/switch-case-braces': 'off',
291
+ 'unicorn/template-indent': 'off',
292
+ 'use-isnan': 'error',
293
+ }
294
+
295
+ export const baseTestOverrides = {
296
+ // because of jest
297
+ files: ['**/*.test.ts'],
298
+ rules: {
299
+ '@typescript-eslint/explicit-function-return-type': 'off',
300
+ '@typescript-eslint/no-empty-function': 'off',
301
+ '@typescript-eslint/no-explicit-any': 'off',
302
+ '@typescript-eslint/no-misused-promises': 'off',
303
+ '@typescript-eslint/no-unsafe-assignment': 'off',
304
+ '@typescript-eslint/no-unsafe-return': 'off',
305
+ '@typescript-eslint/require-await': 'off',
306
+ 'unicorn/no-array-callback-reference': 'off',
307
+ 'unicorn/no-await-expression-member': 'off',
308
+ },
309
+ }
package/functional.js ADDED
@@ -0,0 +1,34 @@
1
+ import functionalPlugin from 'eslint-plugin-functional'
2
+
3
+ import nodeConfig from './node.js'
4
+
5
+ const config = [
6
+ ...nodeConfig,
7
+ {
8
+ plugins: { functional: functionalPlugin },
9
+ rules: {
10
+ 'functional/immutable-data': 'error',
11
+ 'functional/no-classes': 'error',
12
+ 'functional/no-let': 'error',
13
+ 'functional/no-this-expressions': 'error',
14
+ // base.js turns these off citing perf/semantics of in-place mutation;
15
+ // in a functional codebase the same reasoning that bans property
16
+ // mutation bans .reverse() / .sort() too — they mutate in place.
17
+ 'unicorn/no-array-reverse': 'error',
18
+ 'unicorn/no-array-sort': 'error',
19
+ },
20
+ },
21
+ {
22
+ // tests routinely need `let` in beforeEach setup, mock mutation, and
23
+ // sometimes class-based fixtures — same spirit as baseTestOverrides.
24
+ files: ['**/*.test.ts'],
25
+ rules: {
26
+ 'functional/immutable-data': 'off',
27
+ 'functional/no-classes': 'off',
28
+ 'functional/no-let': 'off',
29
+ 'functional/no-this-expressions': 'off',
30
+ },
31
+ },
32
+ ]
33
+
34
+ export default config
package/index.js CHANGED
@@ -1,19 +1,23 @@
1
- import stylisticPlugin from '@stylistic/eslint-plugin'
2
1
  import tsPlugin from '@typescript-eslint/eslint-plugin'
3
- import globals from 'globals'
4
- import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'
2
+ import vitestPlugin from '@vitest/eslint-plugin'
5
3
  import importPlugin from 'eslint-plugin-import'
6
- import jestPlugin from 'eslint-plugin-jest'
7
- import perfectionistPlugin from 'eslint-plugin-perfectionist'
8
- import preferArrowPlugin from 'eslint-plugin-prefer-arrow'
4
+ import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'
9
5
  import prettierConfig from 'eslint-plugin-prettier/recommended'
10
6
  import reactPlugin from 'eslint-plugin-react'
11
7
  import reactHooksPlugin from 'eslint-plugin-react-hooks'
12
- import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort'
13
8
  import unicornPlugin from 'eslint-plugin-unicorn'
14
- import vitestPlugin from '@vitest/eslint-plugin'
9
+ import globals from 'globals'
10
+
11
+ import {
12
+ baseGlobals,
13
+ baseParserOptions,
14
+ basePlugins,
15
+ baseRules,
16
+ baseSettings,
17
+ baseTestOverrides,
18
+ } from './base.js'
15
19
 
16
- export default [
20
+ const config = [
17
21
  ...tsPlugin.configs['flat/recommended-type-checked'],
18
22
  ...tsPlugin.configs['flat/stylistic-type-checked'],
19
23
  unicornPlugin.configs['flat/recommended'],
@@ -28,217 +32,25 @@ export default [
28
32
  languageOptions: {
29
33
  globals: {
30
34
  ...globals.browser,
31
- ...globals.node,
32
- ...jestPlugin.environments.globals.globals,
35
+ ...baseGlobals,
33
36
  },
34
37
  parserOptions: {
38
+ ...baseParserOptions,
35
39
  ecmaFeatures: {
36
40
  jsx: true,
37
41
  },
38
- ecmaVersion: 2021,
39
- project: 'tsconfig.json',
40
- sourceType: 'module',
41
- warnOnUnsupportedTypeScriptVersion: false,
42
- },
43
- },
44
- plugins: {
45
- '@stylistic': stylisticPlugin,
46
- jest: jestPlugin,
47
- perfectionist: perfectionistPlugin,
48
- 'prefer-arrow': preferArrowPlugin,
49
- 'simple-import-sort': simpleImportSortPlugin,
50
- },
51
- settings: {
52
- react: { version: 'detect' },
53
- 'import/parsers': {
54
- '@typescript-eslint/parser': ['.ts', '.mts', '.cts', '.tsx', '.d.ts'],
55
- },
56
- 'import/resolver': {
57
- node: { extensions: ['.js', '.jsx', '.ts', '.tsx'] },
58
- typescript: { alwaysTryTypes: true },
59
42
  },
60
43
  },
44
+ plugins: basePlugins,
61
45
  rules: {
62
- // perfectionist — replaces eslint-plugin-typescript-sort-keys
63
- // (string-enum equivalent: sort-enums with default sortByValue: 'ifNumericEnum')
64
- 'perfectionist/sort-enums': 'error',
65
- 'perfectionist/sort-heritage-clauses': 'error',
66
- 'perfectionist/sort-interfaces': 'error',
67
- 'perfectionist/sort-object-types': 'error',
68
- // react-hooks override (recommended-latest has exhaustive-deps as 'warn')
69
- 'react-hooks/exhaustive-deps': 'error',
70
- '@stylistic/padding-line-between-statements': [
71
- 'error',
72
- {
73
- blankLine: 'always',
74
- prev: '*',
75
- next: 'return',
76
- },
77
- ],
78
- '@stylistic/spaced-comment': [
79
- 'error',
80
- 'always',
81
- {
82
- markers: ['/'],
83
- },
84
- ],
85
- '@typescript-eslint/adjacent-overload-signatures': 'error',
86
- '@typescript-eslint/array-type': [
87
- 'error',
88
- {
89
- default: 'array',
90
- },
91
- ],
92
- '@typescript-eslint/consistent-type-assertions': 'error',
93
- '@typescript-eslint/consistent-type-definitions': 'off',
94
- '@typescript-eslint/explicit-function-return-type': 'off',
95
- '@typescript-eslint/explicit-module-boundary-types': [
96
- 'warn', // TODO: strict later
97
- {
98
- allowArgumentsExplicitlyTypedAsAny: true,
99
- allowDirectConstAssertionInArrowFunctions: true,
100
- allowHigherOrderFunctions: false,
101
- allowTypedFunctionExpressions: false,
102
- },
103
- ],
104
- '@typescript-eslint/naming-convention': [
105
- 'error',
106
- {
107
- custom: {
108
- match: true,
109
- regex: '^I[A-Z]',
110
- },
111
- format: ['PascalCase'],
112
- selector: 'interface',
113
- },
114
- ],
115
- '@typescript-eslint/no-base-to-string': 'warn',
116
- '@typescript-eslint/no-dynamic-delete': 'error',
117
- // need for tests' mockups
118
- '@typescript-eslint/no-empty-function': 'warn',
119
- '@typescript-eslint/no-empty-object-type': 'error',
120
- '@typescript-eslint/no-explicit-any': 'warn',
121
- // turn on later
122
- '@typescript-eslint/no-floating-promises': 'warn',
123
- '@typescript-eslint/no-for-in-array': 'error',
124
- '@typescript-eslint/no-inferrable-types': 'error',
125
- '@typescript-eslint/no-misused-new': 'error',
126
- '@typescript-eslint/no-namespace': 'error',
127
- '@typescript-eslint/no-redundant-type-constituents': 'warn',
128
- '@typescript-eslint/no-require-imports': 'error',
129
- '@typescript-eslint/no-shadow': [
130
- 'error',
131
- {
132
- hoist: 'all',
133
- },
134
- ],
135
- '@typescript-eslint/no-this-alias': 'error',
46
+ ...baseRules,
136
47
  '@typescript-eslint/no-unnecessary-type-assertion': 'warn',
137
- '@typescript-eslint/no-unsafe-argument': 'warn',
138
- '@typescript-eslint/no-unsafe-assignment': 'warn',
139
- '@typescript-eslint/no-unsafe-call': 'warn',
140
- '@typescript-eslint/no-unsafe-enum-comparison': 'off',
141
- '@typescript-eslint/no-unsafe-member-access': 'off',
142
- '@typescript-eslint/no-unsafe-return': 'warn',
143
- '@typescript-eslint/no-unused-expressions': 'error',
144
48
  '@typescript-eslint/no-unused-vars': [
145
49
  'error',
146
50
  { varsIgnorePattern: '^jsx$' },
147
51
  ],
148
- '@typescript-eslint/no-use-before-define': 'error',
149
- '@typescript-eslint/prefer-for-of': 'error',
150
- '@typescript-eslint/prefer-function-type': 'error',
151
- '@typescript-eslint/prefer-namespace-keyword': 'error',
152
- '@typescript-eslint/prefer-nullish-coalescing': 'off',
153
- '@typescript-eslint/prefer-promise-reject-errors': 'warn',
154
- '@typescript-eslint/restrict-template-expressions': 'warn',
155
- '@typescript-eslint/triple-slash-reference': [
156
- 'error',
157
- {
158
- path: 'always',
159
- types: 'prefer-import',
160
- lib: 'always',
161
- },
162
- ],
163
- '@typescript-eslint/unified-signatures': 'error',
164
- 'arrow-body-style': ['error', 'as-needed'],
165
- complexity: [
166
- 'error',
167
- {
168
- max: 15,
169
- },
170
- ],
171
- 'constructor-super': 'error',
172
- eqeqeq: ['error', 'smart'],
173
- 'guard-for-in': 'error',
174
- 'id-denylist': [
175
- 'error',
176
- 'any',
177
- 'Number',
178
- 'number',
179
- 'String',
180
- 'string',
181
- 'Boolean',
182
- 'boolean',
183
- 'Undefined',
184
- 'undefined',
185
- ],
186
- 'id-match': 'error',
187
- 'import/no-anonymous-default-export': [
188
- 'error',
189
- {
190
- allowAnonymousClass: false,
191
- allowAnonymousFunction: false,
192
- allowArray: false,
193
- allowArrowFunction: true,
194
- allowCallExpression: true,
195
- // The true value here is for backward compatibility
196
- allowLiteral: false,
197
- allowObject: true,
198
- },
199
- ],
200
- 'import/no-extraneous-dependencies': [
201
- 'error',
202
- {
203
- devDependencies: true,
204
- },
205
- ],
206
- 'jest/no-disabled-tests': 'warn',
207
- 'jest/no-focused-tests': 'error',
208
- 'jest/no-identical-title': 'error',
209
- 'jest/valid-expect': 'error',
210
- 'max-classes-per-file': ['error', 1],
211
- 'no-bitwise': 'error',
212
- 'no-caller': 'error',
213
- 'no-cond-assign': 'error',
214
- 'no-console': 'error',
215
- 'no-debugger': 'error',
216
- 'no-duplicate-case': 'error',
217
- 'no-duplicate-imports': 'error',
218
- 'no-empty': 'error',
219
- 'no-eval': 'error',
220
- 'no-extra-bind': 'error',
221
- 'no-multiple-empty-lines': 'error',
222
- 'no-new-func': 'error',
223
- 'no-new-wrappers': 'error',
224
- 'no-param-reassign': 'error',
225
- 'no-redeclare': 'error',
226
- 'no-sequences': 'error',
227
- 'no-sparse-arrays': 'error',
228
- 'no-template-curly-in-string': 'error',
229
- 'no-throw-literal': 'error',
230
- 'no-trailing-spaces': 'error',
231
- 'no-undef-init': 'error',
232
- 'no-unsafe-finally': 'error',
233
- 'no-unused-labels': 'error',
234
- 'no-var': 'error',
235
- 'object-shorthand': 'error',
236
- 'one-var': ['error', 'never'],
237
- 'prefer-arrow/prefer-arrow-functions': 'warn',
238
- 'prefer-const': 'error',
239
- 'prefer-object-spread': 'error',
240
- 'prefer-template': 'error',
241
- radix: 'error',
52
+ // react-hooks override (recommended-latest has exhaustive-deps as 'warn')
53
+ 'react-hooks/exhaustive-deps': 'error',
242
54
  'react/jsx-curly-brace-presence': [
243
55
  'error',
244
56
  {
@@ -257,103 +69,14 @@ export default [
257
69
  'react/no-unknown-property': ['error', { ignore: ['css'] }],
258
70
  'react/prop-types': 'off',
259
71
  'react/react-in-jsx-scope': 'off',
260
- 'simple-import-sort/exports': 'error',
261
- 'simple-import-sort/imports': 'error',
262
- 'sort-keys': 'error',
263
- 'unicorn/better-regex': 'off',
264
- 'unicorn/consistent-existence-index-check': 'off',
265
- 'unicorn/expiring-todo-comments': 'off',
266
- 'unicorn/filename-case': [
267
- 'warn',
268
- {
269
- cases: {
270
- camelCase: true,
271
- pascalCase: true,
272
- },
273
- },
274
- ],
275
- 'unicorn/import-style': 'warn',
276
- 'unicorn/no-anonymous-default-export': 'off',
277
- 'unicorn/no-array-reduce': 'off',
278
- // .reverse() mutates in place — semantic + perf cost to force .toReversed()
279
- 'unicorn/no-array-reverse': 'off',
280
- // .sort() mutates in place — same story as no-array-reverse
281
- 'unicorn/no-array-sort': 'off',
282
- 'unicorn/no-await-expression-member': 'warn',
283
- 'unicorn/no-console-spaces': 'off',
284
- 'unicorn/no-document-cookie': 'warn',
285
- 'unicorn/no-empty-file': 'off',
286
- 'unicorn/no-hex-escape': 'warn',
287
- 'unicorn/no-invalid-remove-event-listener': 'warn',
288
- 'unicorn/no-named-default': 'warn',
289
- 'unicorn/no-nested-ternary': 'off',
290
- // because we have a lot of logic depends on null
291
- 'unicorn/no-null': 'warn',
292
- 'unicorn/no-object-as-default-parameter': 'warn',
293
- 'unicorn/no-single-promise-in-promise-methods': 'warn',
294
- 'unicorn/no-unreadable-array-destructuring': 'off',
295
- 'unicorn/no-unused-properties': 'warn',
296
- 'unicorn/no-zero-fractions': 'off',
297
- 'unicorn/prefer-class-fields': 'warn',
298
- 'unicorn/prefer-export-from': 'off',
299
- 'unicorn/prefer-global-this': 'warn',
300
- 'unicorn/prefer-includes': 'off',
301
- 'unicorn/prefer-math-min-max': 'warn',
302
- 'unicorn/prefer-module': 'warn',
303
- 'unicorn/prefer-node-protocol': 'off',
304
- 'unicorn/prefer-optional-catch-binding': 'off',
305
- 'unicorn/prefer-prototype-methods': 'off',
306
- 'unicorn/prefer-query-selector': 'off',
307
- 'unicorn/prefer-regexp-test': 'off',
308
- 'unicorn/prefer-set-has': 'off',
309
- 'unicorn/prefer-set-size': 'off',
310
- // opinionated condition ordering — too noisy
311
- 'unicorn/prefer-simple-condition-first': 'off',
312
- 'unicorn/prefer-string-raw': 'off',
313
- 'unicorn/prefer-string-replace-all': 'off',
314
- 'unicorn/prefer-structured-clone': 'warn',
315
- 'unicorn/prevent-abbreviations': [
316
- 'error',
317
- {
318
- allowList: {
319
- params: true,
320
- props: true,
321
- utils: true,
322
- },
323
- replacements: {
324
- params: {
325
- properties: false,
326
- },
327
- props: {
328
- properties: false,
329
- },
330
- utils: {
331
- utilities: false,
332
- },
333
- },
334
- },
335
- ],
336
- 'unicorn/relative-url-style': 'off',
337
- 'unicorn/require-module-attributes': 'warn',
338
- 'unicorn/switch-case-braces': 'off',
339
- 'unicorn/template-indent': 'off',
340
- 'use-isnan': 'error',
341
72
  },
342
- },
343
- {
344
- // because of jest
345
- files: ['**/*.test.ts'],
346
- rules: {
347
- '@typescript-eslint/explicit-function-return-type': 'off',
348
- '@typescript-eslint/no-empty-function': 'off',
349
- '@typescript-eslint/no-explicit-any': 'off',
350
- '@typescript-eslint/no-misused-promises': 'off',
351
- '@typescript-eslint/no-unsafe-assignment': 'off',
352
- '@typescript-eslint/no-unsafe-return': 'off',
353
- '@typescript-eslint/require-await': 'off',
354
- 'unicorn/no-array-callback-reference': 'off',
355
- 'unicorn/no-await-expression-member': 'off',
73
+ settings: {
74
+ ...baseSettings,
75
+ react: { version: 'detect' },
356
76
  },
357
77
  },
78
+ baseTestOverrides,
358
79
  prettierConfig,
359
80
  ]
81
+
82
+ export default config
package/node.js CHANGED
@@ -1,16 +1,19 @@
1
- import stylisticPlugin from '@stylistic/eslint-plugin'
2
1
  import tsPlugin from '@typescript-eslint/eslint-plugin'
3
- import globals from 'globals'
2
+ import vitestPlugin from '@vitest/eslint-plugin'
4
3
  import importPlugin from 'eslint-plugin-import'
5
- import jestPlugin from 'eslint-plugin-jest'
6
- import perfectionistPlugin from 'eslint-plugin-perfectionist'
7
- import preferArrowPlugin from 'eslint-plugin-prefer-arrow'
8
4
  import prettierConfig from 'eslint-plugin-prettier/recommended'
9
- import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort'
10
5
  import unicornPlugin from 'eslint-plugin-unicorn'
11
- import vitestPlugin from '@vitest/eslint-plugin'
12
6
 
13
- export default [
7
+ import {
8
+ baseGlobals,
9
+ baseParserOptions,
10
+ basePlugins,
11
+ baseRules,
12
+ baseSettings,
13
+ baseTestOverrides,
14
+ } from './base.js'
15
+
16
+ const config = [
14
17
  ...tsPlugin.configs['flat/recommended-type-checked'],
15
18
  ...tsPlugin.configs['flat/stylistic-type-checked'],
16
19
  unicornPlugin.configs['flat/recommended'],
@@ -19,303 +22,19 @@ export default [
19
22
  vitestPlugin.configs.recommended,
20
23
  {
21
24
  languageOptions: {
22
- globals: {
23
- ...globals.node,
24
- ...jestPlugin.environments.globals.globals,
25
- },
26
- parserOptions: {
27
- ecmaVersion: 2021,
28
- project: 'tsconfig.json',
29
- sourceType: 'module',
30
- warnOnUnsupportedTypeScriptVersion: false,
31
- },
32
- },
33
- plugins: {
34
- '@stylistic': stylisticPlugin,
35
- jest: jestPlugin,
36
- perfectionist: perfectionistPlugin,
37
- 'prefer-arrow': preferArrowPlugin,
38
- 'simple-import-sort': simpleImportSortPlugin,
39
- },
40
- settings: {
41
- 'import/parsers': {
42
- '@typescript-eslint/parser': ['.ts', '.mts', '.cts', '.tsx', '.d.ts'],
43
- },
44
- 'import/resolver': {
45
- node: { extensions: ['.js', '.jsx', '.ts', '.tsx'] },
46
- typescript: { alwaysTryTypes: true },
47
- },
25
+ globals: baseGlobals,
26
+ parserOptions: baseParserOptions,
48
27
  },
28
+ plugins: basePlugins,
49
29
  rules: {
50
- // perfectionist — replaces eslint-plugin-typescript-sort-keys
51
- // (string-enum equivalent: sort-enums with default sortByValue: 'ifNumericEnum')
52
- 'perfectionist/sort-enums': 'error',
53
- 'perfectionist/sort-heritage-clauses': 'error',
54
- 'perfectionist/sort-interfaces': 'error',
55
- 'perfectionist/sort-object-types': 'error',
56
- '@stylistic/padding-line-between-statements': [
57
- 'error',
58
- {
59
- blankLine: 'always',
60
- prev: '*',
61
- next: 'return',
62
- },
63
- ],
64
- '@stylistic/spaced-comment': [
65
- 'error',
66
- 'always',
67
- {
68
- markers: ['/'],
69
- },
70
- ],
71
- '@typescript-eslint/adjacent-overload-signatures': 'error',
72
- '@typescript-eslint/array-type': [
73
- 'error',
74
- {
75
- default: 'array',
76
- },
77
- ],
78
- '@typescript-eslint/consistent-type-assertions': 'error',
79
- '@typescript-eslint/consistent-type-definitions': 'off',
30
+ ...baseRules,
80
31
  '@typescript-eslint/dot-notation': 'error',
81
- '@typescript-eslint/explicit-function-return-type': 'off',
82
- '@typescript-eslint/explicit-module-boundary-types': [
83
- 'warn', // TODO: strict later
84
- {
85
- allowArgumentsExplicitlyTypedAsAny: true,
86
- allowDirectConstAssertionInArrowFunctions: true,
87
- allowHigherOrderFunctions: false,
88
- allowTypedFunctionExpressions: false,
89
- },
90
- ],
91
- '@typescript-eslint/naming-convention': [
92
- 'error',
93
- {
94
- custom: {
95
- match: true,
96
- regex: '^I[A-Z]',
97
- },
98
- format: ['PascalCase'],
99
- selector: 'interface',
100
- },
101
- ],
102
- '@typescript-eslint/no-base-to-string': 'warn',
103
- '@typescript-eslint/no-dynamic-delete': 'error',
104
- // need for tests' mockups
105
- '@typescript-eslint/no-empty-function': 'warn',
106
- '@typescript-eslint/no-empty-object-type': 'error',
107
- '@typescript-eslint/no-explicit-any': 'warn',
108
- // turn on later
109
- '@typescript-eslint/no-floating-promises': 'warn',
110
- '@typescript-eslint/no-for-in-array': 'error',
111
- '@typescript-eslint/no-inferrable-types': 'error',
112
- '@typescript-eslint/no-misused-new': 'error',
113
- '@typescript-eslint/no-namespace': 'error',
114
- '@typescript-eslint/no-redundant-type-constituents': 'warn',
115
- '@typescript-eslint/no-require-imports': 'error',
116
- '@typescript-eslint/no-shadow': [
117
- 'error',
118
- {
119
- hoist: 'all',
120
- },
121
- ],
122
- '@typescript-eslint/no-this-alias': 'error',
123
- '@typescript-eslint/no-unsafe-argument': 'warn',
124
- '@typescript-eslint/no-unsafe-assignment': 'warn',
125
- '@typescript-eslint/no-unsafe-call': 'warn',
126
- '@typescript-eslint/no-unsafe-enum-comparison': 'off',
127
- '@typescript-eslint/no-unsafe-member-access': 'off',
128
- '@typescript-eslint/no-unsafe-return': 'warn',
129
- '@typescript-eslint/no-unused-expressions': 'error',
130
32
  '@typescript-eslint/no-unused-vars': 'error',
131
- '@typescript-eslint/no-use-before-define': 'error',
132
- '@typescript-eslint/prefer-for-of': 'error',
133
- '@typescript-eslint/prefer-function-type': 'error',
134
- '@typescript-eslint/prefer-namespace-keyword': 'error',
135
- '@typescript-eslint/prefer-nullish-coalescing': 'off',
136
- '@typescript-eslint/prefer-promise-reject-errors': 'warn',
137
- '@typescript-eslint/restrict-template-expressions': 'warn',
138
- '@typescript-eslint/triple-slash-reference': [
139
- 'error',
140
- {
141
- path: 'always',
142
- types: 'prefer-import',
143
- lib: 'always',
144
- },
145
- ],
146
- '@typescript-eslint/unified-signatures': 'error',
147
- 'arrow-body-style': ['error', 'as-needed'],
148
- complexity: [
149
- 'error',
150
- {
151
- max: 15,
152
- },
153
- ],
154
- 'constructor-super': 'error',
155
- eqeqeq: ['error', 'smart'],
156
- 'guard-for-in': 'error',
157
- 'id-denylist': [
158
- 'error',
159
- 'any',
160
- 'Number',
161
- 'number',
162
- 'String',
163
- 'string',
164
- 'Boolean',
165
- 'boolean',
166
- 'Undefined',
167
- 'undefined',
168
- ],
169
- 'id-match': 'error',
170
- 'import/no-anonymous-default-export': [
171
- 'error',
172
- {
173
- allowAnonymousClass: false,
174
- allowAnonymousFunction: false,
175
- allowArray: false,
176
- allowArrowFunction: true,
177
- allowCallExpression: true,
178
- // The true value here is for backward compatibility
179
- allowLiteral: false,
180
- allowObject: true,
181
- },
182
- ],
183
- 'import/no-extraneous-dependencies': [
184
- 'error',
185
- {
186
- devDependencies: true,
187
- },
188
- ],
189
- 'jest/no-disabled-tests': 'warn',
190
- 'jest/no-focused-tests': 'error',
191
- 'jest/no-identical-title': 'error',
192
- 'jest/valid-expect': 'error',
193
- 'max-classes-per-file': ['error', 1],
194
- 'no-bitwise': 'error',
195
- 'no-caller': 'error',
196
- 'no-cond-assign': 'error',
197
- 'no-console': 'error',
198
- 'no-debugger': 'error',
199
- 'no-duplicate-case': 'error',
200
- 'no-duplicate-imports': 'error',
201
- 'no-empty': 'error',
202
- 'no-eval': 'error',
203
- 'no-extra-bind': 'error',
204
- 'no-multiple-empty-lines': 'error',
205
- 'no-new-func': 'error',
206
- 'no-new-wrappers': 'error',
207
- 'no-param-reassign': 'error',
208
- 'no-redeclare': 'error',
209
- 'no-sequences': 'error',
210
- 'no-sparse-arrays': 'error',
211
- 'no-template-curly-in-string': 'error',
212
- 'no-throw-literal': 'error',
213
- 'no-trailing-spaces': 'error',
214
- 'no-undef-init': 'error',
215
- 'no-unsafe-finally': 'error',
216
- 'no-unused-labels': 'error',
217
- 'no-var': 'error',
218
- 'object-shorthand': 'error',
219
- 'one-var': ['error', 'never'],
220
- 'prefer-arrow/prefer-arrow-functions': 'warn',
221
- 'prefer-const': 'error',
222
- 'prefer-object-spread': 'error',
223
- 'prefer-template': 'error',
224
- radix: 'error',
225
- 'simple-import-sort/exports': 'error',
226
- 'simple-import-sort/imports': 'error',
227
- 'sort-keys': 'error',
228
- 'unicorn/better-regex': 'off',
229
- 'unicorn/consistent-existence-index-check': 'off',
230
- 'unicorn/expiring-todo-comments': 'off',
231
- 'unicorn/filename-case': [
232
- 'warn',
233
- {
234
- cases: {
235
- camelCase: true,
236
- pascalCase: true,
237
- },
238
- },
239
- ],
240
- 'unicorn/import-style': 'warn',
241
- 'unicorn/no-anonymous-default-export': 'off',
242
- 'unicorn/no-array-reduce': 'off',
243
- 'unicorn/no-array-reverse': 'off',
244
- 'unicorn/no-array-sort': 'off',
245
- 'unicorn/no-await-expression-member': 'warn',
246
- 'unicorn/no-console-spaces': 'off',
247
- 'unicorn/no-document-cookie': 'warn',
248
- 'unicorn/no-empty-file': 'off',
249
- 'unicorn/no-hex-escape': 'warn',
250
- 'unicorn/no-invalid-remove-event-listener': 'warn',
251
- 'unicorn/no-named-default': 'warn',
252
- 'unicorn/no-nested-ternary': 'off',
253
- // because we have a lot of logic depends on null
254
- 'unicorn/no-null': 'warn',
255
- 'unicorn/no-object-as-default-parameter': 'warn',
256
- 'unicorn/no-single-promise-in-promise-methods': 'warn',
257
- 'unicorn/no-unreadable-array-destructuring': 'off',
258
- 'unicorn/no-unused-properties': 'warn',
259
- 'unicorn/no-zero-fractions': 'off',
260
- 'unicorn/prefer-class-fields': 'warn',
261
- 'unicorn/prefer-export-from': 'off',
262
- 'unicorn/prefer-global-this': 'warn',
263
- 'unicorn/prefer-includes': 'off',
264
- 'unicorn/prefer-math-min-max': 'warn',
265
- 'unicorn/prefer-module': 'warn',
266
- 'unicorn/prefer-node-protocol': 'off',
267
- 'unicorn/prefer-optional-catch-binding': 'off',
268
- 'unicorn/prefer-prototype-methods': 'off',
269
- 'unicorn/prefer-query-selector': 'off',
270
- 'unicorn/prefer-regexp-test': 'off',
271
- 'unicorn/prefer-set-has': 'off',
272
- 'unicorn/prefer-set-size': 'off',
273
- 'unicorn/prefer-simple-condition-first': 'off',
274
- 'unicorn/prefer-string-raw': 'off',
275
- 'unicorn/prefer-string-replace-all': 'off',
276
- 'unicorn/prefer-structured-clone': 'warn',
277
- 'unicorn/prevent-abbreviations': [
278
- 'error',
279
- {
280
- allowList: {
281
- params: true,
282
- props: true,
283
- utils: true,
284
- },
285
- replacements: {
286
- params: {
287
- properties: false,
288
- },
289
- props: {
290
- properties: false,
291
- },
292
- utils: {
293
- utilities: false,
294
- },
295
- },
296
- },
297
- ],
298
- 'unicorn/relative-url-style': 'off',
299
- 'unicorn/require-module-attributes': 'warn',
300
- 'unicorn/switch-case-braces': 'off',
301
- 'unicorn/template-indent': 'off',
302
- 'use-isnan': 'error',
303
- },
304
- },
305
- {
306
- // because of jest
307
- files: ['**/*.test.ts'],
308
- rules: {
309
- '@typescript-eslint/explicit-function-return-type': 'off',
310
- '@typescript-eslint/no-empty-function': 'off',
311
- '@typescript-eslint/no-explicit-any': 'off',
312
- '@typescript-eslint/no-misused-promises': 'off',
313
- '@typescript-eslint/no-unsafe-assignment': 'off',
314
- '@typescript-eslint/no-unsafe-return': 'off',
315
- '@typescript-eslint/require-await': 'off',
316
- 'unicorn/no-array-callback-reference': 'off',
317
- 'unicorn/no-await-expression-member': 'off',
318
33
  },
34
+ settings: baseSettings,
319
35
  },
36
+ baseTestOverrides,
320
37
  prettierConfig,
321
38
  ]
39
+
40
+ export default config
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allthings/eslint-config",
3
- "version": "3.7.0",
3
+ "version": "3.8.0-alpha.1",
4
4
  "type": "module",
5
5
  "description": "ESlint shareable config for Allthings style",
6
6
  "main": "index.js",
@@ -10,15 +10,18 @@
10
10
  },
11
11
  "exports": {
12
12
  ".": "./index.js",
13
+ "./functional": "./functional.js",
13
14
  "./node": "./node.js"
14
15
  },
15
16
  "files": [
17
+ "base.js",
18
+ "functional.js",
16
19
  "index.js",
17
20
  "node.js"
18
21
  ],
19
22
  "packageManager": "yarn@4.14.1",
20
23
  "scripts": {
21
- "lint": "prettier --check .",
24
+ "lint": "prettier --check . && eslint .",
22
25
  "semantic-release": "semantic-release",
23
26
  "test": "yarn test:audit && yarn test:behavioral",
24
27
  "test:audit": "node test/audit.js",
@@ -34,6 +37,7 @@
34
37
  "eslint-config-prettier": "^10.1.8",
35
38
  "eslint-import-resolver-node": "^0.4.0",
36
39
  "eslint-import-resolver-typescript": "^4.4.4",
40
+ "eslint-plugin-functional": "^9.0.5",
37
41
  "eslint-plugin-import": "^2.32.0",
38
42
  "eslint-plugin-jest": "^29.0.0",
39
43
  "eslint-plugin-jsx-a11y": "^6.10.2",