@@ -146,6 +146,7 @@ import {formatOwnerStack} from '../shared/DevToolsOwnerStack';
146
146
// Kinds
147
147
const FIBER_INSTANCE = 0 ;
148
148
const VIRTUAL_INSTANCE = 1 ;
149
+ const FILTERED_FIBER_INSTANCE = 2 ;
149
150
150
151
// Flags
151
152
const FORCE_SUSPENSE_FALLBACK = /* */ 0b001 ;
@@ -157,9 +158,9 @@ const FORCE_ERROR_RESET = /* */ 0b100;
157
158
type FiberInstance = {
158
159
kind : 0 ,
159
160
id : number ,
160
- parent : null | DevToolsInstance , // filtered parent, including virtual
161
- firstChild : null | DevToolsInstance , // filtered first child, including virtual
162
- nextSibling : null | DevToolsInstance , // filtered next sibling, including virtual
161
+ parent : null | DevToolsInstance ,
162
+ firstChild : null | DevToolsInstance ,
163
+ nextSibling : null | DevToolsInstance ,
163
164
flags : number , // Force Error/Suspense
164
165
source : null | string | Error | Source , // source location of this component function, or owned child stack
165
166
errors : null | Map < string , number> , // error messages and count
@@ -184,6 +185,39 @@ function createFiberInstance(fiber: Fiber): FiberInstance {
184
185
} ;
185
186
}
186
187
188
+ type FilteredFiberInstance = {
189
+ kind : 2 ,
190
+ // We exclude id from the type to get errors if we try to access it.
191
+ // However it is still in the object to preserve hidden class.
192
+ // id: number,
193
+ parent : null | DevToolsInstance ,
194
+ firstChild : null | DevToolsInstance ,
195
+ nextSibling : null | DevToolsInstance ,
196
+ flags : number , // Force Error/Suspense
197
+ source : null | string | Error | Source , // always null here.
198
+ errors : null , // error messages and count
199
+ warnings : null , // warning messages and count
200
+ treeBaseDuration : number , // the profiled time of the last render of this subtree
201
+ data : Fiber , // one of a Fiber pair
202
+ } ;
203
+
204
+ // This is used to represent a filtered Fiber but still lets us find its host instance.
205
+ function createFilteredFiberInstance ( fiber : Fiber ) : FilteredFiberInstance {
206
+ return ( {
207
+ kind : FILTERED_FIBER_INSTANCE ,
208
+ id : 0 ,
209
+ parent : null ,
210
+ firstChild : null ,
211
+ nextSibling : null ,
212
+ flags : 0 ,
213
+ componentStack : null ,
214
+ errors : null ,
215
+ warnings : null ,
216
+ treeBaseDuration : 0 ,
217
+ data : fiber ,
218
+ } : any ) ;
219
+ }
220
+
187
221
// This type represents a stateful instance of a Server Component or a Component
188
222
// that gets optimized away - e.g. call-through without creating a Fiber.
189
223
// It's basically a virtual Fiber. This is not a semantic concept in React.
@@ -192,9 +226,9 @@ function createFiberInstance(fiber: Fiber): FiberInstance {
192
226
type VirtualInstance = {
193
227
kind : 1 ,
194
228
id : number ,
195
- parent : null | DevToolsInstance , // filtered parent, including virtual
196
- firstChild : null | DevToolsInstance , // filtered first child, including virtual
197
- nextSibling : null | DevToolsInstance , // filtered next sibling, including virtual
229
+ parent : null | DevToolsInstance ,
230
+ firstChild : null | DevToolsInstance ,
231
+ nextSibling : null | DevToolsInstance ,
198
232
flags : number ,
199
233
source : null | string | Error | Source , // source location of this server component, or owned child stack
200
234
// Errors and Warnings happen per ReactComponentInfo which can appear in
@@ -226,7 +260,7 @@ function createVirtualInstance(
226
260
} ;
227
261
}
228
262
229
- type DevToolsInstance = FiberInstance | VirtualInstance ;
263
+ type DevToolsInstance = FiberInstance | VirtualInstance | FilteredFiberInstance ;
230
264
231
265
type getDisplayNameForFiberType = ( fiber : Fiber ) => string | null ;
232
266
type getTypeSymbolType = ( type : any ) => symbol | number ;
@@ -739,7 +773,8 @@ const fiberToFiberInstanceMap: Map<Fiber, FiberInstance> = new Map();
739
773
// Map of id to one (arbitrary) Fiber in a pair.
740
774
// This Map is used to e.g. get the display name for a Fiber or schedule an update,
741
775
// operations that should be the same whether the current and work-in-progress Fiber is used.
742
- const idToDevToolsInstanceMap : Map < number , DevToolsInstance > = new Map ( ) ;
776
+ const idToDevToolsInstanceMap : Map < number , FiberInstance | VirtualInstance > =
777
+ new Map ( ) ;
743
778
744
779
// Map of canonical HostInstances to the nearest parent DevToolsInstance.
745
780
const publicInstanceToDevToolsInstanceMap : Map < HostInstance , DevToolsInstance > =
@@ -1144,13 +1179,22 @@ export function attach(
1144
1179
function debugTree ( instance : DevToolsInstance , indent : number = 0 ) {
1145
1180
if ( __DEBUG__ ) {
1146
1181
const name =
1147
- ( instance . kind === FIBER_INSTANCE
1182
+ ( instance . kind !== VIRTUAL_INSTANCE
1148
1183
? getDisplayNameForFiber ( instance . data )
1149
1184
: instance . data . name ) || '' ;
1150
1185
console . log (
1151
- ' ' . repeat ( indent ) + '- ' + instance . id + ' (' + name + ')' ,
1186
+ ' ' . repeat ( indent ) +
1187
+ '- ' +
1188
+ ( instance . kind === FILTERED_FIBER_INSTANCE ? 0 : instance . id ) +
1189
+ ' (' +
1190
+ name +
1191
+ ')' ,
1152
1192
'parent' ,
1153
- instance . parent === null ? ' ' : instance . parent . id ,
1193
+ instance . parent === null
1194
+ ? ' '
1195
+ : instance . parent . kind === FILTERED_FIBER_INSTANCE
1196
+ ? 0
1197
+ : instance . parent . id ,
1154
1198
'next' ,
1155
1199
instance . nextSibling === null ? ' ' : instance . nextSibling . id ,
1156
1200
) ;
@@ -2263,7 +2307,12 @@ export function attach(
2263
2307
ownerInstance . source = fiber . _debugStack ;
2264
2308
}
2265
2309
const ownerID = ownerInstance === null ? 0 : ownerInstance . id ;
2266
- const parentID = parentInstance ? parentInstance . id : 0 ;
2310
+ const parentID = parentInstance
2311
+ ? parentInstance . kind === FILTERED_FIBER_INSTANCE
2312
+ ? // A Filtered Fiber Instance will always have a Virtual Instance as a parent.
2313
+ ( ( parentInstance . parent : any ) : VirtualInstance ) . id
2314
+ : parentInstance . id
2315
+ : 0 ;
2267
2316
2268
2317
const displayNameStringID = getStringID ( displayName ) ;
2269
2318
@@ -2347,7 +2396,12 @@ export function attach(
2347
2396
ownerInstance . source = componentInfo . debugStack ;
2348
2397
}
2349
2398
const ownerID = ownerInstance === null ? 0 : ownerInstance . id ;
2350
- const parentID = parentInstance ? parentInstance . id : 0 ;
2399
+ const parentID = parentInstance
2400
+ ? parentInstance . kind === FILTERED_FIBER_INSTANCE
2401
+ ? // A Filtered Fiber Instance will always have a Virtual Instance as a parent.
2402
+ ( ( parentInstance . parent : any ) : VirtualInstance ) . id
2403
+ : parentInstance . id
2404
+ : 0 ;
2351
2405
2352
2406
const displayNameStringID = getStringID ( displayName ) ;
2353
2407
@@ -2712,6 +2766,25 @@ export function attach(
2712
2766
if ( shouldIncludeInTree ) {
2713
2767
newInstance = recordMount ( fiber , reconcilingParent ) ;
2714
2768
insertChild ( newInstance ) ;
2769
+ } else if (
2770
+ reconcilingParent !== null &&
2771
+ reconcilingParent . kind === VIRTUAL_INSTANCE
2772
+ ) {
2773
+ // If the parent is a Virtual Instance and we filtered this Fiber we include a
2774
+ // hidden node.
2775
+
2776
+ if (
2777
+ reconcilingParent . data === fiber . _debugOwner &&
2778
+ fiber . _debugStack != null &&
2779
+ reconcilingParent . source === null
2780
+ ) {
2781
+ // The new Fiber is directly owned by the parent. Therefore somewhere on the
2782
+ // debugStack will be a stack frame inside parent that we can use as its soruce.
2783
+ reconcilingParent . source = fiber . _debugStack ;
2784
+ }
2785
+
2786
+ newInstance = createFilteredFiberInstance ( fiber ) ;
2787
+ insertChild ( newInstance ) ;
2715
2788
}
2716
2789
2717
2790
// If we have the tree selection from previous reload, try to match this Fiber.
@@ -2724,7 +2797,7 @@ export function attach(
2724
2797
const stashedParent = reconcilingParent ;
2725
2798
const stashedPrevious = previouslyReconciledSibling ;
2726
2799
const stashedRemaining = remainingReconcilingChildren ;
2727
- if ( shouldIncludeInTree ) {
2800
+ if ( newInstance !== null ) {
2728
2801
// Push a new DevTools instance parent while reconciling this subtree.
2729
2802
reconcilingParent = newInstance ;
2730
2803
previouslyReconciledSibling = null ;
@@ -2809,7 +2882,7 @@ export function attach(
2809
2882
}
2810
2883
}
2811
2884
} finally {
2812
- if ( shouldIncludeInTree ) {
2885
+ if ( newInstance !== null ) {
2813
2886
reconcilingParent = stashedParent ;
2814
2887
previouslyReconciledSibling = stashedPrevious ;
2815
2888
remainingReconcilingChildren = stashedRemaining ;
@@ -2849,8 +2922,10 @@ export function attach(
2849
2922
}
2850
2923
if ( instance . kind === FIBER_INSTANCE ) {
2851
2924
recordUnmount ( instance ) ;
2852
- } else {
2925
+ } else if ( instance . kind === VIRTUAL_INSTANCE ) {
2853
2926
recordVirtualUnmount ( instance ) ;
2927
+ } else {
2928
+ untrackFiber ( instance , instance . data ) ;
2854
2929
}
2855
2930
removeChild ( instance , null ) ;
2856
2931
}
@@ -2955,7 +3030,9 @@ export function attach(
2955
3030
virtualInstance . treeBaseDuration = treeBaseDuration ;
2956
3031
}
2957
3032
2958
- function recordResetChildren ( parentInstance : DevToolsInstance ) {
3033
+ function recordResetChildren (
3034
+ parentInstance : FiberInstance | VirtualInstance ,
3035
+ ) {
2959
3036
if ( __DEBUG__ ) {
2960
3037
if (
2961
3038
parentInstance . firstChild !== null &&
@@ -2975,7 +3052,17 @@ export function attach(
2975
3052
2976
3053
let child : null | DevToolsInstance = parentInstance . firstChild ;
2977
3054
while ( child !== null ) {
2978
- nextChildren . push ( child . id ) ;
3055
+ if ( child . kind === FILTERED_FIBER_INSTANCE ) {
3056
+ for (
3057
+ let innerChild : null | DevToolsInstance = parentInstance . firstChild ;
3058
+ innerChild !== null ;
3059
+ innerChild = innerChild . nextSibling
3060
+ ) {
3061
+ nextChildren . push ( ( innerChild : any ) . id ) ;
3062
+ }
3063
+ } else {
3064
+ nextChildren . push ( child . id ) ;
3065
+ }
2979
3066
child = child . nextSibling ;
2980
3067
}
2981
3068
@@ -3791,7 +3878,7 @@ export function attach(
3791
3878
devtoolsInstance : DevToolsInstance ,
3792
3879
hostInstances : Array < HostInstance > ,
3793
3880
) {
3794
- if ( devtoolsInstance . kind === FIBER_INSTANCE ) {
3881
+ if ( devtoolsInstance . kind !== VIRTUAL_INSTANCE ) {
3795
3882
const fiber = devtoolsInstance . data ;
3796
3883
appendHostInstancesByFiber ( fiber , hostInstances ) ;
3797
3884
return ;
@@ -3892,6 +3979,10 @@ export function attach(
3892
3979
): number | null {
3893
3980
const instance = publicInstanceToDevToolsInstanceMap . get ( publicInstance ) ;
3894
3981
if ( instance !== undefined ) {
3982
+ if ( instance . kind === FILTERED_FIBER_INSTANCE ) {
3983
+ // A Filtered Fiber Instance will always have a Virtual Instance as a parent.
3984
+ return ( ( instance . parent : any ) : VirtualInstance ) . id ;
3985
+ }
3895
3986
return instance . id ;
3896
3987
}
3897
3988
return null ;
@@ -3944,7 +4035,7 @@ export function attach(
3944
4035
}
3945
4036
3946
4037
function instanceToSerializedElement (
3947
- instance : DevToolsInstance ,
4038
+ instance : FiberInstance | VirtualInstance ,
3948
4039
) : SerializedElement {
3949
4040
if ( instance . kind === FIBER_INSTANCE ) {
3950
4041
const fiber = instance . data ;
@@ -4039,7 +4130,7 @@ export function attach(
4039
4130
function findNearestOwnerInstance (
4040
4131
parentInstance : null | DevToolsInstance ,
4041
4132
owner : void | null | ReactComponentInfo | Fiber ,
4042
- ) : null | DevToolsInstance {
4133
+ ) : null | FiberInstance | VirtualInstance {
4043
4134
if ( owner == null ) {
4044
4135
return null ;
4045
4136
}
@@ -4054,6 +4145,9 @@ export function attach(
4054
4145
// needs a duck type check anyway.
4055
4146
parentInstance . data === ( owner : any ) . alternate
4056
4147
) {
4148
+ if ( parentInstance . kind === FILTERED_FIBER_INSTANCE ) {
4149
+ return null ;
4150
+ }
4057
4151
return parentInstance;
4058
4152
}
4059
4153
parentInstance = parentInstance . parent ;
@@ -4131,7 +4225,11 @@ export function attach(
4131
4225
if (devtoolsInstance.kind === VIRTUAL_INSTANCE) {
4132
4226
return inspectVirtualInstanceRaw ( devtoolsInstance ) ;
4133
4227
}
4134
- return inspectFiberInstanceRaw(devtoolsInstance);
4228
+ if (devtoolsInstance.kind === FIBER_INSTANCE) {
4229
+ return inspectFiberInstanceRaw ( devtoolsInstance ) ;
4230
+ }
4231
+ (devtoolsInstance: FilteredFiberInstance); // assert exhaustive
4232
+ throw new Error('Unsupported instance kind');
4135
4233
}
4136
4234
4137
4235
function inspectFiberInstanceRaw (
@@ -4434,7 +4532,7 @@ export function attach(
4434
4532
let targetErrorBoundaryID = null ;
4435
4533
let parent = virtualInstance . parent ;
4436
4534
while ( parent !== null ) {
4437
- if ( parent . kind === FIBER_INSTANCE ) {
4535
+ if ( parent . kind !== VIRTUAL_INSTANCE ) {
4438
4536
targetErrorBoundaryID = getNearestErrorBoundaryID ( parent . data ) ;
4439
4537
let current = parent . data ;
4440
4538
while ( current . return !== null ) {
@@ -5225,7 +5323,9 @@ export function attach(
5225
5323
) {
5226
5324
// We don't need to convert milliseconds to microseconds in this case,
5227
5325
// because the profiling summary is JSON serialized.
5228
- target . push ( [ instance . id , instance . treeBaseDuration ] ) ;
5326
+ if ( instance . kind !== FILTERED_FIBER_INSTANCE ) {
5327
+ target . push ( [ instance . id , instance . treeBaseDuration ] ) ;
5328
+ }
5229
5329
for (
5230
5330
let child = instance.firstChild;
5231
5331
child !== null;
@@ -5444,7 +5544,7 @@ export function attach(
5444
5544
// In that case, we'll do some extra checks for matching mounts.
5445
5545
let trackedPath : Array < PathFrame > | null = null;
5446
5546
let trackedPathMatchFiber: Fiber | null = null; // This is the deepest unfiltered match of a Fiber.
5447
- let trackedPathMatchInstance: DevToolsInstance | null = null; // This is the deepest matched filtered Instance.
5547
+ let trackedPathMatchInstance: FiberInstance | VirtualInstance | null = null; // This is the deepest matched filtered Instance.
5448
5548
let trackedPathMatchDepth = -1;
5449
5549
let mightBeOnTrackedPath = false;
5450
5550
@@ -5463,7 +5563,7 @@ export function attach(
5463
5563
// The return value signals whether we should keep matching siblings or not.
5464
5564
function updateTrackedPathStateBeforeMount (
5465
5565
fiber : Fiber ,
5466
- fiberInstance : null | FiberInstance ,
5566
+ fiberInstance : null | FiberInstance | FilteredFiberInstance ,
5467
5567
) : boolean {
5468
5568
if ( trackedPath === null || ! mightBeOnTrackedPath ) {
5469
5569
// Fast path: there's nothing to track so do nothing and ignore siblings.
@@ -5492,7 +5592,7 @@ export function attach(
5492
5592
) {
5493
5593
// We have our next match.
5494
5594
trackedPathMatchFiber = fiber ;
5495
- if ( fiberInstance !== null ) {
5595
+ if ( fiberInstance !== null && fiberInstance . kind === FIBER_INSTANCE ) {
5496
5596
trackedPathMatchInstance = fiberInstance ;
5497
5597
}
5498
5598
trackedPathMatchDepth ++ ;
0 commit comments