@@ -25,14 +25,7 @@ import {
25
25
ANSI_STYLE_DIMMING_TEMPLATE ,
26
26
ANSI_STYLE_DIMMING_TEMPLATE_WITH_COMPONENT_STACK ,
27
27
} from 'react-devtools-shared/src/constants' ;
28
- import { getInternalReactConstants , getDispatcherRef } from './fiber/renderer' ;
29
- import {
30
- getStackByFiberInDevAndProd ,
31
- getOwnerStackByFiberInDev ,
32
- supportsOwnerStacks ,
33
- supportsConsoleTasks ,
34
- } from './fiber/DevToolsFiberComponentStack' ;
35
- import { formatOwnerStack } from './shared/DevToolsOwnerStack' ;
28
+ import { getInternalReactConstants } from './fiber/renderer' ;
36
29
import { castBool , castBrowserTheme } from '../utils' ;
37
30
38
31
const OVERRIDE_CONSOLE_METHODS = [ 'error' , 'trace' , 'warn' ] ;
@@ -91,6 +84,9 @@ function restorePotentiallyModifiedArgs(args: Array<any>): Array<any> {
91
84
}
92
85
93
86
type OnErrorOrWarning = ( type : 'error' | 'warn' , args : Array < any > ) => void ;
87
+ type GetComponentStack = (
88
+ topFrame : Error ,
89
+ ) => null | { enableOwnerStacks : boolean , componentStack : string } ;
94
90
95
91
const injectedRenderers : Map <
96
92
ReactRenderer ,
@@ -99,6 +95,7 @@ const injectedRenderers: Map<
99
95
getCurrentFiber : ( ) => Fiber | null ,
100
96
onErrorOrWarning : ?OnErrorOrWarning ,
101
97
workTagMap : WorkTagMap ,
98
+ getComponentStack : ?GetComponentStack ,
102
99
} ,
103
100
> = new Map ( ) ;
104
101
@@ -130,6 +127,7 @@ export function dangerous_setTargetConsoleForTesting(
130
127
export function registerRenderer (
131
128
renderer : ReactRenderer ,
132
129
onErrorOrWarning ?: OnErrorOrWarning ,
130
+ getComponentStack ?: GetComponentStack ,
133
131
) : void {
134
132
const { currentDispatcherRef, getCurrentFiber, version} = renderer ;
135
133
@@ -143,6 +141,7 @@ export function registerRenderer(
143
141
getCurrentFiber,
144
142
workTagMap : ReactTypeOfWork ,
145
143
onErrorOrWarning,
144
+ getComponentStack,
146
145
} ) ;
147
146
}
148
147
}
@@ -217,13 +216,12 @@ export function patch({
217
216
// We don't handle the edge case of stacks for more than one (e.g. interleaved renderers?)
218
217
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
219
218
for ( const renderer of injectedRenderers . values ( ) ) {
220
- const currentDispatcherRef = getDispatcherRef ( renderer ) ;
221
- const { getCurrentFiber, onErrorOrWarning, workTagMap} = renderer ;
219
+ const { getComponentStack, onErrorOrWarning} = renderer ;
222
220
try {
223
221
if ( shouldShowInlineWarningsAndErrors ) {
224
222
// patch() is called by two places: (1) the hook and (2) the renderer backend.
225
223
// The backend is what implements a message queue, so it's the only one that injects onErrorOrWarning.
226
- if ( typeof onErrorOrWarning === 'function' ) {
224
+ if ( onErrorOrWarning != null ) {
227
225
onErrorOrWarning (
228
226
( ( method : any ) : 'error' | 'warn' ) ,
229
227
// Restore and copy args before we mutate them (e.g. adding the component stack)
@@ -237,46 +235,26 @@ export function patch({
237
235
throw error ;
238
236
} , 0 ) ;
239
237
}
240
- const current : ?Fiber = getCurrentFiber ( ) ;
241
- if ( current != null ) {
242
- try {
243
- if (
244
- consoleSettingsRef . appendComponentStack &&
245
- ! supportsConsoleTasks ( current )
246
- ) {
247
- const enableOwnerStacks = supportsOwnerStacks ( current ) ;
248
- let componentStack = '' ;
249
- if ( enableOwnerStacks ) {
250
- // Prefix the owner stack with the current stack. I.e. what called
251
- // console.error. While this will also be part of the native stack,
252
- // it is hidden and not presented alongside this argument so we print
253
- // them all together.
254
- const topStackFrames = formatOwnerStack (
255
- new Error ( 'react-stack-top-frame' ) ,
256
- ) ;
257
- if ( topStackFrames ) {
258
- componentStack += '\n' + topStackFrames ;
259
- }
260
- componentStack += getOwnerStackByFiberInDev (
261
- workTagMap ,
262
- current ,
263
- ( currentDispatcherRef : any ) ,
264
- ) ;
265
- } else {
266
- componentStack = getStackByFiberInDevAndProd (
267
- workTagMap ,
268
- current ,
269
- ( currentDispatcherRef : any ) ,
270
- ) ;
271
- }
238
+ try {
239
+ if (
240
+ consoleSettingsRef . appendComponentStack &&
241
+ getComponentStack != null
242
+ ) {
243
+ // This needs to be directly in the wrapper so we can pop exactly one frame.
244
+ const topFrame = Error ( 'react-stack-top-frame' ) ;
245
+ const match = getComponentStack ( topFrame ) ;
246
+ if ( match !== null ) {
247
+ const { enableOwnerStacks, componentStack} = match ;
248
+ // Empty string means we have a match but no component stack.
249
+ // We don't need to look in other renderers but we also don't add anything.
272
250
if ( componentStack !== '' ) {
273
251
// Create a fake Error so that when we print it we get native source maps. Every
274
252
// browser will print the .stack property of the error and then parse it back for source
275
253
// mapping. Rather than print the internal slot. So it doesn't matter that the internal
276
254
// slot doesn't line up.
277
255
const fakeError = new Error ( '' ) ;
278
256
// In Chromium, only the stack property is printed but in Firefox the <name>:<message>
279
- // gets printed so to make the colon make sense, we name it so we print Component Stack:
257
+ // gets printed so to make the colon make sense, we name it so we print Stack:
280
258
// and similarly Safari leave an expandable slot.
281
259
fakeError . name = enableOwnerStacks
282
260
? 'Stack'
@@ -290,6 +268,7 @@ export function patch({
290
268
? 'Error Stack:'
291
269
: 'Error Component Stack:' ) + componentStack
292
270
: componentStack ;
271
+
293
272
if ( alreadyHasComponentStack ) {
294
273
// Only modify the component stack if it matches what we would've added anyway.
295
274
// Otherwise we assume it was a non-React stack.
@@ -325,15 +304,15 @@ export function patch({
325
304
}
326
305
}
327
306
}
307
+ // Don't add stacks from other renderers.
308
+ break ;
328
309
}
329
- } catch ( error ) {
330
- // Don't let a DevTools or React internal error interfere with logging.
331
- setTimeout ( ( ) => {
332
- throw error ;
333
- } , 0 ) ;
334
- } finally {
335
- break ;
336
310
}
311
+ } catch ( error ) {
312
+ // Don't let a DevTools or React internal error interfere with logging.
313
+ setTimeout ( ( ) => {
314
+ throw error ;
315
+ } , 0 ) ;
337
316
}
338
317
}
339
318
0 commit comments