bippy 0.7.3-dev.775b69e → 0.7.3-dev.7d8af8d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -11
- package/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/errors.d.cts +4 -1
- package/dist/errors.d.ts +4 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +27 -27
- package/dist/index.d.ts +27 -27
- package/dist/index.js +1 -1
- package/dist/rdt-hook.cjs +1 -1
- package/dist/rdt-hook.js +1 -1
- package/dist/source.cjs +11 -12
- package/dist/source.d.cts +22 -22
- package/dist/source.d.ts +22 -22
- package/dist/source.js +11 -12
- package/package.json +4 -4
- package/src/call-listener.ts +13 -0
- package/src/core.ts +188 -183
- package/src/rdt-hook.ts +43 -29
- package/src/react-internals/generated/react-work-tags.ts +3 -0
- package/src/react-internals/index.ts +36 -14
- package/src/react.ts +11 -1
- package/src/source/inspect-hooks.ts +5 -3
- package/src/source/parse-stack.ts +111 -63
- package/src/source/symbolication.ts +15 -4
package/src/core.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// React must remain a type-only import because this module loads immediately after the DevTools hook.
|
|
2
2
|
import type * as React from "react";
|
|
3
|
+
import { callListener } from "./call-listener.js";
|
|
3
4
|
|
|
4
5
|
import type {
|
|
5
6
|
Fiber,
|
|
@@ -167,16 +168,20 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
167
168
|
// https://github.com/bvaughn/react-devtools-experimental/issues/197
|
|
168
169
|
return true;
|
|
169
170
|
|
|
170
|
-
case workTags.
|
|
171
|
+
case workTags.HostPortal:
|
|
171
172
|
case workTags.HostText:
|
|
172
173
|
case workTags.LegacyHiddenComponent:
|
|
173
174
|
case workTags.OffscreenComponent:
|
|
175
|
+
case workTags.Throw:
|
|
174
176
|
return true;
|
|
175
177
|
|
|
176
178
|
case workTags.HostRoot:
|
|
177
179
|
// It is never valid to filter the root element.
|
|
178
180
|
return false;
|
|
179
181
|
|
|
182
|
+
case workTags.Fragment:
|
|
183
|
+
return fiber.key === null;
|
|
184
|
+
|
|
180
185
|
default: {
|
|
181
186
|
const symbolOrNumber =
|
|
182
187
|
typeof fiber.type === "object" && fiber.type !== null ? fiber.type.$$typeof : fiber.type;
|
|
@@ -184,7 +189,8 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
184
189
|
if (typeof symbolOrNumber === "symbol") {
|
|
185
190
|
return (
|
|
186
191
|
symbolOrNumber.description === ReactSymbols.CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
187
|
-
symbolOrNumber.description === ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
192
|
+
symbolOrNumber.description === ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION ||
|
|
193
|
+
symbolOrNumber.description === ReactSymbols.STRICT_MODE_SYMBOL_DESCRIPTION
|
|
188
194
|
);
|
|
189
195
|
}
|
|
190
196
|
|
|
@@ -192,6 +198,8 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
192
198
|
case ReactSymbols.CONCURRENT_MODE_NUMBER:
|
|
193
199
|
case ReactSymbols.CONCURRENT_MODE_SYMBOL_STRING:
|
|
194
200
|
case ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
201
|
+
case ReactSymbols.STRICT_MODE_NUMBER:
|
|
202
|
+
case ReactSymbols.STRICT_MODE_SYMBOL_STRING:
|
|
195
203
|
return true;
|
|
196
204
|
|
|
197
205
|
default:
|
|
@@ -240,8 +248,15 @@ export const traverseFiber = ((
|
|
|
240
248
|
while (currentFiber) {
|
|
241
249
|
const selectedFiber = currentFiber;
|
|
242
250
|
const selection = selector(selectedFiber);
|
|
243
|
-
|
|
244
|
-
|
|
251
|
+
const then =
|
|
252
|
+
selection !== null && (typeof selection === "object" || typeof selection === "function")
|
|
253
|
+
? selection.then
|
|
254
|
+
: null;
|
|
255
|
+
if (typeof then === "function") {
|
|
256
|
+
return Promise.resolve<boolean | void>({
|
|
257
|
+
// oxlint-disable-next-line unicorn/no-thenable -- Assimilate the captured method without reading the accessor twice.
|
|
258
|
+
then: (resolve, reject) => Reflect.apply(then, selection, [resolve, reject]),
|
|
259
|
+
}).then((didSelectFiber) =>
|
|
245
260
|
didSelectFiber === true ? selectedFiber : visit(getNextFiber(selectedFiber)),
|
|
246
261
|
);
|
|
247
262
|
}
|
|
@@ -253,12 +268,6 @@ export const traverseFiber = ((
|
|
|
253
268
|
return visit(fiber);
|
|
254
269
|
}) as TraverseFiber;
|
|
255
270
|
|
|
256
|
-
const isPromiseLike = <Result>(value: unknown): value is PromiseLike<Result> =>
|
|
257
|
-
(typeof value === "object" || typeof value === "function") &&
|
|
258
|
-
value !== null &&
|
|
259
|
-
"then" in value &&
|
|
260
|
-
typeof value.then === "function";
|
|
261
|
-
|
|
262
271
|
/**
|
|
263
272
|
* Returns `true` if the {@link Fiber} uses React Compiler's memo cache.
|
|
264
273
|
*/
|
|
@@ -322,6 +331,7 @@ export const isInstrumentationActive = (target: ReactDevToolsTarget = globalThis
|
|
|
322
331
|
export const _fiberRoots = new Set<FiberRoot>();
|
|
323
332
|
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
324
333
|
const rootHooks = new WeakMap<FiberRoot, ReactDevToolsGlobalHook>();
|
|
334
|
+
let currentUnmountingFiber: Fiber | null = null;
|
|
325
335
|
|
|
326
336
|
/**
|
|
327
337
|
* Returns the latest fiber (since it may be double-buffered).
|
|
@@ -330,7 +340,9 @@ export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
|
330
340
|
const alternate = fiber.alternate;
|
|
331
341
|
if (!alternate) return fiber;
|
|
332
342
|
const currentFiber = getCurrentFiberFromRoot(fiber);
|
|
333
|
-
|
|
343
|
+
const isUnmountingPair = fiber === currentUnmountingFiber || alternate === currentUnmountingFiber;
|
|
344
|
+
if (currentFiber && (!isUnmountingPair || currentFiber === currentUnmountingFiber))
|
|
345
|
+
return currentFiber;
|
|
334
346
|
|
|
335
347
|
let rootFiber = fiber;
|
|
336
348
|
while (rootFiber.return) {
|
|
@@ -342,6 +354,7 @@ export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
|
342
354
|
});
|
|
343
355
|
if (latestFiber) return latestFiber;
|
|
344
356
|
}
|
|
357
|
+
if (currentFiber) return currentFiber;
|
|
345
358
|
|
|
346
359
|
if (alternate.actualStartTime && fiber.actualStartTime) {
|
|
347
360
|
return alternate.actualStartTime > fiber.actualStartTime ? alternate : fiber;
|
|
@@ -405,14 +418,24 @@ const fiberIdFinalizationRegistry =
|
|
|
405
418
|
const createFiberReference = (fiber: Fiber): FiberReference =>
|
|
406
419
|
typeof WeakRef === "function" ? new WeakRef(fiber) : { deref: () => fiber };
|
|
407
420
|
|
|
408
|
-
|
|
421
|
+
const getNextFiberId = (): number => {
|
|
422
|
+
if (!Number.isSafeInteger(nextFiberId)) throw new RangeError("Fiber ID space exhausted");
|
|
423
|
+
return nextFiberId++;
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
export const setFiberId = (fiber: Fiber, fiberId: number = getNextFiberId()): void => {
|
|
409
427
|
const previousFiberId = fiberIdMap.get(fiber);
|
|
410
|
-
if (
|
|
428
|
+
if (
|
|
429
|
+
previousFiberId !== undefined &&
|
|
430
|
+
previousFiberId !== fiberId &&
|
|
431
|
+
fiberByIdMap.get(previousFiberId)?.deref() === fiber
|
|
432
|
+
) {
|
|
411
433
|
fiberByIdMap.delete(previousFiberId);
|
|
412
434
|
}
|
|
413
435
|
fiberIdMap.set(fiber, fiberId);
|
|
414
436
|
fiberByIdMap.set(fiberId, createFiberReference(fiber));
|
|
415
|
-
fiberIdFinalizationRegistry?.
|
|
437
|
+
if (previousFiberId !== undefined) fiberIdFinalizationRegistry?.unregister(fiber);
|
|
438
|
+
fiberIdFinalizationRegistry?.register(fiber, fiberId, fiber);
|
|
416
439
|
if (Number.isSafeInteger(fiberId) && fiberId >= nextFiberId) {
|
|
417
440
|
nextFiberId = fiberId + 1;
|
|
418
441
|
}
|
|
@@ -422,10 +445,17 @@ export const getFiberId = (fiber: Fiber): number => {
|
|
|
422
445
|
let currentFiberId = fiberIdMap.get(fiber);
|
|
423
446
|
if (currentFiberId === undefined && fiber.alternate) {
|
|
424
447
|
currentFiberId = fiberIdMap.get(fiber.alternate);
|
|
425
|
-
if (currentFiberId !== undefined)
|
|
448
|
+
if (currentFiberId !== undefined) {
|
|
449
|
+
const assignedFiber = fiberByIdMap.get(currentFiberId)?.deref();
|
|
450
|
+
if (assignedFiber && assignedFiber !== fiber.alternate) {
|
|
451
|
+
fiberIdMap.set(fiber, currentFiberId);
|
|
452
|
+
} else {
|
|
453
|
+
setFiberId(fiber, currentFiberId);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
426
456
|
}
|
|
427
457
|
if (currentFiberId === undefined) {
|
|
428
|
-
currentFiberId =
|
|
458
|
+
currentFiberId = getNextFiberId();
|
|
429
459
|
setFiberId(fiber, currentFiberId);
|
|
430
460
|
}
|
|
431
461
|
return currentFiberId;
|
|
@@ -448,6 +478,7 @@ const releaseFiberId = (fiber: Fiber): void => {
|
|
|
448
478
|
const fiberId = fiberIdMap.get(relatedFiber);
|
|
449
479
|
if (fiberId !== undefined) fiberIds.add(fiberId);
|
|
450
480
|
fiberIdMap.delete(relatedFiber);
|
|
481
|
+
fiberIdFinalizationRegistry?.unregister(relatedFiber);
|
|
451
482
|
}
|
|
452
483
|
|
|
453
484
|
for (const fiberId of fiberIds) {
|
|
@@ -458,131 +489,91 @@ const releaseFiberId = (fiber: Fiber): void => {
|
|
|
458
489
|
}
|
|
459
490
|
};
|
|
460
491
|
|
|
461
|
-
const
|
|
492
|
+
const getRenderedChild = (fiber: Fiber, skipPrimaryWrapper: boolean): Fiber | null => {
|
|
493
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
494
|
+
if (fiber.tag !== workTags.SuspenseComponent) return fiber.child;
|
|
495
|
+
if (fiber.memoizedState !== null) return fiber.child?.sibling?.child ?? null;
|
|
496
|
+
return skipPrimaryWrapper && workTags.OffscreenComponent !== -1
|
|
497
|
+
? (fiber.child?.child ?? null)
|
|
498
|
+
: fiber.child;
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
const mountFiberTree = (
|
|
462
502
|
onRender: RenderHandler,
|
|
463
503
|
firstChild: Fiber,
|
|
464
504
|
traverseSiblings: boolean,
|
|
465
505
|
): void => {
|
|
506
|
+
const pendingSiblings: Fiber[] = [];
|
|
466
507
|
let fiber: Fiber | null = firstChild;
|
|
467
|
-
|
|
468
|
-
while (fiber !== null) {
|
|
508
|
+
while (fiber) {
|
|
469
509
|
getFiberId(fiber);
|
|
470
|
-
|
|
471
|
-
if (
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
if (fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent) {
|
|
476
|
-
const isTimedOut = fiber.memoizedState !== null;
|
|
477
|
-
if (isTimedOut) {
|
|
478
|
-
// Special case: if Suspense mounts in a timed-out state,
|
|
479
|
-
// get the fallback child from the inner fragment and mount
|
|
480
|
-
// it as if it was our own child. Updates handle this too.
|
|
481
|
-
const primaryChildFragment = fiber.child;
|
|
482
|
-
const fallbackChildFragment = primaryChildFragment ? primaryChildFragment.sibling : null;
|
|
483
|
-
if (fallbackChildFragment) {
|
|
484
|
-
const fallbackChild = fallbackChildFragment.child;
|
|
485
|
-
if (fallbackChild !== null) {
|
|
486
|
-
mountFiberRecursively(onRender, fallbackChild, true);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
} else {
|
|
490
|
-
const primaryChild = fiber.child?.child ?? null;
|
|
491
|
-
if (primaryChild !== null) {
|
|
492
|
-
mountFiberRecursively(onRender, primaryChild, true);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
} else if (fiber.child !== null) {
|
|
496
|
-
mountFiberRecursively(onRender, fiber.child, true);
|
|
497
|
-
}
|
|
498
|
-
fiber = traverseSiblings ? fiber.sibling : null;
|
|
510
|
+
if (!shouldFilterFiber(fiber) && didFiberRender(fiber)) onRender(fiber, "mount");
|
|
511
|
+
if (traverseSiblings && fiber.sibling) pendingSiblings.push(fiber.sibling);
|
|
512
|
+
fiber = getRenderedChild(fiber, true) ?? pendingSiblings.pop() ?? null;
|
|
513
|
+
traverseSiblings = true;
|
|
499
514
|
}
|
|
500
515
|
};
|
|
501
516
|
|
|
502
|
-
|
|
517
|
+
interface FiberUpdate {
|
|
518
|
+
fiber: Fiber;
|
|
519
|
+
previousFiber: Fiber | null;
|
|
520
|
+
traverseSiblings: boolean;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const updateFiberTree = (
|
|
503
524
|
onRender: RenderHandler,
|
|
504
525
|
nextFiber: Fiber,
|
|
505
526
|
prevFiber: Fiber | null,
|
|
506
527
|
): void => {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
// The easiest fix is to strip out the intermediate Fragment fibers,
|
|
524
|
-
// so the Elements panel and Profiler don't need to special case them.
|
|
525
|
-
// Suspense components only have a non-null memoizedState if they're timed-out.
|
|
526
|
-
const prevDidTimeout = isSuspense && prevFiber.memoizedState !== null;
|
|
527
|
-
const nextDidTimeOut = isSuspense && nextFiber.memoizedState !== null;
|
|
528
|
-
|
|
529
|
-
// The logic below is inspired by the code paths in updateSuspenseComponent()
|
|
530
|
-
// inside ReactFiberBeginWork in the React source code.
|
|
531
|
-
if (prevDidTimeout && nextDidTimeOut) {
|
|
532
|
-
// Fallback -> Fallback:
|
|
533
|
-
// 1. Reconcile fallback set.
|
|
534
|
-
const nextFallbackChildSet = nextFiber.child?.sibling ?? null;
|
|
535
|
-
// Note: We can't use nextFiber.child.sibling.alternate
|
|
536
|
-
// because the set is special and alternate may not exist.
|
|
537
|
-
const prevFallbackChildSet = prevFiber.child?.sibling ?? null;
|
|
538
|
-
|
|
539
|
-
if (nextFallbackChildSet !== null && prevFallbackChildSet !== null) {
|
|
540
|
-
updateFiberRecursively(onRender, nextFallbackChildSet, prevFallbackChildSet);
|
|
541
|
-
}
|
|
542
|
-
} else if (prevDidTimeout && !nextDidTimeOut) {
|
|
543
|
-
// Fallback -> Primary:
|
|
544
|
-
// 1. Unmount fallback set
|
|
545
|
-
// Note: don't emulate fallback unmount because React actually did it.
|
|
546
|
-
// 2. Mount primary set
|
|
547
|
-
const nextPrimaryChildSet = nextFiber.child;
|
|
548
|
-
|
|
549
|
-
if (nextPrimaryChildSet !== null) {
|
|
550
|
-
mountFiberRecursively(onRender, nextPrimaryChildSet, true);
|
|
528
|
+
if (!prevFiber) {
|
|
529
|
+
getFiberId(nextFiber);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const pendingUpdates: FiberUpdate[] = [
|
|
533
|
+
{ fiber: nextFiber, previousFiber: prevFiber, traverseSiblings: false },
|
|
534
|
+
];
|
|
535
|
+
let update: FiberUpdate | undefined;
|
|
536
|
+
while ((update = pendingUpdates.pop())) {
|
|
537
|
+
const { fiber, previousFiber, traverseSiblings } = update;
|
|
538
|
+
if (traverseSiblings && fiber.sibling) {
|
|
539
|
+
pendingUpdates.push({
|
|
540
|
+
fiber: fiber.sibling,
|
|
541
|
+
previousFiber: fiber.sibling.alternate,
|
|
542
|
+
traverseSiblings: true,
|
|
543
|
+
});
|
|
551
544
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
// This is not a real unmount, so it won't get reported by React.
|
|
556
|
-
// We need to manually walk the previous tree and record unmounts.
|
|
557
|
-
unmountFiberChildrenRecursively(onRender, prevFiber);
|
|
558
|
-
|
|
559
|
-
// 2. Mount fallback set
|
|
560
|
-
const nextFallbackChildSet = nextFiber.child?.sibling ?? null;
|
|
561
|
-
|
|
562
|
-
if (nextFallbackChildSet !== null) {
|
|
563
|
-
mountFiberRecursively(onRender, nextFallbackChildSet, true);
|
|
545
|
+
if (!previousFiber) {
|
|
546
|
+
mountFiberTree(onRender, fiber, false);
|
|
547
|
+
continue;
|
|
564
548
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
} else {
|
|
581
|
-
mountFiberRecursively(onRender, nextChild, false);
|
|
549
|
+
getFiberId(fiber);
|
|
550
|
+
getFiberId(previousFiber);
|
|
551
|
+
if (!shouldFilterFiber(fiber) && didFiberRender(fiber)) onRender(fiber, "update");
|
|
552
|
+
const isSuspense = fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent;
|
|
553
|
+
const wasTimedOut = isSuspense && previousFiber.memoizedState !== null;
|
|
554
|
+
const isTimedOut = isSuspense && fiber.memoizedState !== null;
|
|
555
|
+
if (wasTimedOut && isTimedOut) {
|
|
556
|
+
const nextFallback = fiber.child?.sibling;
|
|
557
|
+
const previousFallback = previousFiber.child?.sibling;
|
|
558
|
+
if (nextFallback) {
|
|
559
|
+
pendingUpdates.push({
|
|
560
|
+
fiber: nextFallback,
|
|
561
|
+
previousFiber: previousFallback ?? null,
|
|
562
|
+
traverseSiblings: false,
|
|
563
|
+
});
|
|
582
564
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
565
|
+
} else if (wasTimedOut && !isTimedOut) {
|
|
566
|
+
if (fiber.child) mountFiberTree(onRender, fiber.child, true);
|
|
567
|
+
} else if (!wasTimedOut && isTimedOut) {
|
|
568
|
+
unmountFiberChildren(onRender, previousFiber);
|
|
569
|
+
const fallback = fiber.child?.sibling;
|
|
570
|
+
if (fallback) mountFiberTree(onRender, fallback, true);
|
|
571
|
+
} else if (fiber.child && fiber.child !== previousFiber.child) {
|
|
572
|
+
pendingUpdates.push({
|
|
573
|
+
fiber: fiber.child,
|
|
574
|
+
previousFiber: fiber.child.alternate,
|
|
575
|
+
traverseSiblings: true,
|
|
576
|
+
});
|
|
586
577
|
}
|
|
587
578
|
}
|
|
588
579
|
};
|
|
@@ -595,30 +586,18 @@ const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
|
595
586
|
}
|
|
596
587
|
};
|
|
597
588
|
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
if (isTimedOutSuspense) {
|
|
605
|
-
// If it's showing fallback tree, let's traverse it instead.
|
|
606
|
-
const primaryChildFragment = fiber.child;
|
|
607
|
-
const fallbackChildFragment = primaryChildFragment?.sibling ?? null;
|
|
608
|
-
|
|
609
|
-
// Skip over to the real Fiber child.
|
|
610
|
-
child = fallbackChildFragment?.child ?? null;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
while (child !== null) {
|
|
614
|
-
// Record simulated unmounts children-first.
|
|
615
|
-
// We skip nodes without return because those are real unmounts.
|
|
589
|
+
const unmountFiberChildren = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
590
|
+
const pendingSiblings: Fiber[] = [];
|
|
591
|
+
let child = getRenderedChild(fiber, false);
|
|
592
|
+
while (child) {
|
|
593
|
+
if (child.sibling) pendingSiblings.push(child.sibling);
|
|
616
594
|
if (child.return !== null) {
|
|
617
595
|
unmountFiber(onRender, child);
|
|
618
|
-
|
|
596
|
+
child = getRenderedChild(child, false);
|
|
597
|
+
} else {
|
|
598
|
+
child = null;
|
|
619
599
|
}
|
|
620
|
-
|
|
621
|
-
child = child.sibling;
|
|
600
|
+
child ??= pendingSiblings.pop() ?? null;
|
|
622
601
|
}
|
|
623
602
|
};
|
|
624
603
|
|
|
@@ -650,35 +629,35 @@ const isRootFiberMounted = (fiber: Fiber): boolean => {
|
|
|
650
629
|
*/
|
|
651
630
|
export const traverseRenderedFibers = (root: Fiber | FiberRoot, onRender: RenderHandler): void => {
|
|
652
631
|
const fiber = "current" in root ? root.current : root;
|
|
632
|
+
const rootKey = "current" in root || !isFiberRoot(root.stateNode) ? root : root.stateNode;
|
|
653
633
|
|
|
654
|
-
let rootInstance = rootInstanceMap.get(
|
|
634
|
+
let rootInstance = rootInstanceMap.get(rootKey);
|
|
655
635
|
|
|
656
636
|
if (!rootInstance) {
|
|
657
637
|
rootInstance = { prevFiber: null };
|
|
658
|
-
rootInstanceMap.set(
|
|
638
|
+
rootInstanceMap.set(rootKey, rootInstance);
|
|
659
639
|
}
|
|
660
640
|
|
|
661
641
|
const { prevFiber } = rootInstance;
|
|
642
|
+
rootInstance.prevFiber = fiber;
|
|
662
643
|
if (!fiber) {
|
|
663
644
|
if (prevFiber) {
|
|
664
645
|
unmountFiber(onRender, prevFiber);
|
|
665
646
|
}
|
|
666
647
|
} else if (prevFiber !== null) {
|
|
667
|
-
const wasMounted = isRootFiberMounted(prevFiber);
|
|
648
|
+
const wasMounted = isRootFiberMounted(fiber.alternate ?? prevFiber);
|
|
668
649
|
const isMounted = isRootFiberMounted(fiber);
|
|
669
650
|
|
|
670
651
|
if (!wasMounted && isMounted) {
|
|
671
|
-
|
|
652
|
+
mountFiberTree(onRender, fiber, false);
|
|
672
653
|
} else if (wasMounted && isMounted) {
|
|
673
|
-
|
|
654
|
+
updateFiberTree(onRender, fiber, fiber.alternate);
|
|
674
655
|
} else if (wasMounted && !isMounted) {
|
|
675
656
|
unmountFiber(onRender, fiber);
|
|
676
657
|
}
|
|
677
658
|
} else {
|
|
678
|
-
|
|
659
|
+
mountFiberTree(onRender, fiber, true);
|
|
679
660
|
}
|
|
680
|
-
|
|
681
|
-
rootInstance.prevFiber = fiber;
|
|
682
661
|
};
|
|
683
662
|
|
|
684
663
|
export interface InstrumentationOptions {
|
|
@@ -734,10 +713,12 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
734
713
|
priority,
|
|
735
714
|
didError,
|
|
736
715
|
) => {
|
|
716
|
+
const isCurrentDispatcher =
|
|
717
|
+
hookDispatchers.get(rdtHook)?.onCommitFiberRoot === dispatchCommitFiberRoot;
|
|
737
718
|
if (prevOnCommitFiberRoot) {
|
|
738
|
-
prevOnCommitFiberRoot
|
|
719
|
+
callListener(prevOnCommitFiberRoot, rdtHook, rendererID, root, priority, didError);
|
|
739
720
|
}
|
|
740
|
-
if (
|
|
721
|
+
if (!isCurrentDispatcher) return;
|
|
741
722
|
setReactWorkTagsForFiber(root.current, rdtHook.renderers.get(rendererID));
|
|
742
723
|
// Custom renderers and test harnesses commit roots without a memoizedState;
|
|
743
724
|
// those must stay tracked, so only explicit unmount evidence removes a root.
|
|
@@ -750,9 +731,14 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
750
731
|
rootRendererIds.set(root, rendererID);
|
|
751
732
|
rootHooks.set(root, rdtHook);
|
|
752
733
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
734
|
+
const subscriptionSnapshot = [...instrumentationSubscriptions];
|
|
735
|
+
for (const subscription of subscriptionSnapshot) {
|
|
736
|
+
const { options, target } = subscription;
|
|
737
|
+
if (instrumentationSubscriptions.has(subscription) && target === hookTargets.get(rdtHook)) {
|
|
738
|
+
callListener(
|
|
739
|
+
() => options.onCommitFiberRoot?.(rendererID, root, priority, didError),
|
|
740
|
+
undefined,
|
|
741
|
+
);
|
|
756
742
|
}
|
|
757
743
|
}
|
|
758
744
|
};
|
|
@@ -769,21 +755,29 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
769
755
|
rendererID,
|
|
770
756
|
fiber,
|
|
771
757
|
) => {
|
|
758
|
+
const isCurrentDispatcher =
|
|
759
|
+
hookDispatchers.get(rdtHook)?.onCommitFiberUnmount === dispatchCommitFiberUnmount;
|
|
772
760
|
setReactWorkTagsForFiber(fiber, rdtHook.renderers.get(rendererID));
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
776
|
-
if (hookDispatchers.get(rdtHook)?.onCommitFiberUnmount !== dispatchCommitFiberUnmount) {
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
761
|
+
const previousUnmountingFiber = currentUnmountingFiber;
|
|
762
|
+
currentUnmountingFiber = fiber;
|
|
779
763
|
try {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
764
|
+
if (prevOnCommitFiberUnmount) {
|
|
765
|
+
callListener(prevOnCommitFiberUnmount, rdtHook, rendererID, fiber);
|
|
766
|
+
}
|
|
767
|
+
if (!isCurrentDispatcher) return;
|
|
768
|
+
const subscriptionSnapshot = [...instrumentationSubscriptions];
|
|
769
|
+
for (const subscription of subscriptionSnapshot) {
|
|
770
|
+
const { options, target } = subscription;
|
|
771
|
+
if (
|
|
772
|
+
instrumentationSubscriptions.has(subscription) &&
|
|
773
|
+
target === hookTargets.get(rdtHook)
|
|
774
|
+
) {
|
|
775
|
+
callListener(() => options.onCommitFiberUnmount?.(rendererID, fiber), undefined);
|
|
783
776
|
}
|
|
784
777
|
}
|
|
785
778
|
} finally {
|
|
786
|
-
|
|
779
|
+
currentUnmountingFiber = previousUnmountingFiber;
|
|
780
|
+
if (isCurrentDispatcher) releaseFiberId(fiber);
|
|
787
781
|
}
|
|
788
782
|
};
|
|
789
783
|
dispatchers.onCommitFiberUnmount = dispatchCommitFiberUnmount;
|
|
@@ -799,15 +793,17 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
799
793
|
rendererID,
|
|
800
794
|
root,
|
|
801
795
|
) => {
|
|
796
|
+
const isCurrentDispatcher =
|
|
797
|
+
hookDispatchers.get(rdtHook)?.onPostCommitFiberRoot === dispatchPostCommitFiberRoot;
|
|
802
798
|
if (prevOnPostCommitFiberRoot) {
|
|
803
|
-
prevOnPostCommitFiberRoot
|
|
804
|
-
}
|
|
805
|
-
if (hookDispatchers.get(rdtHook)?.onPostCommitFiberRoot !== dispatchPostCommitFiberRoot) {
|
|
806
|
-
return;
|
|
799
|
+
callListener(prevOnPostCommitFiberRoot, rdtHook, rendererID, root);
|
|
807
800
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
801
|
+
if (!isCurrentDispatcher) return;
|
|
802
|
+
const subscriptionSnapshot = [...instrumentationSubscriptions];
|
|
803
|
+
for (const subscription of subscriptionSnapshot) {
|
|
804
|
+
const { options, target } = subscription;
|
|
805
|
+
if (instrumentationSubscriptions.has(subscription) && target === hookTargets.get(rdtHook)) {
|
|
806
|
+
callListener(() => options.onPostCommitFiberRoot?.(rendererID, root), undefined);
|
|
811
807
|
}
|
|
812
808
|
}
|
|
813
809
|
};
|
|
@@ -825,13 +821,17 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
825
821
|
root,
|
|
826
822
|
children,
|
|
827
823
|
) => {
|
|
824
|
+
const isCurrentDispatcher =
|
|
825
|
+
hookDispatchers.get(rdtHook)?.onScheduleFiberRoot === dispatchScheduleFiberRoot;
|
|
828
826
|
if (prevOnScheduleFiberRoot) {
|
|
829
|
-
prevOnScheduleFiberRoot
|
|
827
|
+
callListener(prevOnScheduleFiberRoot, rdtHook, rendererID, root, children);
|
|
830
828
|
}
|
|
831
|
-
if (
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
829
|
+
if (!isCurrentDispatcher) return;
|
|
830
|
+
const subscriptionSnapshot = [...instrumentationSubscriptions];
|
|
831
|
+
for (const subscription of subscriptionSnapshot) {
|
|
832
|
+
const { options, target } = subscription;
|
|
833
|
+
if (instrumentationSubscriptions.has(subscription) && target === hookTargets.get(rdtHook)) {
|
|
834
|
+
callListener(() => options.onScheduleFiberRoot?.(rendererID, root, children), undefined);
|
|
835
835
|
}
|
|
836
836
|
}
|
|
837
837
|
};
|
|
@@ -887,15 +887,20 @@ try {
|
|
|
887
887
|
*/
|
|
888
888
|
export const instrument = (options: InstrumentationOptions): Unsubscribe => {
|
|
889
889
|
const target = options.target ?? globalThis;
|
|
890
|
-
const
|
|
890
|
+
const activeListener = options.onActive;
|
|
891
|
+
const onActive = activeListener ? () => activeListener() : undefined;
|
|
892
|
+
const existingHook = target.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
893
|
+
if (existingHook) wireHookEventDispatchers(existingHook, target);
|
|
894
|
+
const rdtHook = getRDTHook(undefined, target);
|
|
891
895
|
rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
|
|
892
896
|
|
|
893
897
|
wireHookEventDispatchers(rdtHook, target);
|
|
894
898
|
const subscription: InstrumentationSubscription = { options, target };
|
|
895
899
|
instrumentationSubscriptions.add(subscription);
|
|
900
|
+
if (onActive) getRDTHook(onActive, target);
|
|
896
901
|
|
|
897
902
|
return createUnsubscribe(() => {
|
|
898
|
-
if (
|
|
903
|
+
if (onActive) removeActiveListener(onActive, target);
|
|
899
904
|
instrumentationSubscriptions.delete(subscription);
|
|
900
905
|
});
|
|
901
906
|
};
|