diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts index e288c227ad25..d363e11831d3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts @@ -35,8 +35,41 @@ function throwInvalidReact( }); CompilerError.throw(detail); } + +function isAutodepsSigil( + arg: NodePath, +): boolean { + // Check for AUTODEPS identifier imported from React + if (arg.isIdentifier() && arg.node.name === 'AUTODEPS') { + const binding = arg.scope.getBinding(arg.node.name); + if (binding && binding.path.isImportSpecifier()) { + const importSpecifier = binding.path.node as t.ImportSpecifier; + if (importSpecifier.imported.type === 'Identifier') { + return (importSpecifier.imported as t.Identifier).name === 'AUTODEPS'; + } + } + return false; + } + + // Check for React.AUTODEPS member expression + if (arg.isMemberExpression() && !arg.node.computed) { + const object = arg.get('object'); + const property = arg.get('property'); + + if ( + object.isIdentifier() && + object.node.name === 'React' && + property.isIdentifier() && + property.node.name === 'AUTODEPS' + ) { + return true; + } + } + + return false; +} function assertValidEffectImportReference( - numArgs: number, + autodepsIndex: number, paths: Array>, context: TraversalState, ): void { @@ -49,11 +82,10 @@ function assertValidEffectImportReference( maybeCalleeLoc != null && context.inferredEffectLocations.has(maybeCalleeLoc); /** - * Only error on untransformed references of the form `useMyEffect(...)` - * or `moduleNamespace.useMyEffect(...)`, with matching argument counts. - * TODO: do we also want a mode to also hard error on non-call references? + * Error on effect calls that still have AUTODEPS in their args */ - if (args.length === numArgs && !hasInferredEffect) { + const hasAutodepsArg = args.some(isAutodepsSigil); + if (hasAutodepsArg && !hasInferredEffect) { const maybeErrorDiagnostic = matchCompilerDiagnostic( path, context.transformErrors, @@ -128,12 +160,12 @@ export default function validateNoUntransformedReferences( if (env.inferEffectDependencies) { for (const { function: {source, importSpecifierName}, - numRequiredArgs, + autodepsIndex, } of env.inferEffectDependencies) { const module = getOrInsertWith(moduleLoadChecks, source, () => new Map()); module.set( importSpecifierName, - assertValidEffectImportReference.bind(null, numRequiredArgs), + assertValidEffectImportReference.bind(null, autodepsIndex), ); } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 90a352620ce3..a552803171ad 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -265,21 +265,19 @@ export const EnvironmentConfigSchema = z.object({ * { * module: 'react', * imported: 'useEffect', - * numRequiredArgs: 1, + * autodepsIndex: 1, * },{ * module: 'MyExperimentalEffectHooks', * imported: 'useExperimentalEffect', - * numRequiredArgs: 2, + * autodepsIndex: 2, * }, * ] * would insert dependencies for calls of `useEffect` imported from `react` and calls of * useExperimentalEffect` from `MyExperimentalEffectHooks`. * - * `numRequiredArgs` tells the compiler the amount of arguments required to append a dependency - * array to the end of the call. With the configuration above, we'd insert dependencies for - * `useEffect` if it is only given a single argument and it would be appended to the argument list. - * - * numRequiredArgs must always be greater than 0, otherwise there is no function to analyze for dependencies + * `autodepsIndex` tells the compiler which index we expect the AUTODEPS to appear in. + * With the configuration above, we'd insert dependencies for `useEffect` if it has two + * arguments, and the second is AUTODEPS. * * Still experimental. */ @@ -288,7 +286,7 @@ export const EnvironmentConfigSchema = z.object({ z.array( z.object({ function: ExternalFunctionSchema, - numRequiredArgs: z.number().min(1, 'numRequiredArgs must be > 0'), + autodepsIndex: z.number().min(1, 'autodepsIndex must be > 0'), }), ), ) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts index 83b4a9a67eb6..2997a449dead 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts @@ -79,7 +79,7 @@ export function inferEffectDependencies(fn: HIRFunction): void { ); moduleTargets.set( effectTarget.function.importSpecifierName, - effectTarget.numRequiredArgs, + effectTarget.autodepsIndex, ); } const autodepFnLoads = new Map(); @@ -177,9 +177,14 @@ export function inferEffectDependencies(fn: HIRFunction): void { arg.identifier.type.kind === 'Object' && arg.identifier.type.shapeId === BuiltInAutodepsId, ); + const autodepsArgExpectedIndex = autodepFnLoads.get( + callee.identifier.id, + ); + if ( - value.args.length > 1 && - autodepsArgIndex > 0 && + value.args.length > 0 && + autodepsArgExpectedIndex != null && + autodepsArgIndex === autodepsArgExpectedIndex && autodepFnLoads.has(callee.identifier.id) && value.args[0].kind === 'Identifier' ) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Utils/TestUtils.ts b/compiler/packages/babel-plugin-react-compiler/src/Utils/TestUtils.ts index 6c2cfd5d0749..badca01dde23 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Utils/TestUtils.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Utils/TestUtils.ts @@ -75,21 +75,21 @@ const testComplexConfigDefaults: PartialEnvironmentConfig = { source: 'react', importSpecifierName: 'useEffect', }, - numRequiredArgs: 1, + autodepsIndex: 1, }, { function: { source: 'shared-runtime', importSpecifierName: 'useSpecialEffect', }, - numRequiredArgs: 2, + autodepsIndex: 2, }, { function: { source: 'useEffectWrapper', importSpecifierName: 'default', }, - numRequiredArgs: 1, + autodepsIndex: 1, }, ], }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts index a96af5b3918b..500edab8f20f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts @@ -33,12 +33,12 @@ describe('parseConfigPragma()', () => { source: 'react', importSpecifierName: 'useEffect', }, - numRequiredArgs: 0, + autodepsIndex: 0, }, ], } as any); }).toThrowErrorMatchingInlineSnapshot( - `"InvalidConfig: Could not validate environment config. Update React Compiler config to fix the error. Validation error: numRequiredArgs must be > 0 at "inferEffectDependencies[0].numRequiredArgs""`, + `"InvalidConfig: Could not validate environment config. Update React Compiler config to fix the error. Validation error: autodepsIndex must be > 0 at "inferEffectDependencies[0].autodepsIndex""`, ); }); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.dynamic-gating-invalid-identifier-nopanic-required-feature.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.dynamic-gating-invalid-identifier-nopanic-required-feature.expect.md index 7f9f608383bd..bdbba2feaa89 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.dynamic-gating-invalid-identifier-nopanic-required-feature.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.dynamic-gating-invalid-identifier-nopanic-required-feature.expect.md @@ -3,13 +3,13 @@ ```javascript // @dynamicGating:{"source":"shared-runtime"} @panicThreshold:"none" @inferEffectDependencies -import {useEffect} from 'react'; +import {useEffect, AUTODEPS} from 'react'; import {print} from 'shared-runtime'; function ReactiveVariable({propVal}) { 'use memo if(invalid identifier)'; const arr = [propVal]; - useEffect(() => print(arr)); + useEffect(() => print(arr), AUTODEPS); } export const FIXTURE_ENTRYPOINT = { @@ -25,8 +25,8 @@ export const FIXTURE_ENTRYPOINT = { ``` 6 | 'use memo if(invalid identifier)'; 7 | const arr = [propVal]; -> 8 | useEffect(() => print(arr)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (8:8) +> 8 | useEffect(() => print(arr), AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (8:8) 9 | } 10 | 11 | export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.dynamic-gating-invalid-identifier-nopanic-required-feature.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.dynamic-gating-invalid-identifier-nopanic-required-feature.js index 7d5b74acc796..c753bc263827 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.dynamic-gating-invalid-identifier-nopanic-required-feature.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.dynamic-gating-invalid-identifier-nopanic-required-feature.js @@ -1,11 +1,11 @@ // @dynamicGating:{"source":"shared-runtime"} @panicThreshold:"none" @inferEffectDependencies -import {useEffect} from 'react'; +import {useEffect, AUTODEPS} from 'react'; import {print} from 'shared-runtime'; function ReactiveVariable({propVal}) { 'use memo if(invalid identifier)'; const arr = [propVal]; - useEffect(() => print(arr)); + useEffect(() => print(arr), AUTODEPS); } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.expect.md index 8cbe8da62f66..522579200220 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.expect.md @@ -4,9 +4,10 @@ ```javascript // @inferEffectDependencies @compilationMode:"infer" @panicThreshold:"none" import useMyEffect from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; function nonReactFn(arg) { - useMyEffect(() => [1, 2, arg]); + useMyEffect(() => [1, 2, arg], AUTODEPS); } ``` @@ -15,12 +16,12 @@ function nonReactFn(arg) { ## Error ``` - 3 | - 4 | function nonReactFn(arg) { -> 5 | useMyEffect(() => [1, 2, arg]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5) - 6 | } - 7 | + 4 | + 5 | function nonReactFn(arg) { +> 6 | useMyEffect(() => [1, 2, arg], AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6) + 7 | } + 8 | ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.js index 992cd828ad18..adfe3ffadd59 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.js @@ -1,6 +1,7 @@ // @inferEffectDependencies @compilationMode:"infer" @panicThreshold:"none" import useMyEffect from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; function nonReactFn(arg) { - useMyEffect(() => [1, 2, arg]); + useMyEffect(() => [1, 2, arg], AUTODEPS); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.expect.md index 8feec2537625..adf04dfd12ab 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.expect.md @@ -3,10 +3,10 @@ ```javascript // @inferEffectDependencies @compilationMode:"infer" @panicThreshold:"none" -import {useEffect} from 'react'; +import {useEffect, AUTODEPS} from 'react'; function nonReactFn(arg) { - useEffect(() => [1, 2, arg]); + useEffect(() => [1, 2, arg], AUTODEPS); } ``` @@ -17,8 +17,8 @@ function nonReactFn(arg) { ``` 3 | 4 | function nonReactFn(arg) { -> 5 | useEffect(() => [1, 2, arg]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5) +> 5 | useEffect(() => [1, 2, arg], AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5) 6 | } 7 | ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.js index 106a3c73526f..9cbc47086b23 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.js @@ -1,6 +1,6 @@ // @inferEffectDependencies @compilationMode:"infer" @panicThreshold:"none" -import {useEffect} from 'react'; +import {useEffect, AUTODEPS} from 'react'; function nonReactFn(arg) { - useEffect(() => [1, 2, arg]); + useEffect(() => [1, 2, arg], AUTODEPS); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.expect.md index eeec00995b04..6a724849d930 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.expect.md @@ -3,7 +3,7 @@ ```javascript // @inferEffectDependencies @panicThreshold:"none" -import {useEffect} from 'react'; +import {useEffect, AUTODEPS} from 'react'; /** * Error on non-inlined effect functions: @@ -21,7 +21,7 @@ function Component({foo}) { } // No inferred dep array, the argument is not a lambda - useEffect(f); + useEffect(f, AUTODEPS); } ``` @@ -32,8 +32,8 @@ function Component({foo}) { ``` 18 | 19 | // No inferred dep array, the argument is not a lambda -> 20 | useEffect(f); - | ^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (20:20) +> 20 | useEffect(f, AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (20:20) 21 | } 22 | ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.js index dac029e0caa1..c113fe363c53 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.js @@ -1,5 +1,5 @@ // @inferEffectDependencies @panicThreshold:"none" -import {useEffect} from 'react'; +import {useEffect, AUTODEPS} from 'react'; /** * Error on non-inlined effect functions: @@ -17,5 +17,5 @@ function Component({foo}) { } // No inferred dep array, the argument is not a lambda - useEffect(f); + useEffect(f, AUTODEPS); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-dynamic-gating.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-dynamic-gating.expect.md index ec5ef238b782..1daa56d2fda6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-dynamic-gating.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-dynamic-gating.expect.md @@ -5,6 +5,7 @@ // @dynamicGating:{"source":"shared-runtime"} @inferEffectDependencies @panicThreshold:"none" import useEffectWrapper from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; /** * TODO: run the non-forget enabled version through the effect inference @@ -13,7 +14,7 @@ import useEffectWrapper from 'useEffectWrapper'; function Component({foo}) { 'use memo if(getTrue)'; const arr = []; - useEffectWrapper(() => arr.push(foo)); + useEffectWrapper(() => arr.push(foo), AUTODEPS); arr.push(2); return arr; } @@ -30,13 +31,13 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` - 10 | 'use memo if(getTrue)'; - 11 | const arr = []; -> 12 | useEffectWrapper(() => arr.push(foo)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (12:12) - 13 | arr.push(2); - 14 | return arr; - 15 | } + 11 | 'use memo if(getTrue)'; + 12 | const arr = []; +> 13 | useEffectWrapper(() => arr.push(foo), AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (13:13) + 14 | arr.push(2); + 15 | return arr; + 16 | } ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-dynamic-gating.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-dynamic-gating.js index 4d1ceb92b78a..667abfea6f26 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-dynamic-gating.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-dynamic-gating.js @@ -1,6 +1,7 @@ // @dynamicGating:{"source":"shared-runtime"} @inferEffectDependencies @panicThreshold:"none" import useEffectWrapper from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; /** * TODO: run the non-forget enabled version through the effect inference @@ -9,7 +10,7 @@ import useEffectWrapper from 'useEffectWrapper'; function Component({foo}) { 'use memo if(getTrue)'; const arr = []; - useEffectWrapper(() => arr.push(foo)); + useEffectWrapper(() => arr.push(foo), AUTODEPS); arr.push(2); return arr; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-gating.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-gating.expect.md index e071e37cb99d..90eb064bf920 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-gating.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-gating.expect.md @@ -4,6 +4,7 @@ ```javascript // @gating @inferEffectDependencies @panicThreshold:"none" import useEffectWrapper from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; /** * TODO: run the non-forget enabled version through the effect inference @@ -11,7 +12,7 @@ import useEffectWrapper from 'useEffectWrapper'; */ function Component({foo}) { const arr = []; - useEffectWrapper(() => arr.push(foo)); + useEffectWrapper(() => arr.push(foo), AUTODEPS); arr.push(2); return arr; } @@ -28,13 +29,13 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` - 8 | function Component({foo}) { - 9 | const arr = []; -> 10 | useEffectWrapper(() => arr.push(foo)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (10:10) - 11 | arr.push(2); - 12 | return arr; - 13 | } + 9 | function Component({foo}) { + 10 | const arr = []; +> 11 | useEffectWrapper(() => arr.push(foo), AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (11:11) + 12 | arr.push(2); + 13 | return arr; + 14 | } ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-gating.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-gating.js index 651b24074f2b..60bd9a362e30 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-gating.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-gating.js @@ -1,5 +1,6 @@ // @gating @inferEffectDependencies @panicThreshold:"none" import useEffectWrapper from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; /** * TODO: run the non-forget enabled version through the effect inference @@ -7,7 +8,7 @@ import useEffectWrapper from 'useEffectWrapper'; */ function Component({foo}) { const arr = []; - useEffectWrapper(() => arr.push(foo)); + useEffectWrapper(() => arr.push(foo), AUTODEPS); arr.push(2); return arr; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.expect.md index 416ede4556b9..dcb364b1e7fa 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.expect.md @@ -7,7 +7,7 @@ import React from 'react'; function NonReactiveDepInEffect() { const obj = makeObject_Primitives(); - React.useEffect(() => print(obj)); + React.useEffect(() => print(obj), React.AUTODEPS); } ``` @@ -18,8 +18,8 @@ function NonReactiveDepInEffect() { ``` 4 | function NonReactiveDepInEffect() { 5 | const obj = makeObject_Primitives(); -> 6 | React.useEffect(() => print(obj)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6) +> 6 | React.useEffect(() => print(obj), React.AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6) 7 | } 8 | ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.js index b1b0e8d2fe8f..c3044274cd25 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.js @@ -3,5 +3,5 @@ import React from 'react'; function NonReactiveDepInEffect() { const obj = makeObject_Primitives(); - React.useEffect(() => print(obj)); + React.useEffect(() => print(obj), React.AUTODEPS); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md index a396b9c3cae6..25e539a6079e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md @@ -4,6 +4,7 @@ ```javascript // @inferEffectDependencies @panicThreshold:"none" import {useSpecialEffect} from 'shared-runtime'; +import {AUTODEPS} from 'react'; /** * Note that a react compiler-based transform still has limitations on JS syntax. @@ -11,13 +12,17 @@ import {useSpecialEffect} from 'shared-runtime'; */ function Component({prop1}) { 'use memo'; - useSpecialEffect(() => { - try { - console.log(prop1); - } finally { - console.log('exiting'); - } - }, [prop1]); + useSpecialEffect( + () => { + try { + console.log(prop1); + } finally { + console.log('exiting'); + } + }, + [prop1], + AUTODEPS + ); return
{prop1}
; } @@ -27,25 +32,33 @@ function Component({prop1}) { ## Error ``` - 8 | function Component({prop1}) { - 9 | 'use memo'; -> 10 | useSpecialEffect(() => { - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 11 | try { - | ^^^^^^^^^ -> 12 | console.log(prop1); - | ^^^^^^^^^ -> 13 | } finally { - | ^^^^^^^^^ -> 14 | console.log('exiting'); - | ^^^^^^^^^ -> 15 | } - | ^^^^^^^^^ -> 16 | }, [prop1]); - | ^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.. (Bailout reason: Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (11:15)) (10:16) - 17 | return
{prop1}
; - 18 | } - 19 | + 9 | function Component({prop1}) { + 10 | 'use memo'; +> 11 | useSpecialEffect( + | ^^^^^^^^^^^^^^^^^ +> 12 | () => { + | ^^^^^^^^^^^ +> 13 | try { + | ^^^^^^^^^^^ +> 14 | console.log(prop1); + | ^^^^^^^^^^^ +> 15 | } finally { + | ^^^^^^^^^^^ +> 16 | console.log('exiting'); + | ^^^^^^^^^^^ +> 17 | } + | ^^^^^^^^^^^ +> 18 | }, + | ^^^^^^^^^^^ +> 19 | [prop1], + | ^^^^^^^^^^^ +> 20 | AUTODEPS + | ^^^^^^^^^^^ +> 21 | ); + | ^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.. (Bailout reason: Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (13:17)) (11:21) + 22 | return
{prop1}
; + 23 | } + 24 | ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.js index 2c53d1a21b3b..ad1e58532953 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.js @@ -1,5 +1,6 @@ // @inferEffectDependencies @panicThreshold:"none" import {useSpecialEffect} from 'shared-runtime'; +import {AUTODEPS} from 'react'; /** * Note that a react compiler-based transform still has limitations on JS syntax. @@ -7,12 +8,16 @@ import {useSpecialEffect} from 'shared-runtime'; */ function Component({prop1}) { 'use memo'; - useSpecialEffect(() => { - try { - console.log(prop1); - } finally { - console.log('exiting'); - } - }, [prop1]); + useSpecialEffect( + () => { + try { + console.log(prop1); + } finally { + console.log('exiting'); + } + }, + [prop1], + AUTODEPS + ); return
{prop1}
; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.expect.md index c3413f9fd092..a739bf367d41 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.expect.md @@ -3,11 +3,11 @@ ```javascript // @inferEffectDependencies @panicThreshold:"none" -import {useEffect} from 'react'; +import {useEffect, AUTODEPS} from 'react'; function Component({propVal}) { 'use no memo'; - useEffect(() => [propVal]); + useEffect(() => [propVal], AUTODEPS); } ``` @@ -18,8 +18,8 @@ function Component({propVal}) { ``` 4 | function Component({propVal}) { 5 | 'use no memo'; -> 6 | useEffect(() => [propVal]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6) +> 6 | useEffect(() => [propVal], AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6) 7 | } 8 | ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.js index d00ce2ac98df..30fbd8c2a61e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.js @@ -1,7 +1,7 @@ // @inferEffectDependencies @panicThreshold:"none" -import {useEffect} from 'react'; +import {useEffect, AUTODEPS} from 'react'; function Component({propVal}) { 'use no memo'; - useEffect(() => [propVal]); + useEffect(() => [propVal], AUTODEPS); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index-no-func.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index-no-func.expect.md new file mode 100644 index 000000000000..61b6a3464bcf --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index-no-func.expect.md @@ -0,0 +1,26 @@ + +## Input + +```javascript +// @inferEffectDependencies +import {useEffect, AUTODEPS} from 'react'; + +function Component({foo}) { + useEffect(AUTODEPS); +} + +``` + + +## Error + +``` + 3 | + 4 | function Component({foo}) { +> 5 | useEffect(AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5) + 6 | } + 7 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index-no-func.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index-no-func.js new file mode 100644 index 000000000000..973798b5731b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index-no-func.js @@ -0,0 +1,6 @@ +// @inferEffectDependencies +import {useEffect, AUTODEPS} from 'react'; + +function Component({foo}) { + useEffect(AUTODEPS); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.expect.md new file mode 100644 index 000000000000..54ae5af19cc7 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.expect.md @@ -0,0 +1,45 @@ + +## Input + +```javascript +// @inferEffectDependencies +import {AUTODEPS} from 'react'; +import useEffectWrapper from 'useEffectWrapper'; + +function Component({foo}) { + useEffectWrapper( + () => { + console.log(foo); + }, + [foo], + AUTODEPS + ); +} + +``` + + +## Error + +``` + 4 | + 5 | function Component({foo}) { +> 6 | useEffectWrapper( + | ^^^^^^^^^^^^^^^^^ +> 7 | () => { + | ^^^^^^^^^^^ +> 8 | console.log(foo); + | ^^^^^^^^^^^ +> 9 | }, + | ^^^^^^^^^^^ +> 10 | [foo], + | ^^^^^^^^^^^ +> 11 | AUTODEPS + | ^^^^^^^^^^^ +> 12 | ); + | ^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:12) + 13 | } + 14 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.js new file mode 100644 index 000000000000..b0898e3dbde9 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.js @@ -0,0 +1,13 @@ +// @inferEffectDependencies +import {AUTODEPS} from 'react'; +import useEffectWrapper from 'useEffectWrapper'; + +function Component({foo}) { + useEffectWrapper( + () => { + console.log(foo); + }, + [foo], + AUTODEPS + ); +} diff --git a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts index 8f1612f20ea7..39c5dc2ea8e8 100644 --- a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts +++ b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts @@ -250,9 +250,10 @@ const tests: CompilerTestCases = { name: 'Pipeline errors are reported', code: normalizeIndent` import useMyEffect from 'useMyEffect'; + import {AUTODEPS} from 'react'; function Component({a}) { 'use no memo'; - useMyEffect(() => console.log(a.b)); + useMyEffect(() => console.log(a.b), AUTODEPS); return
Hello world
; } `, @@ -265,7 +266,7 @@ const tests: CompilerTestCases = { source: 'useMyEffect', importSpecifierName: 'default', }, - numRequiredArgs: 1, + autodepsIndex: 1, }, ], },