bippy 0.6.1-dev.93556ef → 0.6.1-dev.94e5797
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 +173 -476
- package/dist/core.cjs +1 -1
- package/dist/core.d.cts +8 -57
- package/dist/core.d.ts +8 -57
- 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 +5 -5
- package/dist/source.js +6 -6
- package/package.json +6 -3
- package/src/core.ts +43 -390
- package/src/index.ts +1 -0
- package/src/react.ts +76 -0
- 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
|
*
|
|
@@ -85,7 +70,7 @@ export const isValidElement = (element: unknown): element is React.ReactElement
|
|
|
85
70
|
/**
|
|
86
71
|
* Returns `true` if object is a React Fiber.
|
|
87
72
|
*/
|
|
88
|
-
export const
|
|
73
|
+
export const isFiber = (fiber: unknown): fiber is Fiber =>
|
|
89
74
|
typeof fiber === "object" &&
|
|
90
75
|
fiber !== null &&
|
|
91
76
|
"tag" in fiber &&
|
|
@@ -99,7 +84,7 @@ const isFiberRoot = (fiberRoot: unknown): fiberRoot is FiberRoot =>
|
|
|
99
84
|
typeof fiberRoot === "object" &&
|
|
100
85
|
fiberRoot !== null &&
|
|
101
86
|
"current" in fiberRoot &&
|
|
102
|
-
|
|
87
|
+
isFiber(fiberRoot.current);
|
|
103
88
|
|
|
104
89
|
/**
|
|
105
90
|
* Returns `true` if fiber is a host fiber. Host fibers are DOM nodes in react-dom, `View` in react-native, etc.
|
|
@@ -138,100 +123,6 @@ export const isCompositeFiber = (fiber: Fiber): boolean => {
|
|
|
138
123
|
}
|
|
139
124
|
};
|
|
140
125
|
|
|
141
|
-
/**
|
|
142
|
-
* Returns `true` if the object is a {@link Fiber}
|
|
143
|
-
*/
|
|
144
|
-
export const isFiber = (maybeFiber: unknown): maybeFiber is Fiber => {
|
|
145
|
-
if (!maybeFiber || typeof maybeFiber !== "object") return false;
|
|
146
|
-
// this is a fast check. pendingProps will ALWAYS exist in fiber
|
|
147
|
-
// `containerInfo` is in FiberRootNode, not FiberNode
|
|
148
|
-
return "pendingProps" in maybeFiber && !("containerInfo" in maybeFiber);
|
|
149
|
-
};
|
|
150
|
-
|
|
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
126
|
/**
|
|
236
127
|
* 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
128
|
*/
|
|
@@ -271,46 +162,6 @@ export const didFiberCommit = (fiber: Fiber): boolean => {
|
|
|
271
162
|
);
|
|
272
163
|
};
|
|
273
164
|
|
|
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
165
|
/**
|
|
315
166
|
* Returns `true` if the {@link Fiber} should be filtered out during reconciliation.
|
|
316
167
|
*/
|
|
@@ -359,47 +210,6 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
359
210
|
}
|
|
360
211
|
};
|
|
361
212
|
|
|
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
213
|
/**
|
|
404
214
|
* Traverses up or down a {@link Fiber}, return `true` to stop and select a node.
|
|
405
215
|
*/
|
|
@@ -468,27 +278,6 @@ const traverseFiberSiblings = (
|
|
|
468
278
|
return match ?? traverseFiberSiblings(nextSibling, selector, ascending);
|
|
469
279
|
};
|
|
470
280
|
|
|
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
281
|
/**
|
|
493
282
|
* Returns `true` if the {@link Fiber} uses React Compiler's memo cache.
|
|
494
283
|
*/
|
|
@@ -542,6 +331,7 @@ export const isInstrumentationActive = (): boolean => {
|
|
|
542
331
|
};
|
|
543
332
|
|
|
544
333
|
export const _fiberRoots = new Set<FiberRoot>();
|
|
334
|
+
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
545
335
|
|
|
546
336
|
/**
|
|
547
337
|
* Returns the latest fiber (since it may be double-buffered).
|
|
@@ -561,6 +351,25 @@ export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
|
561
351
|
return fiber;
|
|
562
352
|
};
|
|
563
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Returns the renderer that owns the {@link Fiber}.
|
|
356
|
+
*/
|
|
357
|
+
export const getRenderer = (fiber: Fiber): ReactRenderer | null => {
|
|
358
|
+
if (!hasRDTHook()) return null;
|
|
359
|
+
|
|
360
|
+
let rootFiber = fiber;
|
|
361
|
+
while (rootFiber.return) {
|
|
362
|
+
rootFiber = rootFiber.return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const fiberRoot = rootFiber.stateNode;
|
|
366
|
+
if (!isFiberRoot(fiberRoot)) return null;
|
|
367
|
+
|
|
368
|
+
const rendererID = rootRendererIds.get(fiberRoot);
|
|
369
|
+
if (rendererID === undefined) return null;
|
|
370
|
+
return getRDTHook().renderers.get(rendererID) ?? null;
|
|
371
|
+
};
|
|
372
|
+
|
|
564
373
|
export type RenderPhase = "mount" | "unmount" | "update";
|
|
565
374
|
|
|
566
375
|
let nextFiberId = 0;
|
|
@@ -806,168 +615,6 @@ export const traverseRenderedFibers = (root: Fiber | FiberRoot, onRender: Render
|
|
|
806
615
|
rootInstance.prevFiber = fiber;
|
|
807
616
|
};
|
|
808
617
|
|
|
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
618
|
export interface InstrumentationOptions {
|
|
972
619
|
name?: string;
|
|
973
620
|
onActive?: () => unknown;
|
|
@@ -998,8 +645,6 @@ interface HookDispatchers {
|
|
|
998
645
|
const hookDispatchers = new WeakMap<ReactDevToolsGlobalHook, Partial<HookDispatchers>>();
|
|
999
646
|
let didSubscribeToHookReplacements = false;
|
|
1000
647
|
|
|
1001
|
-
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
1002
|
-
|
|
1003
648
|
// each hook event is dispatched from a single re-installable wrapper. If
|
|
1004
649
|
// something overwrites the hook method (devtools, direct assignment), the
|
|
1005
650
|
// next instrument() call installs a fresh wrapper over it; a superseded
|
|
@@ -1123,6 +768,18 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
1123
768
|
}
|
|
1124
769
|
};
|
|
1125
770
|
|
|
771
|
+
const wireHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
772
|
+
if (!didSubscribeToHookReplacements) {
|
|
773
|
+
onRDTHookReplace(setHookEventDispatchers);
|
|
774
|
+
didSubscribeToHookReplacements = true;
|
|
775
|
+
}
|
|
776
|
+
setHookEventDispatchers(rdtHook);
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
if (hasRDTHook()) {
|
|
780
|
+
wireHookEventDispatchers(getRDTHook());
|
|
781
|
+
}
|
|
782
|
+
|
|
1126
783
|
/**
|
|
1127
784
|
* Instruments the DevTools hook. Each hook event is patched once and
|
|
1128
785
|
* dispatches to a set of listeners, so multiple `instrument` calls compose
|
|
@@ -1142,15 +799,9 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
1142
799
|
*/
|
|
1143
800
|
export const instrument = (options: InstrumentationOptions): Unsubscribe => {
|
|
1144
801
|
const rdtHook = getRDTHook(options.onActive);
|
|
1145
|
-
|
|
1146
|
-
if (!didSubscribeToHookReplacements) {
|
|
1147
|
-
onRDTHookReplace(setHookEventDispatchers);
|
|
1148
|
-
didSubscribeToHookReplacements = true;
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
802
|
rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
|
|
1152
803
|
|
|
1153
|
-
|
|
804
|
+
wireHookEventDispatchers(rdtHook);
|
|
1154
805
|
const subscription: InstrumentationSubscription = { options };
|
|
1155
806
|
instrumentationSubscriptions.add(subscription);
|
|
1156
807
|
|
|
@@ -1176,14 +827,14 @@ const getLegacyRootFiber = (hostInstance: object): Fiber | null => {
|
|
|
1176
827
|
const current = Reflect.get(internalRoot, "current");
|
|
1177
828
|
if (typeof current !== "object" || current === null) return null;
|
|
1178
829
|
const child = Reflect.get(current, "child");
|
|
1179
|
-
return
|
|
830
|
+
return isFiber(child) ? child : null;
|
|
1180
831
|
};
|
|
1181
832
|
|
|
1182
833
|
const getInternalInstanceHandle = (hostInstance: object): Fiber | null => {
|
|
1183
834
|
const internalInstanceHandle =
|
|
1184
835
|
Reflect.get(hostInstance, "__internalInstanceHandle") ??
|
|
1185
836
|
Reflect.get(hostInstance, "_internalInstanceHandle");
|
|
1186
|
-
return
|
|
837
|
+
return isFiber(internalInstanceHandle) ? internalInstanceHandle : null;
|
|
1187
838
|
};
|
|
1188
839
|
|
|
1189
840
|
const getPublicHostInstance = (stateNode: unknown): unknown => {
|
|
@@ -1199,7 +850,7 @@ const getPublicHostInstance = (stateNode: unknown): unknown => {
|
|
|
1199
850
|
return typeof nativeTag === "number" ? nativeTag : stateNode;
|
|
1200
851
|
};
|
|
1201
852
|
|
|
1202
|
-
export const
|
|
853
|
+
export const getFiber = <T>(hostInstance: T): Fiber | null => {
|
|
1203
854
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
1204
855
|
if (rdtHook?.renderers) {
|
|
1205
856
|
for (const renderer of rdtHook.renderers.values()) {
|
|
@@ -1219,14 +870,14 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1219
870
|
|
|
1220
871
|
for (const knownKey of knownFiberPropertyKeys) {
|
|
1221
872
|
const fiber = Reflect.get(hostInstance, knownKey);
|
|
1222
|
-
if (
|
|
873
|
+
if (isFiber(fiber)) return fiber;
|
|
1223
874
|
}
|
|
1224
875
|
|
|
1225
876
|
for (const key of Object.keys(hostInstance)) {
|
|
1226
877
|
if (isFiberPropertyKey(key)) {
|
|
1227
878
|
knownFiberPropertyKeys.add(key);
|
|
1228
879
|
const fiber = Reflect.get(hostInstance, key);
|
|
1229
|
-
if (
|
|
880
|
+
if (isFiber(fiber)) return fiber;
|
|
1230
881
|
}
|
|
1231
882
|
}
|
|
1232
883
|
}
|
|
@@ -1249,6 +900,8 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1249
900
|
return null;
|
|
1250
901
|
};
|
|
1251
902
|
|
|
903
|
+
export const getFiberFromHostInstance = getFiber;
|
|
904
|
+
|
|
1252
905
|
export {
|
|
1253
906
|
BIPPY_INSTRUMENTATION_STRING,
|
|
1254
907
|
_onActiveListeners,
|
|
@@ -1262,4 +915,4 @@ export {
|
|
|
1262
915
|
version,
|
|
1263
916
|
} from "./rdt-hook.js";
|
|
1264
917
|
export type { Unsubscribe } from "./rdt-hook.js";
|
|
1265
|
-
export
|
|
918
|
+
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 { isFiber } 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 && isFiber(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;
|