From b7a14ecc8d931922b44e92496eedf9710ea3ce39 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Tue, 3 Oct 2023 09:00:11 -0700 Subject: [PATCH] Detect unknown hooks on React namespace Handles an edge-case from earlier in the stack. When looking up a property on a shape, if the property is defined we return it. But if it isn't defined, and the property name is a hook, we treat it like a default custom hook. --- .../src/HIR/Environment.ts | 9 +++++--- .../drop-methodcall-usememo.expect.md | 17 ++++++++++---- ...ook-unknown-hook-react-namespace.expect.md | 22 +++++++++++++++++++ ...ional-hook-unknown-hook-react-namespace.js | 7 ++++++ 4 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.expect.md create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.js diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts index f0b2298ca4..0e1f2662d5 100644 --- a/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts @@ -374,9 +374,12 @@ export class Environment { loc: null, suggestions: null, }); - return ( - shape.properties.get(property) ?? shape.properties.get("*") ?? null - ); + let value = + shape.properties.get(property) ?? shape.properties.get("*") ?? null; + if (value === null && isHookName(property)) { + value = this.#getCustomHookType(); + } + return value; } else if (isHookName(property)) { return this.#getCustomHookType(); } else { diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/drop-methodcall-usememo.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/drop-methodcall-usememo.expect.md index 84e25bb733..0a1381c761 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/drop-methodcall-usememo.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/drop-methodcall-usememo.expect.md @@ -27,21 +27,30 @@ import { unstable_useMemoCache as useMemoCache } from "react"; import * as React from "react"; function Component(props) { - const $ = useMemoCache(2); + const $ = useMemoCache(4); const c_0 = $[0] !== props.value; let t0; if (c_0) { - t0 = (() => { + t0 = () => { const x = []; x.push(props.value); return x; - })(); + }; $[0] = props.value; $[1] = t0; } else { t0 = $[1]; } - const x_0 = t0; + const c_2 = $[2] !== t0; + let t1; + if (c_2) { + t1 = t0(); + $[2] = t0; + $[3] = t1; + } else { + t1 = $[3]; + } + const x_0 = t1; return x_0; } diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.expect.md new file mode 100644 index 0000000000..8b55ba9e0c --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.expect.md @@ -0,0 +1,22 @@ + +## Input + +```javascript +function Component(props) { + let x = null; + if (props.cond) { + x = React.useNonexistentHook(); + } + return x; +} + +``` + + +## Error + +``` +[ReactForget] InvalidReact: 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/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.js new file mode 100644 index 0000000000..0698132c61 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.js @@ -0,0 +1,7 @@ +function Component(props) { + let x = null; + if (props.cond) { + x = React.useNonexistentHook(); + } + return x; +}