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 ""