diff --git a/.eslintrc.js b/.eslintrc.js
index 4f902576ad82c..9d142d359afc3 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -517,6 +517,14 @@ module.exports = {
__IS_INTERNAL_VERSION__: 'readonly',
},
},
+ {
+ files: ['packages/react-devtools-*/**/*.js'],
+ excludedFiles: '**/__tests__/**/*.js',
+ plugins: ['eslint-plugin-react-hooks-published'],
+ rules: {
+ 'react-hooks-published/rules-of-hooks': ERROR,
+ },
+ },
{
files: ['packages/eslint-plugin-react-hooks/src/**/*'],
extends: ['plugin:@typescript-eslint/recommended'],
diff --git a/package.json b/package.json
index 3439ed756a346..7bb0c2c5c9350 100644
--- a/package.json
+++ b/package.json
@@ -71,6 +71,7 @@
"eslint-plugin-no-for-of-loops": "^1.0.0",
"eslint-plugin-no-function-declare-after-return": "^1.0.0",
"eslint-plugin-react": "^6.7.1",
+ "eslint-plugin-react-hooks-published": "npm:eslint-plugin-react-hooks@^5.2.0",
"eslint-plugin-react-internal": "link:./scripts/eslint-rules",
"fbjs-scripts": "^3.0.1",
"filesize": "^6.0.1",
diff --git a/packages/react-devtools-shared/src/devtools/cache.js b/packages/react-devtools-shared/src/devtools/cache.js
index 5ed2133b06898..c927b4d3dbf36 100644
--- a/packages/react-devtools-shared/src/devtools/cache.js
+++ b/packages/react-devtools-shared/src/devtools/cache.js
@@ -42,6 +42,7 @@ export type Resource = {
let readContext;
if (typeof React.use === 'function') {
readContext = function (Context: ReactContext) {
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
return React.use(Context);
};
} else if (
@@ -141,6 +142,7 @@ export function createResource(
const key = hashInput(input);
const result: Thenable = accessResult(resource, fetch, input, key);
if (typeof React.use === 'function') {
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
return React.use(result);
}
diff --git a/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/cache.js b/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/cache.js
index 6da066a110338..6ae471ec993c4 100644
--- a/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/cache.js
+++ b/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/cache.js
@@ -23,6 +23,7 @@ const API_TIMEOUT = 3000;
function readRecord(record: Thenable): T | null {
if (typeof React.use === 'function') {
try {
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
return React.use(record);
} catch (x) {
if (x === null) {
diff --git a/packages/react-devtools-shared/src/dynamicImportCache.js b/packages/react-devtools-shared/src/dynamicImportCache.js
index 3b48a8459d149..c4dfa3b7ca05d 100644
--- a/packages/react-devtools-shared/src/dynamicImportCache.js
+++ b/packages/react-devtools-shared/src/dynamicImportCache.js
@@ -30,6 +30,7 @@ const moduleLoaderFunctionToModuleMap: Map =
function readRecord(record: Thenable): T | null {
if (typeof React.use === 'function') {
try {
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
return React.use(record);
} catch (x) {
if (x === null) {
diff --git a/packages/react-devtools-shared/src/hookNamesCache.js b/packages/react-devtools-shared/src/hookNamesCache.js
index 669f5f31c2bcd..5a9d1ba7d2123 100644
--- a/packages/react-devtools-shared/src/hookNamesCache.js
+++ b/packages/react-devtools-shared/src/hookNamesCache.js
@@ -30,6 +30,7 @@ const TIMEOUT = 30000;
function readRecord(record: Thenable): T | null {
if (typeof React.use === 'function') {
try {
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
return React.use(record);
} catch (x) {
if (record.status === 'rejected') {
diff --git a/packages/react-devtools-shared/src/inspectedElementCache.js b/packages/react-devtools-shared/src/inspectedElementCache.js
index a7e3206db70b8..820baf3380ddc 100644
--- a/packages/react-devtools-shared/src/inspectedElementCache.js
+++ b/packages/react-devtools-shared/src/inspectedElementCache.js
@@ -32,6 +32,7 @@ import type {
function readRecord(record: Thenable): T {
if (typeof React.use === 'function') {
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
return React.use(record);
}
if (record.status === 'fulfilled') {
diff --git a/packages/react-devtools-shell/src/app/SuspenseTree/index.js b/packages/react-devtools-shell/src/app/SuspenseTree/index.js
index ff0b0ce294225..f9b687cbc8792 100644
--- a/packages/react-devtools-shell/src/app/SuspenseTree/index.js
+++ b/packages/react-devtools-shell/src/app/SuspenseTree/index.js
@@ -314,6 +314,7 @@ function LoadLater() {
function readRecord(promise: any): any {
if (typeof React.use === 'function') {
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
return React.use(promise);
}
switch (promise.status) {
diff --git a/packages/react-devtools-timeline/src/timelineCache.js b/packages/react-devtools-timeline/src/timelineCache.js
index a778ec9444ae9..ee32e4440f0b9 100644
--- a/packages/react-devtools-timeline/src/timelineCache.js
+++ b/packages/react-devtools-timeline/src/timelineCache.js
@@ -29,6 +29,7 @@ const fileNameToProfilerDataMap: Map<
function readRecord(record: Thenable): T | Error {
if (typeof React.use === 'function') {
try {
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
return React.use(record);
} catch (x) {
if (record.status === 'rejected') {
diff --git a/yarn.lock b/yarn.lock
index b24896f5c81a9..d2f2e8d017c61 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8207,6 +8207,11 @@ eslint-plugin-no-unsanitized@4.0.2:
resolved "https://registry.yarnpkg.com/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.0.2.tgz#e872b302cdfb5fe1262db989ba29cfcc334b499b"
integrity sha512-Pry0S9YmHoz8NCEMRQh7N0Yexh2MYCNPIlrV52hTmS7qXnTghWsjXouF08bgsrrZqaW9tt1ZiK3j5NEmPE+EjQ==
+"eslint-plugin-react-hooks-published@npm:eslint-plugin-react-hooks@^5.2.0":
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz#1be0080901e6ac31ce7971beed3d3ec0a423d9e3"
+ integrity sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==
+
"eslint-plugin-react-internal@link:./scripts/eslint-rules":
version "0.0.0"
uid ""