Skip to content

Commit 0f36451

Browse files
committed
Split filter and parsing
filterStackTrace evolved into being both parsing and filtering. Split them so we can use the same parsed value twice without relying on caching.
1 parent 47214f4 commit 0f36451

File tree

1 file changed

+25
-18
lines changed

1 file changed

+25
-18
lines changed

packages/react-server/src/ReactFlightServer.js

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import type {
6464
ReactAsyncInfo,
6565
ReactTimeInfo,
6666
ReactStackTrace,
67+
ReactCallSite,
6768
ReactFunctionLocation,
6869
ReactErrorInfo,
6970
ReactErrorInfoDev,
@@ -166,16 +167,14 @@ function defaultFilterStackFrame(
166167

167168
function filterStackTrace(
168169
request: Request,
169-
error: Error,
170-
skipFrames: number,
170+
stack: ReactStackTrace,
171171
): ReactStackTrace {
172172
// Since stacks can be quite large and we pass a lot of them, we filter them out eagerly
173173
// to save bandwidth even in DEV. We'll also replay these stacks on the client so by
174174
// stripping them early we avoid that overhead. Otherwise we'd normally just rely on
175175
// the DevTools or framework's ignore lists to filter them out.
176176
const filterStackFrame = request.filterStackFrame;
177-
const stack = parseStackTrace(error, skipFrames);
178-
const filteredStack = [];
177+
const filteredStack: ReactStackTrace = [];
179178
for (let i = 0; i < stack.length; i++) {
180179
const callsite = stack[i];
181180
const functionName = callsite[0];
@@ -194,7 +193,7 @@ function filterStackTrace(
194193
if (filterStackFrame(url, functionName)) {
195194
// Use a clone because the Flight protocol isn't yet resilient to deduping
196195
// objects in the debug info. TODO: Support deduping stacks.
197-
const clone = callsite.slice(0);
196+
const clone: ReactCallSite = (callsite.slice(0): any);
198197
clone[1] = url;
199198
filteredStack.push(clone);
200199
}
@@ -227,8 +226,7 @@ function patchConsole(consoleInst: typeof console, methodName: string) {
227226
// one stack frame but keeping it simple for now and include all frames.
228227
const stack = filterStackTrace(
229228
request,
230-
new Error('react-stack-top-frame'),
231-
1,
229+
parseStackTrace(new Error('react-stack-top-frame'), 1),
232230
);
233231
request.pendingChunks++;
234232
const owner: null | ReactComponentInfo = resolveOwner();
@@ -1065,7 +1063,7 @@ function callWithDebugContextInDEV<A, T>(
10651063
componentDebugInfo.stack =
10661064
task.debugStack === null
10671065
? null
1068-
: filterStackTrace(request, task.debugStack, 1);
1066+
: filterStackTrace(request, parseStackTrace(task.debugStack, 1));
10691067
// $FlowFixMe[cannot-write]
10701068
componentDebugInfo.debugStack = task.debugStack;
10711069
// $FlowFixMe[cannot-write]
@@ -1266,7 +1264,7 @@ function renderFunctionComponent<Props>(
12661264
componentDebugInfo.stack =
12671265
task.debugStack === null
12681266
? null
1269-
: filterStackTrace(request, task.debugStack, 1);
1267+
: filterStackTrace(request, parseStackTrace(task.debugStack, 1));
12701268
// $FlowFixMe[cannot-write]
12711269
componentDebugInfo.props = props;
12721270
// $FlowFixMe[cannot-write]
@@ -1602,7 +1600,7 @@ function renderClientElement(
16021600
task.debugOwner,
16031601
task.debugStack === null
16041602
? null
1605-
: filterStackTrace(request, task.debugStack, 1),
1603+
: filterStackTrace(request, parseStackTrace(task.debugStack, 1)),
16061604
validated,
16071605
]
16081606
: [REACT_ELEMENT_TYPE, type, key, props];
@@ -1735,7 +1733,7 @@ function renderElement(
17351733
stack:
17361734
task.debugStack === null
17371735
? null
1738-
: filterStackTrace(request, task.debugStack, 1),
1736+
: filterStackTrace(request, parseStackTrace(task.debugStack, 1)),
17391737
props: props,
17401738
debugStack: task.debugStack,
17411739
debugTask: task.debugTask,
@@ -1864,7 +1862,10 @@ function visitAsyncNode(
18641862
// We don't log it yet though. We return it to be logged by the point where it's awaited.
18651863
// The ioNode might be another PromiseNode in the case where none of the AwaitNode had
18661864
// unfiltered stacks.
1867-
if (filterStackTrace(request, node.stack, 1).length === 0) {
1865+
if (
1866+
filterStackTrace(request, parseStackTrace(node.stack, 1)).length ===
1867+
0
1868+
) {
18681869
// Typically we assume that the outer most Promise that was awaited in user space has the
18691870
// most actionable stack trace for the start of the operation. However, if this Promise
18701871
// Promise was created inside only third party code, then try to use the inner node instead.
@@ -1885,7 +1886,10 @@ function visitAsyncNode(
18851886
if (awaited !== null) {
18861887
const ioNode = visitAsyncNode(request, task, awaited, cutOff, visited);
18871888
if (ioNode !== null) {
1888-
const stack = filterStackTrace(request, node.stack, 1);
1889+
const stack = filterStackTrace(
1890+
request,
1891+
parseStackTrace(node.stack, 1),
1892+
);
18891893
if (stack.length === 0) {
18901894
// If this await was fully filtered out, then it was inside third party code
18911895
// such as in an external library. We return the I/O node and try another await.
@@ -3259,7 +3263,7 @@ function emitPostponeChunk(
32593263
try {
32603264
// eslint-disable-next-line react-internal/safe-string-coercion
32613265
reason = String(postponeInstance.message);
3262-
stack = filterStackTrace(request, postponeInstance, 0);
3266+
stack = filterStackTrace(request, parseStackTrace(postponeInstance, 0));
32633267
} catch (x) {
32643268
stack = [];
32653269
}
@@ -3282,7 +3286,7 @@ function serializeErrorValue(request: Request, error: Error): string {
32823286
name = error.name;
32833287
// eslint-disable-next-line react-internal/safe-string-coercion
32843288
message = String(error.message);
3285-
stack = filterStackTrace(request, error, 0);
3289+
stack = filterStackTrace(request, parseStackTrace(error, 0));
32863290
const errorEnv = (error: any).environmentName;
32873291
if (typeof errorEnv === 'string') {
32883292
// This probably came from another FlightClient as a pass through.
@@ -3321,7 +3325,7 @@ function emitErrorChunk(
33213325
name = error.name;
33223326
// eslint-disable-next-line react-internal/safe-string-coercion
33233327
message = String(error.message);
3324-
stack = filterStackTrace(request, error, 0);
3328+
stack = filterStackTrace(request, parseStackTrace(error, 0));
33253329
const errorEnv = (error: any).environmentName;
33263330
if (typeof errorEnv === 'string') {
33273331
// This probably came from another FlightClient as a pass through.
@@ -3564,7 +3568,7 @@ function serializeIONode(
35643568
let stack = null;
35653569
let name = '';
35663570
if (ioNode.stack !== null) {
3567-
stack = filterStackTrace(request, ioNode.stack, 1);
3571+
stack = filterStackTrace(request, parseStackTrace(ioNode.stack, 1));
35683572
name = '';
35693573
}
35703574
request.pendingChunks++;
@@ -3710,7 +3714,10 @@ function renderConsoleValue(
37103714
let debugStack: null | ReactStackTrace = null;
37113715
if (element._debugStack != null) {
37123716
// Outline the debug stack so that it doesn't get cut off.
3713-
debugStack = filterStackTrace(request, element._debugStack, 1);
3717+
debugStack = filterStackTrace(
3718+
request,
3719+
parseStackTrace(element._debugStack, 1),
3720+
);
37143721
doNotLimit.add(debugStack);
37153722
for (let i = 0; i < debugStack.length; i++) {
37163723
doNotLimit.add(debugStack[i]);

0 commit comments

Comments
 (0)