[Flight] Pass line/column to `filterStackFrame` (#33707)
Sebastian "Sebbie" Silbermann committed
Jul 7, 2025 at 13:51 UTC
bb402876f7413ae2e6809ad442405a5a90c69b45
2 files changed
+27
-6
packages/react-client/src/__tests__/ReactFlight-test.js
+7
-2
@@ -1309,6 +1309,8 @@ describe('ReactFlight', () => {
1309
// third-party RSC frame
1310
// Ideally this would be a real frame produced by React not a mocked one.
1311
' at ThirdParty (rsc://React/ThirdParty/file:///code/%5Broot%2520of%2520the%2520server%5D.js?42:1:1)',
1312
+ // We'll later filter this out based on line/column in `filterStackFrame`.
1313
+ ' at ThirdPartyModule (file:///file-with-index-source-map.js:52656:16374)',
1314
// host component in parent stack
1315
' at div (<anonymous>)',
1316
...originalStackLines.slice(2),
@@ -1357,7 +1359,10 @@ describe('ReactFlight', () => {
1359
}
1360
return `digest(${String(x)})`;
1361
},
1360
- filterStackFrame(filename, functionName) {
1362
+ filterStackFrame(filename, functionName, lineNumber, columnNumber) {
1363
+ if (lineNumber === 52656 && columnNumber === 16374) {
1364
+ return false;
1365
+ }
1366
if (!filename) {
1367
// Allow anonymous
1368
return functionName === 'div';
@@ -3682,7 +3687,7 @@ describe('ReactFlight', () => {
3687
onError(x) {
3688
return `digest("${x.message}")`;
3689
},
3685
- filterStackFrame(url, functionName) {
3690
+ filterStackFrame(url, functionName, lineNumber, columnNumber) {
3691
return functionName !== 'intermediate';
3692
},
3693
},
packages/react-server/src/ReactFlightServer.js
+20
-4
@@ -204,11 +204,13 @@ function findCalledFunctionNameFromStackTrace(
204
const callsite = stack[i];
205
const functionName = callsite[0];
206
const url = devirtualizeURL(callsite[1]);
207
+ const lineNumber = callsite[2];
208
+ const columnNumber = callsite[3];
209
if (functionName === 'new Promise') {
210
// Ignore Promise constructors.
211
} else if (url === 'node:internal/async_hooks') {
212
// Ignore the stack frames from the async hooks themselves.
211
- } else if (filterStackFrame(url, functionName)) {
213
+ } else if (filterStackFrame(url, functionName, lineNumber, columnNumber)) {
214
if (bestMatch === '') {
215
// If we had no good stack frames for internal calls, just use the last
216
// first party function name.
@@ -236,7 +238,9 @@ function filterStackTrace(
238
const callsite = stack[i];
239
const functionName = callsite[0];
240
const url = devirtualizeURL(callsite[1]);
239
- if (filterStackFrame(url, functionName)) {
241
+ const lineNumber = callsite[2];
242
+ const columnNumber = callsite[3];
243
+ if (filterStackFrame(url, functionName, lineNumber, columnNumber)) {
244
// Use a clone because the Flight protocol isn't yet resilient to deduping
245
// objects in the debug info. TODO: Support deduping stacks.
246
const clone: ReactCallSite = (callsite.slice(0): any);
@@ -466,7 +470,12 @@ export type Request = {
470
// DEV-only
471
completedDebugChunks: Array<Chunk | BinaryChunk>,
472
environmentName: () => string,
469
- filterStackFrame: (url: string, functionName: string) => boolean,
473
+ filterStackFrame: (
474
+ url: string,
475
+ functionName: string,
476
+ lineNumber: number,
477
+ columnNumber: number,
478
+ ) => boolean,
479
didWarnForKey: null | WeakSet<ReactComponentInfo>,
480
writtenDebugObjects: WeakMap<Reference, string>,
481
deferredDebugObjects: null | DeferredDebugStore,
@@ -2180,7 +2189,14 @@ function visitAsyncNode(
2189
const callsite = fullStack[firstFrame];
2190
const functionName = callsite[0];
2191
const url = devirtualizeURL(callsite[1]);
2183
- isAwaitInUserspace = filterStackFrame(url, functionName);
2192
+ const lineNumber = callsite[2];
2193
+ const columnNumber = callsite[3];
2194
+ isAwaitInUserspace = filterStackFrame(
2195
+ url,
2196
+ functionName,
2197
+ lineNumber,
2198
+ columnNumber,
2199
+ );
2200
}
2201
if (!isAwaitInUserspace) {
2202
// If this await was fully filtered out, then it was inside third party code