Skip to content

Conversation

poteto
Copy link
Member

@poteto poteto commented Oct 2, 2025

Previously, the recommended config used the legacy ESLint format (plugins as an array of strings). This causes errors when used with ESLint v9's defineConfig() helper. This was following eslint's own docs:

With this approach, both configuration systems recognize "recommended". The old config system uses the recommended key while the current config system uses the flat/recommended key. The defineConfig() helper first looks at the recommended key, and if that is not in the correct format, it looks for the flat/recommended key. This allows you an upgrade path if you’d later like to rename flat/recommended to recommended when you no longer need to support the old config system.

However, isLegacyConfig() (also see eslintrcKeys) function doesn't check for the plugins key, so our config was incorrectly treated as flat config despite being in legacy format.

This PR fixes the issue, along with a few other fixes combined:

  1. Convert recommended to flat config format
  2. Separate basic rules (exhaustive-deps, rules-of-hooks) from compiler rules
  3. Add recommended-latest-legacy config for non-flat config users who want all recommended rules (including compiler rules)
  4. Adding more types for the exported config

Our shipped presets in 6.x.x will essentially be:

  • recommended-legacy: legacy (non-flat), with basic rules only
  • recommended-latest-legacy: legacy (non-flat), all rules (basic + compiler)
  • flat/recommended: flat, basic rules only (now the same as recommended, but to avoid making a breaking change we'll just keep it around in 6.x.x)
  • recommended-latest: flat, all rules (basic + compiler)
  • recommended: flat, basic rules only

In the next breaking release 7.x.x, we will collapse down the presets into three:

  • recommended-legacy: all recommended rules
  • recommended: all recommended rules
  • recommended-experimental: all recommended rules + new bleeding edge experimental rules

Closes #34679


Stack created with Sapling. Best reviewed with ReviewStack.

Copy link
Member

@josephsavona josephsavona left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the thorough debugging on this. It's incredibly frustrating that we were following ESLint's docs, and this worked in 9.20 or so, and seems to break in more recent minors of ESLint. The new setup here is more tedious, but from your/our investigation seems to be the only reasonable compromise. Other plugins (eslint-plugin-react for example) also define separate sets of configs for legacy and flat mode too.

@poteto poteto marked this pull request as ready for review October 2, 2025 21:37
@hernan-yadiel
Copy link

Hi! I know working with ESLint is messier than it should be. Quick naming idea: -latest feels a bit opaque since it actually means “includes compiler rules.” Would recommended-compiler or recommended-extended be clearer?
Super excited for react conf!

@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Oct 2, 2025
@react-sizebot
Copy link

react-sizebot commented Oct 2, 2025

Comparing: 056a586...846e132

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.68 kB 6.68 kB = 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 536.14 kB 536.14 kB = 94.81 kB 94.81 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 663.96 kB 663.96 kB = 117.04 kB 117.04 kB
facebook-www/ReactDOM-prod.classic.js = 687.83 kB 687.83 kB = 121.08 kB 121.08 kB
facebook-www/ReactDOM-prod.modern.js = 678.26 kB 678.26 kB = 119.44 kB 119.43 kB

Significant size changes

Includes any change greater than 0.2%:

(No significant changes)

Generated by 🚫 dangerJS against 846e132

poteto added a commit that referenced this pull request Oct 2, 2025
Adds back the compiler rules to the recommended preset, intended for the
next release.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34675).
* #34703
* #34700
* #34699
* __->__ #34675
poteto added a commit that referenced this pull request Oct 2, 2025
Updates the eslint fixture lockfiles.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34699).
* #34703
* #34700
* __->__ #34699
* #34675
@poteto
Copy link
Member Author

poteto commented Oct 2, 2025

@hernan-yadiel the latest suffix is going to be short-lived :) In 7.x we'll go back down to 3 presets:

recommended-legacy: legacy config, all recommended rules
recommended: flat config, all recommended rules
recommended-experimental: all recommended rules + new bleeding edge experimental rules

Previously, the `recommended` config used the legacy ESLint format (plugins as an array of strings). This causes errors when used with ESLint v9's `defineConfig()` helper. This was following [eslint's own docs](https://eslint.org/docs/latest/extend/plugins#backwards-compatibility-for-legacy-configs):

> With this approach, both configuration systems recognize "recommended". The old config system uses the recommended key while the current config system uses the flat/recommended key. The defineConfig() helper first looks at the recommended key, and if that is not in the correct format, it looks for the flat/recommended key. This allows you an upgrade path if you’d later like to rename flat/recommended to recommended when you no longer need to support the old config system.

However, [`isLegacyConfig()`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L73-L81) (also see [`eslintrcKeys`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L24-L35)) function doesn't check for the `plugins` key, so our config was incorrectly treated as flat config despite being in legacy format.

This PR fixes the issue, along with a few other fixes combined:

1. Convert `recommended` to flat config format
2. Separate basic rules (exhaustive-deps, rules-of-hooks) from compiler rules
3. Add `recommended-latest-legacy` config for non-flat config users who want all recommended rules (including compiler rules)
4. Adding more types for the exported config

Our shipped presets in 6.x.x will essentially be:
- `recommended-legacy`: legacy (non-flat), with basic rules only
- `recommended-latest-legacy`: legacy (non-flat), all rules (basic + compiler)
- `flat/recommended`: flat, basic rules only (now the same as recommended, but to avoid making a breaking change we'll just keep it around in 6.x.x)
- `recommended-latest`: flat, all rules (basic + compiler)
- `recommended`: flat, basic rules only

In the next breaking release 7.x.x, we will collapse down the presets into three:

- `recommended-legacy`: all recommended rules
- `recommended`: all recommended rules
- `recommended-experimental`: all recommended rules + new bleeding edge experimental rules

Closes #34679
github-actions bot pushed a commit that referenced this pull request Oct 2, 2025
Updates the eslint fixture lockfiles.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34699).
* #34703
* #34700
* __->__ #34699
* #34675

DiffTrain build for [056a586](056a586)
github-actions bot pushed a commit that referenced this pull request Oct 2, 2025
Updates the eslint fixture lockfiles.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34699).
* #34703
* #34700
* __->__ #34699
* #34675

DiffTrain build for [056a586](056a586)
@poteto poteto merged commit 26b177b into main Oct 2, 2025
241 checks passed
@poteto poteto deleted the pr34700 branch October 2, 2025 22:52
github-actions bot pushed a commit that referenced this pull request Oct 2, 2025
Previously, the `recommended` config used the legacy ESLint format
(plugins as an array of strings). This causes errors when used with
ESLint v9's `defineConfig()` helper. This was following [eslint's own
docs](https://eslint.org/docs/latest/extend/plugins#backwards-compatibility-for-legacy-configs):

> With this approach, both configuration systems recognize
"recommended". The old config system uses the recommended key while the
current config system uses the flat/recommended key. The defineConfig()
helper first looks at the recommended key, and if that is not in the
correct format, it looks for the flat/recommended key. This allows you
an upgrade path if you’d later like to rename flat/recommended to
recommended when you no longer need to support the old config system.

However,
[`isLegacyConfig()`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L73-L81)
(also see
[`eslintrcKeys`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L24-L35))
function doesn't check for the `plugins` key, so our config was
incorrectly treated as flat config despite being in legacy format.

This PR fixes the issue, along with a few other fixes combined:

1. Convert `recommended` to flat config format
2. Separate basic rules (exhaustive-deps, rules-of-hooks) from compiler
rules
3. Add `recommended-latest-legacy` config for non-flat config users who
want all recommended rules (including compiler rules)
4. Adding more types for the exported config

Our shipped presets in 6.x.x will essentially be:
- `recommended-legacy`: legacy (non-flat), with basic rules only
- `recommended-latest-legacy`: legacy (non-flat), all rules (basic +
compiler)
- `flat/recommended`: flat, basic rules only (now the same as
recommended, but to avoid making a breaking change we'll just keep it
around in 6.x.x)
- `recommended-latest`: flat, all rules (basic + compiler)
- `recommended`: flat, basic rules only

In the next breaking release 7.x.x, we will collapse down the presets
into three:

- `recommended-legacy`: all recommended rules
- `recommended`: all recommended rules
- `recommended-experimental`: all recommended rules + new bleeding edge
experimental rules

Closes #34679

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34700).
* #34703
* __->__ #34700

DiffTrain build for [26b177b](26b177b)
github-actions bot pushed a commit that referenced this pull request Oct 2, 2025
Previously, the `recommended` config used the legacy ESLint format
(plugins as an array of strings). This causes errors when used with
ESLint v9's `defineConfig()` helper. This was following [eslint's own
docs](https://eslint.org/docs/latest/extend/plugins#backwards-compatibility-for-legacy-configs):

> With this approach, both configuration systems recognize
"recommended". The old config system uses the recommended key while the
current config system uses the flat/recommended key. The defineConfig()
helper first looks at the recommended key, and if that is not in the
correct format, it looks for the flat/recommended key. This allows you
an upgrade path if you’d later like to rename flat/recommended to
recommended when you no longer need to support the old config system.

However,
[`isLegacyConfig()`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L73-L81)
(also see
[`eslintrcKeys`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L24-L35))
function doesn't check for the `plugins` key, so our config was
incorrectly treated as flat config despite being in legacy format.

This PR fixes the issue, along with a few other fixes combined:

1. Convert `recommended` to flat config format
2. Separate basic rules (exhaustive-deps, rules-of-hooks) from compiler
rules
3. Add `recommended-latest-legacy` config for non-flat config users who
want all recommended rules (including compiler rules)
4. Adding more types for the exported config

Our shipped presets in 6.x.x will essentially be:
- `recommended-legacy`: legacy (non-flat), with basic rules only
- `recommended-latest-legacy`: legacy (non-flat), all rules (basic +
compiler)
- `flat/recommended`: flat, basic rules only (now the same as
recommended, but to avoid making a breaking change we'll just keep it
around in 6.x.x)
- `recommended-latest`: flat, all rules (basic + compiler)
- `recommended`: flat, basic rules only

In the next breaking release 7.x.x, we will collapse down the presets
into three:

- `recommended-legacy`: all recommended rules
- `recommended`: all recommended rules
- `recommended-experimental`: all recommended rules + new bleeding edge
experimental rules

Closes #34679

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34700).
* #34703
* __->__ #34700

DiffTrain build for [26b177b](26b177b)
poteto added a commit that referenced this pull request Oct 2, 2025
This rule was a leftover from a while ago and doesn't actually lint
anything useful. Specifically, you get a lint error if you try to opt
out a component that isn't already bailing out. If there's a bailout the
compiler already safely skips over it, so adding `'use no memo'` there
is unnecessary.

Fixes #31407

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34703).
* __->__ #34703
* #34700
github-actions bot pushed a commit that referenced this pull request Oct 2, 2025
This rule was a leftover from a while ago and doesn't actually lint
anything useful. Specifically, you get a lint error if you try to opt
out a component that isn't already bailing out. If there's a bailout the
compiler already safely skips over it, so adding `'use no memo'` there
is unnecessary.

Fixes #31407

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34703).
* __->__ #34703
* #34700

DiffTrain build for [19f65ff](19f65ff)
github-actions bot pushed a commit that referenced this pull request Oct 2, 2025
This rule was a leftover from a while ago and doesn't actually lint
anything useful. Specifically, you get a lint error if you try to opt
out a component that isn't already bailing out. If there's a bailout the
compiler already safely skips over it, so adding `'use no memo'` there
is unnecessary.

Fixes #31407

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34703).
* __->__ #34703
* #34700

DiffTrain build for [19f65ff](19f65ff)
github-actions bot pushed a commit to code/lib-react that referenced this pull request Oct 5, 2025
This rule was a leftover from a while ago and doesn't actually lint
anything useful. Specifically, you get a lint error if you try to opt
out a component that isn't already bailing out. If there's a bailout the
compiler already safely skips over it, so adding `'use no memo'` there
is unnecessary.

Fixes facebook#31407

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34703).
* __->__ facebook#34703
* facebook#34700

DiffTrain build for [19f65ff](facebook@19f65ff)
github-actions bot pushed a commit to code/lib-react that referenced this pull request Oct 5, 2025
This rule was a leftover from a while ago and doesn't actually lint
anything useful. Specifically, you get a lint error if you try to opt
out a component that isn't already bailing out. If there's a bailout the
compiler already safely skips over it, so adding `'use no memo'` there
is unnecessary.

Fixes facebook#31407

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34703).
* __->__ facebook#34703
* facebook#34700

DiffTrain build for [19f65ff](facebook@19f65ff)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: eslint-plugin-react-hooks v.6.1.0 recommended config uses array instead of object

4 participants