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/LICENSE +1 -0
- package/README.md +4 -11
- package/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/errors.d.cts +3 -0
- package/dist/errors.d.ts +3 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +32 -29
- package/dist/index.d.ts +34 -31
- 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 +12 -13
- package/package.json +6 -34
- package/src/call-listener.ts +13 -0
- package/src/core.ts +163 -205
- package/src/rdt-hook.ts +7 -4
- package/src/react-internals/current-fiber.ts +84 -0
- package/src/react-internals/generated/react-work-tags.ts +3 -0
- package/src/react-internals/index.ts +38 -11
- package/src/react.ts +141 -56
- package/src/source/inspect-hooks.ts +29 -21
- 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,
|
|
@@ -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
|
-
(
|
|
68
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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, 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
|
-
|
|
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
|
-
|
|
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);
|
|
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
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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
|
-
|
|
585
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
627
|
+
mountFiberTree(onRender, fiber, false);
|
|
672
628
|
} else if (wasMounted && isMounted) {
|
|
673
|
-
|
|
629
|
+
updateFiberTree(onRender, fiber, fiber.alternate);
|
|
674
630
|
} else if (wasMounted && !isMounted) {
|
|
675
631
|
unmountFiber(onRender, fiber);
|
|
676
632
|
}
|
|
677
633
|
} else {
|
|
678
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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))
|
|
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
|
|
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
|
|
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
|
|