bippy 0.7.3-dev.1d4f35b → 0.7.3-dev.699994d

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/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,
@@ -21,6 +22,7 @@ import {
21
22
  removeActiveListener,
22
23
  } from "./rdt-hook.js";
23
24
  import type { ReactDevToolsTarget, Unsubscribe } from "./rdt-hook.js";
25
+ import { getCurrentFiberFromRoot } from "./react-internals/current-fiber.js";
24
26
  import {
25
27
  getReactWorkTagsForFiber,
26
28
  ReactBuildType,
@@ -64,8 +66,8 @@ export const isValidElement = (element: unknown): element is React.ReactElement
64
66
  typeof element === "object" &&
65
67
  element !== null &&
66
68
  "$$typeof" in element &&
67
- (String(element.$$typeof) === ReactSymbols.LEGACY_ELEMENT_SYMBOL_STRING ||
68
- String(element.$$typeof) === ReactSymbols.ELEMENT_SYMBOL_STRING);
69
+ (element.$$typeof === Symbol.for("react.element") ||
70
+ element.$$typeof === Symbol.for("react.transitional.element"));
69
71
 
70
72
  /**
71
73
  * Returns `true` if object is a React Fiber.
@@ -78,7 +80,7 @@ export const isFiber = (fiber: unknown): fiber is Fiber =>
78
80
  "return" in fiber &&
79
81
  "child" in fiber &&
80
82
  "sibling" in fiber &&
81
- "flags" in fiber;
83
+ ("flags" in fiber || "effectTag" in fiber);
82
84
 
83
85
  const isFiberRoot = (fiberRoot: unknown): fiberRoot is FiberRoot =>
84
86
  typeof fiberRoot === "object" &&
@@ -128,7 +130,7 @@ export const isCompositeFiber = (fiber: Fiber): boolean => {
128
130
  */
129
131
  export const didFiberRender = (fiber: Fiber): boolean => {
130
132
  const nextProps = fiber.memoizedProps;
131
- const prevProps = fiber.alternate?.memoizedProps || {};
133
+ const prevProps = fiber.alternate?.memoizedProps;
132
134
  const flags = fiber.flags ?? fiber.effectTag ?? 0;
133
135
  const workTags = getReactWorkTagsForFiber(fiber);
134
136
 
@@ -166,16 +168,20 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
166
168
  // https://github.com/bvaughn/react-devtools-experimental/issues/197
167
169
  return true;
168
170
 
169
- case workTags.Fragment:
171
+ case workTags.HostPortal:
170
172
  case workTags.HostText:
171
173
  case workTags.LegacyHiddenComponent:
172
174
  case workTags.OffscreenComponent:
175
+ case workTags.Throw:
173
176
  return true;
174
177
 
175
178
  case workTags.HostRoot:
176
179
  // It is never valid to filter the root element.
177
180
  return false;
178
181
 
182
+ case workTags.Fragment:
183
+ return fiber.key === null;
184
+
179
185
  default: {
180
186
  const symbolOrNumber =
181
187
  typeof fiber.type === "object" && fiber.type !== null ? fiber.type.$$typeof : fiber.type;
@@ -183,7 +189,8 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
183
189
  if (typeof symbolOrNumber === "symbol") {
184
190
  return (
185
191
  symbolOrNumber.description === ReactSymbols.CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
186
- 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
187
194
  );
188
195
  }
189
196
 
@@ -191,6 +198,8 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
191
198
  case ReactSymbols.CONCURRENT_MODE_NUMBER:
192
199
  case ReactSymbols.CONCURRENT_MODE_SYMBOL_STRING:
193
200
  case ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
201
+ case ReactSymbols.STRICT_MODE_NUMBER:
202
+ case ReactSymbols.STRICT_MODE_SYMBOL_STRING:
194
203
  return true;
195
204
 
196
205
  default:
@@ -200,41 +209,57 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
200
209
  }
201
210
  };
202
211
 
212
+ interface TraverseFiber {
213
+ (
214
+ fiber: Fiber | null,
215
+ selector: (node: Fiber) => boolean | void,
216
+ ascending?: boolean,
217
+ ): Fiber | null;
218
+ (
219
+ fiber: Fiber | null,
220
+ selector: (node: Fiber) => Promise<boolean | void>,
221
+ ascending?: boolean,
222
+ ): Promise<Fiber | null>;
223
+ (
224
+ fiber: Fiber | null,
225
+ selector: FiberSelector,
226
+ ascending?: boolean,
227
+ ): Fiber | null | Promise<Fiber | null>;
228
+ }
229
+
203
230
  /**
204
231
  * Traverses up or down a {@link Fiber}, return `true` to stop and select a node.
205
232
  */
206
- export function traverseFiber(
207
- fiber: Fiber | null,
208
- selector: (node: Fiber) => boolean | void,
209
- ascending?: boolean,
210
- ): Fiber | null;
211
- export function traverseFiber(
212
- fiber: Fiber | null,
213
- selector: (node: Fiber) => Promise<boolean | void>,
214
- ascending?: boolean,
215
- ): Promise<Fiber | null>;
216
- export function traverseFiber(
217
- fiber: Fiber | null,
218
- selector: FiberSelector,
219
- ascending?: boolean,
220
- ): Fiber | null | Promise<Fiber | null>;
221
- export function traverseFiber(
233
+ export const traverseFiber = ((
222
234
  fiber: Fiber | null,
223
235
  selector: FiberSelector,
224
236
  ascending = false,
225
- ): Fiber | null | Promise<Fiber | null> {
226
- if (!fiber) return null;
227
-
228
- const selection = selector(fiber);
229
- if (isPromiseLike<boolean | void>(selection)) {
230
- return Promise.resolve(selection).then((didSelectFiber) =>
231
- didSelectFiber === true ? fiber : traverseFiberChildren(fiber, selector, ascending),
232
- );
233
- }
234
- if (selection === true) return fiber;
235
-
236
- return traverseFiberChildren(fiber, selector, ascending);
237
- }
237
+ ): Fiber | null | Promise<Fiber | null> => {
238
+ const pendingSiblings: Fiber[] = [];
239
+ const getNextFiber = (currentFiber: Fiber): Fiber | null => {
240
+ if (ascending) return currentFiber.return;
241
+ if (currentFiber !== fiber && currentFiber.sibling) {
242
+ pendingSiblings.push(currentFiber.sibling);
243
+ }
244
+ return currentFiber.child ?? pendingSiblings.pop() ?? null;
245
+ };
246
+ const visit = (firstFiber: Fiber | null): Fiber | null | Promise<Fiber | null> => {
247
+ let currentFiber = firstFiber;
248
+ while (currentFiber) {
249
+ const selectedFiber = currentFiber;
250
+ const selection = selector(selectedFiber);
251
+ if (isPromiseLike<boolean | void>(selection)) {
252
+ return Promise.resolve(selection).then((didSelectFiber) =>
253
+ didSelectFiber === true ? selectedFiber : visit(getNextFiber(selectedFiber)),
254
+ );
255
+ }
256
+ if (selection === true) return selectedFiber;
257
+ currentFiber = getNextFiber(selectedFiber);
258
+ }
259
+ return null;
260
+ };
261
+ return visit(fiber);
262
+ }) as TraverseFiber;
238
263
 
239
264
  const isPromiseLike = <Result>(value: unknown): value is PromiseLike<Result> =>
240
265
  (typeof value === "object" || typeof value === "function") &&
@@ -242,32 +267,6 @@ const isPromiseLike = <Result>(value: unknown): value is PromiseLike<Result> =>
242
267
  "then" in value &&
243
268
  typeof value.then === "function";
244
269
 
245
- const traverseFiberChildren = (
246
- fiber: Fiber,
247
- selector: FiberSelector,
248
- ascending: boolean,
249
- ): Fiber | null | Promise<Fiber | null> => {
250
- const firstChild = ascending ? fiber.return : fiber.child;
251
- return traverseFiberSiblings(firstChild, selector, ascending);
252
- };
253
-
254
- const traverseFiberSiblings = (
255
- fiber: Fiber | null,
256
- selector: FiberSelector,
257
- ascending: boolean,
258
- ): Fiber | null | Promise<Fiber | null> => {
259
- if (!fiber) return null;
260
-
261
- const nextSibling = ascending ? null : fiber.sibling;
262
- const match = traverseFiber(fiber, selector, ascending);
263
- if (isPromiseLike<Fiber | null>(match)) {
264
- return Promise.resolve(match).then(
265
- (resolvedMatch) => resolvedMatch ?? traverseFiberSiblings(nextSibling, selector, ascending),
266
- );
267
- }
268
- return match ?? traverseFiberSiblings(nextSibling, selector, ascending);
269
- };
270
-
271
270
  /**
272
271
  * Returns `true` if the {@link Fiber} uses React Compiler's memo cache.
273
272
  */
@@ -281,7 +280,14 @@ export const hasMemoCache = (fiber: Fiber): boolean => {
281
280
  export const getType = (type: unknown): null | React.ComponentType<unknown> => {
282
281
  if (isComponentType(type)) return type;
283
282
  if (typeof type !== "object" || type === null) return null;
284
- return getType(Reflect.get(type, "type") ?? Reflect.get(type, "render"));
283
+ const visitedTypes = new Set<object>();
284
+ let currentType: unknown = type;
285
+ while (typeof currentType === "object" && currentType !== null) {
286
+ if (visitedTypes.has(currentType)) return null;
287
+ visitedTypes.add(currentType);
288
+ currentType = Reflect.get(currentType, "type") ?? Reflect.get(currentType, "render");
289
+ }
290
+ return isComponentType(currentType) ? currentType : null;
285
291
  };
286
292
 
287
293
  /**
@@ -331,6 +337,8 @@ const rootHooks = new WeakMap<FiberRoot, ReactDevToolsGlobalHook>();
331
337
  export const getLatestFiber = (fiber: Fiber): Fiber => {
332
338
  const alternate = fiber.alternate;
333
339
  if (!alternate) return fiber;
340
+ const currentFiber = getCurrentFiberFromRoot(fiber);
341
+ if (currentFiber) return currentFiber;
334
342
 
335
343
  let rootFiber = fiber;
336
344
  while (rootFiber.return) {
@@ -458,131 +466,91 @@ const releaseFiberId = (fiber: Fiber): void => {
458
466
  }
459
467
  };
460
468
 
461
- const mountFiberRecursively = (
469
+ const getRenderedChild = (fiber: Fiber, skipPrimaryWrapper: boolean): Fiber | null => {
470
+ const workTags = getReactWorkTagsForFiber(fiber);
471
+ if (fiber.tag !== workTags.SuspenseComponent) return fiber.child;
472
+ if (fiber.memoizedState !== null) return fiber.child?.sibling?.child ?? null;
473
+ return skipPrimaryWrapper && workTags.OffscreenComponent !== -1
474
+ ? (fiber.child?.child ?? null)
475
+ : fiber.child;
476
+ };
477
+
478
+ const mountFiberTree = (
462
479
  onRender: RenderHandler,
463
480
  firstChild: Fiber,
464
481
  traverseSiblings: boolean,
465
482
  ): void => {
483
+ const pendingSiblings: Fiber[] = [];
466
484
  let fiber: Fiber | null = firstChild;
467
-
468
- while (fiber !== null) {
485
+ while (fiber) {
469
486
  getFiberId(fiber);
470
- const shouldIncludeInTree = !shouldFilterFiber(fiber);
471
- if (shouldIncludeInTree && didFiberRender(fiber)) {
472
- onRender(fiber, "mount");
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, false);
487
- }
488
- }
489
- } else {
490
- const primaryChild = fiber.child?.child ?? null;
491
- if (primaryChild !== null) {
492
- mountFiberRecursively(onRender, primaryChild, false);
493
- }
494
- }
495
- } else if (fiber.child !== null) {
496
- mountFiberRecursively(onRender, fiber.child, true);
497
- }
498
- fiber = traverseSiblings ? fiber.sibling : null;
487
+ if (!shouldFilterFiber(fiber) && didFiberRender(fiber)) onRender(fiber, "mount");
488
+ if (traverseSiblings && fiber.sibling) pendingSiblings.push(fiber.sibling);
489
+ fiber = getRenderedChild(fiber, true) ?? pendingSiblings.pop() ?? null;
490
+ traverseSiblings = true;
499
491
  }
500
492
  };
501
493
 
502
- const updateFiberRecursively = (
494
+ interface FiberUpdate {
495
+ fiber: Fiber;
496
+ previousFiber: Fiber | null;
497
+ traverseSiblings: boolean;
498
+ }
499
+
500
+ const updateFiberTree = (
503
501
  onRender: RenderHandler,
504
502
  nextFiber: Fiber,
505
503
  prevFiber: Fiber | null,
506
504
  ): void => {
507
- getFiberId(nextFiber);
508
- if (!prevFiber) return;
509
- getFiberId(prevFiber);
510
-
511
- const isSuspense = nextFiber.tag === getReactWorkTagsForFiber(nextFiber).SuspenseComponent;
512
-
513
- const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
514
- if (shouldIncludeInTree && didFiberRender(nextFiber)) {
515
- onRender(nextFiber, "update");
516
- }
517
-
518
- // The behavior of timed-out Suspense trees is unique.
519
- // Rather than unmount the timed out content (and possibly lose important state),
520
- // React re-parents this content within a hidden Fragment while the fallback is showing.
521
- // This behavior doesn't need to be observable in the DevTools though.
522
- // It might even result in a bad user experience for e.g. node selection in the Elements panel.
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);
505
+ if (!prevFiber) {
506
+ getFiberId(nextFiber);
507
+ return;
508
+ }
509
+ const pendingUpdates: FiberUpdate[] = [
510
+ { fiber: nextFiber, previousFiber: prevFiber, traverseSiblings: false },
511
+ ];
512
+ let update: FiberUpdate | undefined;
513
+ while ((update = pendingUpdates.pop())) {
514
+ const { fiber, previousFiber, traverseSiblings } = update;
515
+ if (traverseSiblings && fiber.sibling) {
516
+ pendingUpdates.push({
517
+ fiber: fiber.sibling,
518
+ previousFiber: fiber.sibling.alternate,
519
+ traverseSiblings: true,
520
+ });
541
521
  }
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);
522
+ if (!previousFiber) {
523
+ mountFiberTree(onRender, fiber, false);
524
+ continue;
551
525
  }
552
- } else if (!prevDidTimeout && nextDidTimeOut) {
553
- // Primary -> Fallback:
554
- // 1. Hide primary set
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);
564
- }
565
- } else if (nextFiber.child !== prevFiber.child) {
566
- // Common case: Primary -> Primary.
567
- // This is the same code path as for non-Suspense fibers.
568
-
569
- // If the first child is different, we need to traverse them.
570
- // Each next child will be either a new child (mount) or an alternate (update).
571
- let nextChild = nextFiber.child;
572
-
573
- while (nextChild) {
574
- // We already know children will be referentially different because
575
- // they are either new mounts or alternates of previous children.
576
- // Schedule updates and mounts depending on whether alternates exist.
577
- // We don't track deletions here because they are reported separately.
578
- if (nextChild.alternate) {
579
- updateFiberRecursively(onRender, nextChild, nextChild.alternate);
580
- } else {
581
- mountFiberRecursively(onRender, nextChild, false);
526
+ getFiberId(fiber);
527
+ getFiberId(previousFiber);
528
+ if (!shouldFilterFiber(fiber) && didFiberRender(fiber)) onRender(fiber, "update");
529
+ const isSuspense = fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent;
530
+ const wasTimedOut = isSuspense && previousFiber.memoizedState !== null;
531
+ const isTimedOut = isSuspense && fiber.memoizedState !== null;
532
+ if (wasTimedOut && isTimedOut) {
533
+ const nextFallback = fiber.child?.sibling;
534
+ const previousFallback = previousFiber.child?.sibling;
535
+ if (nextFallback && previousFallback) {
536
+ pendingUpdates.push({
537
+ fiber: nextFallback,
538
+ previousFiber: previousFallback,
539
+ traverseSiblings: false,
540
+ });
582
541
  }
583
-
584
- // Try the next child.
585
- nextChild = nextChild.sibling;
542
+ } else if (wasTimedOut && !isTimedOut) {
543
+ if (fiber.child) mountFiberTree(onRender, fiber.child, true);
544
+ } else if (!wasTimedOut && isTimedOut) {
545
+ unmountFiberChildren(onRender, previousFiber);
546
+ const fallback = fiber.child?.sibling;
547
+ if (fallback) mountFiberTree(onRender, fallback, true);
548
+ } else if (fiber.child && fiber.child !== previousFiber.child) {
549
+ pendingUpdates.push({
550
+ fiber: fiber.child,
551
+ previousFiber: fiber.child.alternate,
552
+ traverseSiblings: true,
553
+ });
586
554
  }
587
555
  }
588
556
  };
@@ -595,30 +563,18 @@ const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
595
563
  }
596
564
  };
597
565
 
598
- const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber): void => {
599
- // We might meet a nested Suspense on our way.
600
- const isTimedOutSuspense =
601
- fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent && fiber.memoizedState !== null;
602
- let child = fiber.child;
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.
566
+ const unmountFiberChildren = (onRender: RenderHandler, fiber: Fiber): void => {
567
+ const pendingSiblings: Fiber[] = [];
568
+ let child = getRenderedChild(fiber, false);
569
+ while (child) {
570
+ if (child.sibling) pendingSiblings.push(child.sibling);
616
571
  if (child.return !== null) {
617
572
  unmountFiber(onRender, child);
618
- unmountFiberChildrenRecursively(onRender, child);
573
+ child = getRenderedChild(child, false);
574
+ } else {
575
+ child = null;
619
576
  }
620
-
621
- child = child.sibling;
577
+ child ??= pendingSiblings.pop() ?? null;
622
578
  }
623
579
  };
624
580
 
@@ -668,14 +624,14 @@ export const traverseRenderedFibers = (root: Fiber | FiberRoot, onRender: Render
668
624
  const isMounted = isRootFiberMounted(fiber);
669
625
 
670
626
  if (!wasMounted && isMounted) {
671
- mountFiberRecursively(onRender, fiber, false);
627
+ mountFiberTree(onRender, fiber, false);
672
628
  } else if (wasMounted && isMounted) {
673
- updateFiberRecursively(onRender, fiber, fiber.alternate);
629
+ updateFiberTree(onRender, fiber, fiber.alternate);
674
630
  } else if (wasMounted && !isMounted) {
675
631
  unmountFiber(onRender, fiber);
676
632
  }
677
633
  } else {
678
- mountFiberRecursively(onRender, fiber, true);
634
+ mountFiberTree(onRender, fiber, true);
679
635
  }
680
636
 
681
637
  rootInstance.prevFiber = fiber;
@@ -735,7 +691,7 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
735
691
  didError,
736
692
  ) => {
737
693
  if (prevOnCommitFiberRoot) {
738
- prevOnCommitFiberRoot.call(rdtHook, rendererID, root, priority, didError);
694
+ callListener(prevOnCommitFiberRoot, rdtHook, rendererID, root, priority, didError);
739
695
  }
740
696
  if (hookDispatchers.get(rdtHook)?.onCommitFiberRoot !== dispatchCommitFiberRoot) return;
741
697
  setReactWorkTagsForFiber(root.current, rdtHook.renderers.get(rendererID));
@@ -752,7 +708,7 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
752
708
  }
753
709
  for (const { options, target } of instrumentationSubscriptions) {
754
710
  if (target === hookTargets.get(rdtHook) && options.onCommitFiberRoot) {
755
- options.onCommitFiberRoot(rendererID, root, priority, didError);
711
+ callListener(options.onCommitFiberRoot, options, rendererID, root, priority, didError);
756
712
  }
757
713
  }
758
714
  };
@@ -771,7 +727,7 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
771
727
  ) => {
772
728
  setReactWorkTagsForFiber(fiber, rdtHook.renderers.get(rendererID));
773
729
  if (prevOnCommitFiberUnmount) {
774
- prevOnCommitFiberUnmount.call(rdtHook, rendererID, fiber);
730
+ callListener(prevOnCommitFiberUnmount, rdtHook, rendererID, fiber);
775
731
  }
776
732
  if (hookDispatchers.get(rdtHook)?.onCommitFiberUnmount !== dispatchCommitFiberUnmount) {
777
733
  return;
@@ -779,7 +735,7 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
779
735
  try {
780
736
  for (const { options, target } of instrumentationSubscriptions) {
781
737
  if (target === hookTargets.get(rdtHook) && options.onCommitFiberUnmount) {
782
- options.onCommitFiberUnmount(rendererID, fiber);
738
+ callListener(options.onCommitFiberUnmount, options, rendererID, fiber);
783
739
  }
784
740
  }
785
741
  } finally {
@@ -800,14 +756,14 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
800
756
  root,
801
757
  ) => {
802
758
  if (prevOnPostCommitFiberRoot) {
803
- prevOnPostCommitFiberRoot.call(rdtHook, rendererID, root);
759
+ callListener(prevOnPostCommitFiberRoot, rdtHook, rendererID, root);
804
760
  }
805
761
  if (hookDispatchers.get(rdtHook)?.onPostCommitFiberRoot !== dispatchPostCommitFiberRoot) {
806
762
  return;
807
763
  }
808
764
  for (const { options, target } of instrumentationSubscriptions) {
809
765
  if (target === hookTargets.get(rdtHook) && options.onPostCommitFiberRoot) {
810
- options.onPostCommitFiberRoot(rendererID, root);
766
+ callListener(options.onPostCommitFiberRoot, options, rendererID, root);
811
767
  }
812
768
  }
813
769
  };
@@ -826,12 +782,12 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
826
782
  children,
827
783
  ) => {
828
784
  if (prevOnScheduleFiberRoot) {
829
- prevOnScheduleFiberRoot.call(rdtHook, rendererID, root, children);
785
+ callListener(prevOnScheduleFiberRoot, rdtHook, rendererID, root, children);
830
786
  }
831
787
  if (hookDispatchers.get(rdtHook)?.onScheduleFiberRoot !== dispatchScheduleFiberRoot) return;
832
788
  for (const { options, target } of instrumentationSubscriptions) {
833
789
  if (target === hookTargets.get(rdtHook) && options.onScheduleFiberRoot) {
834
- options.onScheduleFiberRoot(rendererID, root, children);
790
+ callListener(options.onScheduleFiberRoot, options, rendererID, root, children);
835
791
  }
836
792
  }
837
793
  };
@@ -967,9 +923,11 @@ export const getFiber = <HostInstance>(
967
923
 
968
924
  for (const key of Object.keys(hostInstance)) {
969
925
  if (isFiberPropertyKey(key)) {
970
- knownFiberPropertyKeys.add(key);
971
926
  const fiber = Reflect.get(hostInstance, key);
972
- if (isFiber(fiber)) return fiber;
927
+ if (isFiber(fiber)) {
928
+ knownFiberPropertyKeys.add(key);
929
+ return fiber;
930
+ }
973
931
  }
974
932
  }
975
933
  }
package/src/rdt-hook.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // This module must load before React so renderers can inject into the hook.
2
2
 
3
+ import { callListener } from "./call-listener.js";
3
4
  import type { FiberRoot, ReactDevToolsGlobalHook, ReactRenderer } from "./react-internals/index.js";
4
5
 
5
6
  export interface Unsubscribe extends Disposable {
@@ -49,6 +50,8 @@ export const isFiberRootUnmounted = (fiberRoot: FiberRoot): boolean => {
49
50
  };
50
51
 
51
52
  const checkDCE = (functionToCheck: unknown): void => {
53
+ // HACK: Node loads React's unbundled entrypoints, where development guards are intentionally not eliminated.
54
+ if (typeof process !== "undefined" && process.versions?.node) return;
52
55
  try {
53
56
  const code = Function.prototype.toString.call(functionToCheck);
54
57
  if (code.indexOf("^_^") > -1) {
@@ -115,7 +118,7 @@ export const removeActiveListener = (
115
118
 
116
119
  const notifyActiveListeners = (target: ReactDevToolsTarget): void => {
117
120
  for (const listener of _onActiveListeners) {
118
- if (activeListenerTargets.get(listener)?.has(target)) listener();
121
+ if (activeListenerTargets.get(listener)?.has(target)) callListener(listener, undefined);
119
122
  }
120
123
  };
121
124
 
@@ -124,7 +127,7 @@ const notifyRendererInjectListeners = (
124
127
  renderer: ReactRenderer,
125
128
  ): void => {
126
129
  for (const subscription of rendererInjectSubscriptions) {
127
- if (subscription.target === target) subscription.listener(renderer);
130
+ if (subscription.target === target) callListener(subscription.listener, subscription, renderer);
128
131
  }
129
132
  };
130
133
 
@@ -133,7 +136,7 @@ const notifyRDTHookReplaceListeners = (
133
136
  target: ReactDevToolsTarget,
134
137
  ): void => {
135
138
  for (const listener of rdtHookReplaceListeners) {
136
- listener(rdtHook, target);
139
+ callListener(listener, undefined, rdtHook, target);
137
140
  }
138
141
  };
139
142
 
@@ -324,7 +327,7 @@ export const patchRDTHook = (
324
327
  };
325
328
  }
326
329
  if (!didNotifyActiveListeners && (renderers.size || rdtHook._instrumentationIsActive)) {
327
- onActive?.();
330
+ if (onActive) callListener(onActive, undefined);
328
331
  }
329
332
  };
330
333