From c7e23cd4df70ff328859483090e8fcd37eea5f20 Mon Sep 17 00:00:00 2001 From: Sebastian Sebbie Silbermann Date: Sat, 5 Jul 2025 18:14:18 +0200 Subject: [PATCH 1/2] [Flight] Pass line/column to `filterStackFrame` --- .../src/__tests__/ReactFlight-test.js | 9 +++- .../react-server/src/ReactFlightServer.js | 44 +++++++++++++++++-- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index d79977beec0d5..54fdd347182b2 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -1309,6 +1309,8 @@ describe('ReactFlight', () => { // third-party RSC frame // Ideally this would be a real frame produced by React not a mocked one. ' at ThirdParty (rsc://React/ThirdParty/file:///code/%5Broot%2520of%2520the%2520server%5D.js?42:1:1)', + // We'll later filter this out based on line/column in `filterStackFrame`. + ' at ThirdPartyModule (file:///file-with-index-source-map.js:52656:16374)', // host component in parent stack ' at div ()', ...originalStackLines.slice(2), @@ -1357,7 +1359,10 @@ describe('ReactFlight', () => { } return `digest(${String(x)})`; }, - filterStackFrame(filename, functionName) { + filterStackFrame(filename, functionName, lineNumber, columnNumber) { + if (lineNumber === 52656 && columnNumber === 16374) { + return false; + } if (!filename) { // Allow anonymous return functionName === 'div'; @@ -3682,7 +3687,7 @@ describe('ReactFlight', () => { onError(x) { return `digest("${x.message}")`; }, - filterStackFrame(url, functionName) { + filterStackFrame(url, functionName, lineNumber, columnNumber) { return functionName !== 'intermediate'; }, }, diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 9ba53d4afb360..ba182bc2c0d62 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -204,11 +204,22 @@ function findCalledFunctionNameFromStackTrace( const callsite = stack[i]; const functionName = callsite[0]; const url = devirtualizeURL(callsite[1]); + const lineNumber = callsite[2]; + const columnNumber = callsite[3]; + const enclosingLineNumber = callsite[4]; + const enclosingColumnNumber = callsite[5]; if (functionName === 'new Promise') { // Ignore Promise constructors. } else if (url === 'node:internal/async_hooks') { // Ignore the stack frames from the async hooks themselves. - } else if (filterStackFrame(url, functionName)) { + } else if ( + filterStackFrame( + url, + functionName, + enclosingLineNumber || lineNumber, + enclosingColumnNumber || columnNumber, + ) + ) { if (bestMatch === '') { // If we had no good stack frames for internal calls, just use the last // first party function name. @@ -236,7 +247,18 @@ function filterStackTrace( const callsite = stack[i]; const functionName = callsite[0]; const url = devirtualizeURL(callsite[1]); - if (filterStackFrame(url, functionName)) { + const lineNumber = callsite[2]; + const columnNumber = callsite[3]; + const enclosingLineNumber = callsite[4]; + const enclosingColumnNumber = callsite[5]; + if ( + filterStackFrame( + url, + functionName, + enclosingLineNumber || lineNumber, + enclosingColumnNumber || columnNumber, + ) + ) { // Use a clone because the Flight protocol isn't yet resilient to deduping // objects in the debug info. TODO: Support deduping stacks. const clone: ReactCallSite = (callsite.slice(0): any); @@ -466,7 +488,12 @@ export type Request = { // DEV-only completedDebugChunks: Array, environmentName: () => string, - filterStackFrame: (url: string, functionName: string) => boolean, + filterStackFrame: ( + url: string, + functionName: string, + lineNumber: number, + columnNumber: number, + ) => boolean, didWarnForKey: null | WeakSet, writtenDebugObjects: WeakMap, deferredDebugObjects: null | DeferredDebugStore, @@ -2180,7 +2207,16 @@ function visitAsyncNode( const callsite = fullStack[firstFrame]; const functionName = callsite[0]; const url = devirtualizeURL(callsite[1]); - isAwaitInUserspace = filterStackFrame(url, functionName); + const lineNumber = callsite[2]; + const columnNumber = callsite[3]; + const enclosingLineNumber = callsite[4]; + const enclosingColumnNumber = callsite[5]; + isAwaitInUserspace = filterStackFrame( + url, + functionName, + enclosingLineNumber || lineNumber, + enclosingColumnNumber || columnNumber, + ); } if (!isAwaitInUserspace) { // If this await was fully filtered out, then it was inside third party code From 28b08da5edc1278a58c96665bd4afb34f1fdd6a1 Mon Sep 17 00:00:00 2001 From: Sebastian Sebbie Silbermann Date: Mon, 7 Jul 2025 09:44:12 +0200 Subject: [PATCH 2/2] Don't pass in enclosing --- .../react-server/src/ReactFlightServer.js | 28 +++---------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index ba182bc2c0d62..f61835723ada6 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -206,20 +206,11 @@ function findCalledFunctionNameFromStackTrace( const url = devirtualizeURL(callsite[1]); const lineNumber = callsite[2]; const columnNumber = callsite[3]; - const enclosingLineNumber = callsite[4]; - const enclosingColumnNumber = callsite[5]; if (functionName === 'new Promise') { // Ignore Promise constructors. } else if (url === 'node:internal/async_hooks') { // Ignore the stack frames from the async hooks themselves. - } else if ( - filterStackFrame( - url, - functionName, - enclosingLineNumber || lineNumber, - enclosingColumnNumber || columnNumber, - ) - ) { + } else if (filterStackFrame(url, functionName, lineNumber, columnNumber)) { if (bestMatch === '') { // If we had no good stack frames for internal calls, just use the last // first party function name. @@ -249,16 +240,7 @@ function filterStackTrace( const url = devirtualizeURL(callsite[1]); const lineNumber = callsite[2]; const columnNumber = callsite[3]; - const enclosingLineNumber = callsite[4]; - const enclosingColumnNumber = callsite[5]; - if ( - filterStackFrame( - url, - functionName, - enclosingLineNumber || lineNumber, - enclosingColumnNumber || columnNumber, - ) - ) { + if (filterStackFrame(url, functionName, lineNumber, columnNumber)) { // Use a clone because the Flight protocol isn't yet resilient to deduping // objects in the debug info. TODO: Support deduping stacks. const clone: ReactCallSite = (callsite.slice(0): any); @@ -2209,13 +2191,11 @@ function visitAsyncNode( const url = devirtualizeURL(callsite[1]); const lineNumber = callsite[2]; const columnNumber = callsite[3]; - const enclosingLineNumber = callsite[4]; - const enclosingColumnNumber = callsite[5]; isAwaitInUserspace = filterStackFrame( url, functionName, - enclosingLineNumber || lineNumber, - enclosingColumnNumber || columnNumber, + lineNumber, + columnNumber, ); } if (!isAwaitInUserspace) {