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 e288c227ad25c..d363e11831d3d 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 90a352620ce35..a552803171ad5 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 83b4a9a67eb6a..2997a449dead3 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 6c2cfd5d07490..badca01dde236 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 a96af5b3918be..500edab8f20f5 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 7f9f608383bdd..bdbba2feaa89b 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 7d5b74acc7960..c753bc263827d 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 8cbe8da62f66f..5225792002203 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 992cd828ad186..adfe3ffadd595 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 8feec2537625b..adf04dfd12abb 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 106a3c73526f6..9cbc47086b23f 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 eeec00995b044..6a724849d9308 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 dac029e0caa13..c113fe363c539 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 ec5ef238b7828..1daa56d2fda6c 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 4d1ceb92b78a8..667abfea6f262 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 e071e37cb99d3..90eb064bf9205 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 651b24074f2bc..60bd9a362e30b 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 416ede4556b9b..dcb364b1e7fa5 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 b1b0e8d2fe8fa..c3044274cd25b 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 a396b9c3cae60..25e539a6079ec 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 2c53d1a21b3b7..ad1e58532953a 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 c3413f9fd092e..a739bf367d414 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 d00ce2ac98dfb..30fbd8c2a61e6 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 0000000000000..61b6a3464bcf9 --- /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 0000000000000..973798b5731b2 --- /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 0000000000000..54ae5af19cc79 --- /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 0000000000000..b0898e3dbde94 --- /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 8f1612f20ea7a..39c5dc2ea8e8c 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 444e070c37e77..3c96c2bf2cf69 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 0f7203b12a750..1ca2fc917bb21 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) + )}
); }