Add unknown location information to component stacks (#30290)
This is the same change as in #30289 but for the main runtime - e.g. parent stacks in errorInfo.componentStack, appended stacks to console.error coming from React itself and when we add virtual frames to owner stacks. Since we don't add location information these frames look weird to some stack parsers - such as the native one. This is an existing issue when you want to use some off-the-shelf parsers to parse production component stacks for example. While we won't add Error objects to logs ourselves necessarily, some third party could want to do the same thing we do in DevTools and so we should provide the same capability to just take this trace and print it using an Error object.
Sebastian Markbåge committed
Jul 8, 2024 at 11:54 UTC
df783f9ea1b6f95e05f830602da1de5ffb325d30
3 files changed
+14
-4
packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js
+1
-1
@@ -475,7 +475,7 @@ describe('ReactDOM HostSingleton', () => {
475
expect(hydrationErrors).toEqual([
476
[
477
"Hydration failed because the server rendered HTML didn't match the client.",
478
- 'at div',
478
+ 'at div (<anonymous>)',
479
],
480
]);
481
expect(persistentElements).toEqual([
packages/react-server/src/ReactFizzServer.js
+1
-1
@@ -924,7 +924,7 @@ function pushServerComponentStack(
924
let name = componentInfo.name;
925
const env = componentInfo.env;
926
if (env) {
927
- name += ' (' + env + ')';
927
+ name += ' [' + env + ']';
928
}
929
task.componentStack = {
930
tag: 3,
packages/shared/ReactComponentStackFrame.js
+12
-2
@@ -24,6 +24,7 @@ import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
24
import ReactSharedInternals from 'shared/ReactSharedInternals';
25
26
let prefix;
27
+let suffix;
28
export function describeBuiltInComponentFrame(name: string): string {
29
if (enableComponentStackLocations) {
30
if (prefix === undefined) {
@@ -33,17 +34,26 @@ export function describeBuiltInComponentFrame(name: string): string {
34
} catch (x) {
35
const match = x.stack.trim().match(/\n( *(at )?)/);
36
prefix = (match && match[1]) || '';
37
+ suffix =
38
+ x.stack.indexOf('\n at') > -1
39
+ ? // V8
40
+ ' (<anonymous>)'
41
+ : // JSC/Spidermonkey
42
+ x.stack.indexOf('@') > -1
43
+ ? '@unknown:0:0'
44
+ : // Other
45
+ '';
46
}
47
}
48
// We use the prefix to ensure our stacks line up with native stack frames.
39
- return '\n' + prefix + name;
49
+ return '\n' + prefix + name + suffix;
50
} else {
51
return describeComponentFrame(name);
52
}
53
}
54
55
export function describeDebugInfoFrame(name: string, env: ?string): string {
46
- return describeBuiltInComponentFrame(name + (env ? ' (' + env + ')' : ''));
56
+ return describeBuiltInComponentFrame(name + (env ? ' [' + env + ']' : ''));
57
}
58
59
let reentry = false;