From 074e92777c22a56269647d614fdae80bf6406485 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 21 Jul 2025 13:04:02 -0700 Subject: [PATCH 1/2] Change autodeps configuration (#33800) --- .../ValidateNoUntransformedReferences.ts | 46 +++++++++++-- .../src/HIR/Environment.ts | 14 ++-- .../src/Inference/InferEffectDependencies.ts | 11 +++- .../src/Utils/TestUtils.ts | 6 +- .../src/__tests__/envConfig-test.ts | 4 +- ...ntifier-nopanic-required-feature.expect.md | 8 +-- ...lid-identifier-nopanic-required-feature.js | 4 +- ...e-in-non-react-fn-default-import.expect.md | 15 +++-- ...callsite-in-non-react-fn-default-import.js | 3 +- .../error.callsite-in-non-react-fn.expect.md | 8 +-- .../error.callsite-in-non-react-fn.js | 4 +- .../error.non-inlined-effect-fn.expect.md | 8 +-- .../error.non-inlined-effect-fn.js | 4 +- .../error.todo-dynamic-gating.expect.md | 17 ++--- .../error.todo-dynamic-gating.js | 3 +- .../bailout-retry/error.todo-gating.expect.md | 17 ++--- .../bailout-retry/error.todo-gating.js | 3 +- ...mport-default-property-useEffect.expect.md | 6 +- ....todo-import-default-property-useEffect.js | 2 +- .../bailout-retry/error.todo-syntax.expect.md | 65 +++++++++++-------- .../bailout-retry/error.todo-syntax.js | 19 ++++-- .../bailout-retry/error.use-no-memo.expect.md | 8 +-- .../bailout-retry/error.use-no-memo.js | 4 +- .../error.wrong-index-no-func.expect.md | 26 ++++++++ .../error.wrong-index-no-func.js | 6 ++ .../error.wrong-index.expect.md | 45 +++++++++++++ .../error.wrong-index.js | 13 ++++ .../__tests__/ReactCompilerRule-test.ts | 5 +- 28 files changed, 262 insertions(+), 112 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index-no-func.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index-no-func.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.js 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 e288c227ad2..d363e11831d 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 90a352620ce..a552803171a 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 83b4a9a67eb..2997a449dea 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 6c2cfd5d074..badca01dde2 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 a96af5b3918..500edab8f20 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 7f9f608383b..bdbba2feaa8 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 7d5b74acc79..c753bc26382 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 8cbe8da62f6..52257920022 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 992cd828ad1..adfe3ffadd5 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 8feec253762..adf04dfd12a 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 106a3c73526..9cbc47086b2 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 eeec00995b0..6a724849d93 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 dac029e0caa..c113fe363c5 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 ec5ef238b78..1daa56d2fda 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 4d1ceb92b78..667abfea6f2 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 e071e37cb99..90eb064bf92 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 651b24074f2..60bd9a362e3 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 416ede4556b..dcb364b1e7f 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 b1b0e8d2fe8..c3044274cd2 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 a396b9c3cae..25e539a6079 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 2c53d1a21b3..ad1e5853295 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 c3413f9fd09..a739bf367d4 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 d00ce2ac98d..30fbd8c2a61 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 00000000000..61b6a3464bc --- /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 00000000000..973798b5731 --- /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 00000000000..54ae5af19cc --- /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 00000000000..b0898e3dbde --- /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 8f1612f20ea..39c5dc2ea8e 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, }, ], }, From bb4418d6470b95c7d487f3b73a9dc980edff6f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Mon, 21 Jul 2025 17:36:37 -0400 Subject: [PATCH 2/2] [DevTools] Linkify Source View (#33954) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes it so you can click the source location itself to view the source. This is similar styling as the link to jump to function props like events and actions. We're going to need a lot more linkifying to jump to various source locations. Also, I always was trying to click this file anyway. Hover state: Screenshot 2025-07-21 at 4 36 10 PM --- .../InspectedElementSourcePanel.css | 15 ++++++ .../Components/InspectedElementSourcePanel.js | 47 ++++++++++++++----- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.css b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.css index 444e070c37e..3c96c2bf2cf 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.css +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.css @@ -18,3 +18,18 @@ max-width: 100%; margin-left: 1rem; } + +.Link { + color: var(--color-link); + white-space: pre; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + cursor: pointer; + border-radius: 0.125rem; + padding: 0px 2px; +} + +.Link:hover { + background-color: var(--color-background-hover); +} diff --git a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js index 0f7203b12a7..1ca2fc917bb 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js @@ -8,6 +8,7 @@ */ import * as React from 'react'; +import {useCallback, useContext} from 'react'; import {copy} from 'clipboard-js'; import {toNormalUrl} from 'jsc-safe-url'; @@ -16,6 +17,8 @@ import ButtonIcon from '../ButtonIcon'; import Skeleton from './Skeleton'; import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck'; +import ViewElementSourceContext from './ViewElementSourceContext'; + import type {Source as InspectedElementSource} from 'react-devtools-shared/src/shared/types'; import styles from './InspectedElementSourcePanel.css'; @@ -87,25 +90,45 @@ function CopySourceButton({source, symbolicatedSourcePromise}: Props) { function FormattedSourceString({source, symbolicatedSourcePromise}: Props) { const symbolicatedSource = React.use(symbolicatedSourcePromise); - if (symbolicatedSource == null) { - const {sourceURL, line} = source; - return ( -
- {formatSourceForDisplay(sourceURL, line)} -
- ); - } + const {canViewElementSourceFunction, viewElementSourceFunction} = useContext( + ViewElementSourceContext, + ); + + // In some cases (e.g. FB internal usage) the standalone shell might not be able to view the source. + // To detect this case, we defer to an injected helper function (if present). + const linkIsEnabled = + viewElementSourceFunction != null && + source != null && + (canViewElementSourceFunction == null || + canViewElementSourceFunction(source, symbolicatedSource)); + + const viewSource = useCallback(() => { + if (viewElementSourceFunction != null && source != null) { + viewElementSourceFunction(source, symbolicatedSource); + } + }, [source, symbolicatedSource]); - const {sourceURL, line} = symbolicatedSource; + let sourceURL, line; + if (symbolicatedSource == null) { + sourceURL = source.sourceURL; + line = source.line; + } else { + sourceURL = symbolicatedSource.sourceURL; + line = symbolicatedSource.line; + } return (
- {formatSourceForDisplay(sourceURL, line)} + {linkIsEnabled ? ( + + {formatSourceForDisplay(sourceURL, line)} + + ) : ( + formatSourceForDisplay(sourceURL, line) + )}
); }