1- // @ts -check
2- import { fixupConfigRules , fixupPluginRules } from '@eslint/compat' ;
3- import { FlatCompat } from '@eslint/eslintrc' ;
1+ import cspellPlugin from '@cspell/eslint-plugin' ;
42import eslint from '@eslint/js' ;
3+ // @ts -expect-error eslint-plugin-next doesn't come with TypeScript definitions
4+ import nextPlugin from '@next/eslint-plugin-next' ;
55import eslintConfigPrettier from 'eslint-config-prettier' ;
6- import reactPlugin from 'eslint-plugin-react' ;
6+ import react from 'eslint-plugin-react' ;
77import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort' ;
88import globals from 'globals' ;
99import tsEslint from 'typescript-eslint' ;
1010import { fileURLToPath } from 'url' ;
1111
12- const tsconfigRootDir = fileURLToPath ( new URL ( '.' , import . meta. url ) ) ,
13- flatCompat = new FlatCompat ( ) ;
12+ /**
13+ * @see {@link https://github.com/typescript-eslint/typescript-eslint/blob/main/eslint.config.mjs }
14+ * @see {@link https://github.com/vercel/next.js/issues/71763#issuecomment-2476838298 }
15+ */
16+
17+ const tsconfigRootDir = fileURLToPath ( new URL ( '.' , import . meta. url ) ) ;
1418
1519export default tsEslint . config (
1620 // register all of the plugins up-front
1721 {
1822 plugins : {
19- '@typescript-eslint' : tsEslint . plugin ,
20- react : fixupPluginRules ( reactPlugin ) ,
2123 'simple-import-sort' : simpleImportSortPlugin ,
24+ '@typescript-eslint' : tsEslint . plugin ,
25+ react,
26+ '@next/next' : nextPlugin ,
27+ '@cspell' : cspellPlugin ,
2228 } ,
2329 } ,
2430 {
@@ -29,7 +35,6 @@ export default tsEslint.config(
2935 // extends ...
3036 eslint . configs . recommended ,
3137 ...tsEslint . configs . recommended ,
32- ...fixupConfigRules ( flatCompat . extends ( 'plugin:@next/next/core-web-vitals' ) ) ,
3338
3439 // base config
3540 {
@@ -42,13 +47,33 @@ export default tsEslint.config(
4247 } ,
4348 } ,
4449 rules : {
50+ ...nextPlugin . configs . recommended . rules ,
51+ ...nextPlugin . configs [ 'core-web-vitals' ] . rules ,
52+ 'arrow-body-style' : [ 'error' , 'as-needed' ] ,
4553 'no-empty-pattern' : 'warn' ,
54+ 'no-console' : [ 'error' , { allow : [ 'warn' , 'error' , 'info' ] } ] ,
55+ 'no-restricted-syntax' : [
56+ 'error' ,
57+ {
58+ selector : "TSPropertySignature[key.name='children']" ,
59+ message : 'Please use PropsWithChildren<T> instead of defining children manually' ,
60+ } ,
61+ ] ,
62+ 'consistent-return' : 'warn' ,
63+ 'prefer-destructuring' : [ 'error' , { object : true , array : true } ] ,
64+ // simple-import-sort
4665 'simple-import-sort/exports' : 'error' ,
4766 'simple-import-sort/imports' : 'error' ,
67+ // typescript
4868 '@typescript-eslint/no-unused-vars' : 'warn' ,
4969 '@typescript-eslint/no-explicit-any' : 'warn' ,
5070 '@typescript-eslint/no-empty-object-type' : 'off' ,
5171 '@typescript-eslint/no-unsafe-declaration-merging' : 'warn' ,
72+ '@typescript-eslint/consistent-type-definitions' : [ 'error' , 'interface' ] ,
73+ // react
74+ 'react/no-unescaped-entities' : 'off' ,
75+ 'react/self-closing-comp' : [ 'error' , { component : true , html : true } ] ,
76+ 'react/jsx-curly-brace-presence' : [ 'error' , { props : 'never' , children : 'never' } ] ,
5277 'react/jsx-no-target-blank' : 'warn' ,
5378 'react/jsx-sort-props' : [
5479 'error' ,
@@ -58,18 +83,18 @@ export default tsEslint.config(
5883 noSortAlphabetically : true ,
5984 } ,
6085 ] ,
86+ // next
6187 '@next/next/no-sync-scripts' : 'warn' ,
62- } ,
63- } ,
64- {
65- files : [ '**/*.js' ] ,
66- extends : [ tsEslint . configs . disableTypeChecked ] ,
67- rules : {
68- // turn off other type-aware rules
69- '@typescript-eslint/internal/no-poorly-typed-ts-props' : 'off' ,
70-
71- // turn off rules that don't apply to JS code
72- '@typescript-eslint/explicit-function-return-type' : 'off' ,
88+ // spellchecker
89+ '@cspell/spellchecker' : [
90+ 'warn' ,
91+ {
92+ cspell : {
93+ language : 'en' ,
94+ dictionaries : [ 'typescript' , 'node' , 'html' , 'css' , 'bash' , 'npm' , 'pnpm' ] ,
95+ } ,
96+ } ,
97+ ] ,
7398 } ,
7499 } ,
75100 eslintConfigPrettier ,
0 commit comments