From 7513996f20e34070141aa605fe282ca6986915a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Tue, 22 Jul 2025 10:53:08 -0400 Subject: [PATCH] [DevTools] Unify by using ReactFunctionLocation type instead of Source (#33955) In RSC and other stacks now we use a lot of `ReactFunctionLocation` type to represent the location of a function. I.e. the location of the beginning of the function (the enclosing line/col) that is represented by the "Source" of the function. This is also what the parent Component Stacks represents. As opposed to `ReactCallSite` which is what normal stack traces and owner stacks represent. I.e. the line/column number of the callsite into the next function. We can start sharing more code by using the `ReactFunctionLocation` type to represent the component source location and it also helps clarify which ones are function locations and which ones are callsites as we start adding more stack traces (e.g. for async debug info and owner stack traces). --- .../react-devtools-core/src/standalone.js | 20 +++-- .../src/main/index.js | 2 +- .../react-devtools-fusebox/src/frontend.d.ts | 19 ++--- .../src/__tests__/utils-test.js | 75 +++++++++---------- .../src/backend/fiber/renderer.js | 14 ++-- .../src/backend/types.js | 4 +- .../src/backend/utils/index.js | 60 ++++++++------- .../react-devtools-shared/src/backendAPI.js | 6 +- .../src/devtools/utils.js | 15 ++-- .../views/Components/InspectedElement.js | 6 +- .../Components/InspectedElementSourcePanel.js | 20 ++--- .../views/Components/InspectedElementView.js | 4 +- .../InspectedElementViewSourceButton.js | 10 +-- .../views/Components/OpenInEditorButton.js | 10 +-- .../src/devtools/views/DevTools.js | 10 +-- .../views/Profiler/SidebarEventInfo.js | 14 ++-- .../src/frontend/types.js | 4 +- .../react-devtools-shared/src/shared/types.js | 14 ---- .../src/symbolicateSource.js | 18 +++-- 19 files changed, 158 insertions(+), 167 deletions(-) delete mode 100644 packages/react-devtools-shared/src/shared/types.js diff --git a/packages/react-devtools-core/src/standalone.js b/packages/react-devtools-core/src/standalone.js index d65b41b478fa2..369dc43a242f9 100644 --- a/packages/react-devtools-core/src/standalone.js +++ b/packages/react-devtools-core/src/standalone.js @@ -26,7 +26,7 @@ import { import {localStorageSetItem} from 'react-devtools-shared/src/storage'; import type {FrontendBridge} from 'react-devtools-shared/src/bridge'; -import type {Source} from 'react-devtools-shared/src/shared/types'; +import type {ReactFunctionLocation} from 'shared/ReactTypes'; export type StatusTypes = 'server-connected' | 'devtools-connected' | 'error'; export type StatusListener = (message: string, status: StatusTypes) => void; @@ -144,29 +144,27 @@ async function fetchFileWithCaching(url: string) { } function canViewElementSourceFunction( - _source: Source, - symbolicatedSource: Source | null, + _source: ReactFunctionLocation, + symbolicatedSource: ReactFunctionLocation | null, ): boolean { if (symbolicatedSource == null) { return false; } + const [, sourceURL, ,] = symbolicatedSource; - return doesFilePathExist(symbolicatedSource.sourceURL, projectRoots); + return doesFilePathExist(sourceURL, projectRoots); } function viewElementSourceFunction( - _source: Source, - symbolicatedSource: Source | null, + _source: ReactFunctionLocation, + symbolicatedSource: ReactFunctionLocation | null, ): void { if (symbolicatedSource == null) { return; } - launchEditor( - symbolicatedSource.sourceURL, - symbolicatedSource.line, - projectRoots, - ); + const [, sourceURL, line] = symbolicatedSource; + launchEditor(sourceURL, line, projectRoots); } function onDisconnected() { diff --git a/packages/react-devtools-extensions/src/main/index.js b/packages/react-devtools-extensions/src/main/index.js index 63d25819539f1..bd95d79a4e72c 100644 --- a/packages/react-devtools-extensions/src/main/index.js +++ b/packages/react-devtools-extensions/src/main/index.js @@ -124,7 +124,7 @@ function createBridgeAndStore() { }; const viewElementSourceFunction = (source, symbolicatedSource) => { - const {sourceURL, line, column} = symbolicatedSource + const [, sourceURL, line, column] = symbolicatedSource ? symbolicatedSource : source; diff --git a/packages/react-devtools-fusebox/src/frontend.d.ts b/packages/react-devtools-fusebox/src/frontend.d.ts index 52536739ccaa1..5a06aec7e49c4 100644 --- a/packages/react-devtools-fusebox/src/frontend.d.ts +++ b/packages/react-devtools-fusebox/src/frontend.d.ts @@ -28,22 +28,23 @@ export type Config = { export function createBridge(wall: Wall): Bridge; export function createStore(bridge: Bridge, config?: Config): Store; -export type Source = { - sourceURL: string, - line: number, - column: number, -}; +export type ReactFunctionLocation = [ + string, // function name + string, // file name TODO: model nested eval locations as nested arrays + number, // enclosing line number + number, // enclosing column number +]; export type ViewElementSource = ( - source: Source, - symbolicatedSource: Source | null, + source: ReactFunctionLocation, + symbolicatedSource: ReactFunctionLocation | null, ) => void; export type ViewAttributeSource = ( id: number, path: Array, ) => void; export type CanViewElementSource = ( - source: Source, - symbolicatedSource: Source | null, + source: ReactFunctionLocation, + symbolicatedSource: ReactFunctionLocation | null, ) => boolean; export type InitializationOptions = { diff --git a/packages/react-devtools-shared/src/__tests__/utils-test.js b/packages/react-devtools-shared/src/__tests__/utils-test.js index 876aaa99f1ea9..57865f90f825d 100644 --- a/packages/react-devtools-shared/src/__tests__/utils-test.js +++ b/packages/react-devtools-shared/src/__tests__/utils-test.js @@ -12,7 +12,7 @@ import { getDisplayNameForReactElement, isPlainObject, } from 'react-devtools-shared/src/utils'; -import {stackToComponentSources} from 'react-devtools-shared/src/devtools/utils'; +import {stackToComponentLocations} from 'react-devtools-shared/src/devtools/utils'; import { formatConsoleArguments, formatConsoleArgumentsToSingleString, @@ -63,14 +63,17 @@ describe('utils', () => { it('should parse a component stack trace', () => { expect( - stackToComponentSources(` + stackToComponentLocations(` at Foobar (http://localhost:3000/static/js/bundle.js:103:74) at a at header at div at App`), ).toEqual([ - ['Foobar', ['http://localhost:3000/static/js/bundle.js', 103, 74]], + [ + 'Foobar', + ['Foobar', 'http://localhost:3000/static/js/bundle.js', 103, 74], + ], ['a', null], ['header', null], ['div', null], @@ -315,12 +318,12 @@ describe('utils', () => { 'at f (https://react.dev/_next/static/chunks/pages/%5B%5B...markdownPath%5D%5D-af2ed613aedf1d57.js:1:8519)\n' + 'at r (https://react.dev/_next/static/chunks/pages/_app-dd0b77ea7bd5b246.js:1:498)\n', ), - ).toEqual({ - sourceURL: - 'https://react.dev/_next/static/chunks/main-78a3b4c2aa4e4850.js', - line: 1, - column: 10389, - }); + ).toEqual([ + '', + 'https://react.dev/_next/static/chunks/main-78a3b4c2aa4e4850.js', + 1, + 10389, + ]); }); it('should construct the source from highest available frame', () => { @@ -338,12 +341,12 @@ describe('utils', () => { ' at tt (https://react.dev/_next/static/chunks/363-3c5f1b553b6be118.js:1:165520)\n' + ' at f (https://react.dev/_next/static/chunks/pages/%5B%5B...markdownPath%5D%5D-af2ed613aedf1d57.js:1:8519)', ), - ).toEqual({ - sourceURL: - 'https://react.dev/_next/static/chunks/848-122f91e9565d9ffa.js', - line: 5, - column: 9236, - }); + ).toEqual([ + '', + 'https://react.dev/_next/static/chunks/848-122f91e9565d9ffa.js', + 5, + 9236, + ]); }); it('should construct the source from frame, which has only url specified', () => { @@ -353,12 +356,12 @@ describe('utils', () => { ' at a\n' + ' at https://react.dev/_next/static/chunks/848-122f91e9565d9ffa.js:5:9236\n', ), - ).toEqual({ - sourceURL: - 'https://react.dev/_next/static/chunks/848-122f91e9565d9ffa.js', - line: 5, - column: 9236, - }); + ).toEqual([ + '', + 'https://react.dev/_next/static/chunks/848-122f91e9565d9ffa.js', + 5, + 9236, + ]); }); it('should parse sourceURL correctly if it includes parentheses', () => { @@ -368,12 +371,12 @@ describe('utils', () => { ' at Router (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/app-router.js:181:11)\n' + ' at ErrorBoundaryHandler (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/error-boundary.js:114:9)', ), - ).toEqual({ - sourceURL: - 'webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js', - line: 307, - column: 11, - }); + ).toEqual([ + '', + 'webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js', + 307, + 11, + ]); }); it('should support Firefox stack', () => { @@ -383,12 +386,12 @@ describe('utils', () => { 'f@https://react.dev/_next/static/chunks/pages/%5B%5B...markdownPath%5D%5D-af2ed613aedf1d57.js:1:8535\n' + 'r@https://react.dev/_next/static/chunks/pages/_app-dd0b77ea7bd5b246.js:1:513', ), - ).toEqual({ - sourceURL: - 'https://react.dev/_next/static/chunks/363-3c5f1b553b6be118.js', - line: 1, - column: 165558, - }); + ).toEqual([ + '', + 'https://react.dev/_next/static/chunks/363-3c5f1b553b6be118.js', + 1, + 165558, + ]); }); }); @@ -398,11 +401,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.f = f; function f() { } //# sourceMappingURL=`; - const result = { - column: 16, - line: 1, - sourceURL: 'http://test/a.mts', - }; + const result = ['', 'http://test/a.mts', 1, 16]; const fs = { 'http://test/a.mts': `export function f() {}`, 'http://test/a.mjs.map': `{"version":3,"file":"a.mjs","sourceRoot":"","sources":["a.mts"],"names":[],"mappings":";;AAAA,cAAsB;AAAtB,SAAgB,CAAC,KAAI,CAAC"}`, diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index e24734b0ab032..937fe8d352fd9 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -145,7 +145,7 @@ import type { ElementType, Plugins, } from 'react-devtools-shared/src/frontend/types'; -import type {Source} from 'react-devtools-shared/src/shared/types'; +import type {ReactFunctionLocation} from 'shared/ReactTypes'; import {getSourceLocationByFiber} from './DevToolsFiberComponentStack'; import {formatOwnerStack} from '../shared/DevToolsOwnerStack'; @@ -162,7 +162,7 @@ type FiberInstance = { parent: null | DevToolsInstance, firstChild: null | DevToolsInstance, nextSibling: null | DevToolsInstance, - source: null | string | Error | Source, // source location of this component function, or owned child stack + source: null | string | Error | ReactFunctionLocation, // source location of this component function, or owned child stack logCount: number, // total number of errors/warnings last seen treeBaseDuration: number, // the profiled time of the last render of this subtree data: Fiber, // one of a Fiber pair @@ -190,7 +190,7 @@ type FilteredFiberInstance = { parent: null | DevToolsInstance, firstChild: null | DevToolsInstance, nextSibling: null | DevToolsInstance, - source: null | string | Error | Source, // always null here. + source: null | string | Error | ReactFunctionLocation, // always null here. logCount: number, // total number of errors/warnings last seen treeBaseDuration: number, // the profiled time of the last render of this subtree data: Fiber, // one of a Fiber pair @@ -222,7 +222,7 @@ type VirtualInstance = { parent: null | DevToolsInstance, firstChild: null | DevToolsInstance, nextSibling: null | DevToolsInstance, - source: null | string | Error | Source, // source location of this server component, or owned child stack + source: null | string | Error | ReactFunctionLocation, // source location of this server component, or owned child stack logCount: number, // total number of errors/warnings last seen treeBaseDuration: number, // the profiled time of the last render of this subtree // The latest info for this instance. This can be updated over time and the @@ -5805,7 +5805,7 @@ export function attach( function getSourceForFiberInstance( fiberInstance: FiberInstance, - ): Source | null { + ): ReactFunctionLocation | null { // Favor the owner source if we have one. const ownerSource = getSourceForInstance(fiberInstance); if (ownerSource !== null) { @@ -5830,7 +5830,9 @@ export function attach( return source; } - function getSourceForInstance(instance: DevToolsInstance): Source | null { + function getSourceForInstance( + instance: DevToolsInstance, + ): ReactFunctionLocation | null { let unresolvedSource = instance.source; if (unresolvedSource === null) { // We don't have any source yet. We can try again later in case an owned child mounts later. diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index f9f11586a022d..c86298850973e 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -32,7 +32,7 @@ import type { import type {InitBackend} from 'react-devtools-shared/src/backend'; import type {TimelineDataExport} from 'react-devtools-timeline/src/types'; import type {BackendBridge} from 'react-devtools-shared/src/bridge'; -import type {Source} from 'react-devtools-shared/src/shared/types'; +import type {ReactFunctionLocation} from 'shared/ReactTypes'; import type Agent from './agent'; type BundleType = @@ -281,7 +281,7 @@ export type InspectedElement = { // List of owners owners: Array | null, - source: Source | null, + source: ReactFunctionLocation | null, type: ElementType, diff --git a/packages/react-devtools-shared/src/backend/utils/index.js b/packages/react-devtools-shared/src/backend/utils/index.js index b7e449869190e..490790e89d9b8 100644 --- a/packages/react-devtools-shared/src/backend/utils/index.js +++ b/packages/react-devtools-shared/src/backend/utils/index.js @@ -12,7 +12,7 @@ import {compareVersions} from 'compare-versions'; import {dehydrate} from 'react-devtools-shared/src/hydration'; import isArray from 'shared/isArray'; -import type {Source} from 'react-devtools-shared/src/shared/types'; +import type {ReactFunctionLocation} from 'shared/ReactTypes'; import type {DehydratedData} from 'react-devtools-shared/src/frontend/types'; export {default as formatWithStyles} from './formatWithStyles'; @@ -258,9 +258,12 @@ export const isReactNativeEnvironment = (): boolean => { return window.document == null; }; -function extractLocation( - url: string, -): null | {sourceURL: string, line?: string, column?: string} { +function extractLocation(url: string): null | { + functionName?: string, + sourceURL: string, + line?: string, + column?: string, +} { if (url.indexOf(':') === -1) { return null; } @@ -275,12 +278,15 @@ function extractLocation( return null; } + const functionName = ''; // TODO: Parse this in the regexp. const [, , sourceURL, line, column] = locationParts; - return {sourceURL, line, column}; + return {functionName, sourceURL, line, column}; } const CHROME_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m; -function parseSourceFromChromeStack(stack: string): Source | null { +function parseSourceFromChromeStack( + stack: string, +): ReactFunctionLocation | null { const frames = stack.split('\n'); // eslint-disable-next-line no-for-of-loops/no-for-of-loops for (const frame of frames) { @@ -297,19 +303,22 @@ function parseSourceFromChromeStack(stack: string): Source | null { continue; } - const {sourceURL, line = '1', column = '1'} = location; + const {functionName, sourceURL, line = '1', column = '1'} = location; - return { + return [ + functionName || '', sourceURL, - line: parseInt(line, 10), - column: parseInt(column, 10), - }; + parseInt(line, 10), + parseInt(column, 10), + ]; } return null; } -function parseSourceFromFirefoxStack(stack: string): Source | null { +function parseSourceFromFirefoxStack( + stack: string, +): ReactFunctionLocation | null { const frames = stack.split('\n'); // eslint-disable-next-line no-for-of-loops/no-for-of-loops for (const frame of frames) { @@ -325,13 +334,14 @@ function parseSourceFromFirefoxStack(stack: string): Source | null { continue; } - const {sourceURL, line = '1', column = '1'} = location; + const {functionName, sourceURL, line = '1', column = '1'} = location; - return { + return [ + functionName || '', sourceURL, - line: parseInt(line, 10), - column: parseInt(column, 10), - }; + parseInt(line, 10), + parseInt(column, 10), + ]; } return null; @@ -339,7 +349,7 @@ function parseSourceFromFirefoxStack(stack: string): Source | null { export function parseSourceFromComponentStack( componentStack: string, -): Source | null { +): ReactFunctionLocation | null { if (componentStack.match(CHROME_STACK_REGEXP)) { return parseSourceFromChromeStack(componentStack); } @@ -347,13 +357,13 @@ export function parseSourceFromComponentStack( return parseSourceFromFirefoxStack(componentStack); } -let collectedLocation: Source | null = null; +let collectedLocation: ReactFunctionLocation | null = null; function collectStackTrace( error: Error, structuredStackTrace: CallSite[], ): string { - let result: null | Source = null; + let result: null | ReactFunctionLocation = null; // Collect structured stack traces from the callsites. // We mirror how V8 serializes stack frames and how we later parse them. for (let i = 0; i < structuredStackTrace.length; i++) { @@ -386,11 +396,7 @@ function collectStackTrace( // Skip eval etc. without source url. They don't have location. continue; } - result = { - sourceURL, - line: line, - column: col, - }; + result = [name, sourceURL, line, col]; } } // At the same time we generate a string stack trace just in case someone @@ -404,7 +410,9 @@ function collectStackTrace( return stack; } -export function parseSourceFromOwnerStack(error: Error): Source | null { +export function parseSourceFromOwnerStack( + error: Error, +): ReactFunctionLocation | null { // First attempt to collected the structured data using prepareStackTrace. collectedLocation = null; const previousPrepare = Error.prepareStackTrace; diff --git a/packages/react-devtools-shared/src/backendAPI.js b/packages/react-devtools-shared/src/backendAPI.js index b3668b9d98c99..bbb171bce0da0 100644 --- a/packages/react-devtools-shared/src/backendAPI.js +++ b/packages/react-devtools-shared/src/backendAPI.js @@ -260,9 +260,9 @@ export function convertInspectedElementBackendToFrontend( rendererPackageName, rendererVersion, rootType, - // Previous backend implementations (<= 5.0.1) have a different interface for Source, with fileName. - // This gates the source features for only compatible backends: >= 5.0.2 - source: source && source.sourceURL ? source : null, + // Previous backend implementations (<= 6.1.5) have a different interface for Source. + // This gates the source features for only compatible backends: >= 6.1.6 + source: Array.isArray(source) ? source : null, type, owners: owners === null diff --git a/packages/react-devtools-shared/src/devtools/utils.js b/packages/react-devtools-shared/src/devtools/utils.js index b6814f73f8d0f..8ce34bf61175c 100644 --- a/packages/react-devtools-shared/src/devtools/utils.js +++ b/packages/react-devtools-shared/src/devtools/utils.js @@ -9,6 +9,7 @@ import JSON5 from 'json5'; +import type {ReactFunctionLocation} from 'shared/ReactTypes'; import type {Element} from 'react-devtools-shared/src/frontend/types'; import type {StateContext} from './views/Components/TreeContext'; import type Store from './store'; @@ -188,16 +189,13 @@ export function smartStringify(value: any): string { return JSON.stringify(value); } -// [url, row, column] -export type Stack = [string, number, number]; - const STACK_DELIMETER = /\n\s+at /; const STACK_SOURCE_LOCATION = /([^\s]+) \((.+):(.+):(.+)\)/; -export function stackToComponentSources( +export function stackToComponentLocations( stack: string, -): Array<[string, ?Stack]> { - const out: Array<[string, ?Stack]> = []; +): Array<[string, ?ReactFunctionLocation]> { + const out: Array<[string, ?ReactFunctionLocation]> = []; stack .split(STACK_DELIMETER) .slice(1) @@ -205,7 +203,10 @@ export function stackToComponentSources( const match = STACK_SOURCE_LOCATION.exec(entry); if (match) { const [, component, url, row, column] = match; - out.push([component, [url, parseInt(row, 10), parseInt(column, 10)]]); + out.push([ + component, + [component, url, parseInt(row, 10), parseInt(column, 10)], + ]); } else { out.push([entry, null]); } diff --git a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js index de9f3490190d9..8210e12331b1b 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js @@ -28,7 +28,7 @@ import Skeleton from './Skeleton'; import styles from './InspectedElement.css'; -import type {Source} from 'react-devtools-shared/src/shared/types'; +import type {ReactFunctionLocation} from 'shared/ReactTypes'; export type Props = {}; @@ -50,7 +50,7 @@ export default function InspectedElementWrapper(_: Props): React.Node { const fetchFileWithCaching = useContext(FetchFileWithCachingContext); - const symbolicatedSourcePromise: null | Promise = + const symbolicatedSourcePromise: null | Promise = React.useMemo(() => { if (inspectedElement == null) return null; if (fetchFileWithCaching == null) return Promise.resolve(null); @@ -58,7 +58,7 @@ export default function InspectedElementWrapper(_: Props): React.Node { const {source} = inspectedElement; if (source == null) return Promise.resolve(null); - const {sourceURL, line, column} = source; + const [, sourceURL, line, column] = source; return symbolicateSourceWithCache( fetchFileWithCaching, sourceURL, 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 1ca2fc917bb21..91780cdc13d75 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js @@ -19,12 +19,12 @@ import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/wit import ViewElementSourceContext from './ViewElementSourceContext'; -import type {Source as InspectedElementSource} from 'react-devtools-shared/src/shared/types'; +import type {ReactFunctionLocation} from 'shared/ReactTypes'; import styles from './InspectedElementSourcePanel.css'; type Props = { - source: InspectedElementSource, - symbolicatedSourcePromise: Promise, + source: ReactFunctionLocation, + symbolicatedSourcePromise: Promise, }; function InspectedElementSourcePanel({ @@ -62,7 +62,7 @@ function InspectedElementSourcePanel({ function CopySourceButton({source, symbolicatedSourcePromise}: Props) { const symbolicatedSource = React.use(symbolicatedSourcePromise); if (symbolicatedSource == null) { - const {sourceURL, line, column} = source; + const [, sourceURL, line, column] = source; const handleCopy = withPermissionsCheck( {permissions: ['clipboardWrite']}, () => copy(`${sourceURL}:${line}:${column}`), @@ -75,7 +75,7 @@ function CopySourceButton({source, symbolicatedSourcePromise}: Props) { ); } - const {sourceURL, line, column} = symbolicatedSource; + const [, sourceURL, line, column] = symbolicatedSource; const handleCopy = withPermissionsCheck( {permissions: ['clipboardWrite']}, () => copy(`${sourceURL}:${line}:${column}`), @@ -109,14 +109,8 @@ function FormattedSourceString({source, symbolicatedSourcePromise}: Props) { } }, [source, symbolicatedSource]); - let sourceURL, line; - if (symbolicatedSource == null) { - sourceURL = source.sourceURL; - line = source.line; - } else { - sourceURL = symbolicatedSource.sourceURL; - line = symbolicatedSource.line; - } + const [, sourceURL, line] = + symbolicatedSource == null ? source : symbolicatedSource; return (
, + symbolicatedSourcePromise: Promise, }; export default function InspectedElementView({ diff --git a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementViewSourceButton.js b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementViewSourceButton.js index 31c6d1e557771..6043bb61df92f 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementViewSourceButton.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementViewSourceButton.js @@ -14,7 +14,7 @@ import Button from '../Button'; import ViewElementSourceContext from './ViewElementSourceContext'; import Skeleton from './Skeleton'; -import type {Source as InspectedElementSource} from 'react-devtools-shared/src/shared/types'; +import type {ReactFunctionLocation} from 'shared/ReactTypes'; import type { CanViewElementSource, ViewElementSource, @@ -24,8 +24,8 @@ const {useCallback, useContext} = React; type Props = { canViewSource: ?boolean, - source: ?InspectedElementSource, - symbolicatedSourcePromise: Promise | null, + source: ?ReactFunctionLocation, + symbolicatedSourcePromise: Promise | null, }; function InspectedElementViewSourceButton({ @@ -52,8 +52,8 @@ function InspectedElementViewSourceButton({ type ActualSourceButtonProps = { canViewSource: ?boolean, - source: ?InspectedElementSource, - symbolicatedSourcePromise: Promise | null, + source: ?ReactFunctionLocation, + symbolicatedSourcePromise: Promise | null, canViewElementSourceFunction: CanViewElementSource | null, viewElementSourceFunction: ViewElementSource | null, }; diff --git a/packages/react-devtools-shared/src/devtools/views/Components/OpenInEditorButton.js b/packages/react-devtools-shared/src/devtools/views/Components/OpenInEditorButton.js index c63e8319b83f8..a0f274490f068 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/OpenInEditorButton.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/OpenInEditorButton.js @@ -11,22 +11,22 @@ import * as React from 'react'; import Button from 'react-devtools-shared/src/devtools/views/Button'; import ButtonIcon from 'react-devtools-shared/src/devtools/views/ButtonIcon'; -import type {Source} from 'react-devtools-shared/src/shared/types'; +import type {ReactFunctionLocation} from 'shared/ReactTypes'; type Props = { editorURL: string, - source: Source, - symbolicatedSourcePromise: Promise, + source: ReactFunctionLocation, + symbolicatedSourcePromise: Promise, }; function checkConditions( editorURL: string, - source: Source, + source: ReactFunctionLocation, ): {url: URL | null, shouldDisableButton: boolean} { try { const url = new URL(editorURL); - let sourceURL = source.sourceURL; + let [, sourceURL, ,] = source; // Check if sourceURL is a correct URL, which has a protocol specified if (sourceURL.includes('://')) { diff --git a/packages/react-devtools-shared/src/devtools/views/DevTools.js b/packages/react-devtools-shared/src/devtools/views/DevTools.js index bd14bdda0f5ba..d18cee5540b7e 100644 --- a/packages/react-devtools-shared/src/devtools/views/DevTools.js +++ b/packages/react-devtools-shared/src/devtools/views/DevTools.js @@ -50,21 +50,21 @@ import type {FetchFileWithCaching} from './Components/FetchFileWithCachingContex import type {HookNamesModuleLoaderFunction} from 'react-devtools-shared/src/devtools/views/Components/HookNamesModuleLoaderContext'; import type {FrontendBridge} from 'react-devtools-shared/src/bridge'; import type {BrowserTheme} from 'react-devtools-shared/src/frontend/types'; -import type {Source} from 'react-devtools-shared/src/shared/types'; +import type {ReactFunctionLocation} from 'shared/ReactTypes'; export type TabID = 'components' | 'profiler'; export type ViewElementSource = ( - source: Source, - symbolicatedSource: Source | null, + source: ReactFunctionLocation, + symbolicatedSource: ReactFunctionLocation | null, ) => void; export type ViewAttributeSource = ( id: number, path: Array, ) => void; export type CanViewElementSource = ( - source: Source, - symbolicatedSource: Source | null, + source: ReactFunctionLocation, + symbolicatedSource: ReactFunctionLocation | null, ) => boolean; export type Props = { diff --git a/packages/react-devtools-shared/src/devtools/views/Profiler/SidebarEventInfo.js b/packages/react-devtools-shared/src/devtools/views/Profiler/SidebarEventInfo.js index 97977380efdb3..b7b031a990caf 100644 --- a/packages/react-devtools-shared/src/devtools/views/Profiler/SidebarEventInfo.js +++ b/packages/react-devtools-shared/src/devtools/views/Profiler/SidebarEventInfo.js @@ -19,7 +19,7 @@ import { formatTimestamp, getSchedulingEventLabel, } from 'react-devtools-timeline/src/utils/formatting'; -import {stackToComponentSources} from 'react-devtools-shared/src/devtools/utils'; +import {stackToComponentLocations} from 'react-devtools-shared/src/devtools/utils'; import {copy} from 'clipboard-js'; import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck'; @@ -63,9 +63,9 @@ function SchedulingEventInfo({eventInfo}: SchedulingEventProps) {
    - {stackToComponentSources(componentStack).map( - ([displayName, stack], index) => { - if (stack == null) { + {stackToComponentLocations(componentStack).map( + ([displayName, location], index) => { + if (location == null) { return (