[eprh] Update plugin config to be compatible with flat and legacy (#34762)
This has been incredibly frustrating as [ESLint's own docs](https://eslint.org/docs/latest/extend/plugins#backwards-compatibility-for-legacy-configs) are clearly wrong (see #34679). This PR uses [eslint-plugin-react's setup](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/index.js) as a reference, where the presets are assigned to `configs.flat` (not documented by eslint).
lauren committed
Oct 7, 2025 at 16:23 UTC
848e0e3a4f12022d396ddbc2b52fd8fa7ac31fa9
4 files changed
+63
-56
fixtures/eslint-v9/eslint.config.ts
+1
-1
@@ -2,6 +2,7 @@ import {defineConfig} from 'eslint/config';
2
import reactHooks from 'eslint-plugin-react-hooks';
3
4
export default defineConfig([
5
+ reactHooks.configs.flat['recommended-latest'],
6
{
7
languageOptions: {
8
ecmaVersion: 'latest',
@@ -12,7 +13,6 @@ export default defineConfig([
13
},
14
},
15
},
15
- extends: [reactHooks.configs['recommended-latest']],
16
rules: {
17
'react-hooks/exhaustive-deps': 'error',
18
},
packages/eslint-plugin-react-hooks/package.json
+1
-1
@@ -50,7 +50,7 @@
50
"@babel/preset-typescript": "^7.26.0",
51
"@babel/types": "^7.19.0",
52
"@tsconfig/strictest": "^2.0.5",
53
- "@types/eslint": "^8.56.12",
53
+ "@types/eslint": "^9.6.1",
54
"@types/estree": "^1.0.6",
55
"@types/estree-jsx": "^1.0.5",
56
"@types/node": "^20.2.5",
packages/eslint-plugin-react-hooks/src/index.ts
+45
-50
@@ -14,7 +14,6 @@ import {
14
} from './shared/ReactCompiler';
15
import RulesOfHooks from './rules/RulesOfHooks';
16
17
-// All rules
17
const rules = {
18
'exhaustive-deps': ExhaustiveDeps,
19
'rules-of-hooks': RulesOfHooks,
@@ -23,7 +22,6 @@ const rules = {
22
),
23
} satisfies Record<string, Rule.RuleModule>;
24
26
-// Basic hooks rules (for recommended config)
25
const basicRuleConfigs = {
26
'react-hooks/rules-of-hooks': 'error',
27
'react-hooks/exhaustive-deps': 'warn',
@@ -38,74 +36,71 @@ const compilerRuleConfigs = Object.fromEntries(
36
}),
37
) as Record<`react-hooks/${string}`, Linter.RuleEntry>;
38
41
-// All rules including compiler rules (for recommended-latest config)
39
const allRuleConfigs: Linter.RulesRecord = {
40
...basicRuleConfigs,
41
...compilerRuleConfigs,
42
};
43
47
-type FlatConfig = {
44
+const plugins = ['react-hooks'];
45
+
46
+type ReactHooksFlatConfig = {
47
plugins: Record<string, any>;
48
rules: Linter.RulesRecord;
49
};
50
51
+const configs = {
52
+ 'recommended-legacy': {
53
+ plugins,
54
+ rules: basicRuleConfigs,
55
+ },
56
+ 'recommended-latest-legacy': {
57
+ plugins,
58
+ rules: allRuleConfigs,
59
+ },
60
+ 'flat/recommended': {
61
+ plugins,
62
+ rules: basicRuleConfigs,
63
+ },
64
+ 'recommended-latest': {
65
+ plugins,
66
+ rules: allRuleConfigs,
67
+ },
68
+ recommended: {
69
+ plugins,
70
+ rules: basicRuleConfigs,
71
+ },
72
+ flat: {} as Record<string, ReactHooksFlatConfig>,
73
+};
74
+
75
const plugin = {
76
meta: {
77
name: 'eslint-plugin-react-hooks',
78
},
79
rules,
57
- configs: {} as {
58
- 'recommended-legacy': {
59
- plugins: Array<string>;
60
- rules: Linter.RulesRecord;
61
- };
62
- 'recommended-latest-legacy': {
63
- plugins: Array<string>;
64
- rules: Linter.RulesRecord;
65
- };
66
- 'flat/recommended': Array<FlatConfig>;
67
- 'recommended-latest': Array<FlatConfig>;
68
- recommended: Array<FlatConfig>;
69
- },
80
+ configs,
81
};
82
72
-Object.assign(plugin.configs, {
83
+Object.assign(configs.flat, {
84
'recommended-legacy': {
74
- plugins: ['react-hooks'],
75
- rules: basicRuleConfigs,
85
+ plugins: {'react-hooks': plugin},
86
+ rules: configs['recommended-legacy'].rules,
87
},
77
-
88
'recommended-latest-legacy': {
79
- plugins: ['react-hooks'],
80
- rules: allRuleConfigs,
89
+ plugins: {'react-hooks': plugin},
90
+ rules: configs['recommended-latest-legacy'].rules,
91
+ },
92
+ 'flat/recommended': {
93
+ plugins: {'react-hooks': plugin},
94
+ rules: configs['flat/recommended'].rules,
95
+ },
96
+ 'recommended-latest': {
97
+ plugins: {'react-hooks': plugin},
98
+ rules: configs['recommended-latest'].rules,
99
+ },
100
+ recommended: {
101
+ plugins: {'react-hooks': plugin},
102
+ rules: configs.recommended.rules,
103
},
82
-
83
- 'flat/recommended': [
84
- {
85
- plugins: {
86
- 'react-hooks': plugin,
87
- },
88
- rules: basicRuleConfigs,
89
- },
90
- ],
91
-
92
- 'recommended-latest': [
93
- {
94
- plugins: {
95
- 'react-hooks': plugin,
96
- },
97
- rules: allRuleConfigs,
98
- },
99
- ],
100
-
101
- recommended: [
102
- {
103
- plugins: {
104
- 'react-hooks': plugin,
105
- },
106
- rules: basicRuleConfigs,
107
- },
108
- ],
104
});
105
106
export default plugin;
yarn.lock
+16
-4
@@ -3907,10 +3907,10 @@
3907
"@types/estree" "*"
3908
"@types/json-schema" "*"
3909
3910
-"@types/eslint@^8.56.12":
3911
- version "8.56.12"
3912
- resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.12.tgz#1657c814ffeba4d2f84c0d4ba0f44ca7ea1ca53a"
3913
- integrity sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==
3910
+"@types/eslint@^9.6.1":
3911
+ version "9.6.1"
3912
+ resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584"
3913
+ integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==
3914
dependencies:
3915
"@types/estree" "*"
3916
"@types/json-schema" "*"
@@ -10190,6 +10190,11 @@ hermes-eslint@^0.32.0:
10190
hermes-estree "0.32.0"
10191
hermes-parser "0.32.0"
10192
10193
+hermes-estree@0.25.1:
10194
+ version "0.25.1"
10195
+ resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480"
10196
+ integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==
10197
+
10198
hermes-estree@0.29.1:
10199
version "0.29.1"
10200
resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.29.1.tgz#043c7db076e0e8ef8c5f6ed23828d1ba463ebcc5"
@@ -10214,6 +10219,13 @@ hermes-parser@0.32.0, hermes-parser@^0.32.0:
10219
dependencies:
10220
hermes-estree "0.32.0"
10221
10222
+hermes-parser@^0.25.1:
10223
+ version "0.25.1"
10224
+ resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1"
10225
+ integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==
10226
+ dependencies:
10227
+ hermes-estree "0.25.1"
10228
+
10229
homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1:
10230
version "1.0.3"
10231
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"