146
// Kinds
147
const FIBER_INSTANCE = 0;
148
const VIRTUAL_INSTANCE = 1;
149
+const FILTERED_FIBER_INSTANCE = 2;
150
151
// Flags
152
const FORCE_SUSPENSE_FALLBACK = /* */ 0b001;
158
type FiberInstance = {
159
kind: 0,
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,
164
flags: number, // Force Error/Suspense
165
source: null | string | Error | Source, // source location of this component function, or owned child stack
166
errors: null | Map<string, number>, // error messages and count
185
};
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
+
221
// This type represents a stateful instance of a Server Component or a Component
222
// that gets optimized away - e.g. call-through without creating a Fiber.
223
// It's basically a virtual Fiber. This is not a semantic concept in React.
226
type VirtualInstance = {
227
kind: 1,
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,
232
flags: number,
233
source: null | string | Error | Source, // source location of this server component, or owned child stack
234
// Errors and Warnings happen per ReactComponentInfo which can appear in
260
};
261
}
262
229
-type DevToolsInstance = FiberInstance | VirtualInstance;
263
+type DevToolsInstance = FiberInstance | VirtualInstance | FilteredFiberInstance;
264
265
type getDisplayNameForFiberType = (fiber: Fiber) => string | null;
266
type getTypeSymbolType = (type: any) => symbol | number;
773
// Map of id to one (arbitrary) Fiber in a pair.
774
// This Map is used to e.g. get the display name for a Fiber or schedule an update,
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();
778
779
// Map of canonical HostInstances to the nearest parent DevToolsInstance.
780
const publicInstanceToDevToolsInstanceMap: Map<HostInstance, DevToolsInstance> =
1179
function debugTree(instance: DevToolsInstance, indent: number = 0) {
1180
if (__DEBUG__) {
1181
const name =
1147
- (instance.kind === FIBER_INSTANCE
1182
+ (instance.kind !== VIRTUAL_INSTANCE
1183
? getDisplayNameForFiber(instance.data)
1184
: instance.data.name) || '';
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
+ ')',
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,
1198
'next',
1199
instance.nextSibling === null ? ' ' : instance.nextSibling.id,
1200
);
2307
ownerInstance.source = fiber._debugStack;
2308
}
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;
2316
2317
const displayNameStringID = getStringID(displayName);
2318
2396
ownerInstance.source = componentInfo.debugStack;
2397
}
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;
2405
2406
const displayNameStringID = getStringID(displayName);
2407
2766
if (shouldIncludeInTree) {
2767
newInstance = recordMount(fiber, reconcilingParent);
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);
2788
}
2789
2790
// If we have the tree selection from previous reload, try to match this Fiber.
2797
const stashedParent = reconcilingParent;
2798
const stashedPrevious = previouslyReconciledSibling;
2799
const stashedRemaining = remainingReconcilingChildren;
2727
- if (shouldIncludeInTree) {
2800
+ if (newInstance !== null) {
2801
// Push a new DevTools instance parent while reconciling this subtree.
2802
reconcilingParent = newInstance;
2803
previouslyReconciledSibling = null;
2882
}
2883
}
2884
} finally {
2812
- if (shouldIncludeInTree) {
2885
+ if (newInstance !== null) {
2886
reconcilingParent = stashedParent;
2887
previouslyReconciledSibling = stashedPrevious;
2888
remainingReconcilingChildren = stashedRemaining;
2922
}
2923
if (instance.kind === FIBER_INSTANCE) {
2924
recordUnmount(instance);
2852
- } else {
2925
+ } else if (instance.kind === VIRTUAL_INSTANCE) {
2926
recordVirtualUnmount(instance);
2927
+ } else {
2928
+ untrackFiber(instance, instance.data);
2929
}
2930
removeChild(instance, null);
2931
}
3030
virtualInstance.treeBaseDuration = treeBaseDuration;
3031
}
3032
2958
- function recordResetChildren(parentInstance: DevToolsInstance) {
3033
+ function recordResetChildren(
3034
+ parentInstance: FiberInstance | VirtualInstance,
3035
+ ) {
3036
if (__DEBUG__) {
3037
if (
3038
parentInstance.firstChild !== null &&
3052
3053
let child: null | DevToolsInstance = parentInstance.firstChild;
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
+ }
3066
child = child.nextSibling;
3067
}
3068
3878
devtoolsInstance: DevToolsInstance,
3879
hostInstances: Array<HostInstance>,
3880
) {
3794
- if (devtoolsInstance.kind === FIBER_INSTANCE) {
3881
+ if (devtoolsInstance.kind !== VIRTUAL_INSTANCE) {
3882
const fiber = devtoolsInstance.data;
3883
appendHostInstancesByFiber(fiber, hostInstances);
3884
return;
3979
): number | null {
3980
const instance = publicInstanceToDevToolsInstanceMap.get(publicInstance);
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
+ }
3986
return instance.id;
3987
}
3988
return null;
4035
}
4036
4037
function instanceToSerializedElement(
3947
- instance: DevToolsInstance,
4038
+ instance: FiberInstance | VirtualInstance,
4039
): SerializedElement {
4040
if (instance.kind === FIBER_INSTANCE) {
4041
const fiber = instance.data;
4130
function findNearestOwnerInstance(
4131
parentInstance: null | DevToolsInstance,
4132
owner: void | null | ReactComponentInfo | Fiber,
4042
- ): null | DevToolsInstance {
4133
+ ): null | FiberInstance | VirtualInstance {
4134
if (owner == null) {
4135
return null;
4136
}
4145
// needs a duck type check anyway.
4146
parentInstance.data === (owner: any).alternate
4147
) {
4148
+ if (parentInstance.kind === FILTERED_FIBER_INSTANCE) {
4149
+ return null;
4150
+ }
4151
return parentInstance;
4152
}
4153
parentInstance = parentInstance.parent;
4225
if (devtoolsInstance.kind === VIRTUAL_INSTANCE) {
4226
return inspectVirtualInstanceRaw(devtoolsInstance);
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');
4233
}
4234
4235
function inspectFiberInstanceRaw(
4532
let targetErrorBoundaryID = null;
4533
let parent = virtualInstance.parent;
4534
while (parent !== null) {
4437
- if (parent.kind === FIBER_INSTANCE) {
4535
+ if (parent.kind !== VIRTUAL_INSTANCE) {
4536
targetErrorBoundaryID = getNearestErrorBoundaryID(parent.data);
4537
let current = parent.data;
4538
while (current.return !== null) {
5323
) {
5324
// We don't need to convert milliseconds to microseconds in this case,
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
+ }
5329
for (
5330
let child = instance.firstChild;
5331
child !== null;
5544
// In that case, we'll do some extra checks for matching mounts.
5545
let trackedPath: Array<PathFrame> | null = null;
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.
5548
let trackedPathMatchDepth = -1;
5549
let mightBeOnTrackedPath = false;
5550
5563
// The return value signals whether we should keep matching siblings or not.
5564
function updateTrackedPathStateBeforeMount(
5565
fiber: Fiber,
5466
- fiberInstance: null | FiberInstance,
5566
+ fiberInstance: null | FiberInstance | FilteredFiberInstance,
5567
): boolean {
5568
if (trackedPath === null || !mightBeOnTrackedPath) {
5569
// Fast path: there's nothing to track so do nothing and ignore siblings.
5592
) {
5593
// We have our next match.
5594
trackedPathMatchFiber = fiber;
5495
- if (fiberInstance !== null) {
5595
+ if (fiberInstance !== null && fiberInstance.kind === FIBER_INSTANCE) {
5596
trackedPathMatchInstance = fiberInstance;
5597
}
5598
trackedPathMatchDepth++;