@willbooster/eslint-config-next 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # eslint-config-next
2
2
 
3
- A ESLint config for Next.js.
3
+ A ESLint flat config for Next.js.
4
4
  You need to do the following command to install peer dependencies.
5
5
 
6
6
  ```sh
@@ -0,0 +1,346 @@
1
+ /* eslint-disable unicorn/no-null */
2
+
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ import { FlatCompat } from '@eslint/eslintrc';
7
+ import js from '@eslint/js';
8
+ import eslintConfigPrettier from 'eslint-config-prettier';
9
+ import eslintPluginReact from 'eslint-plugin-react';
10
+ import eslintPluginSortClassMembers from 'eslint-plugin-sort-class-members';
11
+ import eslintPluginSortDestructureKeys from 'eslint-plugin-sort-destructure-keys';
12
+ import eslintPluginUnicorn from 'eslint-plugin-unicorn';
13
+ import eslintPluginUnusedImports from 'eslint-plugin-unused-imports';
14
+ import globals from 'globals';
15
+ import tseslint from 'typescript-eslint';
16
+
17
+ // mimic CommonJS variables -- not needed if using CommonJS
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+
21
+ const compat = new FlatCompat({
22
+ baseDirectory: __dirname,
23
+ });
24
+
25
+ const config = [
26
+ ...compat.extends('next/core-web-vitals').map((config) => ({
27
+ ...config,
28
+ })),
29
+
30
+ // We import configs of eslint-config-js/js-react/ts/ts-react manually
31
+ // because next/core-web-vitals depends on eslint-plugin-import,
32
+ // and we want to use eslint-plugin-import-x in the above configs.
33
+
34
+ // --------------- from eslint-config-js ---------------
35
+ // Note: don't merge the below two objects!
36
+ {
37
+ files: ['{,src/**/,tests/**/,scripts/**/}*.{cjs,js,jsx,mjs}'],
38
+ },
39
+ {
40
+ ignores: [
41
+ // Directories
42
+ '.yarn/**',
43
+ '3rd-party/**',
44
+ '@types/**',
45
+ '__generated__/**',
46
+ 'android/**',
47
+ 'build/**',
48
+ 'coverage/**',
49
+ 'dist/**',
50
+ 'ios/**',
51
+ 'no-format/**',
52
+ 'node_modules/**',
53
+ 'temp/**',
54
+ 'test-fixtures/**',
55
+ // Files
56
+ '*.d.ts',
57
+ '*.min.*js',
58
+ ],
59
+ },
60
+ // cf. https://github.com/eslint/eslint/blob/main/packages/js/src/configs/eslint-recommended.js
61
+ js.configs.recommended,
62
+ // cf. https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config
63
+ eslintPluginUnicorn.configs.recommended,
64
+ {
65
+ plugins: {
66
+ 'sort-class-members': eslintPluginSortClassMembers,
67
+ 'sort-destructure-keys': eslintPluginSortDestructureKeys,
68
+ },
69
+ languageOptions: {
70
+ ecmaVersion: 'latest',
71
+ globals: {
72
+ // for Web
73
+ ...globals.browser,
74
+ ...globals.serviceworker,
75
+ // for Node.js
76
+ ...globals.node,
77
+ },
78
+ },
79
+ rules: {
80
+ eqeqeq: 'warn',
81
+ 'no-console': 'off', // Allow `console.log()`.
82
+ 'no-unused-vars': ['warn', { ignoreRestSiblings: true }], // Allow unused vars in object destructuring.
83
+ 'object-shorthand': 'error',
84
+ 'one-var': ['error', 'never'], // We prefer one variable declaration per line.
85
+ 'spaced-comment': 'error', // Enforce consistency of spacing after the start of a comment // or /*.
86
+ 'import/newline-after-import': 'error',
87
+ 'import/no-duplicates': 'error',
88
+ 'import/order': [
89
+ 'error',
90
+ {
91
+ 'newlines-between': 'always',
92
+ alphabetize: {
93
+ order: 'asc',
94
+ },
95
+ },
96
+ ],
97
+ 'sort-destructure-keys/sort-destructure-keys': 'error',
98
+ 'unicorn/filename-case': [
99
+ 'error',
100
+ {
101
+ cases: {
102
+ camelCase: true,
103
+ pascalCase: true,
104
+ },
105
+ },
106
+ ],
107
+ 'unicorn/no-abusive-eslint-disable': 'off',
108
+ 'unicorn/no-array-callback-reference': 'off',
109
+ 'unicorn/no-array-reduce': 'warn',
110
+ 'unicorn/no-null': 'warn',
111
+ 'unicorn/no-process-exit': 'off',
112
+ 'unicorn/no-useless-undefined': [
113
+ 'error',
114
+ {
115
+ checkArguments: false,
116
+ },
117
+ ],
118
+ 'unicorn/prefer-top-level-await': 'warn',
119
+ 'unicorn/prevent-abbreviations': 'off',
120
+ },
121
+ },
122
+ // -----------------------------------------------------------
123
+
124
+ // --------------- from eslint-config-js-react ---------------
125
+ // cf. https://github.com/jsx-eslint/eslint-plugin-react#flat-configs
126
+ eslintPluginReact.configs.flat.recommended,
127
+ eslintPluginReact.configs.flat['jsx-runtime'],
128
+ {
129
+ settings: {
130
+ react: {
131
+ version: 'detect',
132
+ },
133
+ },
134
+ rules: {
135
+ 'react/jsx-sort-props': [
136
+ 'error',
137
+ {
138
+ callbacksLast: true,
139
+ shorthandFirst: true,
140
+ reservedFirst: true,
141
+ },
142
+ ],
143
+ 'react/no-unknown-property': [
144
+ 'error',
145
+ {
146
+ ignore: ['global', 'jsx'],
147
+ },
148
+ ],
149
+ 'react/prop-types': 'off',
150
+ },
151
+ },
152
+ // -----------------------------------------------------------
153
+
154
+ // --------------- from eslint-config-ts ---------------
155
+ ...tseslint.configs.recommendedTypeChecked.map((config) => ({
156
+ ...config,
157
+ files: ['{,src/**/,tests/**/,scripts/**/}*.{cts,mts,ts,tsx}'],
158
+ ignores: ['*.{cjs,js,mjs}'],
159
+ })),
160
+ {
161
+ files: ['{,src/**/,tests/**/,scripts/**/}*.{cts,mts,ts,tsx}'],
162
+ ignores: ['*.{cjs,js,mjs}'],
163
+ languageOptions: {
164
+ parserOptions: {
165
+ projectService: true,
166
+ tsconfigRootDir: import.meta.dirname,
167
+ },
168
+ },
169
+ rules: {
170
+ '@typescript-eslint/camelcase': 'off', // c.f. https://github.com/typescript-eslint/typescript-eslint/issues/2050
171
+ '@typescript-eslint/consistent-type-imports': 'error',
172
+ '@typescript-eslint/explicit-function-return-type': [
173
+ 'error',
174
+ {
175
+ allowExpressions: true,
176
+ allowHigherOrderFunctions: true,
177
+ },
178
+ ],
179
+ '@typescript-eslint/explicit-member-accessibility': [
180
+ 'error',
181
+ {
182
+ accessibility: 'no-public',
183
+ },
184
+ ],
185
+ '@typescript-eslint/naming-convention': [
186
+ 'error',
187
+ {
188
+ selector: 'default',
189
+ format: ['camelCase'],
190
+ leadingUnderscore: 'allow',
191
+ trailingUnderscore: 'allow',
192
+ },
193
+ {
194
+ selector: 'parameter',
195
+ format: ['camelCase'],
196
+ leadingUnderscore: 'allow',
197
+ },
198
+ {
199
+ selector: 'variable',
200
+ format: ['camelCase', 'PascalCase', 'UPPER_CASE'], // to use 'const ReactElem = () => { ... };'
201
+ leadingUnderscore: 'allow',
202
+ trailingUnderscore: 'allow',
203
+ },
204
+ {
205
+ selector: 'typeLike',
206
+ format: ['PascalCase'],
207
+ },
208
+ {
209
+ selector: 'enumMember',
210
+ format: ['PascalCase'],
211
+ },
212
+ {
213
+ // allow any name when referring to import and property
214
+ selector: ['import', 'property'],
215
+ format: null,
216
+ },
217
+ {
218
+ // allow any name in object destructuring of variable
219
+ selector: 'variable',
220
+ modifiers: ['destructured'],
221
+ format: null,
222
+ },
223
+ {
224
+ // allow any name in object destructuring of parameter
225
+ selector: 'parameter',
226
+ modifiers: ['destructured'],
227
+ format: null,
228
+ },
229
+ ],
230
+ '@typescript-eslint/no-explicit-any': 'error', // let's try avoiding `any`
231
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', ignoreRestSiblings: true }], // allow unused vars in object destructuring
232
+ '@typescript-eslint/no-use-before-define': 'off', // abstract code should appear first
233
+ },
234
+ },
235
+ // cf. https://github.com/sweepline/eslint-plugin-unused-imports#usage
236
+ {
237
+ plugins: {
238
+ 'unused-imports': eslintPluginUnusedImports,
239
+ },
240
+ rules: {
241
+ '@typescript-eslint/no-unused-vars': 'off',
242
+ 'unused-imports/no-unused-imports': 'error',
243
+ 'unused-imports/no-unused-vars': [
244
+ 'warn',
245
+ {
246
+ vars: 'all',
247
+ varsIgnorePattern: '^_',
248
+ args: 'after-used',
249
+ argsIgnorePattern: '^_',
250
+ },
251
+ ],
252
+ },
253
+ },
254
+ // cf. https://github.com/prettier/eslint-config-prettier#installation
255
+ eslintConfigPrettier,
256
+ // -----------------------------------------------------------
257
+
258
+ {
259
+ // A default export is required in app and page files.
260
+ // See also https://nextjs.org/docs/app/building-your-application/routing#file-conventions
261
+ files: [
262
+ 'src/app/**/{layout,page,loading,not-found,error,template,default}.tsx',
263
+ 'src/app/global-error.tsx',
264
+ 'src/pages/**/*.tsx',
265
+ 'src/pages/api/**/*.ts',
266
+ ],
267
+ rules: {
268
+ 'import/no-default-export': 'off',
269
+ },
270
+ },
271
+ {
272
+ // You can use brackets for dynamic routes.
273
+ // cf. https://nextjs.org/docs/getting-started/project-structure
274
+ files: ['src/pages/**/*.tsx', 'src/pages/api/**/*.ts'],
275
+ rules: {
276
+ 'unicorn/filename-case': [
277
+ 'error',
278
+ {
279
+ case: 'kebabCase',
280
+ ignore: [String.raw`^\[.+\]\.tsx?$`],
281
+ },
282
+ ],
283
+ },
284
+ },
285
+ {
286
+ // Request handlers must have the same name as the HTTP methods name (uppercase, e.g. `GET`).
287
+ // https://nextjs.org/docs/app/api-reference/file-conventions/route
288
+ files: ['src/app/api/**/route.ts'],
289
+ rules: {
290
+ '@typescript-eslint/naming-convention': [
291
+ 'error',
292
+ {
293
+ selector: 'default',
294
+ format: ['camelCase'],
295
+ leadingUnderscore: 'allow',
296
+ trailingUnderscore: 'allow',
297
+ },
298
+ {
299
+ selector: 'parameter',
300
+ format: ['camelCase'],
301
+ leadingUnderscore: 'allow',
302
+ },
303
+ {
304
+ selector: 'variable',
305
+ format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
306
+ // to use 'const ReactElem = () => { ... };'
307
+ leadingUnderscore: 'allow',
308
+ trailingUnderscore: 'allow',
309
+ },
310
+ {
311
+ selector: 'typeLike',
312
+ format: ['PascalCase'],
313
+ },
314
+ {
315
+ selector: 'enumMember',
316
+ format: ['PascalCase'],
317
+ },
318
+ {
319
+ // allow any name when referring to import and property
320
+ selector: ['import', 'property'],
321
+ format: null,
322
+ },
323
+ {
324
+ // allow any name in object destructuring of variable
325
+ selector: 'variable',
326
+ modifiers: ['destructured'],
327
+ format: null,
328
+ },
329
+ {
330
+ // allow any name in object destructuring of parameter
331
+ selector: 'parameter',
332
+ modifiers: ['destructured'],
333
+ format: null,
334
+ },
335
+ {
336
+ filter: '^(?:DELETE|GET|HEAD|OPTIONS|PATCH|POST|PUT)$',
337
+ selector: 'function',
338
+ modifiers: ['exported'],
339
+ format: null,
340
+ },
341
+ ],
342
+ },
343
+ },
344
+ ];
345
+
346
+ export default config;
package/package.json CHANGED
@@ -1,64 +1,71 @@
1
1
  {
2
2
  "name": "@willbooster/eslint-config-next",
3
- "version": "1.2.0",
4
- "description": "A ESLint config for Next.js",
3
+ "version": "2.0.0",
4
+ "description": "A ESLint flat config for Next.js",
5
5
  "license": "Apache-2.0",
6
6
  "author": "WillBooster Inc.",
7
- "main": ".eslintrc.json",
7
+ "type": "module",
8
+ "main": "eslint.config.js",
8
9
  "files": [
9
- ".eslintrc.json"
10
+ "eslint.config.js"
10
11
  ],
11
12
  "scripts": {
12
13
  "cleanup": "yarn format && yarn lint-fix",
13
14
  "format": "sort-package-json && yarn prettify",
14
- "lint": "eslint --color \"./{scripts,src,tests}/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}\"",
15
+ "lint": "eslint --color",
15
16
  "lint-fix": "yarn lint --fix --rule \"{ react-hooks/exhaustive-deps: 0 }\"",
16
- "prettify": "prettier --cache --color --write \"**/{.*/,}*.{cjs,css,cts,htm,html,js,json,json5,jsx,md,mjs,mts,scss,ts,tsx,vue,yaml,yml}\" \"!**/test-fixtures/**\"",
17
+ "prettify": "prettier --cache --color --write \"**/{.*/,}*.{cjs,css,cts,htm,html,js,json,json5,jsonc,jsx,md,mjs,mts,scss,ts,tsx,vue,yaml,yml}\" \"!**/test-fixtures/**\"",
17
18
  "test": "yarn lint",
18
19
  "typecheck": "tsc --noEmit --Pretty"
19
20
  },
20
21
  "prettier": "@willbooster/prettier-config",
21
- "dependencies": {
22
- "@willbooster/eslint-config-ts": "10.6.0"
23
- },
24
22
  "devDependencies": {
25
- "@types/eslint": "8.56.10",
26
- "@types/micromatch": "4.0.7",
27
- "@types/node": "20.12.7",
28
- "@types/react": "18.3.0",
29
- "@typescript-eslint/eslint-plugin": "7.7.1",
30
- "@typescript-eslint/parser": "7.7.1",
31
- "@willbooster/prettier-config": "9.1.2",
32
- "eslint": "8.57.0",
33
- "eslint-config-next": "14.2.3",
34
- "eslint-config-prettier": "9.1.0",
35
- "eslint-import-resolver-typescript": "3.6.1",
36
- "eslint-plugin-import": "2.29.1",
37
- "eslint-plugin-react": "7.34.1",
38
- "eslint-plugin-react-hooks": "4.6.1",
39
- "eslint-plugin-sort-class-members": "1.20.0",
23
+ "@eslint/eslintrc": "3.3.0",
24
+ "@eslint/js": "9.21.0",
25
+ "@types/eslint": "9.6.1",
26
+ "@types/micromatch": "4.0.9",
27
+ "@types/node": "22.13.9",
28
+ "@types/react": "19.0.10",
29
+ "@typescript-eslint/parser": "8.26.0",
30
+ "@willbooster/prettier-config": "10.0.0",
31
+ "eslint": "9.21.0",
32
+ "eslint-config-next": "15.2.1",
33
+ "eslint-config-prettier": "10.0.2",
34
+ "eslint-import-resolver-typescript": "3.8.3",
35
+ "eslint-plugin-import-x": "4.6.1",
36
+ "eslint-plugin-react": "7.37.4",
37
+ "eslint-plugin-react-hooks": "5.2.0",
38
+ "eslint-plugin-sort-class-members": "1.21.0",
40
39
  "eslint-plugin-sort-destructure-keys": "2.0.0",
41
- "eslint-plugin-unicorn": "52.0.0",
42
- "lint-staged": "15.2.2",
43
- "micromatch": "4.0.5",
44
- "prettier": "3.2.5",
45
- "react": "18.3.0",
46
- "sort-package-json": "2.10.0",
47
- "typescript": "5.4.5",
48
- "use-immer": "0.9.0"
40
+ "eslint-plugin-unicorn": "57.0.0",
41
+ "eslint-plugin-unused-imports": "4.1.4",
42
+ "globals": "16.0.0",
43
+ "lint-staged": "15.4.3",
44
+ "micromatch": "4.0.8",
45
+ "next": "15.2.1",
46
+ "prettier": "3.5.3",
47
+ "react": "19.0.0",
48
+ "sort-package-json": "3.0.0",
49
+ "typescript": "5.8.2",
50
+ "typescript-eslint": "8.26.0",
51
+ "use-immer": "0.11.0"
49
52
  },
50
53
  "peerDependencies": {
51
- "@typescript-eslint/eslint-plugin": ">=5",
52
- "@typescript-eslint/parser": ">=5",
53
- "@willbooster/prettier-config": "9.1.2",
54
- "eslint": ">=8",
55
- "eslint-config-next": ">=13",
56
- "eslint-config-prettier": ">=8",
57
- "eslint-import-resolver-typescript": ">=2",
58
- "eslint-plugin-import": ">=2",
59
- "eslint-plugin-sort-class-members": ">=1.14",
60
- "eslint-plugin-sort-destructure-keys": ">=1.4",
61
- "typescript": ">=4"
54
+ "@eslint/eslintrc": ">=3",
55
+ "@eslint/js": ">=9",
56
+ "@typescript-eslint/parser": ">=8",
57
+ "eslint": ">=9",
58
+ "eslint-config-next": ">=15",
59
+ "eslint-config-prettier": ">=10",
60
+ "eslint-import-resolver-typescript": ">=3",
61
+ "eslint-plugin-import-x": ">=4",
62
+ "eslint-plugin-sort-class-members": ">=1.21",
63
+ "eslint-plugin-sort-destructure-keys": ">=2",
64
+ "eslint-plugin-unicorn": ">=57",
65
+ "eslint-plugin-unused-imports": ">=4",
66
+ "globals": ">=16",
67
+ "typescript": ">=5",
68
+ "typescript-eslint": ">=8"
62
69
  },
63
70
  "publishConfig": {
64
71
  "access": "public"
package/.eslintrc.json DELETED
@@ -1,109 +0,0 @@
1
- {
2
- "extends": ["next/core-web-vitals", "@willbooster/eslint-config-ts"],
3
- "rules": {
4
- "import/no-default-export": "error",
5
- "react/jsx-sort-props": [
6
- "error",
7
- {
8
- "callbacksLast": true,
9
- "shorthandFirst": true,
10
- "reservedFirst": true
11
- }
12
- ],
13
- "react/no-unknown-property": [
14
- "error",
15
- {
16
- "ignore": ["global", "jsx"]
17
- }
18
- ],
19
- "react/prop-types": "off"
20
- },
21
- "overrides": [
22
- {
23
- // A default export is required in app and page files.
24
- // See also https://nextjs.org/docs/app/building-your-application/routing#file-conventions
25
- "files": [
26
- "src/app/**/{layout,page,loading,not-found,error,template,default}.tsx",
27
- "src/app/global-error.tsx",
28
- "src/pages/**/*.tsx",
29
- "src/pages/api/**/*.ts"
30
- ],
31
- "rules": {
32
- "import/no-default-export": "off"
33
- }
34
- },
35
- {
36
- // You can use brackets for dynamic routes.
37
- // cf. https://nextjs.org/docs/getting-started/project-structure
38
- "files": ["src/pages/**/*.tsx", "src/pages/api/**/*.ts"],
39
- "rules": {
40
- "unicorn/filename-case": [
41
- "error",
42
- {
43
- "case": "kebabCase",
44
- "ignore": ["^\\[.+\\]\\.tsx?$"]
45
- }
46
- ]
47
- }
48
- },
49
- {
50
- // Request handlers must have the same name as the HTTP methods name (uppercase, e.g. `GET`).
51
- // https://nextjs.org/docs/app/api-reference/file-conventions/route
52
- "files": ["src/app/api/**/route.ts"],
53
- "rules": {
54
- "@typescript-eslint/naming-convention": [
55
- "error",
56
- {
57
- "selector": "default",
58
- "format": ["camelCase"],
59
- "leadingUnderscore": "allow",
60
- "trailingUnderscore": "allow"
61
- },
62
- {
63
- "selector": "parameter",
64
- "format": ["camelCase"],
65
- "leadingUnderscore": "allow"
66
- },
67
- {
68
- "selector": "variable",
69
- "format": ["camelCase", "PascalCase", "UPPER_CASE"],
70
- // to use 'const ReactElem = () => { ... };'
71
- "leadingUnderscore": "allow",
72
- "trailingUnderscore": "allow"
73
- },
74
- {
75
- "selector": "typeLike",
76
- "format": ["PascalCase"]
77
- },
78
- {
79
- "selector": "enumMember",
80
- "format": ["PascalCase"]
81
- },
82
- {
83
- // allow any name when referring to import and property
84
- "selector": ["import", "property"],
85
- "format": null
86
- },
87
- {
88
- // allow any name in object destructuring of variable
89
- "selector": "variable",
90
- "modifiers": ["destructured"],
91
- "format": null
92
- },
93
- {
94
- // allow any name in object destructuring of parameter
95
- "selector": "parameter",
96
- "modifiers": ["destructured"],
97
- "format": null
98
- },
99
- {
100
- "filter": "^(?:DELETE|GET|HEAD|OPTIONS|PATCH|POST|PUT)$",
101
- "selector": "function",
102
- "modifiers": ["exported"],
103
- "format": null
104
- }
105
- ]
106
- }
107
- }
108
- ]
109
- }