@foray1010/eslint-config 14.0.0 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,23 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [15.0.0](https://github.com/foray1010/common-presets/compare/@foray1010/eslint-config@14.0.1...@foray1010/eslint-config@15.0.0) (2025-03-13)
7
+
8
+ ### ⚠ BREAKING CHANGES
9
+
10
+ - drop applyConfig utils
11
+ - require eslint ^9.22.0
12
+
13
+ ### Features
14
+
15
+ - migrate to eslint defineConfig utils ([faf0aa7](https://github.com/foray1010/common-presets/commit/faf0aa74048b2536e188a45b8371ebe416dcb851))
16
+
17
+ ## [14.0.1](https://github.com/foray1010/common-presets/compare/@foray1010/eslint-config@14.0.0...@foray1010/eslint-config@14.0.1) (2025-03-11)
18
+
19
+ ### Bug Fixes
20
+
21
+ - **eslint-config:** drop eslint-plugin-functional ([fdc506c](https://github.com/foray1010/common-presets/commit/fdc506cf24792dfb55a2c39e7f96a49cd76044af))
22
+
6
23
  ## [14.0.0](https://github.com/foray1010/common-presets/compare/@foray1010/eslint-config@13.0.3...@foray1010/eslint-config@14.0.0) (2025-03-11)
7
24
 
8
25
  ### ⚠ BREAKING CHANGES
package/README.md CHANGED
@@ -29,8 +29,9 @@ Z for looser rules
29
29
  eslintIgnoresConfig,
30
30
  eslintNodeConfig,
31
31
  } from '@foray1010/eslint-config'
32
+ import { defineConfig } from 'eslint/config'
32
33
 
33
- const config = [...eslintIgnoresConfig, ...eslintNodeConfig]
34
+ const config = defineConfig(eslintIgnoresConfig, eslintNodeConfig)
34
35
  export default config
35
36
  ```
36
37
 
@@ -41,8 +42,9 @@ Z for looser rules
41
42
  eslintIgnoresConfig,
42
43
  eslintBrowserConfig,
43
44
  } from '@foray1010/eslint-config'
45
+ import { defineConfig } from 'eslint/config'
44
46
 
45
- const config = [...eslintIgnoresConfig, ...eslintBrowserConfig]
47
+ const config = defineConfig(eslintIgnoresConfig, eslintBrowserConfig)
46
48
  export default config
47
49
  ```
48
50
 
@@ -53,8 +55,9 @@ Z for looser rules
53
55
  eslintIgnoresConfig,
54
56
  eslintReactConfig,
55
57
  } from '@foray1010/eslint-config'
58
+ import { defineConfig } from 'eslint/config'
56
59
 
57
- const config = [...eslintIgnoresConfig, ...eslintReactConfig]
60
+ const config = defineConfig(eslintIgnoresConfig, eslintReactConfig)
58
61
  export default config
59
62
  ```
60
63
 
@@ -62,28 +65,23 @@ Z for looser rules
62
65
 
63
66
  ```js
64
67
  import {
65
- applyConfig,
66
68
  eslintIgnoresConfig,
67
69
  eslintNodeConfig,
68
70
  eslintReactConfig,
69
71
  } from '@foray1010/eslint-config'
72
+ import { defineConfig } from 'eslint/config'
70
73
 
71
- const config = [
72
- ...eslintIgnoresConfig,
73
- ...applyConfig(
74
- {
75
- filePrefixes: '.',
76
- ignores: ['src/**'],
77
- },
78
- eslintNodeConfig,
79
- ),
80
- ...applyConfig(
81
- {
82
- filePrefixes: ['src'],
83
- },
84
- eslintReactConfig,
85
- ),
86
- ]
74
+ const config = defineConfig(
75
+ eslintIgnoresConfig,
76
+ {
77
+ ignores: ['src/**'],
78
+ extends: [eslintNodeConfig],
79
+ },
80
+ {
81
+ files: ['src/**'],
82
+ extends: [eslintReactConfig],
83
+ },
84
+ )
87
85
  export default config
88
86
  ```
89
87
 
package/bases/base.mjs CHANGED
@@ -1,6 +1,9 @@
1
1
  import js from '@eslint/js'
2
- import eslintPluginEslintComments from '@eslint-community/eslint-plugin-eslint-comments'
2
+ // eslint-disable-next-line import-x/extensions
3
+ import eslintPluginEslintCommentsConfigs from '@eslint-community/eslint-plugin-eslint-comments/configs'
3
4
  import { hasDep, isESM } from '@foray1010/common-presets-utils'
5
+ // eslint-disable-next-line import-x/extensions, import-x/no-unresolved
6
+ import { defineConfig } from 'eslint/config'
4
7
  import eslintPluginImportX from 'eslint-plugin-import-x'
5
8
  import eslintPluginJest from 'eslint-plugin-jest'
6
9
  import eslintPluginRegexp from 'eslint-plugin-regexp'
@@ -14,26 +17,20 @@ import {
14
17
  typeScriptTestFileGlobs,
15
18
  } from '../constants.mjs'
16
19
 
17
- /** @typedef {import('../types/internal.d.ts').EslintConfig} EslintConfig */
18
-
19
- /** @returns {Promise<EslintConfig>} */
20
20
  async function generateTypeScriptConfig() {
21
21
  // typescript plugins are depended on `typescript` package
22
- if (!hasDep('typescript')) return []
22
+ if (!hasDep('typescript')) return defineConfig({})
23
23
 
24
24
  // eslint-disable-next-line import-x/no-unresolved
25
25
  const tseslint = (await import('typescript-eslint')).default
26
- // eslint-disable-next-line import-x/no-unresolved
27
- const eslintPluginFunctional = (await import('eslint-plugin-functional'))
28
- .default
29
26
 
30
- return [
31
- // @ts-expect-error `Type 'Config' is not assignable to type 'Readonly<FlatConfig<RulesRecord>>' with 'exactOptionalPropertyTypes: true'`
32
- ...tseslint.config({
27
+ return defineConfig(
28
+ {
33
29
  files: typeScriptFileGlobs,
34
30
  extends: [
35
31
  tseslint.configs.eslintRecommended,
36
- ...tseslint.configs.recommendedTypeChecked,
32
+ tseslint.configs.recommendedTypeChecked,
33
+ eslintPluginImportX.configs['typescript'],
37
34
  esmConfig,
38
35
  ],
39
36
  languageOptions: {
@@ -45,16 +42,7 @@ async function generateTypeScriptConfig() {
45
42
  project: ['./tsconfig*.json', './packages/*/tsconfig*.json'],
46
43
  },
47
44
  },
48
- settings: {
49
- 'import-x/resolver': {
50
- typescript: true,
51
- },
52
- },
53
- plugins: {
54
- functional: eslintPluginFunctional,
55
- },
56
45
  rules: {
57
- ...eslintPluginImportX.configs['typescript']?.rules,
58
46
  // separate type exports which allow certain optimizations within compilers
59
47
  '@typescript-eslint/consistent-type-exports': [
60
48
  'error',
@@ -123,47 +111,6 @@ async function generateTypeScriptConfig() {
123
111
  '@typescript-eslint/switch-exhaustiveness-check': 'error',
124
112
  // ignore static function as those are not supposed to use `this`
125
113
  '@typescript-eslint/unbound-method': ['error', { ignoreStatic: true }],
126
- // use with functional/type-declaration-immutability
127
- 'functional/prefer-immutable-types': [
128
- 'error',
129
- {
130
- // as there is no native way to achieve `ReadonlyDeep` in TypeScript
131
- enforcement: 'ReadonlyShallow',
132
- // reduce the difficult to use this rule
133
- ignoreInferredTypes: true,
134
- // escape hatch without using eslint-disable
135
- ignoreNamePattern: /Mutable$/u.source,
136
- ignoreTypePattern: [
137
- /^React\./u.source, // Some React types does not work with `Readonly`
138
- ],
139
- },
140
- ],
141
- // forbid unnecessary callback wrapper
142
- 'functional/prefer-tacit': 'error',
143
- // use with functional/prefer-immutable-types
144
- 'functional/type-declaration-immutability': [
145
- 'error',
146
- {
147
- rules: [
148
- {
149
- identifiers: '.+',
150
- immutability: 'ReadonlyShallow',
151
- comparator: 'AtLeast',
152
- // modified from https://github.com/eslint-functional/eslint-plugin-functional/blob/main/docs/rules/type-declaration-immutability.md#preset-overrides
153
- fixer: [
154
- {
155
- pattern: /^(Array|Map|Set)<(.+)>$/u.source,
156
- replace: /Readonly\$1<\$2>/u.source,
157
- },
158
- {
159
- pattern: /^(.+)$/u.source,
160
- replace: /Readonly<\$1>/u.source,
161
- },
162
- ],
163
- },
164
- ],
165
- },
166
- ],
167
114
  'no-restricted-syntax': [
168
115
  'error',
169
116
  {
@@ -183,8 +130,7 @@ async function generateTypeScriptConfig() {
183
130
  // It is disabled in recommended config but re-enabled here to enforce a subset of global variables that supported by both node.js and browsers
184
131
  'no-undef': 'error',
185
132
  },
186
- }),
187
- // @ts-expect-error As previous item's type is not correct, this item is affected too
133
+ },
188
134
  {
189
135
  files: typeScriptTestFileGlobs,
190
136
  rules: {
@@ -196,11 +142,10 @@ async function generateTypeScriptConfig() {
196
142
  'jest/unbound-method': ['error', { ignoreStatic: true }],
197
143
  },
198
144
  },
199
- ]
145
+ )
200
146
  }
201
147
 
202
- /** @type {EslintConfig[number]} */
203
- const cjsConfig = {
148
+ const cjsConfig = defineConfig({
204
149
  languageOptions: {
205
150
  globals: globals.commonjs,
206
151
  sourceType: 'script',
@@ -209,10 +154,9 @@ const cjsConfig = {
209
154
  // commonjs must use strict mode
210
155
  strict: ['error', 'global'],
211
156
  },
212
- }
157
+ })
213
158
 
214
- /** @type {EslintConfig[number]} */
215
- const esmConfig = {
159
+ const esmConfig = defineConfig({
216
160
  languageOptions: {
217
161
  sourceType: 'module',
218
162
  },
@@ -225,16 +169,25 @@ const esmConfig = {
225
169
  // auto sort import statements
226
170
  'simple-import-sort/imports': 'error',
227
171
  },
228
- }
172
+ })
229
173
 
230
- /** @type {EslintConfig} */
231
- const baseConfig = [
174
+ const baseConfig = defineConfig(
232
175
  js.configs.recommended,
233
- eslintPluginRegexp.configs['flat/recommended'],
234
176
  {
235
- ...eslintPluginImportX.flatConfigs.recommended,
177
+ extends: [eslintPluginEslintCommentsConfigs['recommended']],
178
+ rules: {
179
+ // allow disable eslint rules for whole file without re-enable it in the end of the file
180
+ '@eslint-community/eslint-comments/disable-enable-pair': [
181
+ 'error',
182
+ { allowWholeFile: true },
183
+ ],
184
+ // make sure every eslint-disable comments are in use
185
+ '@eslint-community/eslint-comments/no-unused-disable': 'error',
186
+ },
187
+ },
188
+ {
189
+ extends: [eslintPluginImportX.flatConfigs.recommended],
236
190
  rules: {
237
- ...eslintPluginImportX.flatConfigs.recommended.rules,
238
191
  // this rule doesn't support commonjs, some dependencies are using commonjs
239
192
  'import-x/default': 'off',
240
193
  // Does not work after upgrading to eslint-plugin-import-x v4, got this error message: `sourceType 'module' is not supported when ecmaVersion < 2015. Consider adding `{ ecmaVersion: 2015 }` to the parser options. (undefined:undefined)`
@@ -309,27 +262,17 @@ const baseConfig = [
309
262
  },
310
263
  },
311
264
  {
312
- linterOptions: {
313
- reportUnusedDisableDirectives: 'error',
314
- reportUnusedInlineConfigs: 'error',
315
- },
316
- languageOptions: {
317
- ecmaVersion: 2023,
318
- globals: {
319
- ...globals.es2023,
320
- /* Not using `node` to explicitly import node.js only built-in modules, e.g.
321
- * import { Buffer } from 'node:buffer'
322
- * import process from 'node:process'
323
- */
324
- ...globals['shared-node-browser'],
325
- },
265
+ extends: [eslintPluginRegexp.configs['flat/recommended']],
266
+ rules: {
267
+ // enable regexp strict mode (use `v` flag instead when it is widely supported)
268
+ 'regexp/require-unicode-regexp': 'error',
326
269
  },
270
+ },
271
+ {
327
272
  plugins: {
328
- '@eslint-community/eslint-comments': eslintPluginEslintComments,
329
273
  unicorn: eslintPluginUnicorn,
330
274
  },
331
275
  rules: {
332
- ...eslintPluginEslintComments.configs['recommended']?.rules,
333
276
  ...Object.fromEntries(
334
277
  Object.entries(
335
278
  eslintPluginUnicorn.configs['flat/recommended']?.rules ?? {},
@@ -341,13 +284,40 @@ const baseConfig = [
341
284
  )
342
285
  }),
343
286
  ),
344
- // allow disable eslint rules for whole file without re-enable it in the end of the file
345
- '@eslint-community/eslint-comments/disable-enable-pair': [
346
- 'error',
347
- { allowWholeFile: true },
348
- ],
349
- // make sure every eslint-disable comments are in use
350
- '@eslint-community/eslint-comments/no-unused-disable': 'error',
287
+ // use with `unicorn/throw-new-error`
288
+ // disallow builtins to be created without `new` operator, to be consistent with es6 class syntax
289
+ 'unicorn/new-for-builtins': 'error',
290
+ // some legacy projects still use commonjs
291
+ 'unicorn/prefer-module': 'off',
292
+ // `querySelector` is slower than `getElementById`
293
+ 'unicorn/prefer-query-selector': 'off',
294
+ // `Array.from(iterable)` is more readable than `[...iterable]`
295
+ 'unicorn/prefer-spread': 'off',
296
+ // sometimes it is less readable using ternary expressions
297
+ 'unicorn/prefer-ternary': 'off',
298
+ // webpack support on `top level await` is still experimental, and some legacy projects still use commonjs
299
+ 'unicorn/prefer-top-level-await': 'off',
300
+ // use with `unicorn/new-for-builtins`
301
+ 'unicorn/throw-new-error': 'error',
302
+ },
303
+ },
304
+ {
305
+ linterOptions: {
306
+ reportUnusedDisableDirectives: 'error',
307
+ reportUnusedInlineConfigs: 'error',
308
+ },
309
+ languageOptions: {
310
+ ecmaVersion: 2023,
311
+ globals: {
312
+ ...globals.es2023,
313
+ /* Not using `node` to explicitly import node.js only built-in modules, e.g.
314
+ * import { Buffer } from 'node:buffer'
315
+ * import process from 'node:process'
316
+ */
317
+ ...globals['shared-node-browser'],
318
+ },
319
+ },
320
+ rules: {
351
321
  // always use named function for easier to debug via stack trace
352
322
  'func-names': ['error', 'as-needed'],
353
323
  // prefer explicitly convert type for readability
@@ -364,49 +334,31 @@ const baseConfig = [
364
334
  destructuring: 'all',
365
335
  },
366
336
  ],
367
- // enable regexp strict mode (use `v` flag instead when it is widely supported)
368
- 'regexp/require-unicode-regexp': 'error',
369
- // use with `unicorn/throw-new-error`
370
- // disallow builtins to be created without `new` operator, to be consistent with es6 class syntax
371
- 'unicorn/new-for-builtins': 'error',
372
- // some legacy projects still use commonjs
373
- 'unicorn/prefer-module': 'off',
374
- // `querySelector` is slower than `getElementById`
375
- 'unicorn/prefer-query-selector': 'off',
376
- // `Array.from(iterable)` is more readable than `[...iterable]`
377
- 'unicorn/prefer-spread': 'off',
378
- // sometimes it is less readable using ternary expressions
379
- 'unicorn/prefer-ternary': 'off',
380
- // webpack support on `top level await` is still experimental, and some legacy projects still use commonjs
381
- 'unicorn/prefer-top-level-await': 'off',
382
- // use with `unicorn/new-for-builtins`
383
- 'unicorn/throw-new-error': 'error',
384
337
  },
385
338
  },
386
339
  {
387
340
  files: ['**/*.js'],
388
- ...(isESM() ? esmConfig : cjsConfig),
341
+ extends: [isESM() ? esmConfig : cjsConfig],
389
342
  },
390
343
  {
391
344
  files: ['**/*.cjs'],
392
- ...cjsConfig,
345
+ extends: [cjsConfig],
393
346
  },
394
347
  {
395
348
  files: ['**/*.mjs'],
396
- ...esmConfig,
397
- },
398
- {
399
- files: testFileGlobs,
400
- ...eslintPluginJest.configs['flat/recommended'],
401
- ...eslintPluginJest.configs['flat/style'],
349
+ extends: [esmConfig],
402
350
  },
403
351
  {
404
352
  files: testFileGlobs,
353
+ extends: [
354
+ eslintPluginJest.configs['flat/recommended'],
355
+ eslintPluginJest.configs['flat/style'],
356
+ ],
405
357
  rules: {
406
358
  // make sure lifecycle hooks on the top for readability
407
359
  'jest/prefer-hooks-on-top': 'error',
408
360
  },
409
361
  },
410
- ...(await generateTypeScriptConfig()),
411
- ]
362
+ await generateTypeScriptConfig(),
363
+ )
412
364
  export default baseConfig
package/bases/browser.mjs CHANGED
@@ -1,30 +1,26 @@
1
1
  import { hasDep } from '@foray1010/common-presets-utils'
2
2
  import restrictedGlobals from 'confusing-browser-globals'
3
+ // eslint-disable-next-line import-x/extensions, import-x/no-unresolved
4
+ import { defineConfig } from 'eslint/config'
3
5
  import eslintPluginCompat from 'eslint-plugin-compat'
4
6
  import eslintPluginTestingLibrary from 'eslint-plugin-testing-library'
5
7
  import globals from 'globals'
6
8
 
7
9
  import { testFileGlobs } from '../constants.mjs'
8
10
 
9
- /** @typedef {import('../types/internal.d.ts').EslintConfig} EslintConfig */
10
-
11
- /** @returns {Promise<EslintConfig>} */
12
11
  async function generateJestDomConfig() {
13
12
  // `eslint-plugin-jest-dom` depends on `@testing-library/dom` package
14
- if (!hasDep('@testing-library/dom')) return []
13
+ if (!hasDep('@testing-library/dom')) return defineConfig({})
15
14
 
16
15
  const eslintPluginJestDom = (await import('eslint-plugin-jest-dom')).default
17
16
 
18
- return [
19
- {
20
- files: testFileGlobs,
21
- ...eslintPluginJestDom.configs['flat/recommended'],
22
- },
23
- ]
17
+ return defineConfig({
18
+ files: testFileGlobs,
19
+ extends: [eslintPluginJestDom.configs['flat/recommended']],
20
+ })
24
21
  }
25
22
 
26
- /** @type {EslintConfig} */
27
- const browserConfig = [
23
+ const browserConfig = defineConfig(
28
24
  {
29
25
  languageOptions: {
30
26
  globals: {
@@ -44,22 +40,14 @@ const browserConfig = [
44
40
  'no-restricted-globals': ['error', ...restrictedGlobals],
45
41
  },
46
42
  },
47
- ...(await generateJestDomConfig()),
48
- {
49
- files: testFileGlobs,
50
- plugins: {
51
- 'testing-library': eslintPluginTestingLibrary,
52
- },
53
- rules: {
54
- ...eslintPluginTestingLibrary.configs['flat/dom']?.rules,
55
- },
56
- },
43
+ await generateJestDomConfig(),
57
44
  {
58
45
  files: testFileGlobs,
46
+ extends: [eslintPluginTestingLibrary.configs['flat/dom']],
59
47
  rules: {
60
48
  // allow to use nodejs modules in tests
61
49
  'import-x/no-nodejs-modules': 'off',
62
50
  },
63
51
  },
64
- ]
52
+ )
65
53
  export default browserConfig
package/bases/ignores.mjs CHANGED
@@ -1,16 +1,13 @@
1
- /** @typedef {import('../types/internal.d.ts').EslintConfig} EslintConfig */
1
+ // eslint-disable-next-line import-x/extensions, import-x/no-unresolved
2
+ import { defineConfig, globalIgnores } from 'eslint/config'
2
3
 
3
- /** @type {EslintConfig} */
4
- const ignoresConfig = [
5
- {
6
- // replace `.eslintignore`
7
- ignores: [
8
- '**/.yarn/**',
9
- '**/build/**',
10
- '**/coverage/**',
11
- '**/dist/**',
12
- '**/node_modules/**',
13
- ],
14
- },
15
- ]
4
+ const ignoresConfig = defineConfig(
5
+ globalIgnores([
6
+ '**/.yarn/**',
7
+ '**/build/**',
8
+ '**/coverage/**',
9
+ '**/dist/**',
10
+ '**/node_modules/**',
11
+ ]),
12
+ )
16
13
  export default ignoresConfig
package/bases/node.mjs CHANGED
@@ -1,20 +1,18 @@
1
1
  import { isESM } from '@foray1010/common-presets-utils'
2
+ // eslint-disable-next-line import-x/extensions, import-x/no-unresolved
3
+ import { defineConfig } from 'eslint/config'
2
4
  import eslintPluginN from 'eslint-plugin-n'
3
5
 
4
- /** @typedef {import('../types/internal.d.ts').EslintConfig} EslintConfig */
5
-
6
- /** @type {EslintConfig[number]} */
7
- const cjsConfig = {
6
+ const cjsConfig = defineConfig({
8
7
  languageOptions: {
9
8
  globals: {
10
9
  __dirname: 'readonly',
11
10
  __filename: 'readonly',
12
11
  },
13
12
  },
14
- }
13
+ })
15
14
 
16
- /** @type {EslintConfig} */
17
- const nodeConfig = [
15
+ const nodeConfig = defineConfig(
18
16
  {
19
17
  plugins: {
20
18
  n: eslintPluginN,
@@ -67,12 +65,12 @@ const nodeConfig = [
67
65
  : [
68
66
  {
69
67
  files: ['**/*.js'],
70
- ...cjsConfig,
68
+ extends: [cjsConfig],
71
69
  },
72
70
  ]),
73
71
  {
74
72
  files: ['**/*.{cjs,cts}'],
75
- ...cjsConfig,
73
+ extends: [cjsConfig],
76
74
  },
77
- ]
75
+ )
78
76
  export default nodeConfig
@@ -1,12 +1,11 @@
1
+ // eslint-disable-next-line import-x/extensions, import-x/no-unresolved
2
+ import { defineConfig } from 'eslint/config'
1
3
  // eslint-disable-next-line import-x/extensions
2
4
  import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
3
5
 
4
- /** @typedef {import('../types/internal.d.ts').EslintConfig} EslintConfig */
5
-
6
- /** @type {EslintConfig} */
7
6
  // should be placed at the end to override other configs
8
- const prettierConfig = [
7
+ const prettierConfig = defineConfig(
9
8
  // This includes `eslint-config-prettier` as peer dependency
10
9
  eslintPluginPrettierRecommended,
11
- ]
10
+ )
12
11
  export default prettierConfig
package/bases/react.mjs CHANGED
@@ -1,26 +1,24 @@
1
+ // eslint-disable-next-line import-x/extensions, import-x/no-unresolved
2
+ import { defineConfig } from 'eslint/config'
1
3
  import eslintPluginReact from 'eslint-plugin-react'
2
4
  import eslintPluginReactHooks from 'eslint-plugin-react-hooks'
3
5
  import eslintPluginTestingLibrary from 'eslint-plugin-testing-library'
4
6
 
5
7
  import { testFileGlobs } from '../constants.mjs'
6
8
 
7
- /** @typedef {import('../types/internal.d.ts').EslintConfig} EslintConfig */
8
-
9
- /** @type {EslintConfig} */
10
- const reactConfig = [
11
- eslintPluginReact.configs.flat.recommended,
12
- eslintPluginReact.configs.flat['jsx-runtime'],
9
+ const reactConfig = defineConfig(
13
10
  {
14
11
  settings: {
15
12
  react: {
16
13
  version: 'detect',
17
14
  },
18
15
  },
19
- plugins: {
20
- 'react-hooks': eslintPluginReactHooks,
21
- },
16
+ extends: [
17
+ eslintPluginReact.configs.flat.recommended,
18
+ eslintPluginReact.configs.flat['jsx-runtime'],
19
+ eslintPluginReactHooks.configs['recommended-latest'],
20
+ ],
22
21
  rules: {
23
- ...eslintPluginReactHooks.configs['recommended']?.rules,
24
22
  // avoid unexpected form submits
25
23
  'react/button-has-type': 'error',
26
24
  'react/jsx-no-useless-fragment': [
@@ -61,13 +59,7 @@ const reactConfig = [
61
59
  },
62
60
  {
63
61
  files: testFileGlobs,
64
- rules: {
65
- // Do not use flat config directly as eslint v9 does not support duplicated plugins (already defined in browser.mjs)
66
- ...eslintPluginTestingLibrary.configs['flat/react']?.rules,
67
- },
68
- },
69
- {
70
- files: testFileGlobs,
62
+ extends: [eslintPluginTestingLibrary.configs['flat/react']],
71
63
  rules: {
72
64
  // avoid using unnecessary `await` as workaround for `not wrapped in act(...)` warnings
73
65
  'testing-library/no-await-sync-events': [
@@ -82,5 +74,5 @@ const reactConfig = [
82
74
  'testing-library/prefer-user-event': 'error',
83
75
  },
84
76
  },
85
- ]
77
+ )
86
78
  export default reactConfig
package/index.mjs CHANGED
@@ -1,33 +1,29 @@
1
+ // eslint-disable-next-line import-x/extensions, import-x/no-unresolved
2
+ import { defineConfig } from 'eslint/config'
3
+
1
4
  import baseConfig from './bases/base.mjs'
2
5
  import browserConfig from './bases/browser.mjs'
3
6
  import nodeConfig from './bases/node.mjs'
4
7
  import prettierConfig from './bases/prettier.mjs'
5
8
  import reactConfig from './bases/react.mjs'
6
9
 
7
- export * from './utils/applyConfig.mjs'
8
-
9
- /** @typedef {import('./types/internal.d.ts').EslintConfig} EslintConfig */
10
-
11
- /** @type {EslintConfig} */
12
- export const eslintBrowserConfig = [
13
- ...baseConfig,
14
- ...browserConfig,
15
- ...prettierConfig,
16
- ]
10
+ export const eslintBrowserConfig = defineConfig(
11
+ baseConfig,
12
+ browserConfig,
13
+ prettierConfig,
14
+ )
17
15
 
18
- /** @type {EslintConfig} */
19
- export const eslintNodeConfig = [
20
- ...baseConfig,
21
- ...nodeConfig,
22
- ...prettierConfig,
23
- ]
16
+ export const eslintNodeConfig = defineConfig(
17
+ baseConfig,
18
+ nodeConfig,
19
+ prettierConfig,
20
+ )
24
21
 
25
- /** @type {EslintConfig} */
26
- export const eslintReactConfig = [
27
- ...baseConfig,
28
- ...browserConfig,
29
- ...reactConfig,
30
- ...prettierConfig,
31
- ]
22
+ export const eslintReactConfig = defineConfig(
23
+ baseConfig,
24
+ browserConfig,
25
+ reactConfig,
26
+ prettierConfig,
27
+ )
32
28
 
33
29
  export { default as eslintIgnoresConfig } from './bases/ignores.mjs'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@foray1010/eslint-config",
4
- "version": "14.0.0",
4
+ "version": "15.0.0",
5
5
  "homepage": "https://github.com/foray1010/common-presets/tree/master/packages/eslint-config#readme",
6
6
  "bugs": "https://github.com/foray1010/common-presets/issues",
7
7
  "repository": {
@@ -27,7 +27,6 @@
27
27
  "eslint-config-prettier": "^10.0.2",
28
28
  "eslint-import-resolver-typescript": "^3.8.3",
29
29
  "eslint-plugin-compat": "^6.0.2",
30
- "eslint-plugin-functional": "^9.0.1",
31
30
  "eslint-plugin-import-x": "^4.6.1",
32
31
  "eslint-plugin-jest": "^28.11.0",
33
32
  "eslint-plugin-jest-dom": "^5.5.0",
@@ -48,7 +47,7 @@
48
47
  },
49
48
  "peerDependencies": {
50
49
  "@testing-library/dom": "^10.0.0",
51
- "eslint": "^9.21.0",
50
+ "eslint": "^9.22.0",
52
51
  "prettier": "^3.0.0",
53
52
  "typescript": "^5.0.2"
54
53
  },
@@ -66,5 +65,5 @@
66
65
  "publishConfig": {
67
66
  "access": "public"
68
67
  },
69
- "gitHead": "7ab2a92cce9564e8f577500663a09ab3a75641ed"
68
+ "gitHead": "321d60ec7b05e7c861bf019fb4c3f7b1410795e9"
70
69
  }
@@ -1,72 +0,0 @@
1
- import path from 'node:path'
2
-
3
- import { supportedFilesGlob } from '../constants.mjs'
4
-
5
- /** @typedef {import('../types/internal.d.ts').EslintConfig} EslintConfig */
6
-
7
- /**
8
- * Extend the flat configs with default files and ignores
9
- * @param {{
10
- * readonly filePrefixes: string | string[],
11
- * readonly ignores?: string[] | undefined
12
- * }} options
13
- * @param {EslintConfig} eslintConfig
14
- * @returns {EslintConfig}
15
- */
16
- export function applyConfig(options, eslintConfig) {
17
- const filePrefixes = [options.filePrefixes].flat()
18
- if (filePrefixes.length === 0) {
19
- throw new TypeError('filePrefixes must not be empty')
20
- }
21
-
22
- // Do not allow string such as "eslint:recommended" because it cannot be overridden by files/ignores
23
- for (const config of eslintConfig) {
24
- if (typeof config === 'string') {
25
- throw new TypeError(
26
- `Cannot extend ${config} with files/ignores, use \`@eslint/js\` instead`,
27
- )
28
- }
29
-
30
- if (Object.keys(config).includes('ignores')) {
31
- throw new TypeError('Do not use `ignores` in config')
32
- }
33
- }
34
-
35
- return eslintConfig.map((config) => {
36
- const files = generateCombinations(filePrefixes, config.files)
37
- return {
38
- ...config,
39
- ...(files ? { files } : {}),
40
- ...(options.ignores ? { ignores: options.ignores } : {}),
41
- }
42
- })
43
- }
44
-
45
- /**
46
- * @param {string[]} prefixes
47
- * @param {EslintConfig[number]['files']} originalGlobs
48
- * @returns {string[] | undefined}
49
- */
50
- function generateCombinations(prefixes, originalGlobs) {
51
- if (!originalGlobs || originalGlobs.length === 0) {
52
- return prefixes.map((prefix) => path.join(prefix, supportedFilesGlob))
53
- }
54
-
55
- const verifiedOriginalGlobs = originalGlobs.filter(
56
- /** @returns {originalGlob is string} */
57
- (originalGlob) => typeof originalGlob === 'string',
58
- )
59
- if (originalGlobs.length !== verifiedOriginalGlobs.length) {
60
- throw new TypeError(
61
- `Do not support using non-string value in files/ignores`,
62
- )
63
- }
64
-
65
- return prefixes.flatMap((prefix) => {
66
- return verifiedOriginalGlobs.flatMap((originalGlob) => {
67
- const signRegexp = /^!/u
68
- const sign = originalGlob.match(signRegexp)?.[0] ?? ''
69
- return sign + path.join(prefix, originalGlob.replace(signRegexp, ''))
70
- })
71
- })
72
- }