@@ -165,6 +165,50 @@ function defaultFilterStackFrame(
165
165
) ;
166
166
}
167
167
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
+
168
212
function filterStackTrace (
169
213
request : Request ,
170
214
stack : ReactStackTrace ,
@@ -178,18 +222,7 @@ function filterStackTrace(
178
222
for ( let i = 0 ; i < stack . length ; i ++ ) {
179
223
const callsite = stack [ i ] ;
180
224
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 ] ) ;
193
226
if ( filterStackFrame ( url , functionName ) ) {
194
227
// Use a clone because the Flight protocol isn't yet resilient to deduping
195
228
// objects in the debug info. TODO: Support deduping stacks.
@@ -3568,9 +3601,18 @@ function serializeIONode(
3568
3601
let stack = null ;
3569
3602
let name = '';
3570
3603
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
+ }
3573
3614
}
3615
+
3574
3616
request . pendingChunks ++ ;
3575
3617
const id = request . nextChunkId ++ ;
3576
3618
emitIOInfoChunk ( request , id , name , ioNode . start , ioNode . end , stack ) ;
0 commit comments