@@ -165,6 +165,50 @@ function defaultFilterStackFrame(
165165 ) ;
166166}
167167
168+ function devirtualizeURL(url: string): string {
169+ if ( url . startsWith ( 'rsc://React/' ) ) {
170+ // This callsite is a virtual fake callsite that came from another Flight client.
171+ // We need to reverse it back into the original location by stripping its prefix
172+ // and suffix. We don't need the environment name because it's available on the
173+ // parent object that will contain the stack.
174+ const envIdx = url . indexOf ( '/' , 12 ) ;
175+ const suffixIdx = url . lastIndexOf ( '?' ) ;
176+ if ( envIdx > - 1 && suffixIdx > - 1 ) {
177+ return url . slice ( envIdx + 1 , suffixIdx ) ;
178+ }
179+ }
180+ return url ;
181+ }
182+
183+ function findCalledFunctionNameFromStackTrace (
184+ request : Request ,
185+ stack : ReactStackTrace ,
186+ ) : string {
187+ // Gets the name of the first function called from first party code.
188+ let bestMatch = '' ;
189+ const filterStackFrame = request . filterStackFrame ;
190+ for ( let i = 0 ; i < stack . length ; i ++ ) {
191+ const callsite = stack [ i ] ;
192+ const functionName = callsite [ 0 ] ;
193+ const url = devirtualizeURL ( callsite [ 1 ] ) ;
194+ if ( filterStackFrame ( url , functionName ) ) {
195+ if ( bestMatch === '' ) {
196+ // If we had no good stack frames for internal calls, just use the last
197+ // first party function name.
198+ return functionName ;
199+ }
200+ return bestMatch ;
201+ } else if ( functionName === 'new Promise' ) {
202+ // Ignore Promise constructors.
203+ } else if ( url === 'node:internal/async_hooks' ) {
204+ // Ignore the stack frames from the async hooks themselves.
205+ } else {
206+ bestMatch = functionName ;
207+ }
208+ }
209+ return '' ;
210+ }
211+
168212function filterStackTrace (
169213 request : Request ,
170214 stack : ReactStackTrace ,
@@ -178,18 +222,7 @@ function filterStackTrace(
178222 for ( let i = 0 ; i < stack . length ; i ++ ) {
179223 const callsite = stack [ i ] ;
180224 const functionName = callsite [ 0 ] ;
181- let url = callsite [ 1 ] ;
182- if ( url . startsWith ( 'rsc://React/' ) ) {
183- // This callsite is a virtual fake callsite that came from another Flight client.
184- // We need to reverse it back into the original location by stripping its prefix
185- // and suffix. We don't need the environment name because it's available on the
186- // parent object that will contain the stack.
187- const envIdx = url . indexOf ( '/' , 12 ) ;
188- const suffixIdx = url . lastIndexOf ( '?' ) ;
189- if ( envIdx > - 1 && suffixIdx > - 1 ) {
190- url = url . slice ( envIdx + 1 , suffixIdx ) ;
191- }
192- }
225+ const url = devirtualizeURL ( callsite [ 1 ] ) ;
193226 if ( filterStackFrame ( url , functionName ) ) {
194227 // Use a clone because the Flight protocol isn't yet resilient to deduping
195228 // objects in the debug info. TODO: Support deduping stacks.
@@ -3568,9 +3601,18 @@ function serializeIONode(
35683601 let stack = null ;
35693602 let name = '';
35703603 if ( ioNode . stack !== null ) {
3571- stack = filterStackTrace ( request , parseStackTrace ( ioNode . stack , 1 ) ) ;
3572- name = '';
3604+ const fullStack = parseStackTrace ( ioNode . stack , 1 ) ;
3605+ stack = filterStackTrace ( request , fullStack ) ;
3606+ name = findCalledFunctionNameFromStackTrace ( request , fullStack ) ;
3607+ // The name can include the object that this was called on but sometimes that's
3608+ // just unnecessary context.
3609+ if ( name . startsWith ( 'Window.' ) ) {
3610+ name = name . slice ( 7 ) ;
3611+ } else if ( name . startsWith ( '<anonymous>.' ) ) {
3612+ name = name . slice ( 7 ) ;
3613+ }
35733614 }
3615+
35743616 request . pendingChunks ++ ;
35753617 const id = request . nextChunkId ++ ;
35763618 emitIOInfoChunk ( request , id , name , ioNode . start , ioNode . end , stack ) ;
0 commit comments