@reckona/mreact-compat 0.0.65 → 0.0.67

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.
@@ -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
+ }