@samitouri / QOS-React-2 / commits / df783f9ea1

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;