From fd007be0d07e37ccbd38b805842a89faf7a16e04 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Mon, 8 May 2023 12:10:43 -0700 Subject: [PATCH] Fixtures from ESLint rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Incorporates the fixtures from eslint-plugin-react-hooks using a script, so that we can easily update them in the future. For each fixture we run the compiler with and without hooks validation first so that we know if the fixture is expected to pass — we have some false positives and false negatives that i can work through. For example we accidentally think that `userFetch()` is a hook, oops. Fixtures that should pass but error, or that should error but pass, are marked as `todo.` or `todo.error.`. While i was here i added the ability to have fixtures in subdirectories for grouping purposes. --- compiler/forget/package.json | 1 + .../scripts/build-react-hooks-fixures.js | 91 ++ .../eslint-plugin-react-hooks-test-cases.js | 985 ++++++++++++++++++ ....invalid-hook-after-early-return.expect.md | 21 + .../error.invalid-hook-after-early-return.js | 0 .../error.invalid-hook-for.expect.md | 26 + .../error.invalid-hook-for.js | 0 .../error.invalid-hook-if-alternate.expect.md | 23 + .../error.invalid-hook-if-alternate.js | 0 ...error.invalid-hook-if-consequent.expect.md | 22 + .../error.invalid-hook-if-consequent.js | 0 .../error.rules-of-hooks-32.expect.md | 22 + .../rules-of-hooks/error.rules-of-hooks-32.js | 7 + .../error.rules-of-hooks-38.expect.md | 22 + .../rules-of-hooks/error.rules-of-hooks-38.js | 7 + .../error.rules-of-hooks-40.expect.md | 20 + .../rules-of-hooks/error.rules-of-hooks-40.js | 5 + .../error.rules-of-hooks-47.expect.md | 22 + .../rules-of-hooks/error.rules-of-hooks-47.js | 7 + .../error.rules-of-hooks-51.expect.md | 22 + .../rules-of-hooks/error.rules-of-hooks-51.js | 7 + .../error.rules-of-hooks-52.expect.md | 35 + .../rules-of-hooks/error.rules-of-hooks-52.js | 14 + .../error.rules-of-hooks-53.expect.md | 26 + .../rules-of-hooks/error.rules-of-hooks-53.js | 9 + .../error.rules-of-hooks-54.expect.md | 23 + .../rules-of-hooks/error.rules-of-hooks-54.js | 8 + .../error.rules-of-hooks-56.expect.md | 21 + .../rules-of-hooks/error.rules-of-hooks-56.js | 6 + .../error.rules-of-hooks-57.expect.md | 26 + .../rules-of-hooks/error.rules-of-hooks-57.js | 11 + .../error.rules-of-hooks-58.expect.md | 26 + .../rules-of-hooks/error.rules-of-hooks-58.js | 11 + .../error.rules-of-hooks-59.expect.md | 23 + .../rules-of-hooks/error.rules-of-hooks-59.js | 6 + .../rules-of-hooks/rules-of-hooks-0.expect.md | 21 + .../rules-of-hooks/rules-of-hooks-0.js | 4 + .../rules-of-hooks/rules-of-hooks-1.expect.md | 33 + .../rules-of-hooks/rules-of-hooks-1.js | 6 + .../rules-of-hooks-10.expect.md | 23 + .../rules-of-hooks/rules-of-hooks-10.js | 5 + .../rules-of-hooks-11.expect.md | 35 + .../rules-of-hooks/rules-of-hooks-11.js | 7 + .../rules-of-hooks-12.expect.md | 21 + .../rules-of-hooks/rules-of-hooks-12.js | 4 + .../rules-of-hooks-13.expect.md | 21 + .../rules-of-hooks/rules-of-hooks-13.js | 4 + .../rules-of-hooks-14.expect.md | 21 + .../rules-of-hooks/rules-of-hooks-14.js | 4 + .../rules-of-hooks-16.expect.md | 25 + .../rules-of-hooks/rules-of-hooks-16.js | 6 + .../rules-of-hooks-17.expect.md | 25 + .../rules-of-hooks/rules-of-hooks-17.js | 6 + .../rules-of-hooks-19.expect.md | 25 + .../rules-of-hooks/rules-of-hooks-19.js | 6 + .../rules-of-hooks/rules-of-hooks-2.expect.md | 21 + .../rules-of-hooks/rules-of-hooks-2.js | 4 + .../rules-of-hooks-20.expect.md | 29 + .../rules-of-hooks/rules-of-hooks-20.js | 8 + .../rules-of-hooks-22.expect.md | 27 + .../rules-of-hooks/rules-of-hooks-22.js | 7 + .../rules-of-hooks-23.expect.md | 35 + .../rules-of-hooks/rules-of-hooks-23.js | 11 + .../rules-of-hooks-25.expect.md | 27 + .../rules-of-hooks/rules-of-hooks-25.js | 7 + .../rules-of-hooks-29.expect.md | 243 +++++ .../rules-of-hooks/rules-of-hooks-29.js | 136 +++ .../rules-of-hooks/rules-of-hooks-3.expect.md | 33 + .../rules-of-hooks/rules-of-hooks-3.js | 6 + .../rules-of-hooks/rules-of-hooks-4.expect.md | 21 + .../rules-of-hooks/rules-of-hooks-4.js | 4 + .../rules-of-hooks/rules-of-hooks-5.expect.md | 21 + .../rules-of-hooks/rules-of-hooks-5.js | 4 + .../rules-of-hooks/rules-of-hooks-6.expect.md | 25 + .../rules-of-hooks/rules-of-hooks-6.js | 6 + .../rules-of-hooks/rules-of-hooks-8.expect.md | 22 + .../rules-of-hooks/rules-of-hooks-8.js | 6 + .../todo.error.rules-of-hooks-33.js | 7 + .../todo.error.rules-of-hooks-34.js | 8 + .../todo.error.rules-of-hooks-35.js | 11 + .../todo.error.rules-of-hooks-36.js | 9 + .../todo.error.rules-of-hooks-37.js | 11 + .../todo.error.rules-of-hooks-39.js | 11 + .../todo.error.rules-of-hooks-41.js | 9 + .../todo.error.rules-of-hooks-42.js | 11 + .../todo.error.rules-of-hooks-45.js | 9 + .../todo.error.rules-of-hooks-46.js | 11 + .../todo.error.rules-of-hooks-48.js | 11 + .../todo.error.rules-of-hooks-49.js | 9 + .../todo.error.rules-of-hooks-50.js | 9 + .../todo.error.rules-of-hooks-63.js | 10 + .../todo.error.rules-of-hooks-64.js | 10 + .../todo.error.rules-of-hooks-65.js | 7 + .../todo.error.rules-of-hooks-66.js | 13 + .../todo.error.rules-of-hooks-67.js | 14 + .../todo.error.rules-of-hooks-68.js | 9 + .../todo.error.rules-of-hooks-69.js | 7 + .../todo.error.rules-of-hooks-70.js | 7 + .../todo.error.rules-of-hooks-71.js | 7 + .../todo.error.rules-of-hooks-72.js | 7 + .../todo.error.rules-of-hooks-73.js | 7 + .../rules-of-hooks/todo.rules-of-hooks-15.js | 8 + .../rules-of-hooks/todo.rules-of-hooks-18.js | 8 + .../rules-of-hooks/todo.rules-of-hooks-21.js | 7 + .../rules-of-hooks/todo.rules-of-hooks-24.js | 9 + .../rules-of-hooks/todo.rules-of-hooks-26.js | 7 + .../rules-of-hooks/todo.rules-of-hooks-27.js | 9 + .../rules-of-hooks/todo.rules-of-hooks-28.js | 11 + .../rules-of-hooks/todo.rules-of-hooks-30.js | 69 ++ .../rules-of-hooks/todo.rules-of-hooks-31.js | 12 + .../rules-of-hooks/todo.rules-of-hooks-43.js | 10 + .../rules-of-hooks/todo.rules-of-hooks-44.js | 10 + .../rules-of-hooks/todo.rules-of-hooks-55.js | 40 + .../rules-of-hooks/todo.rules-of-hooks-60.js | 10 + .../rules-of-hooks/todo.rules-of-hooks-61.js | 9 + .../rules-of-hooks/todo.rules-of-hooks-62.js | 10 + .../rules-of-hooks/todo.rules-of-hooks-7.js | 8 + .../rules-of-hooks/todo.rules-of-hooks-9.js | 39 + .../test-utils/generateTestsFromFixtures.ts | 16 +- compiler/forget/yarn.lock | 65 +- 120 files changed, 3126 insertions(+), 15 deletions(-) create mode 100644 compiler/forget/scripts/build-react-hooks-fixures.js create mode 100644 compiler/forget/scripts/eslint-plugin-react-hooks-test-cases.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-after-early-return.expect.md rename compiler/forget/src/__tests__/fixtures/compiler/{ => rules-of-hooks}/error.invalid-hook-after-early-return.js (100%) create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-for.expect.md rename compiler/forget/src/__tests__/fixtures/compiler/{ => rules-of-hooks}/error.invalid-hook-for.js (100%) create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-alternate.expect.md rename compiler/forget/src/__tests__/fixtures/compiler/{ => rules-of-hooks}/error.invalid-hook-if-alternate.js (100%) create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-consequent.expect.md rename compiler/forget/src/__tests__/fixtures/compiler/{ => rules-of-hooks}/error.invalid-hook-if-consequent.js (100%) create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-32.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-32.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-38.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-38.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-40.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-40.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-47.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-47.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-51.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-51.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-52.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-52.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-53.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-53.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-54.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-54.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-56.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-56.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-57.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-57.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-58.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-58.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-59.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-59.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-0.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-0.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-1.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-1.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-10.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-10.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-11.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-11.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-12.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-12.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-13.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-13.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-14.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-14.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-16.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-16.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-17.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-17.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-19.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-19.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-2.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-2.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-20.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-20.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-22.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-22.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-23.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-23.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-25.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-25.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-29.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-29.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-3.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-3.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-4.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-4.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-5.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-5.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-6.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-6.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-8.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-8.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-33.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-34.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-35.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-36.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-37.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-39.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-41.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-42.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-45.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-46.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-48.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-49.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-50.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-63.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-64.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-65.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-66.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-67.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-68.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-69.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-70.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-71.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-72.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-73.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-15.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-18.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-21.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-24.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-26.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-27.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-28.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-30.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-31.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-43.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-44.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-55.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-60.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-61.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-62.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-7.js create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-9.js diff --git a/compiler/forget/package.json b/compiler/forget/package.json index 6721176bd4..07b345f77f 100644 --- a/compiler/forget/package.json +++ b/compiler/forget/package.json @@ -49,6 +49,7 @@ "@babel/traverse": "^7.19.1", "@testing-library/react": "^13.4.0", "@tsconfig/node16-strictest": "^1.0.3", + "@types/glob": "^8.1.0", "@types/invariant": "^2.2.35", "@types/jest": "^29.0.3", "@types/node": "^18.7.18", diff --git a/compiler/forget/scripts/build-react-hooks-fixures.js b/compiler/forget/scripts/build-react-hooks-fixures.js new file mode 100644 index 0000000000..04c238278a --- /dev/null +++ b/compiler/forget/scripts/build-react-hooks-fixures.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +const { tests } = require("./eslint-plugin-react-hooks-test-cases"); +const { + runReactForgetBabelPlugin, +} = require("../dist/Babel/RunReactForgetBabelPlugin"); +const fs = require("fs"); +const path = require("path"); +const prettier = require("prettier"); +const prettierConfigPath = require.resolve("../.prettierrc"); +const process = require("process"); + +const FIXTURES_DIR = path.join( + process.cwd(), + "src", + "__tests__", + "fixtures", + "compiler", + "rules-of-hooks" +); + +const fixtures = []; +for (const test of tests.valid) { + fixtures.push({ code: test.code, valid: true }); +} +for (const test of tests.invalid) { + fixtures.push({ code: test.code, valid: false }); +} + +let index = 0; +for (const fixture of fixtures) { + let error = null; + let passes = true; + try { + // Does the fixture pass with hooks validation disabled? if not skip it + runReactForgetBabelPlugin(fixture.code, "rules-of-hooks.js", "typescript", { + environment: { + validateHooksUsage: false, + }, + }); + // Does the fixture pass with hooks validation enabled? + try { + runReactForgetBabelPlugin( + fixture.code, + "rules-of-hooks.js", + "typescript", + { + environment: { + validateHooksUsage: true, + }, + } + ); + } catch (e) { + passes = false; + } + } catch (e) { + error = e; + } + let name = `rules-of-hooks-${index}.js`; + let code = fixture.code; + if (error !== null) { + name = `todo.${name}`; + code = `// @skip\n${code}`; + } else if (fixture.valid === false) { + name = `error.${name}`; + if (passes) { + // oops, passed when we expected an error + name = `todo.${name}`; + code = `// @skip\n${code}`; + } + } else if (!passes) { + // oops, error when it should have passed + name = `todo.${name}`; + code = `// @skip\n${code}`; + } + + const fixturePath = path.join(FIXTURES_DIR, name); + const options = prettier.resolveConfig.sync(fixturePath, { + config: prettierConfigPath, + }); + const formatted = prettier.format(code, options); + fs.writeFileSync(fixturePath, formatted, "utf8"); + index++; +} diff --git a/compiler/forget/scripts/eslint-plugin-react-hooks-test-cases.js b/compiler/forget/scripts/eslint-plugin-react-hooks-test-cases.js new file mode 100644 index 0000000000..d47a1d38b7 --- /dev/null +++ b/compiler/forget/scripts/eslint-plugin-react-hooks-test-cases.js @@ -0,0 +1,985 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +// NOTE: Extracted from https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js + +/** + * A string template tag that removes padding from the left side of multi-line strings + */ +function normalizeIndent(strings) { + const codeLines = strings[0].split("\n"); + const leftPadding = codeLines[1].match(/\s+/)[0]; + return codeLines.map((line) => line.slice(leftPadding.length)).join("\n"); +} + +module.exports.tests = { + valid: [ + { + code: normalizeIndent` + // Valid because components can use hooks. + function ComponentWithHook() { + useHook(); + } + `, + }, + { + code: normalizeIndent` + // Valid because components can use hooks. + function createComponentWithHook() { + return function ComponentWithHook() { + useHook(); + }; + } + `, + }, + { + code: normalizeIndent` + // Valid because hooks can use hooks. + function useHookWithHook() { + useHook(); + } + `, + }, + { + code: normalizeIndent` + // Valid because hooks can use hooks. + function createHook() { + return function useHookWithHook() { + useHook(); + } + } + `, + }, + { + code: normalizeIndent` + // Valid because components can call functions. + function ComponentWithNormalFunction() { + doSomething(); + } + `, + }, + { + code: normalizeIndent` + // Valid because functions can call functions. + function normalFunctionWithNormalFunction() { + doSomething(); + } + `, + }, + { + code: normalizeIndent` + // Valid because functions can call functions. + function normalFunctionWithConditionalFunction() { + if (cond) { + doSomething(); + } + } + `, + }, + { + code: normalizeIndent` + // Valid because functions can call functions. + function functionThatStartsWithUseButIsntAHook() { + if (cond) { + userFetch(); + } + } + `, + }, + { + code: normalizeIndent` + // Valid although unconditional return doesn't make sense and would fail other rules. + // We could make it invalid but it doesn't matter. + function useUnreachable() { + return; + useHook(); + } + `, + }, + { + code: normalizeIndent` + // Valid because hooks can call hooks. + function useHook() { useState(); } + const whatever = function useHook() { useState(); }; + const useHook1 = () => { useState(); }; + let useHook2 = () => useState(); + useHook2 = () => { useState(); }; + ({useHook: () => { useState(); }}); + ({useHook() { useState(); }}); + const {useHook3 = () => { useState(); }} = {}; + ({useHook = () => { useState(); }} = {}); + Namespace.useHook = () => { useState(); }; + `, + }, + { + code: normalizeIndent` + // Valid because hooks can call hooks. + function useHook() { + useHook1(); + useHook2(); + } + `, + }, + { + code: normalizeIndent` + // Valid because hooks can call hooks. + function createHook() { + return function useHook() { + useHook1(); + useHook2(); + }; + } + `, + }, + { + code: normalizeIndent` + // Valid because hooks can call hooks. + function useHook() { + useState() && a; + } + `, + }, + { + code: normalizeIndent` + // Valid because hooks can call hooks. + function useHook() { + return useHook1() + useHook2(); + } + `, + }, + { + code: normalizeIndent` + // Valid because hooks can call hooks. + function useHook() { + return useHook1(useHook2()); + } + `, + }, + { + code: normalizeIndent` + // Valid because hooks can be used in anonymous arrow-function arguments + // to forwardRef. + const FancyButton = React.forwardRef((props, ref) => { + useHook(); + return ; + }); + `, + errors: [], + }, + { + code: normalizeIndent` + // Invalid because it's dangerous and might not warn otherwise. + // This *must* be invalid. + const FancyButton = forwardRef(function(props, ref) { + if (props.fancy) { + useCustomHook(); + } + return ; + }); + `, + errors: [], + }, + { + code: normalizeIndent` + // Invalid because it's dangerous and might not warn otherwise. + // This *must* be invalid. + const MemoizedButton = memo(function(props) { + if (props.fancy) { + useCustomHook(); + } + return ; + }); + `, + errors: [], + }, + { + code: normalizeIndent` + // This is invalid because "use"-prefixed functions used in named + // functions are assumed to be hooks. + React.unknownFunction(function notAComponent(foo, bar) { + useProbablyAHook(bar) + }); + `, + errors: [], + }, + { + code: normalizeIndent` + // Invalid because it's dangerous. + // Normally, this would crash, but not if you use inline requires. + // This *must* be invalid. + // It's expected to have some false positives, but arguably + // they are confusing anyway due to the use*() convention + // already being associated with Hooks. + useState(); + if (foo) { + const foo = React.useCallback(() => {}); + } + useCustomHook(); + `, + errors: [], + }, + { + code: normalizeIndent` + // Technically this is a false positive. + // We *could* make it valid (and it used to be). + // + // However, top-level Hook-like calls can be very dangerous + // in environments with inline requires because they can mask + // the runtime error by accident. + // So we prefer to disallow it despite the false positive. + + const {createHistory, useBasename} = require('history-2.1.2'); + const browserHistory = useBasename(createHistory)({ + basename: '/', + }); + `, + errors: [], + }, + { + code: normalizeIndent` + class ClassComponentWithFeatureFlag extends React.Component { + render() { + if (foo) { + useFeatureFlag(); + } + } + } + `, + errors: [], + }, + { + code: normalizeIndent` + class ClassComponentWithHook extends React.Component { + render() { + React.useState(); + } + } + `, + errors: [], + }, + { + code: normalizeIndent` + (class {useHook = () => { useState(); }}); + `, + errors: [], + }, + { + code: normalizeIndent` + (class {useHook() { useState(); }}); + `, + errors: [], + }, + { + code: normalizeIndent` + (class {h = () => { useState(); }}); + `, + errors: [], + }, + { + code: normalizeIndent` + (class {i() { useState(); }}); + `, + errors: [], + }, + ], +}; diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-after-early-return.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-after-early-return.expect.md new file mode 100644 index 0000000000..f8146c8510 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-after-early-return.expect.md @@ -0,0 +1,21 @@ + +## Input + +```javascript +function Component(props) { + if (props.cond) { + return null; + } + return useHook(); +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (5:5) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/error.invalid-hook-after-early-return.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-after-early-return.js similarity index 100% rename from compiler/forget/src/__tests__/fixtures/compiler/error.invalid-hook-after-early-return.js rename to compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-after-early-return.js diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-for.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-for.expect.md new file mode 100644 index 0000000000..a9acc25430 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-for.expect.md @@ -0,0 +1,26 @@ + +## Input + +```javascript +function Component(props) { + let i = 0; + for (let x = 0; useHook(x) < 10; useHook(i), x++) { + i += useHook(x); + } + return i; +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3) + +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (4:4) + +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/error.invalid-hook-for.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-for.js similarity index 100% rename from compiler/forget/src/__tests__/fixtures/compiler/error.invalid-hook-for.js rename to compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-for.js diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-alternate.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-alternate.expect.md new file mode 100644 index 0000000000..51c2925634 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-alternate.expect.md @@ -0,0 +1,23 @@ + +## Input + +```javascript +function Component(props) { + let x = null; + if (props.cond) { + } else { + x = useHook(); + } + return x; +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (5:5) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/error.invalid-hook-if-alternate.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-alternate.js similarity index 100% rename from compiler/forget/src/__tests__/fixtures/compiler/error.invalid-hook-if-alternate.js rename to compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-alternate.js diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-consequent.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-consequent.expect.md new file mode 100644 index 0000000000..d21db9859d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-consequent.expect.md @@ -0,0 +1,22 @@ + +## Input + +```javascript +function Component(props) { + let x = null; + if (props.cond) { + x = useHook(); + } + return x; +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (4:4) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/error.invalid-hook-if-consequent.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-consequent.js similarity index 100% rename from compiler/forget/src/__tests__/fixtures/compiler/error.invalid-hook-if-consequent.js rename to compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-if-consequent.js diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-32.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-32.expect.md new file mode 100644 index 0000000000..48f4ab1bd8 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-32.expect.md @@ -0,0 +1,22 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function ComponentWithConditionalHook() { + if (cond) { + useConditionalHook(); + } +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (5:5) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-32.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-32.js new file mode 100644 index 0000000000..455b7fb367 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-32.js @@ -0,0 +1,7 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function ComponentWithConditionalHook() { + if (cond) { + useConditionalHook(); + } +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-38.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-38.expect.md new file mode 100644 index 0000000000..0703fedacd --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-38.expect.md @@ -0,0 +1,22 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHookWithConditionalHook() { + if (cond) { + useConditionalHook(); + } +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (5:5) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-38.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-38.js new file mode 100644 index 0000000000..8b82456138 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-38.js @@ -0,0 +1,7 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHookWithConditionalHook() { + if (cond) { + useConditionalHook(); + } +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-40.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-40.expect.md new file mode 100644 index 0000000000..a79a682a9f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-40.expect.md @@ -0,0 +1,20 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function ComponentWithTernaryHook() { + cond ? useTernaryHook() : null; +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (4:4) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-40.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-40.js new file mode 100644 index 0000000000..c5754bd624 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-40.js @@ -0,0 +1,5 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function ComponentWithTernaryHook() { + cond ? useTernaryHook() : null; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-47.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-47.expect.md new file mode 100644 index 0000000000..a8401e92a7 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-47.expect.md @@ -0,0 +1,22 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function ComponentWithHookInsideLoop() { + while (cond) { + useHookInsideLoop(); + } +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (5:5) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-47.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-47.js new file mode 100644 index 0000000000..25177f5938 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-47.js @@ -0,0 +1,7 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function ComponentWithHookInsideLoop() { + while (cond) { + useHookInsideLoop(); + } +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-51.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-51.expect.md new file mode 100644 index 0000000000..9b14df8262 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-51.expect.md @@ -0,0 +1,22 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function normalFunctionWithConditionalHook() { + if (cond) { + useHookInsideNormalFunction(); + } +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (5:5) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-51.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-51.js new file mode 100644 index 0000000000..89bfce1eda --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-51.js @@ -0,0 +1,7 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function normalFunctionWithConditionalHook() { + if (cond) { + useHookInsideNormalFunction(); + } +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-52.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-52.expect.md new file mode 100644 index 0000000000..c73794ad22 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-52.expect.md @@ -0,0 +1,35 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHookInLoops() { + while (a) { + useHook1(); + if (b) return; + useHook2(); + } + while (c) { + useHook3(); + if (d) return; + useHook4(); + } +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (5:5) + +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (7:7) + +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (10:10) + +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (12:12) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-52.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-52.js new file mode 100644 index 0000000000..2fb10722a6 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-52.js @@ -0,0 +1,14 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHookInLoops() { + while (a) { + useHook1(); + if (b) return; + useHook2(); + } + while (c) { + useHook3(); + if (d) return; + useHook4(); + } +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-53.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-53.expect.md new file mode 100644 index 0000000000..33298ad7c6 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-53.expect.md @@ -0,0 +1,26 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHookInLoops() { + while (a) { + useHook1(); + if (b) continue; + useHook2(); + } +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (5:5) + +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (7:7) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-53.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-53.js new file mode 100644 index 0000000000..c00a73a62d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-53.js @@ -0,0 +1,9 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHookInLoops() { + while (a) { + useHook1(); + if (b) continue; + useHook2(); + } +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-54.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-54.expect.md new file mode 100644 index 0000000000..bf1bf3336d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-54.expect.md @@ -0,0 +1,23 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useLabeledBlock() { + label: { + if (a) break label; + useHook(); + } +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (6:6) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-54.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-54.js new file mode 100644 index 0000000000..e5f6732143 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-54.js @@ -0,0 +1,8 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useLabeledBlock() { + label: { + if (a) break label; + useHook(); + } +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-56.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-56.expect.md new file mode 100644 index 0000000000..c466eb9725 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-56.expect.md @@ -0,0 +1,21 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHook() { + if (a) return; + useState(); +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (5:5) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-56.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-56.js new file mode 100644 index 0000000000..7e77918794 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-56.js @@ -0,0 +1,6 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHook() { + if (a) return; + useState(); +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-57.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-57.expect.md new file mode 100644 index 0000000000..6f3a91949e --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-57.expect.md @@ -0,0 +1,26 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHook() { + if (a) return; + if (b) { + console.log("true"); + } else { + console.log("false"); + } + useState(); +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (10:10) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-57.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-57.js new file mode 100644 index 0000000000..fb5423937b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-57.js @@ -0,0 +1,11 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHook() { + if (a) return; + if (b) { + console.log("true"); + } else { + console.log("false"); + } + useState(); +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-58.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-58.expect.md new file mode 100644 index 0000000000..95b2471979 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-58.expect.md @@ -0,0 +1,26 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHook() { + if (b) { + console.log("true"); + } else { + console.log("false"); + } + if (a) return; + useState(); +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (10:10) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-58.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-58.js new file mode 100644 index 0000000000..c2fc30ec88 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-58.js @@ -0,0 +1,11 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHook() { + if (b) { + console.log("true"); + } else { + console.log("false"); + } + if (a) return; + useState(); +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-59.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-59.expect.md new file mode 100644 index 0000000000..528da10835 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-59.expect.md @@ -0,0 +1,23 @@ + +## Input + +```javascript +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHook() { + a && useHook1(); + b && useHook2(); +} + +``` + + +## Error + +``` +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (4:4) + +[ReactForget] InvalidInput: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (5:5) +``` + + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-59.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-59.js new file mode 100644 index 0000000000..09245c8884 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.rules-of-hooks-59.js @@ -0,0 +1,6 @@ +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +function useHook() { + a && useHook1(); + b && useHook2(); +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-0.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-0.expect.md new file mode 100644 index 0000000000..d84e26368f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-0.expect.md @@ -0,0 +1,21 @@ + +## Input + +```javascript +// Valid because components can use hooks. +function ComponentWithHook() { + useHook(); +} + +``` + +## Code + +```javascript +// Valid because components can use hooks. +function ComponentWithHook() { + useHook(); +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-0.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-0.js new file mode 100644 index 0000000000..d0a47a7001 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-0.js @@ -0,0 +1,4 @@ +// Valid because components can use hooks. +function ComponentWithHook() { + useHook(); +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-1.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-1.expect.md new file mode 100644 index 0000000000..e4d4d1c66d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-1.expect.md @@ -0,0 +1,33 @@ + +## Input + +```javascript +// Valid because components can use hooks. +function createComponentWithHook() { + return function ComponentWithHook() { + useHook(); + }; +} + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; // Valid because components can use hooks. +function createComponentWithHook() { + const $ = useMemoCache(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = function ComponentWithHook() { + useHook(); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-1.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-1.js new file mode 100644 index 0000000000..9b5bcde4be --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-1.js @@ -0,0 +1,6 @@ +// Valid because components can use hooks. +function createComponentWithHook() { + return function ComponentWithHook() { + useHook(); + }; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-10.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-10.expect.md new file mode 100644 index 0000000000..50fb6486c9 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-10.expect.md @@ -0,0 +1,23 @@ + +## Input + +```javascript +// Valid because hooks can call hooks. +function useHook() { + useHook1(); + useHook2(); +} + +``` + +## Code + +```javascript +// Valid because hooks can call hooks. +function useHook() { + useHook1(); + useHook2(); +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-10.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-10.js new file mode 100644 index 0000000000..2ab771dc31 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-10.js @@ -0,0 +1,5 @@ +// Valid because hooks can call hooks. +function useHook() { + useHook1(); + useHook2(); +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-11.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-11.expect.md new file mode 100644 index 0000000000..ca7131796f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-11.expect.md @@ -0,0 +1,35 @@ + +## Input + +```javascript +// Valid because hooks can call hooks. +function createHook() { + return function useHook() { + useHook1(); + useHook2(); + }; +} + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; // Valid because hooks can call hooks. +function createHook() { + const $ = useMemoCache(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = function useHook() { + useHook1(); + useHook2(); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-11.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-11.js new file mode 100644 index 0000000000..e2c9e72adf --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-11.js @@ -0,0 +1,7 @@ +// Valid because hooks can call hooks. +function createHook() { + return function useHook() { + useHook1(); + useHook2(); + }; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-12.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-12.expect.md new file mode 100644 index 0000000000..6db60d1c8b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-12.expect.md @@ -0,0 +1,21 @@ + +## Input + +```javascript +// Valid because hooks can call hooks. +function useHook() { + useState() && a; +} + +``` + +## Code + +```javascript +// Valid because hooks can call hooks. +function useHook() { + useState() && a; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-12.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-12.js new file mode 100644 index 0000000000..6936266e58 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-12.js @@ -0,0 +1,4 @@ +// Valid because hooks can call hooks. +function useHook() { + useState() && a; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-13.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-13.expect.md new file mode 100644 index 0000000000..4bcb4dee57 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-13.expect.md @@ -0,0 +1,21 @@ + +## Input + +```javascript +// Valid because hooks can call hooks. +function useHook() { + return useHook1() + useHook2(); +} + +``` + +## Code + +```javascript +// Valid because hooks can call hooks. +function useHook() { + return useHook1() + useHook2(); +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-13.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-13.js new file mode 100644 index 0000000000..c1356a4b6d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-13.js @@ -0,0 +1,4 @@ +// Valid because hooks can call hooks. +function useHook() { + return useHook1() + useHook2(); +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-14.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-14.expect.md new file mode 100644 index 0000000000..ae50157e09 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-14.expect.md @@ -0,0 +1,21 @@ + +## Input + +```javascript +// Valid because hooks can call hooks. +function useHook() { + return useHook1(useHook2()); +} + +``` + +## Code + +```javascript +// Valid because hooks can call hooks. +function useHook() { + return useHook1(useHook2()); +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-14.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-14.js new file mode 100644 index 0000000000..1c16d086bf --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-14.js @@ -0,0 +1,4 @@ +// Valid because hooks can call hooks. +function useHook() { + return useHook1(useHook2()); +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-16.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-16.expect.md new file mode 100644 index 0000000000..978a78e0c1 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-16.expect.md @@ -0,0 +1,25 @@ + +## Input + +```javascript +// Valid because hooks can be used in anonymous function arguments to +// forwardRef. +const FancyButton = React.forwardRef(function (props, ref) { + useHook(); + return ; +}); diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-64.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-64.js new file mode 100644 index 0000000000..a257479288 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-64.js @@ -0,0 +1,10 @@ +// @skip + +// Invalid because it's dangerous and might not warn otherwise. +// This *must* be invalid. +const MemoizedButton = memo(function (props) { + if (props.fancy) { + useCustomHook(); + } + return ; +}); diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-65.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-65.js new file mode 100644 index 0000000000..b647cb6968 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-65.js @@ -0,0 +1,7 @@ +// @skip + +// This is invalid because "use"-prefixed functions used in named +// functions are assumed to be hooks. +React.unknownFunction(function notAComponent(foo, bar) { + useProbablyAHook(bar); +}); diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-66.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-66.js new file mode 100644 index 0000000000..e60fc3c5ec --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-66.js @@ -0,0 +1,13 @@ +// @skip + +// Invalid because it's dangerous. +// Normally, this would crash, but not if you use inline requires. +// This *must* be invalid. +// It's expected to have some false positives, but arguably +// they are confusing anyway due to the use*() convention +// already being associated with Hooks. +useState(); +if (foo) { + const foo = React.useCallback(() => {}); +} +useCustomHook(); diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-67.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-67.js new file mode 100644 index 0000000000..175e9631a2 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-67.js @@ -0,0 +1,14 @@ +// @skip + +// Technically this is a false positive. +// We *could* make it valid (and it used to be). +// +// However, top-level Hook-like calls can be very dangerous +// in environments with inline requires because they can mask +// the runtime error by accident. +// So we prefer to disallow it despite the false positive. + +const { createHistory, useBasename } = require("history-2.1.2"); +const browserHistory = useBasename(createHistory)({ + basename: "/", +}); diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-68.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-68.js new file mode 100644 index 0000000000..861a9bb241 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-68.js @@ -0,0 +1,9 @@ +// @skip + +class ClassComponentWithFeatureFlag extends React.Component { + render() { + if (foo) { + useFeatureFlag(); + } + } +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-69.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-69.js new file mode 100644 index 0000000000..59b5f2eeee --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-69.js @@ -0,0 +1,7 @@ +// @skip + +class ClassComponentWithHook extends React.Component { + render() { + React.useState(); + } +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-70.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-70.js new file mode 100644 index 0000000000..d0217a5ab2 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-70.js @@ -0,0 +1,7 @@ +// @skip + +(class { + useHook = () => { + useState(); + }; +}); diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-71.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-71.js new file mode 100644 index 0000000000..17ade6f71f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-71.js @@ -0,0 +1,7 @@ +// @skip + +(class { + useHook() { + useState(); + } +}); diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-72.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-72.js new file mode 100644 index 0000000000..54cd765991 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-72.js @@ -0,0 +1,7 @@ +// @skip + +(class { + h = () => { + useState(); + }; +}); diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-73.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-73.js new file mode 100644 index 0000000000..3500c604ff --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-73.js @@ -0,0 +1,7 @@ +// @skip + +(class { + i() { + useState(); + } +}); diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-15.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-15.js new file mode 100644 index 0000000000..05d81522d1 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-15.js @@ -0,0 +1,8 @@ +// @skip + +// Valid because hooks can be used in anonymous arrow-function arguments +// to forwardRef. +const FancyButton = React.forwardRef((props, ref) => { + useHook(); + return ; +}); diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-7.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-7.js new file mode 100644 index 0000000000..93f6a55ac5 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-7.js @@ -0,0 +1,8 @@ +// @skip + +// Valid because functions can call functions. +function functionThatStartsWithUseButIsntAHook() { + if (cond) { + userFetch(); + } +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-9.js b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-9.js new file mode 100644 index 0000000000..c43ac4caae --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.rules-of-hooks-9.js @@ -0,0 +1,39 @@ +// @skip + +// Valid because hooks can call hooks. +function useHook() { + useState(); +} +const whatever = function useHook() { + useState(); +}; +const useHook1 = () => { + useState(); +}; +let useHook2 = () => useState(); +useHook2 = () => { + useState(); +}; +({ + useHook: () => { + useState(); + }, +}); +({ + useHook() { + useState(); + }, +}); +const { + useHook3 = () => { + useState(); + }, +} = {}; +({ + useHook = () => { + useState(); + }, +} = {}); +Namespace.useHook = () => { + useState(); +}; diff --git a/compiler/forget/src/__tests__/test-utils/generateTestsFromFixtures.ts b/compiler/forget/src/__tests__/test-utils/generateTestsFromFixtures.ts index ffaf53ae70..149f4cd65f 100644 --- a/compiler/forget/src/__tests__/test-utils/generateTestsFromFixtures.ts +++ b/compiler/forget/src/__tests__/test-utils/generateTestsFromFixtures.ts @@ -10,6 +10,7 @@ /* global expect,test */ import fs from "fs"; +import glob from "glob"; import path from "path"; import { GatingOptions, PluginOptions } from "../../Babel/PluginOptions"; @@ -55,7 +56,9 @@ export default function generateTestsFromFixtures( ) { let files: Array; try { - files = fs.readdirSync(fixturesPath); + files = glob.sync("**/*.{js,md}", { + cwd: fixturesPath, + }); } catch (e) { if (e.code === "ENOENT") { files = []; @@ -233,11 +236,12 @@ function matchInputOutputFixtures(files: string[], fixturesPath: string) { ); } entry.input = resolvedPath; - const outputFile = path.format({ - dir: fixturesPath, - name: basename, - ext: EXPECT_SUFFIX, - }); + const outputName = `${basename}${EXPECT_SUFFIX}`; + const outputFile = path.join( + fixturesPath, + path.dirname(file), + outputName + ); entry.output = outputFile; } } diff --git a/compiler/forget/yarn.lock b/compiler/forget/yarn.lock index b0e6f1b618..d971d03b4b 100644 --- a/compiler/forget/yarn.lock +++ b/compiler/forget/yarn.lock @@ -81,7 +81,7 @@ source-map "^0.5.0" trim-right "^1.0.1" -"@babel/generator@^7.0.0", "@babel/generator@^7.21.4": +"@babel/generator@^7.0.0": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== @@ -100,6 +100,16 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.5.tgz#c0c0e5449504c7b7de8236d99338c3e2a340745f" + integrity sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w== + dependencies: + "@babel/types" "^7.21.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -135,6 +145,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba" + integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ== + "@babel/helper-function-name@^7.1.0", "@babel/helper-function-name@^7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" @@ -259,6 +274,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" + integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" @@ -307,7 +327,12 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3" integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ== -"@babel/parser@^7.21.4", "@babel/parser@^7.7.4": +"@babel/parser@^7.21.5": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" + integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== + +"@babel/parser@^7.7.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== @@ -582,18 +607,18 @@ lodash "^4.17.10" "@babel/traverse@^7.19.1": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" - integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" + integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== dependencies: "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-environment-visitor" "^7.18.9" + "@babel/generator" "^7.21.5" + "@babel/helper-environment-visitor" "^7.21.5" "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.4" - "@babel/types" "^7.21.4" + "@babel/parser" "^7.21.5" + "@babel/types" "^7.21.5" debug "^4.1.0" globals "^11.1.0" @@ -659,6 +684,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" + integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== + dependencies: + "@babel/helper-string-parser" "^7.21.5" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1110,6 +1144,14 @@ dependencies: "@babel/types" "^7.3.0" +"@types/glob@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" + integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== + dependencies: + "@types/minimatch" "^5.1.2" + "@types/node" "*" + "@types/graceful-fs@^4.1.3": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -1171,6 +1213,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/minimatch@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + "@types/node@*", "@types/node@^18.7.18": version "18.7.19" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.19.tgz#ad83aa9b7af470fab7e0f562be87e97dc8ffe08e"