From b65e6fc58b8c9a35e2c2ea7d1952fc1499cef09b Mon Sep 17 00:00:00 2001 From: lauren Date: Mon, 6 Oct 2025 12:43:39 -0400 Subject: [PATCH 1/2] Revert [eprh] Remove hermes-parser (#34747) Adds back HermesParser to eslint-plugin-react-hooks. There are still [external users of Flow](https://github.com/facebook/react/pull/34719#issuecomment-3368137743) using the plugin, so we shouldn't break the plugin for them. However, we still have the problem of double parsing: once from eslint (which we discard) and then another via babel/hermes parser. In the long run we should investigate a translation layer from estree to babel (or alternatively, update the compiler to take estree as input). But for now, I am reverting the PR. This does mean that [Sandpack in react.dev](https://github.com/reactjs/react.dev/blob/11cb6b591571caf5fa2a192117b6a6445c3f2027/src/components/MDX/Sandpack/runESLint.tsx#L31) cannot update to the latest eprh as HermesParser does not appear to be able to be run in a browser. I discovered this while trying to update eprh on react.dev last week, but didn't investigate deeply. I'll need to double check that again to find out more. --- .../eslint-plugin-react-hooks/package.json | 1 + .../src/shared/RunReactCompiler.ts | 31 ++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/eslint-plugin-react-hooks/package.json b/packages/eslint-plugin-react-hooks/package.json index 557e48af65..fdea392820 100644 --- a/packages/eslint-plugin-react-hooks/package.json +++ b/packages/eslint-plugin-react-hooks/package.json @@ -41,6 +41,7 @@ "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", "zod": "^3.22.4 || ^4.0.0", "zod-validation-error": "^3.0.3 || ^4.0.0" }, diff --git a/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts b/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts index 7755dbe947..02fd68badf 100644 --- a/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts +++ b/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts @@ -17,6 +17,7 @@ import BabelPluginReactCompiler, { LoggerEvent, } from 'babel-plugin-react-compiler'; import type {SourceCode} from 'eslint'; +import * as HermesParser from 'hermes-parser'; import {isDeepStrictEqual} from 'util'; import type {ParseResult} from '@babel/parser'; @@ -113,14 +114,28 @@ function runReactCompilerImpl({ } let babelAST: ParseResult | null = null; - try { - babelAST = babelParse(sourceCode.text, { - sourceFilename: filename, - sourceType: 'unambiguous', - plugins: ['typescript', 'jsx'], - }); - } catch (err: unknown) { - /* empty */ + + if (filename.endsWith('.tsx') || filename.endsWith('.ts')) { + try { + babelAST = babelParse(sourceCode.text, { + sourceFilename: filename, + sourceType: 'unambiguous', + plugins: ['typescript', 'jsx'], + }); + } catch { + /* empty */ + } + } else { + try { + babelAST = HermesParser.parse(sourceCode.text, { + babel: true, + enableExperimentalComponentSyntax: true, + sourceFilename: filename, + sourceType: 'module', + }); + } catch { + /* empty */ + } } if (babelAST != null) { From ed49061027952b8ed5bd617a5a29d3643215ba82 Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 6 Oct 2025 16:59:28 -0700 Subject: [PATCH 2/2] [eprh] Prepare for 7.0.0 For 7.0.0: Slim down presets to just 3 configurations: - `recommended-legacy`: legacy config with all recommended rules - `recommended`: flat config with all recommended rules, and - `recommended-experimental`: flat config with all recommended rules plus new bleeding edge experimental compiler rules Removed: - `recommended-latest-legacy` - `flat/recommended configs` --- ReactVersions.js | 2 +- .../eslint-plugin-react-hooks/CHANGELOG.md | 4 ++ packages/eslint-plugin-react-hooks/README.md | 70 ++++++++++++------- .../eslint-plugin-react-hooks/package.json | 2 +- .../eslint-plugin-react-hooks/src/index.ts | 16 +---- 5 files changed, 52 insertions(+), 42 deletions(-) diff --git a/ReactVersions.js b/ReactVersions.js index c657857cbb..ba7b222bf0 100644 --- a/ReactVersions.js +++ b/ReactVersions.js @@ -33,7 +33,7 @@ const canaryChannelLabel = 'canary'; const rcNumber = 0; const stablePackages = { - 'eslint-plugin-react-hooks': '6.2.0', + 'eslint-plugin-react-hooks': '7.0.0', 'jest-react': '0.18.0', react: ReactVersion, 'react-art': ReactVersion, diff --git a/packages/eslint-plugin-react-hooks/CHANGELOG.md b/packages/eslint-plugin-react-hooks/CHANGELOG.md index cd376cd4dc..507bae879e 100644 --- a/packages/eslint-plugin-react-hooks/CHANGELOG.md +++ b/packages/eslint-plugin-react-hooks/CHANGELOG.md @@ -1,3 +1,7 @@ +## 7.0.0 + +- **Breaking:** Slim down presets to just 3 configurations. Removed `recommended-latest-legacy` and `flat/recommended` configs. The plugin now provides `recommended-legacy` (legacy config with all recommended rules), `recommended` (flat config with all recommended rules), and `recommended-experimental` (flat config with all recommended rules plus new bleeding edge experimental compiler rules). ([@poteto](https://github.com/poteto) in [#TODO](https://github.com/facebook/react/pull/TODO)) + ## 6.1.1 **Note:** 6.1.0 accidentally allowed use of `recommended` without flat config, causing errors when used with ESLint v9's `defineConfig()` helper. This has been fixed in 6.1.1. diff --git a/packages/eslint-plugin-react-hooks/README.md b/packages/eslint-plugin-react-hooks/README.md index afd89ab226..1900dd2a6b 100644 --- a/packages/eslint-plugin-react-hooks/README.md +++ b/packages/eslint-plugin-react-hooks/README.md @@ -4,8 +4,6 @@ The official ESLint plugin for [React](https://react.dev) which enforces the [Ru ## Installation -**Note: If you're using Create React App, please use `react-scripts` >= 3 instead of adding it directly.** - Assuming you already have ESLint installed, run: ```sh @@ -18,9 +16,9 @@ yarn add eslint-plugin-react-hooks --dev ### Flat Config (eslint.config.js|ts) -#### >= 6.0.0 +#### >= 7.0.0 -For users of 6.0 and beyond, add the `recommended` config. +For users of 7.0 and beyond, add the `recommended` config for all recommended rules: ```js // eslint.config.js @@ -38,11 +36,10 @@ export default defineConfig([ ]); ``` -#### 5.2.0 - -For users of 5.2.0 (the first version with flat config support), add the `recommended-latest` config. +If you want to try bleeding edge experimental compiler rules, use `recommended-experimental`: ```js +// eslint.config.js import reactHooks from 'eslint-plugin-react-hooks'; import { defineConfig } from 'eslint/config'; @@ -52,16 +49,16 @@ export default defineConfig([ plugins: { 'react-hooks': reactHooks, }, - extends: ['react-hooks/recommended-latest'], + extends: ['react-hooks/recommended-experimental'], }, ]); ``` ### Legacy Config (.eslintrc) -#### >= 5.2.0 +#### >= 7.0.0 -If you are still using ESLint below 9.0.0, you can use `recommended-legacy` for accessing a legacy version of the recommended config. +If you are still using ESLint below 9.0.0, you can use `recommended-legacy` for accessing a legacy version of the recommended config with all recommended rules. ```js { @@ -72,19 +69,6 @@ If you are still using ESLint below 9.0.0, you can use `recommended-legacy` for } ``` -#### < 5.2.0 - -If you're using a version earlier than 5.2.0, the legacy config was simply `recommended`. - -```js -{ - "extends": [ - // ... - "plugin:react-hooks/recommended" - ] -} -``` - ### Custom Configuration If you want more fine-grained configuration, you can instead choose to enable specific rules. However, we strongly encourage using the recommended presets — see above — so that you will automatically receive new recommended rules as we add them in future versions of the plugin. @@ -92,7 +76,7 @@ If you want more fine-grained configuration, you can instead choose to enable sp #### Flat Config (eslint.config.js|ts) ```js -import * as reactHooks from 'eslint-plugin-react-hooks'; +import reactHooks from 'eslint-plugin-react-hooks'; export default [ { @@ -100,8 +84,26 @@ export default [ plugins: { 'react-hooks': reactHooks }, // ... rules: { + // Core hooks rules 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn', + + // React Compiler rules + 'react-hooks/config': 'error', + 'react-hooks/error-boundaries': 'error', + 'react-hooks/component-hook-factories': 'error', + 'react-hooks/gating': 'error', + 'react-hooks/globals': 'error', + 'react-hooks/immutability': 'error', + 'react-hooks/preserve-manual-memoization': 'error', + 'react-hooks/purity': 'error', + 'react-hooks/refs': 'error', + 'react-hooks/set-state-in-effect': 'error', + 'react-hooks/set-state-in-render': 'error', + 'react-hooks/static-components': 'error', + 'react-hooks/unsupported-syntax': 'warn', + 'react-hooks/use-memo': 'error', + 'react-hooks/incompatible-library': 'warn', } }, ]; @@ -116,8 +118,26 @@ export default [ ], "rules": { // ... + // Core hooks rules "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn" + "react-hooks/exhaustive-deps": "warn", + + // React Compiler rules + "react-hooks/config": "error", + "react-hooks/error-boundaries": "error", + "react-hooks/component-hook-factories": "error", + "react-hooks/gating": "error", + "react-hooks/globals": "error", + "react-hooks/immutability": "error", + "react-hooks/preserve-manual-memoization": "error", + "react-hooks/purity": "error", + "react-hooks/refs": "error", + "react-hooks/set-state-in-effect": "error", + "react-hooks/set-state-in-render": "error", + "react-hooks/static-components": "error", + "react-hooks/unsupported-syntax": "warn", + "react-hooks/use-memo": "error", + "react-hooks/incompatible-library": "warn" } } ``` diff --git a/packages/eslint-plugin-react-hooks/package.json b/packages/eslint-plugin-react-hooks/package.json index fdea392820..fc12c66f1a 100644 --- a/packages/eslint-plugin-react-hooks/package.json +++ b/packages/eslint-plugin-react-hooks/package.json @@ -1,7 +1,7 @@ { "name": "eslint-plugin-react-hooks", "description": "ESLint rules for React Hooks", - "version": "5.2.0", + "version": "7.0.0", "repository": { "type": "git", "url": "https://github.com/facebook/react.git", diff --git a/packages/eslint-plugin-react-hooks/src/index.ts b/packages/eslint-plugin-react-hooks/src/index.ts index e26fa5aed7..bff2449d29 100644 --- a/packages/eslint-plugin-react-hooks/src/index.ts +++ b/packages/eslint-plugin-react-hooks/src/index.ts @@ -71,25 +71,11 @@ const plugin = { Object.assign(plugin.configs, { 'recommended-legacy': { - plugins: ['react-hooks'], - rules: basicRuleConfigs, - }, - - 'recommended-latest-legacy': { plugins: ['react-hooks'], rules: allRuleConfigs, }, - 'flat/recommended': [ - { - plugins: { - 'react-hooks': plugin, - }, - rules: basicRuleConfigs, - }, - ], - - 'recommended-latest': [ + 'recommended-experimental': [ { plugins: { 'react-hooks': plugin,