Skip to content

Commit 5b37af7

Browse files
authored
Stop filtering owner stacks (#30438)
We still filter them before passing from server to client in Flight Server but when presenting a native stack, we don't need to filter them. That's left to ignore listing in the presentation. The stacks are pretty clean regardless thanks to the bottom stack frames. We can also unify the owner stack formatters into one shared module since Fizz/Flight/Fiber all do the same thing. DevTools currently does the same thing but is forked so it can support multiple versions.
1 parent ab2135c commit 5b37af7

11 files changed

+128
-212
lines changed

packages/react-devtools-shared/src/backend/DevToolsOwnerStack.js

+9-20
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,9 @@
77
* @flow
88
*/
99

10-
// This is a DevTools fork of ReactFiberOwnerStack.
10+
// This is a DevTools fork of shared/ReactOwnerStackFrames.
1111

12-
// TODO: Make this configurable?
13-
const externalRegExp = /\/node\_modules\/|\(\<anonymous\>/;
14-
15-
function isNotExternal(stackFrame: string): boolean {
16-
return !externalRegExp.test(stackFrame);
17-
}
18-
19-
function filterDebugStack(error: Error): string {
20-
// Since stacks can be quite large and we pass a lot of them, we filter them out eagerly
21-
// to save bandwidth even in DEV. We'll also replay these stacks on the client so by
22-
// stripping them early we avoid that overhead. Otherwise we'd normally just rely on
23-
// the DevTools or framework's ignore lists to filter them out.
12+
export function formatOwnerStack(error: Error): string {
2413
const prevPrepareStackTrace = Error.prepareStackTrace;
2514
// $FlowFixMe[incompatible-type] It does accept undefined.
2615
Error.prepareStackTrace = undefined;
@@ -31,7 +20,12 @@ function filterDebugStack(error: Error): string {
3120
// don't want/need.
3221
stack = stack.slice(29);
3322
}
34-
let idx = stack.indexOf('react-stack-bottom-frame');
23+
let idx = stack.indexOf('\n');
24+
if (idx !== -1) {
25+
// Pop the JSX frame.
26+
stack = stack.slice(idx + 1);
27+
}
28+
idx = stack.indexOf('react-stack-bottom-frame');
3529
if (idx !== -1) {
3630
idx = stack.lastIndexOf('\n', idx);
3731
}
@@ -44,10 +38,5 @@ function filterDebugStack(error: Error): string {
4438
// To keep things light we exclude the entire trace in this case.
4539
return '';
4640
}
47-
const frames = stack.split('\n').slice(1); // Pop the JSX frame.
48-
return frames.filter(isNotExternal).join('\n');
49-
}
50-
51-
export function formatOwnerStack(ownerStackTrace: Error): string {
52-
return filterDebugStack(ownerStackTrace);
41+
return stack;
5342
}

packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js

+1
Original file line numberDiff line numberDiff line change
@@ -1848,6 +1848,7 @@ describe('ReactDOMFizzServer', () => {
18481848
(gate(flags => flags.enableOwnerStacks)
18491849
? ' in span (at **)\n' +
18501850
' in mapper (at **)\n' +
1851+
' in Array.map (at **)\n' +
18511852
' in B (at **)\n' +
18521853
' in A (at **)'
18531854
: ' in span (at **)\n' +

packages/react-reconciler/src/ReactFiberComponentStack.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
describeClassComponentFrame,
3131
describeDebugInfoFrame,
3232
} from 'shared/ReactComponentStackFrame';
33-
import {formatOwnerStack} from './ReactFiberOwnerStack';
33+
import {formatOwnerStack} from 'shared/ReactOwnerStackFrames';
3434

3535
function describeFiber(fiber: Fiber): string {
3636
switch (fiber.tag) {

packages/react-reconciler/src/ReactFiberOwnerStack.js

-47
This file was deleted.

packages/react-server/src/ReactFizzComponentStack.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727

2828
import {enableOwnerStacks} from 'shared/ReactFeatureFlags';
2929

30-
import {formatOwnerStack} from './ReactFizzOwnerStack';
30+
import {formatOwnerStack} from 'shared/ReactOwnerStackFrames';
3131

3232
export type ComponentStackNode = {
3333
parent: null | ComponentStackNode,

packages/react-server/src/ReactFizzOwnerStack.js

-47
This file was deleted.

packages/react-server/src/ReactFlightCallUserSpace.js

+1-35
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@ import type {ReactClientValue} from './ReactFlightServer';
1515

1616
import {setCurrentOwner} from './flight/ReactFlightCurrentOwner';
1717

18-
import {
19-
supportsComponentStorage,
20-
componentStorage,
21-
} from './ReactFlightServerConfig';
22-
23-
import {enableOwnerStacks} from 'shared/ReactFeatureFlags';
24-
2518
// These indirections exists so we can exclude its stack frame in DEV (and anything below it).
2619
// TODO: Consider marking the whole bundle instead of these boundaries.
2720

@@ -30,38 +23,12 @@ const callComponent = {
3023
Component: (p: Props, arg: void) => R,
3124
props: Props,
3225
componentDebugInfo: ReactComponentInfo,
33-
debugTask: null | ConsoleTask,
3426
): R {
3527
// The secondArg is always undefined in Server Components since refs error early.
3628
const secondArg = undefined;
3729
setCurrentOwner(componentDebugInfo);
3830
try {
39-
if (supportsComponentStorage) {
40-
// Run the component in an Async Context that tracks the current owner.
41-
if (enableOwnerStacks && debugTask) {
42-
return debugTask.run(
43-
// $FlowFixMe[method-unbinding]
44-
componentStorage.run.bind(
45-
componentStorage,
46-
componentDebugInfo,
47-
Component,
48-
props,
49-
secondArg,
50-
),
51-
);
52-
}
53-
return componentStorage.run(
54-
componentDebugInfo,
55-
Component,
56-
props,
57-
secondArg,
58-
);
59-
} else {
60-
if (enableOwnerStacks && debugTask) {
61-
return debugTask.run(Component.bind(null, props, secondArg));
62-
}
63-
return Component(props, secondArg);
64-
}
31+
return Component(props, secondArg);
6532
} finally {
6633
setCurrentOwner(null);
6734
}
@@ -72,7 +39,6 @@ export const callComponentInDEV: <Props, R>(
7239
Component: (p: Props, arg: void) => R,
7340
props: Props,
7441
componentDebugInfo: ReactComponentInfo,
75-
debugTask: null | ConsoleTask,
7642
) => R = __DEV__
7743
? // We use this technique to trick minifiers to preserve the function name.
7844
(callComponent['react-stack-bottom-frame'].bind(callComponent): any)

packages/react-server/src/ReactFlightOwnerStack.js

-42
This file was deleted.

packages/react-server/src/ReactFlightServer.js

+74-18
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ import {
8080
createHints,
8181
initAsyncDebugInfo,
8282
parseStackTrace,
83+
supportsComponentStorage,
84+
componentStorage,
8385
} from './ReactFlightServerConfig';
8486

8587
import {
@@ -1035,12 +1037,38 @@ function renderFunctionComponent<Props>(
10351037
}
10361038
}
10371039
prepareToUseHooksForComponent(prevThenableState, componentDebugInfo);
1038-
result = callComponentInDEV(
1039-
Component,
1040-
props,
1041-
componentDebugInfo,
1042-
task.debugTask,
1043-
);
1040+
if (supportsComponentStorage) {
1041+
// Run the component in an Async Context that tracks the current owner.
1042+
if (enableOwnerStacks && task.debugTask) {
1043+
result = task.debugTask.run(
1044+
// $FlowFixMe[method-unbinding]
1045+
componentStorage.run.bind(
1046+
componentStorage,
1047+
componentDebugInfo,
1048+
callComponentInDEV,
1049+
Component,
1050+
props,
1051+
componentDebugInfo,
1052+
),
1053+
);
1054+
} else {
1055+
result = componentStorage.run(
1056+
componentDebugInfo,
1057+
callComponentInDEV,
1058+
Component,
1059+
props,
1060+
componentDebugInfo,
1061+
);
1062+
}
1063+
} else {
1064+
if (enableOwnerStacks && task.debugTask) {
1065+
result = task.debugTask.run(
1066+
callComponentInDEV.bind(null, Component, props, componentDebugInfo),
1067+
);
1068+
} else {
1069+
result = callComponentInDEV(Component, props, componentDebugInfo);
1070+
}
1071+
}
10441072
} else {
10451073
prepareToUseHooksForComponent(prevThenableState, null);
10461074
// The secondArg is always undefined in Server Components since refs error early.
@@ -1222,19 +1250,47 @@ function warnForMissingKey(
12221250

12231251
// Call with the server component as the currently rendering component
12241252
// for context.
1225-
callComponentInDEV(
1226-
() => {
1227-
console.error(
1228-
'Each child in a list should have a unique "key" prop.' +
1229-
'%s%s See https://react.dev/link/warning-keys for more information.',
1230-
'',
1231-
'',
1253+
const logKeyError = () => {
1254+
console.error(
1255+
'Each child in a list should have a unique "key" prop.' +
1256+
'%s%s See https://react.dev/link/warning-keys for more information.',
1257+
'',
1258+
'',
1259+
);
1260+
};
1261+
1262+
if (supportsComponentStorage) {
1263+
// Run the component in an Async Context that tracks the current owner.
1264+
if (enableOwnerStacks && debugTask) {
1265+
debugTask.run(
1266+
// $FlowFixMe[method-unbinding]
1267+
componentStorage.run.bind(
1268+
componentStorage,
1269+
componentDebugInfo,
1270+
callComponentInDEV,
1271+
logKeyError,
1272+
null,
1273+
componentDebugInfo,
1274+
),
12321275
);
1233-
},
1234-
null,
1235-
componentDebugInfo,
1236-
debugTask,
1237-
);
1276+
} else {
1277+
componentStorage.run(
1278+
componentDebugInfo,
1279+
callComponentInDEV,
1280+
logKeyError,
1281+
null,
1282+
componentDebugInfo,
1283+
);
1284+
}
1285+
} else {
1286+
if (enableOwnerStacks && debugTask) {
1287+
debugTask.run(
1288+
callComponentInDEV.bind(null, logKeyError, null, componentDebugInfo),
1289+
);
1290+
} else {
1291+
callComponentInDEV(logKeyError, null, componentDebugInfo);
1292+
}
1293+
}
12381294
}
12391295
}
12401296

packages/react-server/src/flight/ReactFlightComponentStack.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {describeBuiltInComponentFrame} from 'shared/ReactComponentStackFrame';
1313

1414
import {enableOwnerStacks} from 'shared/ReactFeatureFlags';
1515

16-
import {formatOwnerStack} from '../ReactFlightOwnerStack';
16+
import {formatOwnerStack} from 'shared/ReactOwnerStackFrames';
1717

1818
export function getOwnerStackByComponentInfoInDev(
1919
componentInfo: ReactComponentInfo,

0 commit comments

Comments
 (0)