Skip to content

[DevTools] Get source location from structured callsites in prepareStackTrace #33143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 13, 2025

Conversation

sebmarkbage
Copy link
Collaborator

When we get the source location for "View source for this element" we should be using the enclosing function of the callsite of the child. So that we don't just point to some random line within the component.

This is similar to the technique in #33136.

This technique is now really better than the fake throw technique, when available. So I now favor the owner technique. The only problem it's only available in DEV and only if it has a child that's owned (and not filtered).

We could implement this same technique for the error that's thrown in the fake throwing solution. However, we really shouldn't need that at all because for client components we should be able to call inspect(fn) at least in Chrome which is even better.

@sebmarkbage sebmarkbage requested review from eps1lon and hoxyq May 7, 2025 20:46
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label May 7, 2025
const line =
// $FlowFixMe[prop-missing]
typeof callSite.getEnclosingLineNumber === 'function'
? (callSite: any).getEnclosingLineNumber()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the key. Now we're jumping to the beginning of the function.

@sebmarkbage
Copy link
Collaborator Author

One limitation is that once something else consumes the .stack such as if we print some error log that contains that component, then we've already lost the structured information so we no longer have the enclosing function.

Copy link
Contributor

@hoxyq hoxyq left a comment

Choose a reason for hiding this comment

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

One limitation is that once something else consumes the .stack such as if we print some error log that contains that component, then we've already lost the structured information so we no longer have the enclosing function.

Since we generate these Error objects, and React DevTools knows when it appends the owner stack, could we record the structured stacks somewhere and then maybe fetch it later, based on some DevToolsInstance id?

Edit: oh, this is applicable to all errors, not just synthetic ones that are appended by React DevTools. We can't patch throw, but since we patch console.error, we could handle these, but seems like an overkill for precise function header location.

}

export function parseSourceFromOwnerStack(error: Error): Source | null {
// First attempt to collected the structured data using prepareStackTrace.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// First attempt to collected the structured data using prepareStackTrace.
// First attempt to collect the structured data using prepareStackTrace.

Comment on lines +352 to +355
function collectStackTrace(
error: Error,
structuredStackTrace: CallSite[],
): string {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe worth leaving a link to V8 docs for this - https://v8.dev/docs/stack-trace-api#customizing-stack-traces.

Comment on lines +5847 to +5849
return (instance.source = parseSourceFromOwnerStack(
(unresolvedSource: any),
));
Copy link
Contributor

Choose a reason for hiding this comment

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

If you would make isError a type guard, will it be enough to remove the any type casting?

function isError(object: mixed): object is Error {
  return toString.call(object) === '[object Error]';
}

Comment on lines +391 to +399
// At the same time we generate a string stack trace just in case someone
// else reads it.
const name = error.name || 'Error';
const message = error.message || '';
let stack = name + ': ' + message;
for (let i = 0; i < structuredStackTrace.length; i++) {
stack += '\n at ' + structuredStackTrace[i].toString();
}
return stack;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't get how this can be read? You patched Error.prepareStackTrace with this function, so React DevTools is essentially the only user of this while it is installed, because you are not bubbling it up to previous function declared in Error.prepareStackTrace.

@sebmarkbage sebmarkbage merged commit 997c7bc into facebook:main May 13, 2025
245 checks passed
github-actions bot pushed a commit that referenced this pull request May 13, 2025
…ackTrace (#33143)

When we get the source location for "View source for this element" we
should be using the enclosing function of the callsite of the child. So
that we don't just point to some random line within the component.

This is similar to the technique in #33136.

This technique is now really better than the fake throw technique, when
available. So I now favor the owner technique. The only problem it's
only available in DEV and only if it has a child that's owned (and not
filtered).

We could implement this same technique for the error that's thrown in
the fake throwing solution. However, we really shouldn't need that at
all because for client components we should be able to call
`inspect(fn)` at least in Chrome which is even better.

DiffTrain build for [997c7bc](997c7bc)
github-actions bot pushed a commit to code/lib-react that referenced this pull request May 13, 2025
…ackTrace (facebook#33143)

When we get the source location for "View source for this element" we
should be using the enclosing function of the callsite of the child. So
that we don't just point to some random line within the component.

This is similar to the technique in facebook#33136.

This technique is now really better than the fake throw technique, when
available. So I now favor the owner technique. The only problem it's
only available in DEV and only if it has a child that's owned (and not
filtered).

We could implement this same technique for the error that's thrown in
the fake throwing solution. However, we really shouldn't need that at
all because for client components we should be able to call
`inspect(fn)` at least in Chrome which is even better.

DiffTrain build for [997c7bc](facebook@997c7bc)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants