@reckona/mreact-compat 0.0.66 → 0.0.68
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/dist/class-component.js.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/devtools.js.map +1 -1
- package/dist/dom-children.js.map +1 -1
- package/dist/dom-props.js.map +1 -1
- package/dist/element.js.map +1 -1
- package/dist/event-listeners.js.map +1 -1
- package/dist/event-priority.js.map +1 -1
- package/dist/event-replay.js.map +1 -1
- package/dist/event-types.js.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/fiber-child.js.map +1 -1
- package/dist/fiber-commit.js.map +1 -1
- package/dist/fiber-flags.js.map +1 -1
- package/dist/fiber-host.js.map +1 -1
- package/dist/fiber-lanes.js.map +1 -1
- package/dist/fiber-reconciler.js.map +1 -1
- package/dist/fiber-scheduler.js.map +1 -1
- package/dist/fiber-work-loop.js.map +1 -1
- package/dist/fiber.js.map +1 -1
- package/dist/flight-decoder.js.map +1 -1
- package/dist/flight-element-builder.js.map +1 -1
- package/dist/flight-parser.js.map +1 -1
- package/dist/flight-protocol.js.map +1 -1
- package/dist/flight-types.js.map +1 -1
- package/dist/flight.js.map +1 -1
- package/dist/hooks.js.map +1 -1
- package/dist/hydration.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/prop-comparison.js.map +1 -1
- package/dist/reconcile-types.js.map +1 -1
- package/dist/reconciler.js.map +1 -1
- package/dist/render.js.map +1 -1
- package/dist/root.js.map +1 -1
- package/dist/scheduler.js.map +1 -1
- package/dist/suspense.js.map +1 -1
- package/dist/thenable.js.map +1 -1
- package/dist/url-safety.js.map +1 -1
- package/package.json +4 -3
- package/src/class-component.ts +386 -0
- package/src/context.ts +140 -0
- package/src/devtools.ts +1275 -0
- package/src/dom-children.ts +34 -0
- package/src/dom-props.ts +408 -0
- package/src/element.ts +317 -0
- package/src/event-listeners.ts +27 -0
- package/src/event-priority.ts +1 -0
- package/src/event-replay.ts +154 -0
- package/src/event-types.ts +18 -0
- package/src/events.ts +384 -0
- package/src/fiber-child.ts +364 -0
- package/src/fiber-commit.ts +83 -0
- package/src/fiber-flags.ts +21 -0
- package/src/fiber-host.ts +1564 -0
- package/src/fiber-lanes.ts +99 -0
- package/src/fiber-reconciler.ts +639 -0
- package/src/fiber-scheduler.ts +435 -0
- package/src/fiber-work-loop.ts +224 -0
- package/src/fiber.ts +148 -0
- package/src/flight-decoder.ts +205 -0
- package/src/flight-element-builder.ts +110 -0
- package/src/flight-parser.ts +698 -0
- package/src/flight-protocol.ts +71 -0
- package/src/flight-types.ts +148 -0
- package/src/flight.ts +162 -0
- package/src/hooks.ts +1940 -0
- package/src/hydration.ts +314 -0
- package/src/index.ts +95 -0
- package/src/internal.ts +7 -0
- package/src/jsx-dev-runtime.ts +40 -0
- package/src/jsx-runtime.ts +119 -0
- package/src/prop-comparison.ts +50 -0
- package/src/reconcile-types.ts +26 -0
- package/src/reconciler.ts +692 -0
- package/src/render.ts +29 -0
- package/src/root.ts +493 -0
- package/src/scheduler.ts +157 -0
- package/src/suspense.ts +317 -0
- package/src/thenable.ts +7 -0
- package/src/url-safety.ts +7 -0
package/src/devtools.ts
ADDED
|
@@ -0,0 +1,1275 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Fragment,
|
|
3
|
+
Profiler,
|
|
4
|
+
Suspense,
|
|
5
|
+
SuspenseList,
|
|
6
|
+
isReactCompatElement,
|
|
7
|
+
isReactCompatPortal,
|
|
8
|
+
type ReactCompatNode,
|
|
9
|
+
} from "./element.js";
|
|
10
|
+
import type { Fiber, FiberRoot, FiberTag } from "./fiber.js";
|
|
11
|
+
|
|
12
|
+
interface DevToolsHook {
|
|
13
|
+
inject?: (renderer: DevToolsRenderer) => number;
|
|
14
|
+
onCommitFiberRoot?: (
|
|
15
|
+
rendererId: number,
|
|
16
|
+
root: DevToolsRoot,
|
|
17
|
+
priorityLevel?: number,
|
|
18
|
+
didError?: boolean,
|
|
19
|
+
) => void;
|
|
20
|
+
onPostCommitFiberRoot?: (rendererId: number, root: DevToolsRoot) => void;
|
|
21
|
+
onCommitFiberUnmount?: (rendererId: number, fiber: DevToolsFiber) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface DevToolsRenderer {
|
|
25
|
+
bundleType: 0 | 1;
|
|
26
|
+
version: string;
|
|
27
|
+
reconcilerVersion: string;
|
|
28
|
+
rendererPackageName: string;
|
|
29
|
+
supportsFiber: true;
|
|
30
|
+
getLaneLabelMap(): Map<number, string>;
|
|
31
|
+
getCurrentFiber(): DevToolsFiber | null;
|
|
32
|
+
findFiberByHostInstance(hostInstance: unknown): DevToolsFiber | null;
|
|
33
|
+
findHostInstanceByFiber(fiber: DevToolsFiber): object | null;
|
|
34
|
+
findNativeNodesForFiber(fiber: DevToolsFiber): Set<object>;
|
|
35
|
+
getDisplayNameForFiber(fiber: DevToolsFiber): string | null;
|
|
36
|
+
getFiberCurrentPropsFromNode(hostInstance: unknown): unknown;
|
|
37
|
+
getInstanceByFiber(fiber: DevToolsFiber): unknown;
|
|
38
|
+
overrideProps(fiber: DevToolsFiber, path: Array<string | number>, value: unknown): void;
|
|
39
|
+
overridePropsDeletePath(fiber: DevToolsFiber, path: Array<string | number>): void;
|
|
40
|
+
overridePropsRenamePath(
|
|
41
|
+
fiber: DevToolsFiber,
|
|
42
|
+
oldPath: Array<string | number>,
|
|
43
|
+
newPath: Array<string | number>,
|
|
44
|
+
): void;
|
|
45
|
+
overrideHookState(
|
|
46
|
+
fiber: DevToolsFiber,
|
|
47
|
+
id: string,
|
|
48
|
+
path: Array<string | number>,
|
|
49
|
+
value: unknown,
|
|
50
|
+
): void;
|
|
51
|
+
overrideHookStateDeletePath(
|
|
52
|
+
fiber: DevToolsFiber,
|
|
53
|
+
id: string,
|
|
54
|
+
path: Array<string | number>,
|
|
55
|
+
): void;
|
|
56
|
+
overrideHookStateRenamePath(
|
|
57
|
+
fiber: DevToolsFiber,
|
|
58
|
+
id: string,
|
|
59
|
+
oldPath: Array<string | number>,
|
|
60
|
+
newPath: Array<string | number>,
|
|
61
|
+
): void;
|
|
62
|
+
scheduleUpdate(fiber: DevToolsFiber): void;
|
|
63
|
+
scheduleRefresh(root: unknown, update: unknown): void;
|
|
64
|
+
scheduleRetry(fiber: DevToolsFiber): void;
|
|
65
|
+
setSuspenseHandler(handler: (fiber: DevToolsFiber) => boolean): void;
|
|
66
|
+
setErrorHandler(handler: (fiber: DevToolsFiber) => boolean): void;
|
|
67
|
+
shouldSuspend(fiber: DevToolsFiber): boolean;
|
|
68
|
+
shouldError(fiber: DevToolsFiber): boolean;
|
|
69
|
+
injectProfilingHooks(hooks: unknown): void;
|
|
70
|
+
startProfiling(): void;
|
|
71
|
+
stopProfiling(): void;
|
|
72
|
+
clearProfilingData(): void;
|
|
73
|
+
getProfilingData(): DevToolsProfilingData;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface DevToolsRoot {
|
|
77
|
+
containerInfo: Element;
|
|
78
|
+
current: DevToolsFiber;
|
|
79
|
+
pendingChildren: null;
|
|
80
|
+
hydrate: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface DevToolsFiber {
|
|
84
|
+
tag: number;
|
|
85
|
+
key: string | null;
|
|
86
|
+
elementType: unknown;
|
|
87
|
+
type: unknown;
|
|
88
|
+
stateNode: unknown;
|
|
89
|
+
return: DevToolsFiber | null;
|
|
90
|
+
child: DevToolsFiber | null;
|
|
91
|
+
sibling: DevToolsFiber | null;
|
|
92
|
+
index: number;
|
|
93
|
+
ref: unknown;
|
|
94
|
+
pendingProps: unknown;
|
|
95
|
+
memoizedProps: unknown;
|
|
96
|
+
memoizedState: unknown;
|
|
97
|
+
updateQueue: null;
|
|
98
|
+
dependencies: null;
|
|
99
|
+
mode: number;
|
|
100
|
+
flags: number;
|
|
101
|
+
subtreeFlags: number;
|
|
102
|
+
deletions: DevToolsFiber[] | null;
|
|
103
|
+
lanes: number;
|
|
104
|
+
childLanes: number;
|
|
105
|
+
alternate: DevToolsFiber | null;
|
|
106
|
+
actualDuration: number;
|
|
107
|
+
actualStartTime: number;
|
|
108
|
+
selfBaseDuration: number;
|
|
109
|
+
treeBaseDuration: number;
|
|
110
|
+
_debugOwner: null;
|
|
111
|
+
_debugSource: null;
|
|
112
|
+
_debugHookTypes: string[] | null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface DevToolsProfilingData {
|
|
116
|
+
rendererID: number | undefined;
|
|
117
|
+
commitData: DevToolsCommitProfilingData[];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface DevToolsCommitProfilingData {
|
|
121
|
+
timestamp: number;
|
|
122
|
+
duration: number;
|
|
123
|
+
fiberActualDurations: Array<{
|
|
124
|
+
fiber: DevToolsFiber;
|
|
125
|
+
duration: number;
|
|
126
|
+
}>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const roots = new WeakMap<Element, DevToolsRoot>();
|
|
130
|
+
const hostInstanceFibers = new WeakMap<object, DevToolsFiber>();
|
|
131
|
+
const rootHostInstances = new WeakMap<DevToolsRoot, object[]>();
|
|
132
|
+
let rendererRoots = new Set<DevToolsRoot>();
|
|
133
|
+
let rendererId: number | undefined;
|
|
134
|
+
let injectedHook: DevToolsHook | undefined;
|
|
135
|
+
let isProfiling = false;
|
|
136
|
+
let profilingCommitData: DevToolsCommitProfilingData[] = [];
|
|
137
|
+
let suspenseHandler: ((fiber: DevToolsFiber) => boolean) | undefined;
|
|
138
|
+
let errorHandler: ((fiber: DevToolsFiber) => boolean) | undefined;
|
|
139
|
+
|
|
140
|
+
export function commitDevToolsRoot(
|
|
141
|
+
container: Element,
|
|
142
|
+
source: FiberRoot | ReactCompatNode,
|
|
143
|
+
didError = false,
|
|
144
|
+
): void {
|
|
145
|
+
const hook = getDevToolsHook();
|
|
146
|
+
const id = injectDevToolsRenderer(hook);
|
|
147
|
+
|
|
148
|
+
if (hook === undefined || id === undefined) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const previousRoot = roots.get(container);
|
|
153
|
+
const commitStart = getCurrentTime();
|
|
154
|
+
|
|
155
|
+
if (previousRoot !== undefined) {
|
|
156
|
+
rendererRoots.delete(previousRoot);
|
|
157
|
+
clearHostInstanceFibers(previousRoot);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const nextRoot = isFiberRoot(source)
|
|
161
|
+
? createDevToolsFiberRoot(container, source)
|
|
162
|
+
: createFallbackDevToolsRoot(container, source);
|
|
163
|
+
const root = previousRoot ?? nextRoot;
|
|
164
|
+
|
|
165
|
+
if (previousRoot !== undefined) {
|
|
166
|
+
linkDevToolsAlternates(nextRoot.current, previousRoot.current);
|
|
167
|
+
root.current = nextRoot.current;
|
|
168
|
+
root.current.stateNode = root;
|
|
169
|
+
rootHostInstances.set(root, rootHostInstances.get(nextRoot) ?? []);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
roots.set(container, root);
|
|
173
|
+
rendererRoots.add(root);
|
|
174
|
+
recordDevToolsCommit(root, commitStart);
|
|
175
|
+
hook.onCommitFiberRoot?.(id, root, undefined, didError);
|
|
176
|
+
hook.onPostCommitFiberRoot?.(id, root);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function unmountDevToolsRoot(container: Element): void {
|
|
180
|
+
const hook = getDevToolsHook();
|
|
181
|
+
const id = injectDevToolsRenderer(hook);
|
|
182
|
+
const root = roots.get(container);
|
|
183
|
+
|
|
184
|
+
if (hook === undefined || id === undefined || root === undefined) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
notifyFiberUnmounts(hook, id, root.current.child);
|
|
189
|
+
markDevToolsRootUnmounted(root);
|
|
190
|
+
hook.onCommitFiberRoot?.(id, root, undefined, false);
|
|
191
|
+
hook.onPostCommitFiberRoot?.(id, root);
|
|
192
|
+
rendererRoots.delete(root);
|
|
193
|
+
clearHostInstanceFibers(root);
|
|
194
|
+
roots.delete(container);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function injectDevToolsRenderer(hook: DevToolsHook | undefined): number | undefined {
|
|
198
|
+
if (rendererId !== undefined && injectedHook === hook) {
|
|
199
|
+
return rendererId;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (hook?.inject === undefined) {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
injectedHook = hook;
|
|
207
|
+
rendererRoots = new Set();
|
|
208
|
+
rendererId = hook.inject({
|
|
209
|
+
bundleType: 1,
|
|
210
|
+
version: "0.0.0",
|
|
211
|
+
reconcilerVersion: "0.0.0",
|
|
212
|
+
rendererPackageName: "@reckona/mreact-compat",
|
|
213
|
+
supportsFiber: true,
|
|
214
|
+
getLaneLabelMap() {
|
|
215
|
+
return new Map([
|
|
216
|
+
[1, "Sync"],
|
|
217
|
+
[2, "Continuous"],
|
|
218
|
+
[4, "Default"],
|
|
219
|
+
[8, "Transition"],
|
|
220
|
+
]);
|
|
221
|
+
},
|
|
222
|
+
getCurrentFiber() {
|
|
223
|
+
return null;
|
|
224
|
+
},
|
|
225
|
+
findFiberByHostInstance(hostInstance) {
|
|
226
|
+
return typeof hostInstance === "object" && hostInstance !== null
|
|
227
|
+
? (hostInstanceFibers.get(hostInstance) ?? null)
|
|
228
|
+
: null;
|
|
229
|
+
},
|
|
230
|
+
findHostInstanceByFiber(fiber) {
|
|
231
|
+
return findFirstHostInstance(fiber);
|
|
232
|
+
},
|
|
233
|
+
findNativeNodesForFiber(fiber) {
|
|
234
|
+
const hostInstances = new Set<object>();
|
|
235
|
+
collectHostInstances(fiber, hostInstances);
|
|
236
|
+
return hostInstances;
|
|
237
|
+
},
|
|
238
|
+
getDisplayNameForFiber(fiber) {
|
|
239
|
+
return getDisplayNameForDevToolsFiber(fiber);
|
|
240
|
+
},
|
|
241
|
+
getFiberCurrentPropsFromNode(hostInstance) {
|
|
242
|
+
const fiber =
|
|
243
|
+
typeof hostInstance === "object" && hostInstance !== null
|
|
244
|
+
? hostInstanceFibers.get(hostInstance)
|
|
245
|
+
: undefined;
|
|
246
|
+
|
|
247
|
+
return fiber?.memoizedProps ?? null;
|
|
248
|
+
},
|
|
249
|
+
getInstanceByFiber(fiber) {
|
|
250
|
+
return fiber.stateNode;
|
|
251
|
+
},
|
|
252
|
+
overrideProps(fiber, path, value) {
|
|
253
|
+
setFiberPropsPath(fiber, path, value);
|
|
254
|
+
},
|
|
255
|
+
overridePropsDeletePath(fiber, path) {
|
|
256
|
+
deleteFiberPropsPath(fiber, path);
|
|
257
|
+
},
|
|
258
|
+
overridePropsRenamePath(fiber, oldPath, newPath) {
|
|
259
|
+
renameFiberPropsPath(fiber, oldPath, newPath);
|
|
260
|
+
},
|
|
261
|
+
overrideHookState(fiber, id, path, value) {
|
|
262
|
+
setFiberHookStatePath(fiber, id, path, value);
|
|
263
|
+
},
|
|
264
|
+
overrideHookStateDeletePath(fiber, id, path) {
|
|
265
|
+
deleteFiberHookStatePath(fiber, id, path);
|
|
266
|
+
},
|
|
267
|
+
overrideHookStateRenamePath(fiber, id, oldPath, newPath) {
|
|
268
|
+
renameFiberHookStatePath(fiber, id, oldPath, newPath);
|
|
269
|
+
},
|
|
270
|
+
scheduleUpdate(fiber) {
|
|
271
|
+
commitEditedFiberRoot(fiber);
|
|
272
|
+
},
|
|
273
|
+
scheduleRefresh: noop,
|
|
274
|
+
scheduleRetry(fiber) {
|
|
275
|
+
commitEditedFiberRoot(fiber);
|
|
276
|
+
},
|
|
277
|
+
setSuspenseHandler(handler) {
|
|
278
|
+
suspenseHandler = handler;
|
|
279
|
+
},
|
|
280
|
+
setErrorHandler(handler) {
|
|
281
|
+
errorHandler = handler;
|
|
282
|
+
},
|
|
283
|
+
shouldSuspend(fiber) {
|
|
284
|
+
return suspenseHandler?.(fiber) ?? false;
|
|
285
|
+
},
|
|
286
|
+
shouldError(fiber) {
|
|
287
|
+
return errorHandler?.(fiber) ?? false;
|
|
288
|
+
},
|
|
289
|
+
injectProfilingHooks: noop,
|
|
290
|
+
startProfiling() {
|
|
291
|
+
isProfiling = true;
|
|
292
|
+
},
|
|
293
|
+
stopProfiling() {
|
|
294
|
+
isProfiling = false;
|
|
295
|
+
},
|
|
296
|
+
clearProfilingData() {
|
|
297
|
+
profilingCommitData = [];
|
|
298
|
+
},
|
|
299
|
+
getProfilingData() {
|
|
300
|
+
return {
|
|
301
|
+
rendererID: rendererId,
|
|
302
|
+
commitData: profilingCommitData,
|
|
303
|
+
};
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
return rendererId;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function noop(): void {}
|
|
310
|
+
|
|
311
|
+
function getDevToolsHook(): DevToolsHook | undefined {
|
|
312
|
+
return (globalThis as { __REACT_DEVTOOLS_GLOBAL_HOOK__?: DevToolsHook })
|
|
313
|
+
.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function isFiberRoot(value: FiberRoot | ReactCompatNode): value is FiberRoot {
|
|
317
|
+
return (
|
|
318
|
+
typeof value === "object" &&
|
|
319
|
+
value !== null &&
|
|
320
|
+
"current" in value &&
|
|
321
|
+
"container" in value
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function createDevToolsFiberRoot(
|
|
326
|
+
container: Element,
|
|
327
|
+
fiberRoot: FiberRoot,
|
|
328
|
+
): DevToolsRoot {
|
|
329
|
+
const hostInstances: object[] = [];
|
|
330
|
+
const root = {
|
|
331
|
+
containerInfo: container,
|
|
332
|
+
current: undefined as unknown as DevToolsFiber,
|
|
333
|
+
pendingChildren: null,
|
|
334
|
+
hydrate: fiberRoot.hydrationState !== undefined,
|
|
335
|
+
} satisfies DevToolsRoot;
|
|
336
|
+
root.current = toDevToolsFiber(fiberRoot.current, null, 0, root, hostInstances);
|
|
337
|
+
root.current.stateNode = root;
|
|
338
|
+
rootHostInstances.set(root, hostInstances);
|
|
339
|
+
return root;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function toDevToolsFiber(
|
|
343
|
+
fiber: Fiber,
|
|
344
|
+
parent: DevToolsFiber | null,
|
|
345
|
+
index: number,
|
|
346
|
+
root: DevToolsRoot,
|
|
347
|
+
hostInstances: object[],
|
|
348
|
+
): DevToolsFiber {
|
|
349
|
+
const devToolsFiber = createDevToolsFiberShell(fiber, parent, index, root);
|
|
350
|
+
|
|
351
|
+
if (isHostInstance(devToolsFiber.stateNode)) {
|
|
352
|
+
hostInstanceFibers.set(devToolsFiber.stateNode, devToolsFiber);
|
|
353
|
+
hostInstances.push(devToolsFiber.stateNode);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
let previousChild: DevToolsFiber | undefined;
|
|
357
|
+
let childIndex = 0;
|
|
358
|
+
let child = fiber.child;
|
|
359
|
+
|
|
360
|
+
while (child !== undefined) {
|
|
361
|
+
const devToolsChild = toDevToolsFiber(
|
|
362
|
+
child,
|
|
363
|
+
devToolsFiber,
|
|
364
|
+
childIndex,
|
|
365
|
+
root,
|
|
366
|
+
hostInstances,
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
if (previousChild === undefined) {
|
|
370
|
+
devToolsFiber.child = devToolsChild;
|
|
371
|
+
} else {
|
|
372
|
+
previousChild.sibling = devToolsChild;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
previousChild = devToolsChild;
|
|
376
|
+
child = child.sibling;
|
|
377
|
+
childIndex += 1;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
devToolsFiber.deletions =
|
|
381
|
+
fiber.deletions?.map((deletedFiber, deletedIndex) =>
|
|
382
|
+
toDevToolsFiber(deletedFiber, devToolsFiber, deletedIndex, root, hostInstances),
|
|
383
|
+
) ?? null;
|
|
384
|
+
return devToolsFiber;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function createDevToolsFiberShell(
|
|
388
|
+
fiber: Fiber,
|
|
389
|
+
parent: DevToolsFiber | null,
|
|
390
|
+
index: number,
|
|
391
|
+
root: DevToolsRoot,
|
|
392
|
+
): DevToolsFiber {
|
|
393
|
+
const pendingProps =
|
|
394
|
+
fiber.tag === "host-root" ? fiber.pendingProps : fiber.pendingProps;
|
|
395
|
+
const memoizedProps =
|
|
396
|
+
fiber.tag === "host-root" ? fiber.memoizedProps : fiber.memoizedProps;
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
tag: getReactFiberTag(fiber.tag),
|
|
400
|
+
key: fiber.key ?? null,
|
|
401
|
+
elementType: getElementType(fiber),
|
|
402
|
+
type: getFiberType(fiber),
|
|
403
|
+
stateNode: fiber.tag === "host-root" ? root : fiber.stateNode,
|
|
404
|
+
return: parent,
|
|
405
|
+
child: null,
|
|
406
|
+
sibling: null,
|
|
407
|
+
index,
|
|
408
|
+
ref: getFiberRef(fiber),
|
|
409
|
+
pendingProps,
|
|
410
|
+
memoizedProps,
|
|
411
|
+
memoizedState: fiber.tag === "host-root"
|
|
412
|
+
? createMountedHostRootState(memoizedProps)
|
|
413
|
+
: createDevToolsMemoizedState(fiber.memoizedState),
|
|
414
|
+
updateQueue: null,
|
|
415
|
+
dependencies: null,
|
|
416
|
+
mode: 0,
|
|
417
|
+
flags: fiber.flags,
|
|
418
|
+
subtreeFlags: fiber.subtreeFlags,
|
|
419
|
+
deletions: null,
|
|
420
|
+
lanes: fiber.lanes,
|
|
421
|
+
childLanes: fiber.childLanes,
|
|
422
|
+
alternate: null,
|
|
423
|
+
actualDuration: 0,
|
|
424
|
+
actualStartTime: -1,
|
|
425
|
+
selfBaseDuration: 0,
|
|
426
|
+
treeBaseDuration: 0,
|
|
427
|
+
_debugOwner: null,
|
|
428
|
+
_debugSource: null,
|
|
429
|
+
_debugHookTypes: getDevToolsHookTypes(fiber.memoizedState),
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
interface DevToolsHookSnapshot {
|
|
434
|
+
hooks: Array<{
|
|
435
|
+
kind: string;
|
|
436
|
+
value?: unknown;
|
|
437
|
+
deps?: readonly unknown[];
|
|
438
|
+
effectKind?: "insertion" | "layout" | "normal";
|
|
439
|
+
}>;
|
|
440
|
+
hookTypes?: string[];
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
interface DevToolsHookNode {
|
|
444
|
+
memoizedState: unknown;
|
|
445
|
+
baseState: unknown;
|
|
446
|
+
baseQueue: null;
|
|
447
|
+
queue: unknown;
|
|
448
|
+
next: DevToolsHookNode | null;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function createDevToolsHookList(state: unknown): DevToolsHookNode | null {
|
|
452
|
+
if (!isDevToolsHookSnapshot(state)) {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const nodes = state.hooks
|
|
457
|
+
.filter((hook) => hook.kind !== "debug")
|
|
458
|
+
.map(createDevToolsHookNode);
|
|
459
|
+
|
|
460
|
+
for (let index = 0; index < nodes.length - 1; index += 1) {
|
|
461
|
+
const next = nodes[index + 1];
|
|
462
|
+
|
|
463
|
+
if (next !== undefined) {
|
|
464
|
+
nodes[index]!.next = next;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return nodes[0] ?? null;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function createDevToolsMemoizedState(state: unknown): unknown {
|
|
472
|
+
return isDevToolsHookSnapshot(state)
|
|
473
|
+
? createDevToolsHookList(state)
|
|
474
|
+
: state;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function createDevToolsHookNode(
|
|
478
|
+
hook: DevToolsHookSnapshot["hooks"][number],
|
|
479
|
+
): DevToolsHookNode {
|
|
480
|
+
const memoizedState = getDevToolsHookMemoizedState(hook);
|
|
481
|
+
const isStateful =
|
|
482
|
+
hook.kind === "state" ||
|
|
483
|
+
hook.kind === "reducer" ||
|
|
484
|
+
hook.kind === "store" ||
|
|
485
|
+
hook.kind === "transition" ||
|
|
486
|
+
hook.kind === "deferred";
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
memoizedState,
|
|
490
|
+
baseState: isStateful ? memoizedState : null,
|
|
491
|
+
baseQueue: null,
|
|
492
|
+
queue: isStateful
|
|
493
|
+
? {
|
|
494
|
+
pending: null,
|
|
495
|
+
lanes: 0,
|
|
496
|
+
dispatch: null,
|
|
497
|
+
lastRenderedReducer: null,
|
|
498
|
+
lastRenderedState: memoizedState,
|
|
499
|
+
}
|
|
500
|
+
: null,
|
|
501
|
+
next: null,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function getDevToolsHookMemoizedState(
|
|
506
|
+
hook: DevToolsHookSnapshot["hooks"][number],
|
|
507
|
+
): unknown {
|
|
508
|
+
if (hook.kind === "memo" || hook.kind === "callback") {
|
|
509
|
+
return [hook.value, hook.deps ?? null];
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (hook.kind === "ref") {
|
|
513
|
+
return typeof hook.value === "object" && hook.value !== null
|
|
514
|
+
? hook.value
|
|
515
|
+
: { current: hook.value };
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (hook.kind === "effect" || hook.kind === "imperative-handle") {
|
|
519
|
+
return {
|
|
520
|
+
tag: hook.kind === "effect" ? hook.effectKind ?? "normal" : "imperative-handle",
|
|
521
|
+
create: null,
|
|
522
|
+
destroy: null,
|
|
523
|
+
deps: hook.deps ?? null,
|
|
524
|
+
next: null,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return hook.value;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function getDevToolsHookTypes(state: unknown): string[] | null {
|
|
532
|
+
if (!isDevToolsHookSnapshot(state)) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return state.hookTypes === undefined || state.hookTypes.length === 0
|
|
537
|
+
? null
|
|
538
|
+
: [...state.hookTypes];
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function isDevToolsHookSnapshot(state: unknown): state is DevToolsHookSnapshot {
|
|
542
|
+
return (
|
|
543
|
+
typeof state === "object" &&
|
|
544
|
+
state !== null &&
|
|
545
|
+
"hooks" in state &&
|
|
546
|
+
Array.isArray((state as { hooks?: unknown }).hooks)
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function createFallbackDevToolsRoot(
|
|
551
|
+
container: Element,
|
|
552
|
+
element: ReactCompatNode,
|
|
553
|
+
): DevToolsRoot {
|
|
554
|
+
const hostInstances: object[] = [];
|
|
555
|
+
const root = {
|
|
556
|
+
containerInfo: container,
|
|
557
|
+
current: undefined as unknown as DevToolsFiber,
|
|
558
|
+
pendingChildren: null,
|
|
559
|
+
hydrate: false,
|
|
560
|
+
} satisfies DevToolsRoot;
|
|
561
|
+
root.current = {
|
|
562
|
+
...createFallbackDevToolsFiber("host-root", { children: element }, null, 0),
|
|
563
|
+
tag: 3,
|
|
564
|
+
stateNode: root,
|
|
565
|
+
memoizedState: createMountedHostRootState({ children: element }),
|
|
566
|
+
};
|
|
567
|
+
root.current.child = toFallbackDevToolsFiber(
|
|
568
|
+
element,
|
|
569
|
+
root.current,
|
|
570
|
+
0,
|
|
571
|
+
hostInstances,
|
|
572
|
+
);
|
|
573
|
+
rootHostInstances.set(root, hostInstances);
|
|
574
|
+
return root;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function createMountedHostRootState(props: unknown): { element: unknown } {
|
|
578
|
+
return {
|
|
579
|
+
element: typeof props === "object" && props !== null && "children" in props
|
|
580
|
+
? (props as { children?: unknown }).children
|
|
581
|
+
: null,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function markDevToolsRootUnmounted(root: DevToolsRoot): void {
|
|
586
|
+
root.current.child = null;
|
|
587
|
+
root.current.memoizedProps = null;
|
|
588
|
+
root.current.pendingProps = null;
|
|
589
|
+
root.current.memoizedState = null;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function toFallbackDevToolsFiber(
|
|
593
|
+
node: ReactCompatNode,
|
|
594
|
+
parent: DevToolsFiber | null,
|
|
595
|
+
index: number,
|
|
596
|
+
hostInstances: object[],
|
|
597
|
+
): DevToolsFiber | null {
|
|
598
|
+
if (Array.isArray(node)) {
|
|
599
|
+
const fragment = createFallbackDevToolsFiber(Fragment, {}, parent, index);
|
|
600
|
+
linkFallbackChildren(fragment, node, hostInstances);
|
|
601
|
+
return fragment;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (!isReactCompatElement(node)) {
|
|
605
|
+
if (isReactCompatPortal(node)) {
|
|
606
|
+
const portal = createFallbackDevToolsFiber("portal", {}, parent, index);
|
|
607
|
+
portal.stateNode = node.container;
|
|
608
|
+
portal.child = toFallbackDevToolsFiber(node.children, portal, 0, hostInstances);
|
|
609
|
+
return portal;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return createFallbackDevToolsFiber("#text", node, parent, index);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const fiber = createFallbackDevToolsFiber(node.type, node.props, parent, index);
|
|
616
|
+
fiber.key = node.key;
|
|
617
|
+
fiber.ref = node.ref;
|
|
618
|
+
linkFallbackChildren(fiber, getChildren(node.props.children), hostInstances);
|
|
619
|
+
return fiber;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function linkFallbackChildren(
|
|
623
|
+
parent: DevToolsFiber,
|
|
624
|
+
children: readonly ReactCompatNode[],
|
|
625
|
+
hostInstances: object[],
|
|
626
|
+
): void {
|
|
627
|
+
let previous: DevToolsFiber | undefined;
|
|
628
|
+
|
|
629
|
+
for (const [index, child] of children.entries()) {
|
|
630
|
+
const childFiber = toFallbackDevToolsFiber(child, parent, index, hostInstances);
|
|
631
|
+
|
|
632
|
+
if (childFiber === null) {
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (previous === undefined) {
|
|
637
|
+
parent.child = childFiber;
|
|
638
|
+
} else {
|
|
639
|
+
previous.sibling = childFiber;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
previous = childFiber;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function createFallbackDevToolsFiber(
|
|
647
|
+
type: unknown,
|
|
648
|
+
props: unknown,
|
|
649
|
+
parent: DevToolsFiber | null,
|
|
650
|
+
index: number,
|
|
651
|
+
): DevToolsFiber {
|
|
652
|
+
const tag = type === "#text"
|
|
653
|
+
? 6
|
|
654
|
+
: type === "host-root"
|
|
655
|
+
? 3
|
|
656
|
+
: type === "portal"
|
|
657
|
+
? 4
|
|
658
|
+
: typeof type === "string"
|
|
659
|
+
? 5
|
|
660
|
+
: type === Fragment
|
|
661
|
+
? 7
|
|
662
|
+
: type === Profiler
|
|
663
|
+
? 12
|
|
664
|
+
: type === Suspense
|
|
665
|
+
? 13
|
|
666
|
+
: type === SuspenseList
|
|
667
|
+
? 19
|
|
668
|
+
: typeof type === "function"
|
|
669
|
+
? 0
|
|
670
|
+
: 0;
|
|
671
|
+
|
|
672
|
+
return {
|
|
673
|
+
tag,
|
|
674
|
+
key: null,
|
|
675
|
+
elementType: type,
|
|
676
|
+
type,
|
|
677
|
+
stateNode: null,
|
|
678
|
+
return: parent,
|
|
679
|
+
child: null,
|
|
680
|
+
sibling: null,
|
|
681
|
+
index,
|
|
682
|
+
ref: null,
|
|
683
|
+
pendingProps: props,
|
|
684
|
+
memoizedProps: props,
|
|
685
|
+
memoizedState: null,
|
|
686
|
+
updateQueue: null,
|
|
687
|
+
dependencies: null,
|
|
688
|
+
mode: 0,
|
|
689
|
+
flags: 0,
|
|
690
|
+
subtreeFlags: 0,
|
|
691
|
+
deletions: null,
|
|
692
|
+
lanes: 0,
|
|
693
|
+
childLanes: 0,
|
|
694
|
+
alternate: null,
|
|
695
|
+
actualDuration: 0,
|
|
696
|
+
actualStartTime: -1,
|
|
697
|
+
selfBaseDuration: 0,
|
|
698
|
+
treeBaseDuration: 0,
|
|
699
|
+
_debugOwner: null,
|
|
700
|
+
_debugSource: null,
|
|
701
|
+
_debugHookTypes: null,
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function notifyFiberUnmounts(
|
|
706
|
+
hook: DevToolsHook,
|
|
707
|
+
id: number,
|
|
708
|
+
fiber: DevToolsFiber | null,
|
|
709
|
+
): void {
|
|
710
|
+
let cursor = fiber;
|
|
711
|
+
|
|
712
|
+
while (cursor !== null) {
|
|
713
|
+
hook.onCommitFiberUnmount?.(id, cursor);
|
|
714
|
+
notifyFiberUnmounts(hook, id, cursor.child);
|
|
715
|
+
cursor = cursor.sibling;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function clearHostInstanceFibers(root: DevToolsRoot): void {
|
|
720
|
+
for (const hostInstance of rootHostInstances.get(root) ?? []) {
|
|
721
|
+
hostInstanceFibers.delete(hostInstance);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
rootHostInstances.delete(root);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function recordDevToolsCommit(root: DevToolsRoot, commitStart: number): void {
|
|
728
|
+
if (!isProfiling) {
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const timestamp = getCurrentTime();
|
|
733
|
+
const fiberActualDurations: DevToolsCommitProfilingData["fiberActualDurations"] = [];
|
|
734
|
+
collectFiberDurations(root.current, fiberActualDurations);
|
|
735
|
+
profilingCommitData.push({
|
|
736
|
+
timestamp,
|
|
737
|
+
duration: Math.max(0, timestamp - commitStart),
|
|
738
|
+
fiberActualDurations,
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function collectFiberDurations(
|
|
743
|
+
fiber: DevToolsFiber | null,
|
|
744
|
+
durations: DevToolsCommitProfilingData["fiberActualDurations"],
|
|
745
|
+
): void {
|
|
746
|
+
let cursor = fiber;
|
|
747
|
+
|
|
748
|
+
while (cursor !== null) {
|
|
749
|
+
durations.push({
|
|
750
|
+
fiber: cursor,
|
|
751
|
+
duration: cursor.actualDuration,
|
|
752
|
+
});
|
|
753
|
+
collectFiberDurations(cursor.child, durations);
|
|
754
|
+
cursor = cursor.sibling;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function linkDevToolsAlternates(
|
|
759
|
+
current: DevToolsFiber | null,
|
|
760
|
+
alternate: DevToolsFiber | null,
|
|
761
|
+
): void {
|
|
762
|
+
if (current === null || alternate === null || !canLinkDevToolsAlternates(current, alternate)) {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
current.alternate = alternate;
|
|
767
|
+
alternate.alternate = current;
|
|
768
|
+
linkDevToolsChildAlternates(current.child, alternate.child);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function linkDevToolsChildAlternates(
|
|
772
|
+
currentChild: DevToolsFiber | null,
|
|
773
|
+
alternateChild: DevToolsFiber | null,
|
|
774
|
+
): void {
|
|
775
|
+
const unmatchedAlternates = new Set<DevToolsFiber>();
|
|
776
|
+
let alternateCursor = alternateChild;
|
|
777
|
+
|
|
778
|
+
while (alternateCursor !== null) {
|
|
779
|
+
unmatchedAlternates.add(alternateCursor);
|
|
780
|
+
alternateCursor = alternateCursor.sibling;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
let currentCursor = currentChild;
|
|
784
|
+
|
|
785
|
+
while (currentCursor !== null) {
|
|
786
|
+
const alternate = findMatchingDevToolsAlternate(currentCursor, unmatchedAlternates);
|
|
787
|
+
|
|
788
|
+
if (alternate !== null) {
|
|
789
|
+
unmatchedAlternates.delete(alternate);
|
|
790
|
+
linkDevToolsAlternates(currentCursor, alternate);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
currentCursor = currentCursor.sibling;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function findMatchingDevToolsAlternate(
|
|
798
|
+
current: DevToolsFiber,
|
|
799
|
+
candidates: Set<DevToolsFiber>,
|
|
800
|
+
): DevToolsFiber | null {
|
|
801
|
+
for (const candidate of candidates) {
|
|
802
|
+
if (canLinkDevToolsAlternates(current, candidate)) {
|
|
803
|
+
return candidate;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
return null;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function canLinkDevToolsAlternates(current: DevToolsFiber, alternate: DevToolsFiber): boolean {
|
|
811
|
+
return (
|
|
812
|
+
current.tag === alternate.tag &&
|
|
813
|
+
current.key === alternate.key &&
|
|
814
|
+
current.elementType === alternate.elementType &&
|
|
815
|
+
current.type === alternate.type
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function setFiberPropsPath(
|
|
820
|
+
fiber: DevToolsFiber,
|
|
821
|
+
path: Array<string | number>,
|
|
822
|
+
value: unknown,
|
|
823
|
+
): void {
|
|
824
|
+
const props = ensureRecord(fiber.memoizedProps);
|
|
825
|
+
setPath(props, path, value);
|
|
826
|
+
fiber.memoizedProps = props;
|
|
827
|
+
fiber.pendingProps = props;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function deleteFiberPropsPath(
|
|
831
|
+
fiber: DevToolsFiber,
|
|
832
|
+
path: Array<string | number>,
|
|
833
|
+
): void {
|
|
834
|
+
const props = ensureRecord(fiber.memoizedProps);
|
|
835
|
+
deletePath(props, path);
|
|
836
|
+
fiber.memoizedProps = props;
|
|
837
|
+
fiber.pendingProps = props;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function renameFiberPropsPath(
|
|
841
|
+
fiber: DevToolsFiber,
|
|
842
|
+
oldPath: Array<string | number>,
|
|
843
|
+
newPath: Array<string | number>,
|
|
844
|
+
): void {
|
|
845
|
+
const props = ensureRecord(fiber.memoizedProps);
|
|
846
|
+
renamePath(props, oldPath, newPath);
|
|
847
|
+
fiber.memoizedProps = props;
|
|
848
|
+
fiber.pendingProps = props;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function readHookState(fiber: DevToolsFiber, id: string): unknown {
|
|
852
|
+
const index = Number.parseInt(id, 10);
|
|
853
|
+
const state = fiber.memoizedState;
|
|
854
|
+
|
|
855
|
+
if (
|
|
856
|
+
Number.isInteger(index) &&
|
|
857
|
+
typeof state === "object" &&
|
|
858
|
+
state !== null &&
|
|
859
|
+
"hooks" in state &&
|
|
860
|
+
Array.isArray((state as { hooks?: unknown }).hooks)
|
|
861
|
+
) {
|
|
862
|
+
return (state as { hooks: unknown[] }).hooks[index];
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const hookNode = readHookNode(fiber, id);
|
|
866
|
+
|
|
867
|
+
if (hookNode !== null) {
|
|
868
|
+
return hookNode.memoizedState;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
return state;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function setFiberHookStatePath(
|
|
875
|
+
fiber: DevToolsFiber,
|
|
876
|
+
id: string,
|
|
877
|
+
path: Array<string | number>,
|
|
878
|
+
value: unknown,
|
|
879
|
+
): void {
|
|
880
|
+
const hookNode = readHookNode(fiber, id);
|
|
881
|
+
|
|
882
|
+
if (hookNode === null) {
|
|
883
|
+
setPath(readHookState(fiber, id), path, value);
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
if (path.length === 0) {
|
|
888
|
+
hookNode.memoizedState = value;
|
|
889
|
+
hookNode.baseState = value;
|
|
890
|
+
if (isDevToolsStateQueue(hookNode.queue)) {
|
|
891
|
+
hookNode.queue.lastRenderedState = value;
|
|
892
|
+
}
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
setPath(hookNode.memoizedState, path, value);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function deleteFiberHookStatePath(
|
|
900
|
+
fiber: DevToolsFiber,
|
|
901
|
+
id: string,
|
|
902
|
+
path: Array<string | number>,
|
|
903
|
+
): void {
|
|
904
|
+
const hookNode = readHookNode(fiber, id);
|
|
905
|
+
|
|
906
|
+
if (hookNode === null) {
|
|
907
|
+
deletePath(readHookState(fiber, id), path);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
deletePath(hookNode.memoizedState, path);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function renameFiberHookStatePath(
|
|
915
|
+
fiber: DevToolsFiber,
|
|
916
|
+
id: string,
|
|
917
|
+
oldPath: Array<string | number>,
|
|
918
|
+
newPath: Array<string | number>,
|
|
919
|
+
): void {
|
|
920
|
+
const hookNode = readHookNode(fiber, id);
|
|
921
|
+
|
|
922
|
+
if (hookNode === null) {
|
|
923
|
+
renamePath(readHookState(fiber, id), oldPath, newPath);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
renamePath(hookNode.memoizedState, oldPath, newPath);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function readHookNode(fiber: DevToolsFiber, id: string): DevToolsHookNode | null {
|
|
931
|
+
const index = Number.parseInt(id, 10);
|
|
932
|
+
|
|
933
|
+
if (!Number.isInteger(index)) {
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
let cursor = fiber.memoizedState;
|
|
938
|
+
let cursorIndex = 0;
|
|
939
|
+
|
|
940
|
+
while (isDevToolsHookNode(cursor)) {
|
|
941
|
+
if (cursorIndex === index) {
|
|
942
|
+
return cursor;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
cursor = cursor.next;
|
|
946
|
+
cursorIndex += 1;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
return null;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function isDevToolsHookNode(value: unknown): value is DevToolsHookNode {
|
|
953
|
+
return (
|
|
954
|
+
typeof value === "object" &&
|
|
955
|
+
value !== null &&
|
|
956
|
+
"memoizedState" in value &&
|
|
957
|
+
"baseState" in value &&
|
|
958
|
+
"baseQueue" in value &&
|
|
959
|
+
"queue" in value &&
|
|
960
|
+
"next" in value
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function isDevToolsStateQueue(
|
|
965
|
+
value: unknown,
|
|
966
|
+
): value is { lastRenderedState: unknown } {
|
|
967
|
+
return typeof value === "object" && value !== null && "lastRenderedState" in value;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
function commitEditedFiberRoot(fiber: DevToolsFiber): void {
|
|
971
|
+
const hook = getDevToolsHook();
|
|
972
|
+
const id = injectDevToolsRenderer(hook);
|
|
973
|
+
const root = findDevToolsRootForFiber(fiber);
|
|
974
|
+
|
|
975
|
+
if (hook === undefined || id === undefined || root === null) {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
hook.onCommitFiberRoot?.(id, root);
|
|
980
|
+
hook.onPostCommitFiberRoot?.(id, root);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function findDevToolsRootForFiber(fiber: DevToolsFiber): DevToolsRoot | null {
|
|
984
|
+
let cursor: DevToolsFiber | null = fiber;
|
|
985
|
+
|
|
986
|
+
while (cursor.return !== null) {
|
|
987
|
+
cursor = cursor.return;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
return cursor.stateNode !== null &&
|
|
991
|
+
typeof cursor.stateNode === "object" &&
|
|
992
|
+
"current" in cursor.stateNode
|
|
993
|
+
? (cursor.stateNode as DevToolsRoot)
|
|
994
|
+
: null;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function ensureRecord(value: unknown): Record<string | number, unknown> {
|
|
998
|
+
return typeof value === "object" && value !== null
|
|
999
|
+
? value as Record<string | number, unknown>
|
|
1000
|
+
: {};
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function setPath(
|
|
1004
|
+
target: unknown,
|
|
1005
|
+
path: Array<string | number>,
|
|
1006
|
+
value: unknown,
|
|
1007
|
+
): void {
|
|
1008
|
+
const parent = readPathParent(target, path, true);
|
|
1009
|
+
const key = path.at(-1);
|
|
1010
|
+
|
|
1011
|
+
if (parent === undefined || key === undefined) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
if (Array.isArray(parent) && typeof key === "number") {
|
|
1016
|
+
parent[key] = value;
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
(parent as Record<string | number, unknown>)[key] = value;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
function deletePath(target: unknown, path: Array<string | number>): void {
|
|
1024
|
+
const parent = readPathParent(target, path, false);
|
|
1025
|
+
const key = path.at(-1);
|
|
1026
|
+
|
|
1027
|
+
if (parent === undefined || key === undefined) {
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
if (Array.isArray(parent) && typeof key === "number") {
|
|
1032
|
+
parent.splice(key, 1);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
delete (parent as Record<string | number, unknown>)[key];
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
function renamePath(
|
|
1040
|
+
target: unknown,
|
|
1041
|
+
oldPath: Array<string | number>,
|
|
1042
|
+
newPath: Array<string | number>,
|
|
1043
|
+
): void {
|
|
1044
|
+
const value = readPath(target, oldPath);
|
|
1045
|
+
|
|
1046
|
+
if (value === undefined) {
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
deletePath(target, oldPath);
|
|
1051
|
+
setPath(target, newPath, value);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
function readPath(target: unknown, path: Array<string | number>): unknown {
|
|
1055
|
+
let cursor = target;
|
|
1056
|
+
|
|
1057
|
+
for (const key of path) {
|
|
1058
|
+
if (typeof cursor !== "object" || cursor === null) {
|
|
1059
|
+
return undefined;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
cursor = (cursor as Record<string | number, unknown>)[key];
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
return cursor;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
function readPathParent(
|
|
1069
|
+
target: unknown,
|
|
1070
|
+
path: Array<string | number>,
|
|
1071
|
+
createMissing: boolean,
|
|
1072
|
+
): Record<string | number, unknown> | unknown[] | undefined {
|
|
1073
|
+
if (path.length === 0 || typeof target !== "object" || target === null) {
|
|
1074
|
+
return undefined;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
let cursor = target as Record<string | number, unknown>;
|
|
1078
|
+
|
|
1079
|
+
for (const [index, key] of path.slice(0, -1).entries()) {
|
|
1080
|
+
const nextKey = path[index + 1];
|
|
1081
|
+
let next = cursor[key];
|
|
1082
|
+
|
|
1083
|
+
if (typeof next !== "object" || next === null) {
|
|
1084
|
+
if (!createMissing) {
|
|
1085
|
+
return undefined;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
next = typeof nextKey === "number" ? [] : {};
|
|
1089
|
+
cursor[key] = next;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
cursor = next as Record<string | number, unknown>;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
return cursor;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function getCurrentTime(): number {
|
|
1099
|
+
return typeof performance === "undefined" ? Date.now() : performance.now();
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
function findFirstHostInstance(fiber: DevToolsFiber | null): object | null {
|
|
1103
|
+
if (fiber === null) {
|
|
1104
|
+
return null;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
if (isHostInstance(fiber.stateNode)) {
|
|
1108
|
+
return fiber.stateNode;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
let child = fiber.child;
|
|
1112
|
+
|
|
1113
|
+
while (child !== null) {
|
|
1114
|
+
const hostInstance = findFirstHostInstance(child);
|
|
1115
|
+
|
|
1116
|
+
if (hostInstance !== null) {
|
|
1117
|
+
return hostInstance;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
child = child.sibling;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
function collectHostInstances(
|
|
1127
|
+
fiber: DevToolsFiber | null,
|
|
1128
|
+
hostInstances: Set<object>,
|
|
1129
|
+
): void {
|
|
1130
|
+
if (fiber === null) {
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
if (isHostInstance(fiber.stateNode)) {
|
|
1135
|
+
hostInstances.add(fiber.stateNode);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
let child = fiber.child;
|
|
1139
|
+
|
|
1140
|
+
while (child !== null) {
|
|
1141
|
+
collectHostInstances(child, hostInstances);
|
|
1142
|
+
child = child.sibling;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
function getReactFiberTag(tag: FiberTag): number {
|
|
1147
|
+
switch (tag) {
|
|
1148
|
+
case "function-component":
|
|
1149
|
+
return 0;
|
|
1150
|
+
case "class-component":
|
|
1151
|
+
return 1;
|
|
1152
|
+
case "host-root":
|
|
1153
|
+
return 3;
|
|
1154
|
+
case "portal":
|
|
1155
|
+
return 4;
|
|
1156
|
+
case "host-component":
|
|
1157
|
+
return 5;
|
|
1158
|
+
case "host-text":
|
|
1159
|
+
return 6;
|
|
1160
|
+
case "fragment":
|
|
1161
|
+
return 7;
|
|
1162
|
+
case "strict-mode":
|
|
1163
|
+
return 8;
|
|
1164
|
+
case "context-consumer":
|
|
1165
|
+
return 9;
|
|
1166
|
+
case "context-provider":
|
|
1167
|
+
return 10;
|
|
1168
|
+
case "forward-ref":
|
|
1169
|
+
return 11;
|
|
1170
|
+
case "profiler":
|
|
1171
|
+
return 12;
|
|
1172
|
+
case "suspense":
|
|
1173
|
+
return 13;
|
|
1174
|
+
case "memo":
|
|
1175
|
+
return 14;
|
|
1176
|
+
case "lazy":
|
|
1177
|
+
return 16;
|
|
1178
|
+
case "suspense-list":
|
|
1179
|
+
return 19;
|
|
1180
|
+
case "error-boundary":
|
|
1181
|
+
return 1;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
function getElementType(fiber: Fiber): unknown {
|
|
1186
|
+
return fiber.tag === "host-root" || fiber.tag === "host-text"
|
|
1187
|
+
? null
|
|
1188
|
+
: fiber.type;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
function getFiberType(fiber: Fiber): unknown {
|
|
1192
|
+
return fiber.tag === "host-root" || fiber.tag === "host-text"
|
|
1193
|
+
? null
|
|
1194
|
+
: fiber.type;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function getFiberRef(fiber: Fiber): unknown {
|
|
1198
|
+
const props = (fiber.memoizedProps ?? fiber.pendingProps) as
|
|
1199
|
+
| { ref?: unknown }
|
|
1200
|
+
| undefined;
|
|
1201
|
+
|
|
1202
|
+
return props?.ref ?? null;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
function getDisplayNameForDevToolsFiber(fiber: DevToolsFiber): string | null {
|
|
1206
|
+
if (fiber.tag === 3) {
|
|
1207
|
+
return "Root";
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
if (fiber.tag === 6) {
|
|
1211
|
+
return "Text";
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
return getElementTypeName(fiber.elementType ?? fiber.type);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
function isHostInstance(value: unknown): value is object {
|
|
1218
|
+
return (
|
|
1219
|
+
(typeof Element !== "undefined" && value instanceof Element) ||
|
|
1220
|
+
(typeof Text !== "undefined" && value instanceof Text)
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
function getChildren(children: ReactCompatNode | undefined): ReactCompatNode[] {
|
|
1225
|
+
if (children === undefined || children === null || typeof children === "boolean") {
|
|
1226
|
+
return [];
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
return Array.isArray(children) ? children : [children];
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
function getElementTypeName(type: unknown): string | null {
|
|
1233
|
+
if (typeof type === "string") {
|
|
1234
|
+
return type;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
if (typeof type === "function") {
|
|
1238
|
+
return type.name === "" ? "Anonymous" : type.name;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
if (type === Fragment) {
|
|
1242
|
+
return "Fragment";
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
if (type === Profiler) {
|
|
1246
|
+
return "Profiler";
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
if (type === Suspense) {
|
|
1250
|
+
return "Suspense";
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
if (type === SuspenseList) {
|
|
1254
|
+
return "SuspenseList";
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
if (
|
|
1258
|
+
typeof type === "object" &&
|
|
1259
|
+
type !== null &&
|
|
1260
|
+
"render" in type &&
|
|
1261
|
+
typeof (type as { render?: unknown }).render === "function"
|
|
1262
|
+
) {
|
|
1263
|
+
return getElementTypeName((type as { render: Function }).render);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
if (
|
|
1267
|
+
typeof type === "object" &&
|
|
1268
|
+
type !== null &&
|
|
1269
|
+
"type" in type
|
|
1270
|
+
) {
|
|
1271
|
+
return getElementTypeName((type as { type?: unknown }).type);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
return null;
|
|
1275
|
+
}
|