bippy 0.6.1-dev.3702582 → 0.6.1-dev.4ed5147
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 +176 -483
- package/dist/core.cjs +1 -1
- package/dist/core.d.cts +7 -52
- package/dist/core.d.ts +7 -52
- package/dist/core.js +1 -1
- package/dist/core2.cjs +1 -1
- package/dist/core2.d.cts +2 -2
- package/dist/core2.d.ts +2 -2
- package/dist/core2.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +1 -1
- package/dist/rdt-hook.cjs +1 -1
- package/dist/rdt-hook.js +1 -1
- package/dist/source.cjs +8 -8
- package/dist/source.d.cts +3 -1
- package/dist/source.d.ts +3 -1
- package/dist/source.js +12 -12
- package/package.json +6 -3
- package/src/core.ts +37 -374
- package/src/index.ts +1 -0
- package/src/react.ts +76 -0
- package/src/source/get-display-name-from-source.ts +13 -12
- package/src/source/get-source.ts +34 -3
- package/src/source/index.ts +2 -0
- package/src/source/owner-stack.ts +6 -2
- package/src/source/parse-stack.ts +1 -1
- package/src/source/symbolication.ts +218 -54
- package/dist/index.iife.js +0 -9
- package/dist/install-hook-only.iife.js +0 -9
package/src/core.ts
CHANGED
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
import type * as React from "react";
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
|
-
ContextDependency,
|
|
7
6
|
Fiber,
|
|
8
7
|
FiberRoot,
|
|
9
8
|
HostFiber,
|
|
10
|
-
MemoizedState,
|
|
11
9
|
ReactDevToolsGlobalHook,
|
|
12
10
|
ReactRenderer,
|
|
13
11
|
} from "./react-internals/index.js";
|
|
@@ -20,7 +18,6 @@ import {
|
|
|
20
18
|
hasRDTHook,
|
|
21
19
|
isRealReactDevtools,
|
|
22
20
|
onRDTHookReplace,
|
|
23
|
-
onRendererInject,
|
|
24
21
|
} from "./rdt-hook.js";
|
|
25
22
|
import type { Unsubscribe } from "./rdt-hook.js";
|
|
26
23
|
import {
|
|
@@ -32,8 +29,6 @@ import {
|
|
|
32
29
|
setReactWorkTagsForFiber,
|
|
33
30
|
} from "./react-internals/index.js";
|
|
34
31
|
|
|
35
|
-
export { getReactWorkTags, ReactSymbols } from "./react-internals/index.js";
|
|
36
|
-
export type { ReactWorkTagMap, ReactWorkTagVersion } from "./react-internals/index.js";
|
|
37
32
|
export {
|
|
38
33
|
BippyError,
|
|
39
34
|
BippyHookInspectionError,
|
|
@@ -56,20 +51,10 @@ interface FiberSelector {
|
|
|
56
51
|
(node: Fiber): boolean | Promise<boolean | void> | void;
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
export interface FiberTimings {
|
|
60
|
-
selfTime: number;
|
|
61
|
-
totalTime: number;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
54
|
export interface RenderHandler {
|
|
65
55
|
(fiber: Fiber, phase: RenderPhase): unknown;
|
|
66
56
|
}
|
|
67
57
|
|
|
68
|
-
interface ValueWrite {
|
|
69
|
-
path: string[];
|
|
70
|
-
value: unknown;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
58
|
/**
|
|
74
59
|
* Returns `true` if object is a React Element.
|
|
75
60
|
*
|
|
@@ -148,90 +133,6 @@ export const isFiber = (maybeFiber: unknown): maybeFiber is Fiber => {
|
|
|
148
133
|
return "pendingProps" in maybeFiber && !("containerInfo" in maybeFiber);
|
|
149
134
|
};
|
|
150
135
|
|
|
151
|
-
/**
|
|
152
|
-
* Traverses up or down a {@link Fiber}'s contexts, return `true` to stop and select the current and previous context value.
|
|
153
|
-
*/
|
|
154
|
-
export const traverseContexts = (
|
|
155
|
-
fiber: Fiber,
|
|
156
|
-
selector: (
|
|
157
|
-
nextValue: ContextDependency<unknown> | null | undefined,
|
|
158
|
-
prevValue: ContextDependency<unknown> | null | undefined,
|
|
159
|
-
) => boolean | void,
|
|
160
|
-
): boolean => {
|
|
161
|
-
try {
|
|
162
|
-
const nextDependencies = fiber.dependencies;
|
|
163
|
-
const prevDependencies = fiber.alternate?.dependencies;
|
|
164
|
-
|
|
165
|
-
if (!nextDependencies || !prevDependencies) return false;
|
|
166
|
-
if (
|
|
167
|
-
typeof nextDependencies !== "object" ||
|
|
168
|
-
!("firstContext" in nextDependencies) ||
|
|
169
|
-
typeof prevDependencies !== "object" ||
|
|
170
|
-
!("firstContext" in prevDependencies)
|
|
171
|
-
) {
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
let nextContext: ContextDependency<unknown> | null | undefined = nextDependencies.firstContext;
|
|
175
|
-
let prevContext: ContextDependency<unknown> | null | undefined = prevDependencies.firstContext;
|
|
176
|
-
while (
|
|
177
|
-
(nextContext && typeof nextContext === "object" && "memoizedValue" in nextContext) ||
|
|
178
|
-
(prevContext && typeof prevContext === "object" && "memoizedValue" in prevContext)
|
|
179
|
-
) {
|
|
180
|
-
if (selector(nextContext, prevContext) === true) return true;
|
|
181
|
-
|
|
182
|
-
nextContext = nextContext?.next;
|
|
183
|
-
prevContext = prevContext?.next;
|
|
184
|
-
}
|
|
185
|
-
} catch {}
|
|
186
|
-
return false;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Traverses up or down a {@link Fiber}'s states, return `true` to stop and select the current and previous state value. This stores both state values and effects.
|
|
191
|
-
*/
|
|
192
|
-
export const traverseState = (
|
|
193
|
-
fiber: Fiber,
|
|
194
|
-
selector: (
|
|
195
|
-
nextValue: MemoizedState | null | undefined,
|
|
196
|
-
prevValue: MemoizedState | null | undefined,
|
|
197
|
-
) => boolean | void,
|
|
198
|
-
): boolean => {
|
|
199
|
-
try {
|
|
200
|
-
let nextState: MemoizedState | null | undefined = fiber.memoizedState;
|
|
201
|
-
let prevState: MemoizedState | null | undefined = fiber.alternate?.memoizedState;
|
|
202
|
-
|
|
203
|
-
while (nextState || prevState) {
|
|
204
|
-
if (selector(nextState, prevState) === true) return true;
|
|
205
|
-
|
|
206
|
-
nextState = nextState?.next;
|
|
207
|
-
prevState = prevState?.next;
|
|
208
|
-
}
|
|
209
|
-
} catch {}
|
|
210
|
-
return false;
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Traverses up or down a {@link Fiber}'s props, return `true` to stop and select the current and previous props value.
|
|
215
|
-
*/
|
|
216
|
-
export const traverseProps = (
|
|
217
|
-
fiber: Fiber,
|
|
218
|
-
selector: (propName: string, nextValue: unknown, prevValue: unknown) => boolean | void,
|
|
219
|
-
): boolean => {
|
|
220
|
-
try {
|
|
221
|
-
const nextProps = fiber.memoizedProps;
|
|
222
|
-
const prevProps = fiber.alternate?.memoizedProps || {};
|
|
223
|
-
|
|
224
|
-
for (const propName of Object.keys(nextProps)) {
|
|
225
|
-
if (selector(propName, nextProps[propName], prevProps[propName]) === true) return true;
|
|
226
|
-
}
|
|
227
|
-
for (const propName of Object.keys(prevProps)) {
|
|
228
|
-
if (propName in nextProps) continue;
|
|
229
|
-
if (selector(propName, nextProps[propName], prevProps[propName]) === true) return true;
|
|
230
|
-
}
|
|
231
|
-
} catch {}
|
|
232
|
-
return false;
|
|
233
|
-
};
|
|
234
|
-
|
|
235
136
|
/**
|
|
236
137
|
* Returns `true` if the {@link Fiber} has rendered. Note that this does not mean the fiber has rendered in the current commit, just that it has rendered in the past.
|
|
237
138
|
*/
|
|
@@ -271,46 +172,6 @@ export const didFiberCommit = (fiber: Fiber): boolean => {
|
|
|
271
172
|
);
|
|
272
173
|
};
|
|
273
174
|
|
|
274
|
-
/**
|
|
275
|
-
* Returns all host {@link Fiber}s that have committed and rendered.
|
|
276
|
-
*/
|
|
277
|
-
export const getMutatedHostFibers = (fiber: Fiber): Fiber[] => {
|
|
278
|
-
const mutations: Fiber[] = [];
|
|
279
|
-
const stack: Fiber[] = [fiber];
|
|
280
|
-
|
|
281
|
-
while (stack.length) {
|
|
282
|
-
const node = stack.pop();
|
|
283
|
-
if (!node) continue;
|
|
284
|
-
|
|
285
|
-
if (isHostFiber(node) && didFiberCommit(node) && didFiberRender(node)) {
|
|
286
|
-
mutations.push(node);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (node.child) stack.push(node.child);
|
|
290
|
-
if (node.sibling) stack.push(node.sibling);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return mutations;
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Returns the stack of {@link Fiber}s from the current fiber to the root fiber.
|
|
298
|
-
*
|
|
299
|
-
* @example
|
|
300
|
-
* ```ts
|
|
301
|
-
* [fiber, fiber.return, fiber.return.return, ...]
|
|
302
|
-
* ```
|
|
303
|
-
*/
|
|
304
|
-
export const getFiberStack = (fiber: Fiber): Fiber[] => {
|
|
305
|
-
const stack: Fiber[] = [];
|
|
306
|
-
let currentFiber = fiber;
|
|
307
|
-
while (currentFiber.return) {
|
|
308
|
-
stack.push(currentFiber);
|
|
309
|
-
currentFiber = currentFiber.return;
|
|
310
|
-
}
|
|
311
|
-
return stack;
|
|
312
|
-
};
|
|
313
|
-
|
|
314
175
|
/**
|
|
315
176
|
* Returns `true` if the {@link Fiber} should be filtered out during reconciliation.
|
|
316
177
|
*/
|
|
@@ -359,47 +220,6 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
359
220
|
}
|
|
360
221
|
};
|
|
361
222
|
|
|
362
|
-
/**
|
|
363
|
-
* Returns the nearest host {@link Fiber} to the current {@link Fiber}.
|
|
364
|
-
*/
|
|
365
|
-
export const getNearestHostFiber = (fiber: Fiber, ascending = false): Fiber | null => {
|
|
366
|
-
let hostFiber = traverseFiber(fiber, isHostFiber, ascending);
|
|
367
|
-
if (!hostFiber) {
|
|
368
|
-
hostFiber = traverseFiber(fiber, isHostFiber, !ascending);
|
|
369
|
-
}
|
|
370
|
-
return hostFiber;
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Returns all host {@link Fiber}s in the tree that are associated with the current {@link Fiber}.
|
|
375
|
-
*/
|
|
376
|
-
export const getNearestHostFibers = (fiber: Fiber): Fiber[] => {
|
|
377
|
-
const hostFibers: Fiber[] = [];
|
|
378
|
-
const stack: Fiber[] = [];
|
|
379
|
-
|
|
380
|
-
if (isHostFiber(fiber)) {
|
|
381
|
-
hostFibers.push(fiber);
|
|
382
|
-
} else if (fiber.child) {
|
|
383
|
-
stack.push(fiber.child);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
while (stack.length) {
|
|
387
|
-
const currentNode = stack.pop();
|
|
388
|
-
if (!currentNode) break;
|
|
389
|
-
if (isHostFiber(currentNode)) {
|
|
390
|
-
hostFibers.push(currentNode);
|
|
391
|
-
} else if (currentNode.child) {
|
|
392
|
-
stack.push(currentNode.child);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
if (currentNode.sibling) {
|
|
396
|
-
stack.push(currentNode.sibling);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
return hostFibers;
|
|
401
|
-
};
|
|
402
|
-
|
|
403
223
|
/**
|
|
404
224
|
* Traverses up or down a {@link Fiber}, return `true` to stop and select a node.
|
|
405
225
|
*/
|
|
@@ -468,27 +288,6 @@ const traverseFiberSiblings = (
|
|
|
468
288
|
return match ?? traverseFiberSiblings(nextSibling, selector, ascending);
|
|
469
289
|
};
|
|
470
290
|
|
|
471
|
-
/**
|
|
472
|
-
* Returns the timings of the {@link Fiber}.
|
|
473
|
-
*
|
|
474
|
-
* @example
|
|
475
|
-
* ```ts
|
|
476
|
-
* const { selfTime, totalTime } = getTimings(fiber);
|
|
477
|
-
* console.log(selfTime, totalTime);
|
|
478
|
-
* ```
|
|
479
|
-
*/
|
|
480
|
-
export const getTimings = (fiber?: Fiber | null): FiberTimings => {
|
|
481
|
-
const totalTime = fiber?.actualDuration ?? 0;
|
|
482
|
-
let selfTime = totalTime;
|
|
483
|
-
// TODO: calculate a DOM time, which is just host component summed up
|
|
484
|
-
let child = fiber?.child ?? null;
|
|
485
|
-
while (totalTime > 0 && child !== null) {
|
|
486
|
-
selfTime -= child.actualDuration ?? 0;
|
|
487
|
-
child = child.sibling;
|
|
488
|
-
}
|
|
489
|
-
return { selfTime, totalTime };
|
|
490
|
-
};
|
|
491
|
-
|
|
492
291
|
/**
|
|
493
292
|
* Returns `true` if the {@link Fiber} uses React Compiler's memo cache.
|
|
494
293
|
*/
|
|
@@ -542,6 +341,7 @@ export const isInstrumentationActive = (): boolean => {
|
|
|
542
341
|
};
|
|
543
342
|
|
|
544
343
|
export const _fiberRoots = new Set<FiberRoot>();
|
|
344
|
+
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
545
345
|
|
|
546
346
|
/**
|
|
547
347
|
* Returns the latest fiber (since it may be double-buffered).
|
|
@@ -561,6 +361,25 @@ export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
|
561
361
|
return fiber;
|
|
562
362
|
};
|
|
563
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Returns the renderer that owns the {@link Fiber}.
|
|
366
|
+
*/
|
|
367
|
+
export const getRenderer = (fiber: Fiber): ReactRenderer | null => {
|
|
368
|
+
if (!hasRDTHook()) return null;
|
|
369
|
+
|
|
370
|
+
let rootFiber = fiber;
|
|
371
|
+
while (rootFiber.return) {
|
|
372
|
+
rootFiber = rootFiber.return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const fiberRoot = rootFiber.stateNode;
|
|
376
|
+
if (!isFiberRoot(fiberRoot)) return null;
|
|
377
|
+
|
|
378
|
+
const rendererID = rootRendererIds.get(fiberRoot);
|
|
379
|
+
if (rendererID === undefined) return null;
|
|
380
|
+
return getRDTHook().renderers.get(rendererID) ?? null;
|
|
381
|
+
};
|
|
382
|
+
|
|
564
383
|
export type RenderPhase = "mount" | "unmount" | "update";
|
|
565
384
|
|
|
566
385
|
let nextFiberId = 0;
|
|
@@ -806,168 +625,6 @@ export const traverseRenderedFibers = (root: Fiber | FiberRoot, onRender: Render
|
|
|
806
625
|
rootInstance.prevFiber = fiber;
|
|
807
626
|
};
|
|
808
627
|
|
|
809
|
-
const overrideRenderers = new Set<ReactRenderer>();
|
|
810
|
-
let areOverrideRenderersWired = false;
|
|
811
|
-
|
|
812
|
-
const wireOverrideRenderers = (): void => {
|
|
813
|
-
if (!hasRDTHook()) return;
|
|
814
|
-
const rdtHook = getRDTHook();
|
|
815
|
-
|
|
816
|
-
setHookEventDispatchers(rdtHook);
|
|
817
|
-
for (const renderer of rdtHook.renderers.values()) {
|
|
818
|
-
overrideRenderers.add(renderer);
|
|
819
|
-
}
|
|
820
|
-
if (areOverrideRenderersWired) return;
|
|
821
|
-
areOverrideRenderersWired = true;
|
|
822
|
-
onRendererInject((renderer) => {
|
|
823
|
-
overrideRenderers.add(renderer);
|
|
824
|
-
});
|
|
825
|
-
};
|
|
826
|
-
|
|
827
|
-
const getRootRenderer = (fiber: Fiber): ReactRenderer | null => {
|
|
828
|
-
if (!hasRDTHook()) return null;
|
|
829
|
-
let hostRootFiber = fiber;
|
|
830
|
-
while (hostRootFiber.return) {
|
|
831
|
-
hostRootFiber = hostRootFiber.return;
|
|
832
|
-
}
|
|
833
|
-
const fiberRoot = hostRootFiber.stateNode;
|
|
834
|
-
if (!isFiberRoot(fiberRoot)) return null;
|
|
835
|
-
const rendererId = rootRendererIds.get(fiberRoot);
|
|
836
|
-
if (rendererId === undefined) return null;
|
|
837
|
-
return getRDTHook().renderers.get(rendererId) ?? null;
|
|
838
|
-
};
|
|
839
|
-
|
|
840
|
-
const resolveOverrideRenderers = (fiber: Fiber): ReactRenderer[] => {
|
|
841
|
-
wireOverrideRenderers();
|
|
842
|
-
const rootRenderer = getRootRenderer(fiber);
|
|
843
|
-
if (rootRenderer) return [rootRenderer];
|
|
844
|
-
return Array.from(overrideRenderers);
|
|
845
|
-
};
|
|
846
|
-
|
|
847
|
-
const applyPropsOverride = (
|
|
848
|
-
renderers: ReactRenderer[],
|
|
849
|
-
fiber: Fiber,
|
|
850
|
-
path: string[],
|
|
851
|
-
value: unknown,
|
|
852
|
-
): void => {
|
|
853
|
-
for (const renderer of renderers) {
|
|
854
|
-
try {
|
|
855
|
-
renderer.overrideProps?.(fiber, path, value);
|
|
856
|
-
} catch {}
|
|
857
|
-
}
|
|
858
|
-
};
|
|
859
|
-
|
|
860
|
-
const getHookStateDispatch = (
|
|
861
|
-
fiber: Fiber,
|
|
862
|
-
hookIndex: number,
|
|
863
|
-
): ((value: unknown) => void) | null => {
|
|
864
|
-
let hookState = fiber.memoizedState;
|
|
865
|
-
for (let currentHookIndex = 0; currentHookIndex < hookIndex; currentHookIndex++) {
|
|
866
|
-
if (!hookState?.next) return null;
|
|
867
|
-
hookState = hookState.next;
|
|
868
|
-
}
|
|
869
|
-
const queue = hookState?.queue;
|
|
870
|
-
if (!isPOJO(queue)) return null;
|
|
871
|
-
const dispatch = queue.dispatch;
|
|
872
|
-
return typeof dispatch === "function" ? (value) => dispatch(value) : null;
|
|
873
|
-
};
|
|
874
|
-
|
|
875
|
-
const findContextProviderFiber = (fiber: Fiber, contextType: unknown): Fiber | null => {
|
|
876
|
-
let currentFiber: Fiber | null = fiber;
|
|
877
|
-
while (currentFiber) {
|
|
878
|
-
const fiberType = currentFiber.type;
|
|
879
|
-
if (fiberType === contextType || fiberType?.Provider === contextType) {
|
|
880
|
-
return currentFiber;
|
|
881
|
-
}
|
|
882
|
-
currentFiber = currentFiber.return;
|
|
883
|
-
}
|
|
884
|
-
return null;
|
|
885
|
-
};
|
|
886
|
-
|
|
887
|
-
const isPOJO = (maybePOJO: unknown): maybePOJO is Record<string, unknown> => {
|
|
888
|
-
return (
|
|
889
|
-
Object.prototype.toString.call(maybePOJO) === "[object Object]" &&
|
|
890
|
-
(Object.getPrototypeOf(maybePOJO) === Object.prototype ||
|
|
891
|
-
Object.getPrototypeOf(maybePOJO) === null)
|
|
892
|
-
);
|
|
893
|
-
};
|
|
894
|
-
|
|
895
|
-
const buildPathsFromValue = (
|
|
896
|
-
maybePOJO: Record<string, unknown>,
|
|
897
|
-
basePath: string[] = [],
|
|
898
|
-
ancestors = new WeakSet<object>(),
|
|
899
|
-
): ValueWrite[] => {
|
|
900
|
-
if (ancestors.has(maybePOJO)) {
|
|
901
|
-
return [{ path: basePath, value: maybePOJO }];
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
ancestors.add(maybePOJO);
|
|
905
|
-
const paths: ValueWrite[] = [];
|
|
906
|
-
|
|
907
|
-
for (const [key, value] of Object.entries(maybePOJO)) {
|
|
908
|
-
const path = basePath.concat(key);
|
|
909
|
-
|
|
910
|
-
if (isPOJO(value)) {
|
|
911
|
-
paths.push(...buildPathsFromValue(value, path, ancestors));
|
|
912
|
-
} else {
|
|
913
|
-
paths.push({ path, value });
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
ancestors.delete(maybePOJO);
|
|
918
|
-
return paths;
|
|
919
|
-
};
|
|
920
|
-
|
|
921
|
-
const buildValueWrites = (partialValue: unknown): ValueWrite[] =>
|
|
922
|
-
isPOJO(partialValue) ? buildPathsFromValue(partialValue) : [{ path: [], value: partialValue }];
|
|
923
|
-
|
|
924
|
-
export const overrideProps = (fiber: Fiber, partialValue: Record<string, unknown>) => {
|
|
925
|
-
const renderers = resolveOverrideRenderers(fiber);
|
|
926
|
-
for (const { path, value } of buildValueWrites(partialValue)) {
|
|
927
|
-
applyPropsOverride(renderers, fiber, path, value);
|
|
928
|
-
}
|
|
929
|
-
};
|
|
930
|
-
|
|
931
|
-
export const overrideHookState = (fiber: Fiber, id: number, partialValue: unknown) => {
|
|
932
|
-
const renderers = resolveOverrideRenderers(fiber).filter((renderer) =>
|
|
933
|
-
Boolean(renderer.overrideHookState),
|
|
934
|
-
);
|
|
935
|
-
|
|
936
|
-
if (renderers.length > 0) {
|
|
937
|
-
const writes = buildValueWrites(partialValue);
|
|
938
|
-
for (const renderer of renderers) {
|
|
939
|
-
for (const { path, value } of writes) {
|
|
940
|
-
try {
|
|
941
|
-
renderer.overrideHookState?.(fiber, id, path, value);
|
|
942
|
-
} catch {}
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
return;
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
// production renderers don't expose overrideHookState; dispatching through
|
|
949
|
-
// the hook's own queue still works there, but only for whole-value writes
|
|
950
|
-
// (a path write through dispatch would replace the entire hook state)
|
|
951
|
-
if (isPOJO(partialValue)) return;
|
|
952
|
-
const dispatch = getHookStateDispatch(fiber, id);
|
|
953
|
-
if (!dispatch) return;
|
|
954
|
-
try {
|
|
955
|
-
dispatch(partialValue);
|
|
956
|
-
} catch {}
|
|
957
|
-
};
|
|
958
|
-
|
|
959
|
-
export const overrideContext = (fiber: Fiber, contextType: unknown, partialValue: unknown) => {
|
|
960
|
-
const providerFiber = findContextProviderFiber(fiber, contextType);
|
|
961
|
-
if (!providerFiber) return;
|
|
962
|
-
const renderers = resolveOverrideRenderers(providerFiber);
|
|
963
|
-
for (const { path, value } of buildValueWrites(partialValue)) {
|
|
964
|
-
applyPropsOverride(renderers, providerFiber, ["value", ...path], value);
|
|
965
|
-
if (providerFiber.alternate) {
|
|
966
|
-
applyPropsOverride(renderers, providerFiber.alternate, ["value", ...path], value);
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
};
|
|
970
|
-
|
|
971
628
|
export interface InstrumentationOptions {
|
|
972
629
|
name?: string;
|
|
973
630
|
onActive?: () => unknown;
|
|
@@ -998,8 +655,6 @@ interface HookDispatchers {
|
|
|
998
655
|
const hookDispatchers = new WeakMap<ReactDevToolsGlobalHook, Partial<HookDispatchers>>();
|
|
999
656
|
let didSubscribeToHookReplacements = false;
|
|
1000
657
|
|
|
1001
|
-
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
1002
|
-
|
|
1003
658
|
// each hook event is dispatched from a single re-installable wrapper. If
|
|
1004
659
|
// something overwrites the hook method (devtools, direct assignment), the
|
|
1005
660
|
// next instrument() call installs a fresh wrapper over it; a superseded
|
|
@@ -1123,6 +778,18 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
1123
778
|
}
|
|
1124
779
|
};
|
|
1125
780
|
|
|
781
|
+
const wireHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
782
|
+
if (!didSubscribeToHookReplacements) {
|
|
783
|
+
onRDTHookReplace(setHookEventDispatchers);
|
|
784
|
+
didSubscribeToHookReplacements = true;
|
|
785
|
+
}
|
|
786
|
+
setHookEventDispatchers(rdtHook);
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
if (hasRDTHook()) {
|
|
790
|
+
wireHookEventDispatchers(getRDTHook());
|
|
791
|
+
}
|
|
792
|
+
|
|
1126
793
|
/**
|
|
1127
794
|
* Instruments the DevTools hook. Each hook event is patched once and
|
|
1128
795
|
* dispatches to a set of listeners, so multiple `instrument` calls compose
|
|
@@ -1142,15 +809,9 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
1142
809
|
*/
|
|
1143
810
|
export const instrument = (options: InstrumentationOptions): Unsubscribe => {
|
|
1144
811
|
const rdtHook = getRDTHook(options.onActive);
|
|
1145
|
-
|
|
1146
|
-
if (!didSubscribeToHookReplacements) {
|
|
1147
|
-
onRDTHookReplace(setHookEventDispatchers);
|
|
1148
|
-
didSubscribeToHookReplacements = true;
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
812
|
rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
|
|
1152
813
|
|
|
1153
|
-
|
|
814
|
+
wireHookEventDispatchers(rdtHook);
|
|
1154
815
|
const subscription: InstrumentationSubscription = { options };
|
|
1155
816
|
instrumentationSubscriptions.add(subscription);
|
|
1156
817
|
|
|
@@ -1199,7 +860,7 @@ const getPublicHostInstance = (stateNode: unknown): unknown => {
|
|
|
1199
860
|
return typeof nativeTag === "number" ? nativeTag : stateNode;
|
|
1200
861
|
};
|
|
1201
862
|
|
|
1202
|
-
export const
|
|
863
|
+
export const getFiber = <T>(hostInstance: T): Fiber | null => {
|
|
1203
864
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
1204
865
|
if (rdtHook?.renderers) {
|
|
1205
866
|
for (const renderer of rdtHook.renderers.values()) {
|
|
@@ -1249,6 +910,8 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1249
910
|
return null;
|
|
1250
911
|
};
|
|
1251
912
|
|
|
913
|
+
export const getFiberFromHostInstance = getFiber;
|
|
914
|
+
|
|
1252
915
|
export {
|
|
1253
916
|
BIPPY_INSTRUMENTATION_STRING,
|
|
1254
917
|
_onActiveListeners,
|
|
@@ -1262,4 +925,4 @@ export {
|
|
|
1262
925
|
version,
|
|
1263
926
|
} from "./rdt-hook.js";
|
|
1264
927
|
export type { Unsubscribe } from "./rdt-hook.js";
|
|
1265
|
-
export
|
|
928
|
+
export * from "./react-internals/index.js";
|
package/src/index.ts
CHANGED
package/src/react.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import "./install-hook-only.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { isValidFiber } from "./core.js";
|
|
4
|
+
import type { Fiber } from "./react-internals/index.js";
|
|
5
|
+
|
|
6
|
+
export type { Fiber } from "./react-internals/index.js";
|
|
7
|
+
|
|
8
|
+
const preserveState = (state: undefined): undefined => state;
|
|
9
|
+
const readEmptySnapshot = (): undefined => undefined;
|
|
10
|
+
const unsubscribeFromEmptyStore = (): void => {};
|
|
11
|
+
const subscribeToEmptyStore = (): (() => void) => unsubscribeFromEmptyStore;
|
|
12
|
+
const useSyncExternalStore: unknown = Reflect.get(React, "useSyncExternalStore");
|
|
13
|
+
|
|
14
|
+
const captureFiberFromHook = (useCaptureHook: () => void): Fiber | null => {
|
|
15
|
+
const originalBind = Function.prototype.bind;
|
|
16
|
+
let capturedFiber: Fiber | null = null;
|
|
17
|
+
// HACK: React binds hook callbacks to the rendering Fiber in production but exposes no public API for it.
|
|
18
|
+
const bindProxy = new Proxy(originalBind, {
|
|
19
|
+
apply: (bind, functionToBind, boundArguments) => {
|
|
20
|
+
const fiber = boundArguments[1];
|
|
21
|
+
if (!capturedFiber && isValidFiber(fiber)) {
|
|
22
|
+
capturedFiber = fiber;
|
|
23
|
+
}
|
|
24
|
+
return Reflect.apply(bind, functionToBind, boundArguments);
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
Reflect.set(Function.prototype, "bind", bindProxy);
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
useCaptureHook();
|
|
31
|
+
} finally {
|
|
32
|
+
if (Function.prototype.bind === bindProxy) {
|
|
33
|
+
Reflect.set(Function.prototype, "bind", originalBind);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return capturedFiber;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const useExternalStoreCapture = (): void => {
|
|
41
|
+
Reflect.apply(useSyncExternalStore, React, [
|
|
42
|
+
subscribeToEmptyStore,
|
|
43
|
+
readEmptySnapshot,
|
|
44
|
+
readEmptySnapshot,
|
|
45
|
+
]);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const useReducerCapture = (): void => {
|
|
49
|
+
React.useReducer(preserveState, undefined);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const useFiberWithExternalStore = (): Fiber | undefined =>
|
|
53
|
+
captureFiberFromHook(useExternalStoreCapture) ?? undefined;
|
|
54
|
+
|
|
55
|
+
const useFiberWithReducer = (): Fiber | undefined => {
|
|
56
|
+
const committedFiberRef = React.useRef<Fiber | null>(null);
|
|
57
|
+
const renderedFiberRef = React.useRef<Fiber | null>(null);
|
|
58
|
+
const hookFiber = captureFiberFromHook(useReducerCapture);
|
|
59
|
+
const fiber =
|
|
60
|
+
hookFiber ??
|
|
61
|
+
(renderedFiberRef.current !== committedFiberRef.current
|
|
62
|
+
? renderedFiberRef.current
|
|
63
|
+
: committedFiberRef.current?.alternate) ??
|
|
64
|
+
committedFiberRef.current;
|
|
65
|
+
|
|
66
|
+
renderedFiberRef.current = fiber;
|
|
67
|
+
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
committedFiberRef.current = fiber;
|
|
70
|
+
}, [fiber]);
|
|
71
|
+
|
|
72
|
+
return fiber ?? undefined;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const useFiber =
|
|
76
|
+
typeof useSyncExternalStore === "function" ? useFiberWithExternalStore : useFiberWithReducer;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Fiber } from "../react-internals/index.js";
|
|
1
|
+
import type { Fiber } from "../react-internals/index.js";
|
|
2
2
|
import { getDisplayName } from "../core.js";
|
|
3
|
-
import { getRawParentStack } from "./owner-stack.js";
|
|
3
|
+
import { getDefinitionFrameFromOwnedChild, getRawParentStack } from "./owner-stack.js";
|
|
4
4
|
import {
|
|
5
5
|
getSourceContentFromSourceMap,
|
|
6
6
|
getSourceFromSourceMap,
|
|
7
7
|
getSourceMap,
|
|
8
8
|
type SourceFetch,
|
|
9
9
|
} from "./symbolication.js";
|
|
10
|
-
import { StackFrame } from "./parse-stack.js";
|
|
10
|
+
import type { StackFrame } from "./parse-stack.js";
|
|
11
11
|
|
|
12
12
|
const COMPONENT_DECLARATION_PATTERNS = [
|
|
13
13
|
/(?:^|export\s+)(?:const|let|var)\s+(\w+)\s*=/,
|
|
@@ -25,9 +25,6 @@ const findComponentDeclarationOnLine = (line: string): string | null => {
|
|
|
25
25
|
|
|
26
26
|
const MAX_DECLARATION_LINE_DISTANCE = 5;
|
|
27
27
|
|
|
28
|
-
// the frame points inside the component, so the nearest declaration wins;
|
|
29
|
-
// searching by pattern over the whole window instead would let an adjacent
|
|
30
|
-
// component's declaration shadow the right one
|
|
31
28
|
const extractComponentNameFromSource = (
|
|
32
29
|
sourceContent: string,
|
|
33
30
|
lineNumber: number,
|
|
@@ -59,7 +56,9 @@ export const getDisplayNameFromSource = async (
|
|
|
59
56
|
cache = true,
|
|
60
57
|
fetchFn?: SourceFetch,
|
|
61
58
|
): Promise<string | null> => {
|
|
62
|
-
const stackFrame =
|
|
59
|
+
const stackFrame =
|
|
60
|
+
getDefinitionFrameFromOwnedChild(fiber) ??
|
|
61
|
+
getRawParentStack(fiber).find((innerFrame) => innerFrame.fileName);
|
|
63
62
|
|
|
64
63
|
if (!stackFrame?.fileName) {
|
|
65
64
|
return getDisplayName(fiber.type);
|
|
@@ -86,11 +85,13 @@ export const getDisplayNameFromSource = async (
|
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
const sourceContent = getSourceContentFromSourceMap(bundleSourceMap, source.fileName);
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
const extractedName = sourceContent
|
|
89
|
+
? extractComponentNameFromSource(sourceContent, source.lineNumber)
|
|
90
|
+
: null;
|
|
91
|
+
|
|
92
|
+
if (extractedName) {
|
|
93
|
+
return extractedName;
|
|
91
94
|
}
|
|
92
95
|
|
|
93
|
-
return (
|
|
94
|
-
extractComponentNameFromSource(sourceContent, source.lineNumber) ?? getDisplayName(fiber.type)
|
|
95
|
-
);
|
|
96
|
+
return source.functionName ?? getDisplayName(fiber.type);
|
|
96
97
|
};
|