26
ElementTypeSuspense,
27
ElementTypeSuspenseList,
28
ElementTypeTracingMarker,
29
+ ElementTypeVirtual,
30
StrictMode,
31
} from 'react-devtools-shared/src/frontend/types';
32
import {
135
136
// Kinds
137
const FIBER_INSTANCE = 0;
137
-// const VIRTUAL_INSTANCE = 1;
138
+const VIRTUAL_INSTANCE = 1;
139
140
// Flags
141
const FORCE_SUSPENSE_FALLBACK = /* */ 0b001;
198
data: ReactComponentInfo,
199
};
200
201
+function createVirtualInstance(
202
+ debugEntry: ReactComponentInfo,
203
+): VirtualInstance {
204
+ return {
205
+ kind: VIRTUAL_INSTANCE,
206
+ id: getUID(),
207
+ parent: null,
208
+ firstChild: null,
209
+ previousSibling: null,
210
+ nextSibling: null,
211
+ flags: 0,
212
+ componentStack: null,
213
+ errors: null,
214
+ warnings: null,
215
+ data: debugEntry,
216
+ };
217
+}
218
+
219
type DevToolsInstance = FiberInstance | VirtualInstance;
220
221
type getDisplayNameForFiberType = (fiber: Fiber) => string | null;
1442
}
1443
}
1444
1426
- fiberToFiberInstanceMap.delete(fiber);
1445
+ if (fiberToFiberInstanceMap.get(fiber) === fiberInstance) {
1446
+ fiberToFiberInstanceMap.delete(fiber);
1447
+ }
1448
const {alternate} = fiber;
1449
if (alternate !== null) {
1429
- fiberToFiberInstanceMap.delete(alternate);
1450
+ if (fiberToFiberInstanceMap.get(alternate) === fiberInstance) {
1451
+ fiberToFiberInstanceMap.delete(alternate);
1452
+ }
1453
}
1454
}
1455
2087
throw new Error('The root should have been registered at this point');
2088
}
2089
fiberInstance = entry;
2067
- } else if (
2068
- fiberToFiberInstanceMap.has(fiber) ||
2069
- (fiber.alternate !== null && fiberToFiberInstanceMap.has(fiber.alternate))
2070
- ) {
2071
- throw new Error('Did not expect to see this fiber being mounted twice.');
2090
} else {
2091
fiberInstance = createFiberInstance(fiber);
2092
}
2093
+ // If this already exists behind a different FiberInstance, we intentionally
2094
+ // override it here to claim the fiber as part of this new instance.
2095
+ // E.g. if it was part of a reparenting.
2096
fiberToFiberInstanceMap.set(fiber, fiberInstance);
2097
+ const alternate = fiber.alternate;
2098
+ if (alternate !== null && fiberToFiberInstanceMap.has(alternate)) {
2099
+ fiberToFiberInstanceMap.set(alternate, fiberInstance);
2100
+ }
2101
idToDevToolsInstanceMap.set(fiberInstance.id, fiberInstance);
2102
2103
const id = fiberInstance.id;
2106
debug('recordMount()', fiber, parentInstance);
2107
}
2108
2084
- const hasOwnerMetadata = fiber.hasOwnProperty('_debugOwner');
2109
const isProfilingSupported = fiber.hasOwnProperty('treeBaseDuration');
2110
2087
- // Adding a new field here would require a bridge protocol version bump (a backwads breaking change).
2088
- // Instead let's re-purpose a pre-existing field to carry more information.
2089
- let profilingFlags = 0;
2090
- if (isProfilingSupported) {
2091
- profilingFlags = PROFILING_FLAG_BASIC_SUPPORT;
2092
- if (typeof injectProfilingHooks === 'function') {
2093
- profilingFlags |= PROFILING_FLAG_TIMELINE_SUPPORT;
2111
+ if (isRoot) {
2112
+ const hasOwnerMetadata = fiber.hasOwnProperty('_debugOwner');
2113
+
2114
+ // Adding a new field here would require a bridge protocol version bump (a backwads breaking change).
2115
+ // Instead let's re-purpose a pre-existing field to carry more information.
2116
+ let profilingFlags = 0;
2117
+ if (isProfilingSupported) {
2118
+ profilingFlags = PROFILING_FLAG_BASIC_SUPPORT;
2119
+ if (typeof injectProfilingHooks === 'function') {
2120
+ profilingFlags |= PROFILING_FLAG_TIMELINE_SUPPORT;
2121
+ }
2122
}
2095
- }
2123
2097
- if (isRoot) {
2124
// Set supportsStrictMode to false for production renderer builds
2125
const isProductionBuildOfRenderer = renderer.bundleType === 0;
2126
2210
return fiberInstance;
2211
}
2212
2213
+ function recordVirtualMount(
2214
+ instance: VirtualInstance,
2215
+ parentInstance: DevToolsInstance | null,
2216
+ ): void {
2217
+ const id = instance.id;
2218
+
2219
+ idToDevToolsInstanceMap.set(id, instance);
2220
+
2221
+ const isProfilingSupported = false; // TODO: Support Tree Base Duration Based on Children.
2222
+
2223
+ const key = null; // TODO: Track keys on ReactComponentInfo;
2224
+ const env = instance.data.env;
2225
+ let displayName = instance.data.name || '';
2226
+ if (typeof env === 'string') {
2227
+ // We model environment as an HoC name for now.
2228
+ displayName = env + '(' + displayName + ')';
2229
+ }
2230
+ const elementType = ElementTypeVirtual;
2231
+ // TODO: Support Virtual Owners. To do this we need to find a matching
2232
+ // virtual instance which is not a super cheap parent traversal and so
2233
+ // we should ideally only do that lazily. We should maybe change the
2234
+ // frontend to get it lazily.
2235
+ const ownerID: number = 0;
2236
+ const parentID = parentInstance ? parentInstance.id : 0;
2237
+
2238
+ const displayNameStringID = getStringID(displayName);
2239
+
2240
+ // This check is a guard to handle a React element that has been modified
2241
+ // in such a way as to bypass the default stringification of the "key" property.
2242
+ const keyString = key === null ? null : String(key);
2243
+ const keyStringID = getStringID(keyString);
2244
+
2245
+ pushOperation(TREE_OPERATION_ADD);
2246
+ pushOperation(id);
2247
+ pushOperation(elementType);
2248
+ pushOperation(parentID);
2249
+ pushOperation(ownerID);
2250
+ pushOperation(displayNameStringID);
2251
+ pushOperation(keyStringID);
2252
+
2253
+ if (isProfilingSupported) {
2254
+ idToRootMap.set(id, currentRootID);
2255
+ // TODO: Include tree base duration of children somehow.
2256
+ // recordProfilingDurations(...);
2257
+ }
2258
+ }
2259
+
2260
function recordUnmount(fiberInstance: FiberInstance): void {
2261
const fiber = fiberInstance.data;
2262
if (__DEBUG__) {
2334
2335
function removeChild(instance: DevToolsInstance): void {
2336
if (instance.parent === null) {
2337
+ if (remainingReconcilingChildren === instance) {
2338
+ throw new Error(
2339
+ 'Remaining children should not have items with no parent',
2340
+ );
2341
+ } else if (instance.nextSibling !== null) {
2342
+ throw new Error('A deleted instance should not have next siblings');
2343
+ } else if (instance.previousSibling !== null) {
2344
+ throw new Error('A deleted instance should not have previous siblings');
2345
+ }
2346
// Already deleted.
2347
return;
2348
}
2384
}
2385
}
2386
2305
- function mountChildrenRecursively(
2387
+ function mountVirtualInstanceRecursively(
2388
+ virtualInstance: VirtualInstance,
2389
firstChild: Fiber,
2390
+ lastChild: null | Fiber, // non-inclusive
2391
traceNearestHostComponentUpdate: boolean,
2392
+ virtualLevel: number, // the nth level of virtual instances
2393
+ ): void {
2394
+ const stashedParent = reconcilingParent;
2395
+ const stashedPrevious = previouslyReconciledSibling;
2396
+ const stashedRemaining = remainingReconcilingChildren;
2397
+ // Push a new DevTools instance parent while reconciling this subtree.
2398
+ reconcilingParent = virtualInstance;
2399
+ previouslyReconciledSibling = null;
2400
+ remainingReconcilingChildren = null;
2401
+ try {
2402
+ mountVirtualChildrenRecursively(
2403
+ firstChild,
2404
+ lastChild,
2405
+ traceNearestHostComponentUpdate,
2406
+ virtualLevel + 1,
2407
+ );
2408
+ } finally {
2409
+ reconcilingParent = stashedParent;
2410
+ previouslyReconciledSibling = stashedPrevious;
2411
+ remainingReconcilingChildren = stashedRemaining;
2412
+ }
2413
+ }
2414
+
2415
+ function recordVirtualUnmount(instance: VirtualInstance) {
2416
+ if (trackedPathMatchFiber !== null) {
2417
+ // We're in the process of trying to restore previous selection.
2418
+ // TODO: Handle virtual instances on the tracked path.
2419
+ }
2420
+
2421
+ const id = instance.id;
2422
+ pendingRealUnmountedIDs.push(id);
2423
+
2424
+ const isProfilingSupported = false; // TODO: Profiling support.
2425
+ if (isProfilingSupported) {
2426
+ idToRootMap.delete(id);
2427
+ idToTreeBaseDurationMap.delete(id);
2428
+ }
2429
+ }
2430
+
2431
+ function mountVirtualChildrenRecursively(
2432
+ firstChild: Fiber,
2433
+ lastChild: null | Fiber, // non-inclusive
2434
+ traceNearestHostComponentUpdate: boolean,
2435
+ virtualLevel: number, // the nth level of virtual instances
2436
): void {
2437
// Iterate over siblings rather than recursing.
2438
// This reduces the chance of stack overflow for wide trees (e.g. lists with many items).
2439
let fiber: Fiber | null = firstChild;
2312
- while (fiber !== null) {
2313
- mountFiberRecursively(fiber, traceNearestHostComponentUpdate);
2440
+ let previousVirtualInstance: null | VirtualInstance = null;
2441
+ let previousVirtualInstanceFirstFiber: Fiber = firstChild;
2442
+ while (fiber !== null && fiber !== lastChild) {
2443
+ let level = 0;
2444
+ if (fiber._debugInfo) {
2445
+ for (let i = 0; i < fiber._debugInfo.length; i++) {
2446
+ const debugEntry = fiber._debugInfo[i];
2447
+ if (typeof debugEntry.name !== 'string') {
2448
+ // Not a Component. Some other Debug Info.
2449
+ continue;
2450
+ }
2451
+ const componentInfo: ReactComponentInfo = (debugEntry: any);
2452
+ if (level === virtualLevel) {
2453
+ if (
2454
+ previousVirtualInstance === null ||
2455
+ // Consecutive children with the same debug entry as a parent gets
2456
+ // treated as if they share the same virtual instance.
2457
+ previousVirtualInstance.data !== debugEntry
2458
+ ) {
2459
+ if (previousVirtualInstance !== null) {
2460
+ // Mount any previous children that should go into the previous parent.
2461
+ mountVirtualInstanceRecursively(
2462
+ previousVirtualInstance,
2463
+ previousVirtualInstanceFirstFiber,
2464
+ fiber,
2465
+ traceNearestHostComponentUpdate,
2466
+ virtualLevel,
2467
+ );
2468
+ }
2469
+ previousVirtualInstance = createVirtualInstance(componentInfo);
2470
+ recordVirtualMount(previousVirtualInstance, reconcilingParent);
2471
+ insertChild(previousVirtualInstance);
2472
+ previousVirtualInstanceFirstFiber = fiber;
2473
+ }
2474
+ level++;
2475
+ break;
2476
+ } else {
2477
+ level++;
2478
+ }
2479
+ }
2480
+ }
2481
+ if (level === virtualLevel) {
2482
+ if (previousVirtualInstance !== null) {
2483
+ // If we were working on a virtual instance and this is not a virtual
2484
+ // instance, then we end the sequence and mount any previous children
2485
+ // that should go into the previous virtual instance.
2486
+ mountVirtualInstanceRecursively(
2487
+ previousVirtualInstance,
2488
+ previousVirtualInstanceFirstFiber,
2489
+ fiber,
2490
+ traceNearestHostComponentUpdate,
2491
+ virtualLevel,
2492
+ );
2493
+ previousVirtualInstance = null;
2494
+ }
2495
+ // We've reached the end of the virtual levels, but not beyond,
2496
+ // and now continue with the regular fiber.
2497
+ mountFiberRecursively(fiber, traceNearestHostComponentUpdate);
2498
+ }
2499
fiber = fiber.sibling;
2500
}
2501
+ if (previousVirtualInstance !== null) {
2502
+ // Mount any previous children that should go into the previous parent.
2503
+ mountVirtualInstanceRecursively(
2504
+ previousVirtualInstance,
2505
+ previousVirtualInstanceFirstFiber,
2506
+ null,
2507
+ traceNearestHostComponentUpdate,
2508
+ virtualLevel,
2509
+ );
2510
+ }
2511
+ }
2512
+
2513
+ function mountChildrenRecursively(
2514
+ firstChild: Fiber,
2515
+ traceNearestHostComponentUpdate: boolean,
2516
+ ): void {
2517
+ mountVirtualChildrenRecursively(
2518
+ firstChild,
2519
+ null,
2520
+ traceNearestHostComponentUpdate,
2521
+ 0, // first level
2522
+ );
2523
}
2524
2525
function mountFiberRecursively(
2642
previouslyReconciledSibling = null;
2643
// Move all the children of this instance to the remaining set.
2644
remainingReconcilingChildren = instance.firstChild;
2645
+ instance.firstChild = null;
2646
try {
2647
// Unmount the remaining set.
2648
unmountRemainingChildren();
2653
}
2654
if (instance.kind === FIBER_INSTANCE) {
2655
recordUnmount(instance);
2656
+ } else {
2657
+ recordVirtualUnmount(instance);
2658
}
2659
removeChild(instance);
2660
}
2763
}
2764
}
2765
2556
- // Returns whether closest unfiltered fiber parent needs to reset its child list.
2557
- function updateChildrenRecursively(
2558
- nextFirstChild: null | Fiber,
2766
+ function updateVirtualInstanceRecursively(
2767
+ virtualInstance: VirtualInstance,
2768
+ nextFirstChild: Fiber,
2769
+ nextLastChild: null | Fiber, // non-inclusive
2770
prevFirstChild: null | Fiber,
2771
traceNearestHostComponentUpdate: boolean,
2772
+ virtualLevel: number, // the nth level of virtual instances
2773
+ ): void {
2774
+ const stashedParent = reconcilingParent;
2775
+ const stashedPrevious = previouslyReconciledSibling;
2776
+ const stashedRemaining = remainingReconcilingChildren;
2777
+ // Push a new DevTools instance parent while reconciling this subtree.
2778
+ reconcilingParent = virtualInstance;
2779
+ previouslyReconciledSibling = null;
2780
+ // Move all the children of this instance to the remaining set.
2781
+ // We'll move them back one by one, and anything that remains is deleted.
2782
+ remainingReconcilingChildren = virtualInstance.firstChild;
2783
+ virtualInstance.firstChild = null;
2784
+ try {
2785
+ if (
2786
+ updateVirtualChildrenRecursively(
2787
+ nextFirstChild,
2788
+ nextLastChild,
2789
+ prevFirstChild,
2790
+ traceNearestHostComponentUpdate,
2791
+ virtualLevel + 1,
2792
+ )
2793
+ ) {
2794
+ recordResetChildren(virtualInstance);
2795
+ }
2796
+ } finally {
2797
+ unmountRemainingChildren();
2798
+ reconcilingParent = stashedParent;
2799
+ previouslyReconciledSibling = stashedPrevious;
2800
+ remainingReconcilingChildren = stashedRemaining;
2801
+ }
2802
+ }
2803
+
2804
+ function updateVirtualChildrenRecursively(
2805
+ nextFirstChild: Fiber,
2806
+ nextLastChild: null | Fiber, // non-inclusive
2807
+ prevFirstChild: null | Fiber,
2808
+ traceNearestHostComponentUpdate: boolean,
2809
+ virtualLevel: number, // the nth level of virtual instances
2810
): boolean {
2811
let shouldResetChildren = false;
2812
// If the first child is different, we need to traverse them.
2813
// Each next child will be either a new child (mount) or an alternate (update).
2565
- let nextChild = nextFirstChild;
2814
+ let nextChild: null | Fiber = nextFirstChild;
2815
let prevChildAtSameIndex = prevFirstChild;
2567
- while (nextChild) {
2568
- // We already know children will be referentially different because
2569
- // they are either new mounts or alternates of previous children.
2570
- // Schedule updates and mounts depending on whether alternates exist.
2571
- // We don't track deletions here because they are reported separately.
2572
- if (prevChildAtSameIndex === nextChild) {
2573
- // This set is unchanged. We're just going through it to place all the
2574
- // children again.
2575
- if (
2576
- updateFiberRecursively(
2577
- nextChild,
2578
- nextChild,
2579
- traceNearestHostComponentUpdate,
2580
- )
2581
- ) {
2582
- throw new Error('Updating the same fiber should not cause reorder');
2816
+ let previousVirtualInstance: null | VirtualInstance = null;
2817
+ let previousVirtualInstanceWasMount: boolean = false;
2818
+ let previousVirtualInstanceNextFirstFiber: Fiber = nextFirstChild;
2819
+ let previousVirtualInstancePrevFirstFiber: null | Fiber = prevFirstChild;
2820
+ while (nextChild !== null && nextChild !== nextLastChild) {
2821
+ let level = 0;
2822
+ if (nextChild._debugInfo) {
2823
+ for (let i = 0; i < nextChild._debugInfo.length; i++) {
2824
+ const debugEntry = nextChild._debugInfo[i];
2825
+ if (typeof debugEntry.name !== 'string') {
2826
+ // Not a Component. Some other Debug Info.
2827
+ continue;
2828
+ }
2829
+ const componentInfo: ReactComponentInfo = (debugEntry: any);
2830
+ if (level === virtualLevel) {
2831
+ if (
2832
+ previousVirtualInstance === null ||
2833
+ // Consecutive children with the same debug entry as a parent gets
2834
+ // treated as if they share the same virtual instance.
2835
+ previousVirtualInstance.data !== componentInfo
2836
+ ) {
2837
+ if (previousVirtualInstance !== null) {
2838
+ // Mount any previous children that should go into the previous parent.
2839
+ if (previousVirtualInstanceWasMount) {
2840
+ mountVirtualInstanceRecursively(
2841
+ previousVirtualInstance,
2842
+ previousVirtualInstanceNextFirstFiber,
2843
+ nextChild,
2844
+ traceNearestHostComponentUpdate,
2845
+ virtualLevel,
2846
+ );
2847
+ } else {
2848
+ updateVirtualInstanceRecursively(
2849
+ previousVirtualInstance,
2850
+ previousVirtualInstanceNextFirstFiber,
2851
+ nextChild,
2852
+ previousVirtualInstancePrevFirstFiber,
2853
+ traceNearestHostComponentUpdate,
2854
+ virtualLevel,
2855
+ );
2856
+ }
2857
+ }
2858
+ const firstRemainingChild = remainingReconcilingChildren;
2859
+ if (
2860
+ firstRemainingChild !== null &&
2861
+ firstRemainingChild.kind === VIRTUAL_INSTANCE &&
2862
+ firstRemainingChild.data.name === componentInfo.name &&
2863
+ firstRemainingChild.data.env === componentInfo.env
2864
+ ) {
2865
+ // If the previous children had a virtual instance in the same slot
2866
+ // with the same name, then we claim it and reuse it for this update.
2867
+ // Update it with the latest entry.
2868
+ firstRemainingChild.data = componentInfo;
2869
+ moveChild(firstRemainingChild);
2870
+ previousVirtualInstance = firstRemainingChild;
2871
+ previousVirtualInstanceWasMount = false;
2872
+ } else {
2873
+ // Otherwise we create a new instance.
2874
+ const newVirtualInstance = createVirtualInstance(componentInfo);
2875
+ recordVirtualMount(newVirtualInstance, reconcilingParent);
2876
+ insertChild(newVirtualInstance);
2877
+ previousVirtualInstance = newVirtualInstance;
2878
+ previousVirtualInstanceWasMount = true;
2879
+ shouldResetChildren = true;
2880
+ }
2881
+ // Existing children might be reparented into this new virtual instance.
2882
+ // TODO: This will cause the front end to error which needs to be fixed.
2883
+ previousVirtualInstanceNextFirstFiber = nextChild;
2884
+ previousVirtualInstancePrevFirstFiber = prevChildAtSameIndex;
2885
+ }
2886
+ level++;
2887
+ break;
2888
+ } else {
2889
+ level++;
2890
+ }
2891
}
2584
- } else if (nextChild.alternate) {
2585
- const prevChild = nextChild.alternate;
2586
- if (
2587
- updateFiberRecursively(
2588
- nextChild,
2589
- prevChild,
2590
- traceNearestHostComponentUpdate,
2591
- )
2592
- ) {
2593
- // If a nested tree child order changed but it can't handle its own
2594
- // child order invalidation (e.g. because it's filtered out like host nodes),
2595
- // propagate the need to reset child order upwards to this Fiber.
2596
- shouldResetChildren = true;
2892
+ }
2893
+ if (level === virtualLevel) {
2894
+ if (previousVirtualInstance !== null) {
2895
+ // If we were working on a virtual instance and this is not a virtual
2896
+ // instance, then we end the sequence and update any previous children
2897
+ // that should go into the previous virtual instance.
2898
+ if (previousVirtualInstanceWasMount) {
2899
+ mountVirtualInstanceRecursively(
2900
+ previousVirtualInstance,
2901
+ previousVirtualInstanceNextFirstFiber,
2902
+ nextChild,
2903
+ traceNearestHostComponentUpdate,
2904
+ virtualLevel,
2905
+ );
2906
+ } else {
2907
+ updateVirtualInstanceRecursively(
2908
+ previousVirtualInstance,
2909
+ previousVirtualInstanceNextFirstFiber,
2910
+ nextChild,
2911
+ previousVirtualInstancePrevFirstFiber,
2912
+ traceNearestHostComponentUpdate,
2913
+ virtualLevel,
2914
+ );
2915
+ }
2916
+ previousVirtualInstance = null;
2917
}
2598
- // However we also keep track if the order of the children matches
2599
- // the previous order. They are always different referentially, but
2600
- // if the instances line up conceptually we'll want to know that.
2601
- if (prevChild !== prevChildAtSameIndex) {
2918
+ // We've reached the end of the virtual levels, but not beyond,
2919
+ // and now continue with the regular fiber.
2920
+ if (prevChildAtSameIndex === nextChild) {
2921
+ // This set is unchanged. We're just going through it to place all the
2922
+ // children again.
2923
+ if (
2924
+ updateFiberRecursively(
2925
+ nextChild,
2926
+ nextChild,
2927
+ traceNearestHostComponentUpdate,
2928
+ )
2929
+ ) {
2930
+ throw new Error('Updating the same fiber should not cause reorder');
2931
+ }
2932
+ } else if (nextChild.alternate) {
2933
+ const prevChild = nextChild.alternate;
2934
+ if (
2935
+ updateFiberRecursively(
2936
+ nextChild,
2937
+ prevChild,
2938
+ traceNearestHostComponentUpdate,
2939
+ )
2940
+ ) {
2941
+ // If a nested tree child order changed but it can't handle its own
2942
+ // child order invalidation (e.g. because it's filtered out like host nodes),
2943
+ // propagate the need to reset child order upwards to this Fiber.
2944
+ shouldResetChildren = true;
2945
+ }
2946
+ // However we also keep track if the order of the children matches
2947
+ // the previous order. They are always different referentially, but
2948
+ // if the instances line up conceptually we'll want to know that.
2949
+ if (prevChild !== prevChildAtSameIndex) {
2950
+ shouldResetChildren = true;
2951
+ }
2952
+ } else {
2953
+ mountFiberRecursively(nextChild, traceNearestHostComponentUpdate);
2954
shouldResetChildren = true;
2955
}
2604
- } else {
2605
- mountFiberRecursively(nextChild, traceNearestHostComponentUpdate);
2606
- shouldResetChildren = true;
2956
}
2957
// Try the next child.
2958
nextChild = nextChild.sibling;
2962
prevChildAtSameIndex = prevChildAtSameIndex.sibling;
2963
}
2964
}
2965
+ if (previousVirtualInstance !== null) {
2966
+ if (previousVirtualInstanceWasMount) {
2967
+ mountVirtualInstanceRecursively(
2968
+ previousVirtualInstance,
2969
+ previousVirtualInstanceNextFirstFiber,
2970
+ null,
2971
+ traceNearestHostComponentUpdate,
2972
+ virtualLevel,
2973
+ );
2974
+ } else {
2975
+ updateVirtualInstanceRecursively(
2976
+ previousVirtualInstance,
2977
+ previousVirtualInstanceNextFirstFiber,
2978
+ null,
2979
+ previousVirtualInstancePrevFirstFiber,
2980
+ traceNearestHostComponentUpdate,
2981
+ virtualLevel,
2982
+ );
2983
+ }
2984
+ }
2985
// If we have no more children, but used to, they don't line up.
2986
if (prevChildAtSameIndex !== null) {
2987
shouldResetChildren = true;
2989
return shouldResetChildren;
2990
}
2991
2992
+ // Returns whether closest unfiltered fiber parent needs to reset its child list.
2993
+ function updateChildrenRecursively(
2994
+ nextFirstChild: null | Fiber,
2995
+ prevFirstChild: null | Fiber,
2996
+ traceNearestHostComponentUpdate: boolean,
2997
+ ): boolean {
2998
+ if (nextFirstChild === null) {
2999
+ return prevFirstChild !== null;
3000
+ }
3001
+ return updateVirtualChildrenRecursively(
3002
+ nextFirstChild,
3003
+ null,
3004
+ prevFirstChild,
3005
+ traceNearestHostComponentUpdate,
3006
+ 0,
3007
+ );
3008
+ }
3009
+
3010
// Returns whether closest unfiltered fiber parent needs to reset its child list.
3011
function updateFiberRecursively(
3012
nextFiber: Fiber,
3046
const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
3047
if (shouldIncludeInTree) {
3048
const entry = fiberToFiberInstanceMap.get(prevFiber);
2662
- if (entry === undefined) {
2663
- throw new Error(
2664
- 'The previous version of the fiber should have already been registered.',
2665
- );
2666
- }
2667
- fiberInstance = entry;
2668
- // Register the new alternate in case it's not already in.
2669
- fiberToFiberInstanceMap.set(nextFiber, fiberInstance);
3049
+ if (entry !== undefined && entry.parent === reconcilingParent) {
3050
+ // Common case. Match in the same parent.
3051
+ fiberInstance = entry;
3052
+ // Register the new alternate in case it's not already in.
3053
+ fiberToFiberInstanceMap.set(nextFiber, fiberInstance);
3054
+
3055
+ // Update the Fiber so we that we always keep the current Fiber on the data.
3056
+ fiberInstance.data = nextFiber;
3057
+ moveChild(fiberInstance);
3058
+ } else {
3059
+ // It's possible for a FiberInstance to be reparented when virtual parents
3060
+ // get their sequence split or change structure with the same render result.
3061
+ // In this case we unmount the and remount the FiberInstances.
3062
+ // This might cause us to lose the selection but it's an edge case.
3063
2671
- // Update the Fiber so we that we always keep the current Fiber on the data.
2672
- fiberInstance.data = nextFiber;
2673
- moveChild(fiberInstance);
3064
+ // We let the previous instance remain in the "remaining queue" it is
3065
+ // in to be deleted at the end since it'll have no match.
3066
+
3067
+ mountFiberRecursively(nextFiber, traceNearestHostComponentUpdate);
3068
+
3069
+ // Need to mark the parent set to remount the new instance.
3070
+ return true;
3071
+ }
3072
3073
if (
3074
mostRecentlyInspectedElement !== null &&
4013
console.warn(`Could not find DevToolsInstance with id "${id}"`);
4014
return null;
4015
}
3618
- if (devtoolsInstance.kind !== FIBER_INSTANCE) {
3619
- // TODO: Handle VirtualInstance.
3620
- return null;
4016
+ if (devtoolsInstance.kind === VIRTUAL_INSTANCE) {
4017
+ return inspectVirtualInstanceRaw(devtoolsInstance);
4018
}
3622
- const fiber =
3623
- findCurrentFiberUsingSlowPathByFiberInstance(devtoolsInstance);
4019
+ return inspectFiberInstanceRaw(devtoolsInstance);
4020
+ }
4021
+
4022
+ function inspectFiberInstanceRaw(
4023
+ fiberInstance: FiberInstance,
4024
+ ): InspectedElement | null {
4025
+ const fiber = findCurrentFiberUsingSlowPathByFiberInstance(fiberInstance);
4026
if (fiber == null) {
4027
return null;
4028
}
4225
const DidCapture = 0b000000000000000000010000000;
4226
isErrored =
4227
(fiber.flags & DidCapture) !== 0 ||
3826
- (devtoolsInstance.flags & FORCE_ERROR) !== 0;
3827
- targetErrorBoundaryID = isErrored ? id : getNearestErrorBoundaryID(fiber);
4228
+ (fiberInstance.flags & FORCE_ERROR) !== 0;
4229
+ targetErrorBoundaryID = isErrored
4230
+ ? fiberInstance.id
4231
+ : getNearestErrorBoundaryID(fiber);
4232
} else {
4233
targetErrorBoundaryID = getNearestErrorBoundaryID(fiber);
4234
}
4249
}
4250
4251
return {
3848
- id,
4252
+ id: fiberInstance.id,
4253
4254
// Does the current renderer support editable hooks and function props?
4255
canEditHooks: typeof overrideHookState === 'function',
4276
(!isTimedOutSuspense ||
4277
// If it's showing fallback because we previously forced it to,
4278
// allow toggling it back to remove the fallback override.
3875
- (devtoolsInstance.flags & FORCE_SUSPENSE_FALLBACK) !== 0),
4279
+ (fiberInstance.flags & FORCE_SUSPENSE_FALLBACK) !== 0),
4280
4281
// Can view component source location.
4282
canViewSource,
4297
props: memoizedProps,
4298
state: showState ? memoizedState : null,
4299
errors:
3896
- devtoolsInstance.errors === null
4300
+ fiberInstance.errors === null
4301
+ ? []
4302
+ : Array.from(fiberInstance.errors.entries()),
4303
+ warnings:
4304
+ fiberInstance.warnings === null
4305
+ ? []
4306
+ : Array.from(fiberInstance.warnings.entries()),
4307
+
4308
+ // List of owners
4309
+ owners,
4310
+
4311
+ rootType,
4312
+ rendererPackageName: renderer.rendererPackageName,
4313
+ rendererVersion: renderer.version,
4314
+
4315
+ plugins,
4316
+ };
4317
+ }
4318
+
4319
+ function inspectVirtualInstanceRaw(
4320
+ virtualInstance: VirtualInstance,
4321
+ ): InspectedElement | null {
4322
+ const canViewSource = false;
4323
+
4324
+ const key = null; // TODO: Track keys on ReactComponentInfo;
4325
+ const props = null; // TODO: Track props on ReactComponentInfo;
4326
+
4327
+ const env = virtualInstance.data.env;
4328
+ let displayName = virtualInstance.data.name || '';
4329
+ if (typeof env === 'string') {
4330
+ // We model environment as an HoC name for now.
4331
+ displayName = env + '(' + displayName + ')';
4332
+ }
4333
+
4334
+ // TODO: Support Virtual Owners.
4335
+ const owners: null | Array<SerializedElement> = null;
4336
+
4337
+ let rootType = null;
4338
+ let targetErrorBoundaryID = null;
4339
+ let parent = virtualInstance.parent;
4340
+ while (parent !== null) {
4341
+ if (parent.kind === FIBER_INSTANCE) {
4342
+ targetErrorBoundaryID = getNearestErrorBoundaryID(parent.data);
4343
+ let current = parent.data;
4344
+ while (current.return !== null) {
4345
+ current = current.return;
4346
+ }
4347
+ const fiberRoot = current.stateNode;
4348
+ if (fiberRoot != null && fiberRoot._debugRootType !== null) {
4349
+ rootType = fiberRoot._debugRootType;
4350
+ }
4351
+ break;
4352
+ }
4353
+ parent = parent.parent;
4354
+ }
4355
+
4356
+ const plugins: Plugins = {
4357
+ stylex: null,
4358
+ };
4359
+
4360
+ // TODO: Support getting the source location from the owner stack.
4361
+ const source = null;
4362
+
4363
+ return {
4364
+ id: virtualInstance.id,
4365
+
4366
+ canEditHooks: false,
4367
+ canEditFunctionProps: false,
4368
+
4369
+ canEditHooksAndDeletePaths: false,
4370
+ canEditHooksAndRenamePaths: false,
4371
+ canEditFunctionPropsDeletePaths: false,
4372
+ canEditFunctionPropsRenamePaths: false,
4373
+
4374
+ canToggleError: supportsTogglingError && targetErrorBoundaryID != null,
4375
+ isErrored: false,
4376
+ targetErrorBoundaryID,
4377
+
4378
+ canToggleSuspense: supportsTogglingSuspense,
4379
+
4380
+ // Can view component source location.
4381
+ canViewSource,
4382
+ source,
4383
+
4384
+ // Does the component have legacy context attached to it.
4385
+ hasLegacyContext: false,
4386
+
4387
+ key: key != null ? key : null,
4388
+
4389
+ displayName: displayName,
4390
+ type: ElementTypeVirtual,
4391
+
4392
+ // Inspectable properties.
4393
+ // TODO Review sanitization approach for the below inspectable values.
4394
+ context: null,
4395
+ hooks: null,
4396
+ props: props,
4397
+ state: null,
4398
+ errors:
4399
+ virtualInstance.errors === null
4400
? []
3898
- : Array.from(devtoolsInstance.errors.entries()),
4401
+ : Array.from(virtualInstance.errors.entries()),
4402
warnings:
3900
- devtoolsInstance.warnings === null
4403
+ virtualInstance.warnings === null
4404
? []
3902
- : Array.from(devtoolsInstance.warnings.entries()),
4405
+ : Array.from(virtualInstance.warnings.entries()),
4406
4407
// List of owners
4408
owners,