From 2d569a3353012d23b025847fd70869fbd20ef6fa Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Wed, 24 Apr 2024 15:15:34 -0700 Subject: [PATCH] Add detection of dynamic hooks ghstack-source-id: 3acfa4fde5dfd65353fa407378057ca937ee3599 Pull Request resolved: https://github.com/facebook/react-forget/pull/2901 --- .../src/Validation/ValidateHooksUsage.ts | 37 +++++++++++++++---- ...or.hook-property-load-local-hook.expect.md | 4 +- ...ror.invalid-assign-hook-to-local.expect.md | 2 +- ...or.invalid-pass-hook-as-call-arg.expect.md | 2 +- .../error.invalid-pass-hook-as-prop.expect.md | 2 +- ...invalid-ternary-with-hook-values.expect.md | 8 ++-- .../error.propertyload-hook.expect.md | 4 +- ...r.invalid-call-phi-possibly-hook.expect.md | 6 +-- ...-dynamic-hook-via-hooklike-local.expect.md | 25 +++++++++++++ ...invalid-dynamic-hook-via-hooklike-local.js | 5 +++ ...invalid-hook-as-conditional-test.expect.md | 2 +- .../error.invalid-hook-as-prop.expect.md | 22 +++++++++++ .../error.invalid-hook-as-prop.js | 3 ++ ...or.invalid-hook-from-hook-return.expect.md | 26 +++++++++++++ .../error.invalid-hook-from-hook-return.js | 5 +++ ...hook-from-property-of-other-hook.expect.md | 26 +++++++++++++ ...nvalid-hook-from-property-of-other-hook.js | 5 +++ ...d-hook-reassigned-in-conditional.expect.md | 6 +-- 18 files changed, 164 insertions(+), 26 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-dynamic-hook-via-hooklike-local.expect.md create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-dynamic-hook-via-hooklike-local.js create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-prop.expect.md create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-prop.js create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-hook-return.expect.md create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-hook-return.js create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-property-of-other-hook.expect.md create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-property-of-other-hook.js diff --git a/compiler/packages/babel-plugin-react-forget/src/Validation/ValidateHooksUsage.ts b/compiler/packages/babel-plugin-react-forget/src/Validation/ValidateHooksUsage.ts index 27d4004aef..28819f2cd0 100644 --- a/compiler/packages/babel-plugin-react-forget/src/Validation/ValidateHooksUsage.ts +++ b/compiler/packages/babel-plugin-react-forget/src/Validation/ValidateHooksUsage.ts @@ -122,8 +122,7 @@ export function validateHooksUsage(fn: HIRFunction): void { place.loc, new CompilerErrorDetail({ description: null, - reason: - "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)", + reason, loc: place.loc, severity: ErrorSeverity.InvalidReact, suggestions: null, @@ -140,7 +139,24 @@ export function validateHooksUsage(fn: HIRFunction): void { new CompilerErrorDetail({ description: null, reason: - "Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)", + "Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values", + loc: place.loc, + severity: ErrorSeverity.InvalidReact, + suggestions: null, + }) + ); + } + } + function recordDynamicHookUsageError(place: Place): void { + const previousError = + typeof place.loc !== "symbol" ? errorsByPlace.get(place.loc) : undefined; + if (previousError === undefined) { + recordError( + place.loc, + new CompilerErrorDetail({ + description: null, + reason: + "Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks", loc: place.loc, severity: ErrorSeverity.InvalidReact, suggestions: null, @@ -224,7 +240,10 @@ export function validateHooksUsage(fn: HIRFunction): void { case "StoreLocal": case "StoreContext": { visitPlace(instr.value.value); - const kind = getKindForPlace(instr.value.value); + const kind = joinKinds( + getKindForPlace(instr.value.value), + getKindForPlace(instr.value.lvalue.place) + ); setKind(instr.value.lvalue.place, kind); setKind(instr.lvalue, kind); break; @@ -298,10 +317,11 @@ export function validateHooksUsage(fn: HIRFunction): void { calleeKind === Kind.KnownHook || calleeKind === Kind.PotentialHook; if (isHookCallee && !unconditionalBlocks.has(block.id)) { recordConditionalHookError(instr.value.callee); + } else if (calleeKind === Kind.PotentialHook) { + recordDynamicHookUsageError(instr.value.callee); } /** - * We intentionally skip the callee because known/potential hooks - * are always allowed to be called. + * We intentionally skip the callee because it's validated above */ for (const operand of eachInstructionOperand(instr)) { if (operand === instr.value.callee) { @@ -317,10 +337,11 @@ export function validateHooksUsage(fn: HIRFunction): void { calleeKind === Kind.KnownHook || calleeKind === Kind.PotentialHook; if (isHookCallee && !unconditionalBlocks.has(block.id)) { recordConditionalHookError(instr.value.property); + } else if (calleeKind === Kind.PotentialHook) { + recordDynamicHookUsageError(instr.value.property); } /* - * We intentionally skip the callee because known/potential hooks - * are always allowed to be called as methods (`React.useState()`). + * We intentionally skip the property because it's validated above */ for (const operand of eachInstructionOperand(instr)) { if (operand === instr.value.property) { diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.hook-property-load-local-hook.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.hook-property-load-local-hook.expect.md index 2712c57680..a1706079e3 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.hook-property-load-local-hook.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.hook-property-load-local-hook.expect.md @@ -26,9 +26,9 @@ export const FIXTURE_ENTRYPOINT = { 5 | 6 | function Foo() { > 7 | let bar = useFoo.useBar; - | ^^^^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (7:7) + | ^^^^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (7:7) -InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (8:8) +InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (8:8) 8 | return bar(); 9 | } 10 | diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-assign-hook-to-local.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-assign-hook-to-local.expect.md index dc3abe55d6..a4327cf961 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-assign-hook-to-local.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-assign-hook-to-local.expect.md @@ -16,7 +16,7 @@ function Component(props) { ``` 1 | function Component(props) { > 2 | const x = useState; - | ^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2) + | ^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2) 3 | const state = x(null); 4 | return state[0]; 5 | } diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-pass-hook-as-call-arg.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-pass-hook-as-call-arg.expect.md index a6c51d5764..d321a78017 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-pass-hook-as-call-arg.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-pass-hook-as-call-arg.expect.md @@ -14,7 +14,7 @@ function Component(props) { ``` 1 | function Component(props) { > 2 | return foo(useFoo); - | ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2) + | ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2) 3 | } 4 | ``` diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-pass-hook-as-prop.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-pass-hook-as-prop.expect.md index 6cf0413457..6ba2ad6b41 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-pass-hook-as-prop.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-pass-hook-as-prop.expect.md @@ -14,7 +14,7 @@ function Component(props) { ``` 1 | function Component(props) { > 2 | return ; - | ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2) + | ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2) 3 | } 4 | ``` diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-ternary-with-hook-values.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-ternary-with-hook-values.expect.md index b31979bc32..2e5d24e3a8 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-ternary-with-hook-values.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-ternary-with-hook-values.expect.md @@ -15,13 +15,13 @@ function Component(props) { ``` 1 | function Component(props) { > 2 | const x = props.cond ? useA : useB; - | ^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2) + | ^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2) -InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2) +InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2) -InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2) +InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2) -InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3) +InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3) 3 | return x(); 4 | } 5 | diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.propertyload-hook.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.propertyload-hook.expect.md index 54f3e709e3..55e885ba7d 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.propertyload-hook.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.propertyload-hook.expect.md @@ -15,9 +15,9 @@ function Component() { ``` 1 | function Component() { > 2 | const x = Foo.useFoo; - | ^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2) + | ^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2) -InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3) +InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3) 3 | return x(); 4 | } 5 | diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-call-phi-possibly-hook.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-call-phi-possibly-hook.expect.md index 6ccf8aeb06..336d332ea9 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-call-phi-possibly-hook.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-call-phi-possibly-hook.expect.md @@ -21,11 +21,11 @@ function Component(props) { 1 | function Component(props) { 2 | // This is a violation of using a hook as a normal value rule: > 3 | const getUser = props.cond ? useGetUser : emptyFunction; - | ^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3) + | ^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3) -InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3) +InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3) -InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (8:8) +InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (8:8) 4 | 5 | // Ideally we would report a "conditional hook call" error here. 6 | // It's an unconditional call, but the value may or may not be a hook. diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-dynamic-hook-via-hooklike-local.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-dynamic-hook-via-hooklike-local.expect.md new file mode 100644 index 0000000000..0e224f27fe --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-dynamic-hook-via-hooklike-local.expect.md @@ -0,0 +1,25 @@ + +## Input + +```javascript +function Component() { + const someFunction = useContext(FooContext); + const useOhItsNamedLikeAHookNow = someFunction; + useOhItsNamedLikeAHookNow(); +} + +``` + + +## Error + +``` + 2 | const someFunction = useContext(FooContext); + 3 | const useOhItsNamedLikeAHookNow = someFunction; +> 4 | useOhItsNamedLikeAHookNow(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks (4:4) + 5 | } + 6 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-dynamic-hook-via-hooklike-local.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-dynamic-hook-via-hooklike-local.js new file mode 100644 index 0000000000..ace0c6b63a --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-dynamic-hook-via-hooklike-local.js @@ -0,0 +1,5 @@ +function Component() { + const someFunction = useContext(FooContext); + const useOhItsNamedLikeAHookNow = someFunction; + useOhItsNamedLikeAHookNow(); +} diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-conditional-test.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-conditional-test.expect.md index 03792b7922..60175dc9f0 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-conditional-test.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-conditional-test.expect.md @@ -15,7 +15,7 @@ function Component(props) { ``` 1 | function Component(props) { > 2 | const x = props.cond ? (useFoo ? 1 : 2) : 3; - | ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2) + | ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2) 3 | return x; 4 | } 5 | diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-prop.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-prop.expect.md new file mode 100644 index 0000000000..14f483a924 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-prop.expect.md @@ -0,0 +1,22 @@ + +## Input + +```javascript +function Component({ useFoo }) { + useFoo(); +} + +``` + + +## Error + +``` + 1 | function Component({ useFoo }) { +> 2 | useFoo(); + | ^^^^^^ InvalidReact: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks (2:2) + 3 | } + 4 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-prop.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-prop.js new file mode 100644 index 0000000000..40dfcf5b88 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-as-prop.js @@ -0,0 +1,3 @@ +function Component({ useFoo }) { + useFoo(); +} diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-hook-return.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-hook-return.expect.md new file mode 100644 index 0000000000..8c000106b3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-hook-return.expect.md @@ -0,0 +1,26 @@ + +## Input + +```javascript +function useFoo({ data }) { + const useMedia = useVideoPlayer(); + const foo = useMedia(); + return foo; +} + +``` + + +## Error + +``` + 1 | function useFoo({ data }) { + 2 | const useMedia = useVideoPlayer(); +> 3 | const foo = useMedia(); + | ^^^^^^^^ InvalidReact: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks (3:3) + 4 | return foo; + 5 | } + 6 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-hook-return.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-hook-return.js new file mode 100644 index 0000000000..c2bc4b6fce --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-hook-return.js @@ -0,0 +1,5 @@ +function useFoo({ data }) { + const useMedia = useVideoPlayer(); + const foo = useMedia(); + return foo; +} diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-property-of-other-hook.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-property-of-other-hook.expect.md new file mode 100644 index 0000000000..21ff7d9b0e --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-property-of-other-hook.expect.md @@ -0,0 +1,26 @@ + +## Input + +```javascript +function useFoo({ data }) { + const player = useVideoPlayer(); + const foo = player.useMedia(); + return foo; +} + +``` + + +## Error + +``` + 1 | function useFoo({ data }) { + 2 | const player = useVideoPlayer(); +> 3 | const foo = player.useMedia(); + | ^^^^^^^^^^^^^^^ InvalidReact: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks (3:3) + 4 | return foo; + 5 | } + 6 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-property-of-other-hook.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-property-of-other-hook.js new file mode 100644 index 0000000000..08293badde --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-from-property-of-other-hook.js @@ -0,0 +1,5 @@ +function useFoo({ data }) { + const player = useVideoPlayer(); + const foo = player.useMedia(); + return foo; +} diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-reassigned-in-conditional.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-reassigned-in-conditional.expect.md index 2b86a73184..2a51b73e72 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-reassigned-in-conditional.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid-hook-reassigned-in-conditional.expect.md @@ -17,11 +17,11 @@ function Component(props) { 1 | function Component(props) { 2 | let y; > 3 | props.cond ? (y = useFoo) : null; - | ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3) + | ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3) -InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3) +InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3) -InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (4:4) +InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (4:4) 4 | return y(); 5 | } 6 |