Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/wit
import StackTraceView from './StackTraceView';
import OwnerView from './OwnerView';
import {meta} from '../../../hydration';
import useInferredName from '../useInferredName';

import type {
InspectedElement,
Expand Down Expand Up @@ -101,21 +102,7 @@ function SuspendedByRow({
}: RowProps) {
const [isOpen, setIsOpen] = useState(false);
const ioInfo = asyncInfo.awaited;
let name = ioInfo.name;
if (name === '' || name === 'Promise') {
// If all we have is a generic name, we can try to infer a better name from
// the stack. We only do this if the stack has more than one frame since
// otherwise it's likely to just be the name of the component which isn't better.
const bestStack = ioInfo.stack || asyncInfo.stack;
if (bestStack !== null && bestStack.length > 1) {
// TODO: Ideally we'd get the name from the last ignore listed frame before the
// first visible frame since this is the same algorithm as the Flight server uses.
// Ideally, we'd also get the name from the source mapped entry instead of the
// original entry. However, that would require suspending the immediate display
// of these rows to first do source mapping before we can show the name.
name = bestStack[0][0];
}
}
const name = useInferredName(asyncInfo);
const description = ioInfo.description;
const longName = description === '' ? name : name + ' (' + description + ')';
const shortDescription = getShortDescription(name, description);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {use, useContext, useDeferredValue} from 'react';

import type {ReactCallSite} from 'shared/ReactTypes';

import type {SourceMappedLocation} from 'react-devtools-shared/src/symbolicateSource';

import type {SerializedAsyncInfo} from 'react-devtools-shared/src/frontend/types';

import FetchFileWithCachingContext from './Components/FetchFileWithCachingContext';

import {symbolicateSourceWithCache} from 'react-devtools-shared/src/symbolicateSource';

export default function useInferredName(
asyncInfo: SerializedAsyncInfo,
): string {
const fetchFileWithCaching = useContext(FetchFileWithCachingContext);
const name = asyncInfo.awaited.name;
let inferNameFromStack = null;
if (!name || name === 'Promise') {
// If all we have is a generic name, we can try to infer a better name from
// the stack. We only do this if the stack has more than one frame since
// otherwise it's likely to just be the name of the component which isn't better.
const bestStack = asyncInfo.awaited.stack || asyncInfo.stack;
if (bestStack !== null && bestStack.length > 1) {
inferNameFromStack = bestStack;
}
}
// Start by not source mapping and just taking the first name and upgrade to
// the better name asynchronously if we find one. Most of the time it'll just be
// the top of the stack.
const shouldSourceMap = useDeferredValue(inferNameFromStack !== null, false);
if (inferNameFromStack !== null) {
if (shouldSourceMap) {
let bestMatch = '';
for (let i = 0; i < inferNameFromStack.length; i++) {
const callSite: ReactCallSite = inferNameFromStack[i];
const [virtualFunctionName, virtualURL, virtualLine, virtualColumn] =
callSite;
const symbolicatedCallSite: null | SourceMappedLocation =
fetchFileWithCaching !== null
? use(
symbolicateSourceWithCache(
Comment on lines +35 to +42
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we seed these Promises before? This looks like a waterfall at a glance. Or is this intentional that we don't want to sourcemap eagerly in case the very first stack is already not ignored and we'd end up throwing away the later results?

fetchFileWithCaching,
virtualURL,
virtualLine,
virtualColumn,
),
)
: null;
if (symbolicatedCallSite === null) {
// If we can't source map, we treat it as first party code. We called whatever was
// the previous callsite.
if (bestMatch === '') {
return virtualFunctionName || name;
} else {
return bestMatch;
}
} else if (!symbolicatedCallSite.ignored) {
if (bestMatch === '') {
// If we had no good stack frames for internal calls, just use the last
// first party function name.
return symbolicatedCallSite[0] || virtualFunctionName || name;
} else {
return bestMatch;
}
} else {
// This line was ignore listed, it might be the one we called into from first party.
bestMatch = symbolicatedCallSite[0] || virtualFunctionName;
}
}
return name;
} else {
return inferNameFromStack[0][0];
}
} else {
return name;
}
}
Loading