@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.
Files changed (83) hide show
  1. package/dist/class-component.js.map +1 -1
  2. package/dist/context.js.map +1 -1
  3. package/dist/devtools.js.map +1 -1
  4. package/dist/dom-children.js.map +1 -1
  5. package/dist/dom-props.js.map +1 -1
  6. package/dist/element.js.map +1 -1
  7. package/dist/event-listeners.js.map +1 -1
  8. package/dist/event-priority.js.map +1 -1
  9. package/dist/event-replay.js.map +1 -1
  10. package/dist/event-types.js.map +1 -1
  11. package/dist/events.js.map +1 -1
  12. package/dist/fiber-child.js.map +1 -1
  13. package/dist/fiber-commit.js.map +1 -1
  14. package/dist/fiber-flags.js.map +1 -1
  15. package/dist/fiber-host.js.map +1 -1
  16. package/dist/fiber-lanes.js.map +1 -1
  17. package/dist/fiber-reconciler.js.map +1 -1
  18. package/dist/fiber-scheduler.js.map +1 -1
  19. package/dist/fiber-work-loop.js.map +1 -1
  20. package/dist/fiber.js.map +1 -1
  21. package/dist/flight-decoder.js.map +1 -1
  22. package/dist/flight-element-builder.js.map +1 -1
  23. package/dist/flight-parser.js.map +1 -1
  24. package/dist/flight-protocol.js.map +1 -1
  25. package/dist/flight-types.js.map +1 -1
  26. package/dist/flight.js.map +1 -1
  27. package/dist/hooks.js.map +1 -1
  28. package/dist/hydration.js.map +1 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/internal.js.map +1 -1
  31. package/dist/jsx-dev-runtime.js.map +1 -1
  32. package/dist/jsx-runtime.js.map +1 -1
  33. package/dist/prop-comparison.js.map +1 -1
  34. package/dist/reconcile-types.js.map +1 -1
  35. package/dist/reconciler.js.map +1 -1
  36. package/dist/render.js.map +1 -1
  37. package/dist/root.js.map +1 -1
  38. package/dist/scheduler.js.map +1 -1
  39. package/dist/suspense.js.map +1 -1
  40. package/dist/thenable.js.map +1 -1
  41. package/dist/url-safety.js.map +1 -1
  42. package/package.json +4 -3
  43. package/src/class-component.ts +386 -0
  44. package/src/context.ts +140 -0
  45. package/src/devtools.ts +1275 -0
  46. package/src/dom-children.ts +34 -0
  47. package/src/dom-props.ts +408 -0
  48. package/src/element.ts +317 -0
  49. package/src/event-listeners.ts +27 -0
  50. package/src/event-priority.ts +1 -0
  51. package/src/event-replay.ts +154 -0
  52. package/src/event-types.ts +18 -0
  53. package/src/events.ts +384 -0
  54. package/src/fiber-child.ts +364 -0
  55. package/src/fiber-commit.ts +83 -0
  56. package/src/fiber-flags.ts +21 -0
  57. package/src/fiber-host.ts +1564 -0
  58. package/src/fiber-lanes.ts +99 -0
  59. package/src/fiber-reconciler.ts +639 -0
  60. package/src/fiber-scheduler.ts +435 -0
  61. package/src/fiber-work-loop.ts +224 -0
  62. package/src/fiber.ts +148 -0
  63. package/src/flight-decoder.ts +205 -0
  64. package/src/flight-element-builder.ts +110 -0
  65. package/src/flight-parser.ts +698 -0
  66. package/src/flight-protocol.ts +71 -0
  67. package/src/flight-types.ts +148 -0
  68. package/src/flight.ts +162 -0
  69. package/src/hooks.ts +1940 -0
  70. package/src/hydration.ts +314 -0
  71. package/src/index.ts +95 -0
  72. package/src/internal.ts +7 -0
  73. package/src/jsx-dev-runtime.ts +40 -0
  74. package/src/jsx-runtime.ts +119 -0
  75. package/src/prop-comparison.ts +50 -0
  76. package/src/reconcile-types.ts +26 -0
  77. package/src/reconciler.ts +692 -0
  78. package/src/render.ts +29 -0
  79. package/src/root.ts +493 -0
  80. package/src/scheduler.ts +157 -0
  81. package/src/suspense.ts +317 -0
  82. package/src/thenable.ts +7 -0
  83. package/src/url-safety.ts +7 -0
@@ -0,0 +1,692 @@
1
+ import {
2
+ Activity,
3
+ Fragment,
4
+ ERROR_BOUNDARY_TYPE,
5
+ FORWARD_REF_TYPE,
6
+ Profiler,
7
+ Suspense,
8
+ SuspenseList,
9
+ LAZY_TYPE,
10
+ MEMO_TYPE,
11
+ STRICT_MODE_TYPE,
12
+ isReactCompatElement,
13
+ isReactCompatPortal,
14
+ type ReactCompatElement,
15
+ type ReactCompatNode,
16
+ type ReactCompatPortal,
17
+ } from "./element.js";
18
+ import {
19
+ isReactCompatConsumer,
20
+ isReactCompatProvider,
21
+ renderWithContextProvider,
22
+ useContext,
23
+ } from "./context.js";
24
+ import {
25
+ hasStableExternalStores,
26
+ restoreRuntimeSnapshot,
27
+ renderWithProfiler,
28
+ renderWithStrictMode,
29
+ renderWithRootRuntime,
30
+ takeRuntimeSnapshot,
31
+ type RootRuntime,
32
+ } from "./hooks.js";
33
+ import { commitDevToolsRoot } from "./devtools.js";
34
+ import { applyPostChildFormProps, applyProps } from "./dom-props.js";
35
+ import { syncChildNodes, syncScopedChildNodes } from "./dom-children.js";
36
+ import { setLogicalEventParent } from "./events.js";
37
+ import {
38
+ getHydrationScope,
39
+ reportElementTextMismatch,
40
+ reportExtraHydrationNodes,
41
+ reportHydrationNodeTypeMismatch,
42
+ reportMissingHydrationNode,
43
+ reportRecoverable,
44
+ type RenderOptions,
45
+ withHydrationComponentStack,
46
+ } from "./hydration.js";
47
+ import {
48
+ isClassComponentType,
49
+ reconcileClassComponent,
50
+ reconcileErrorBoundary,
51
+ } from "./class-component.js";
52
+ import {
53
+ isSuspensePrimaryRenderActive,
54
+ reconcileSuspense,
55
+ reconcileSuspenseList,
56
+ } from "./suspense.js";
57
+ import { areMemoPropsEqual } from "./prop-comparison.js";
58
+ import type { ReconcileResult } from "./reconcile-types.js";
59
+
60
+ const nodeKeys = new WeakMap<Node, string>();
61
+
62
+ interface MemoRenderState {
63
+ props: Record<string, unknown>;
64
+ nodeCount: number;
65
+ instanceKeys: string[];
66
+ }
67
+
68
+ const memoRenderStates = new WeakMap<RootRuntime, Map<string, MemoRenderState>>();
69
+
70
+ export function renderIntoContainer(
71
+ container: Element,
72
+ element: unknown,
73
+ runtime: RootRuntime,
74
+ options: RenderOptions & {
75
+ resumeId?: string;
76
+ consumeResumeMarkers?: boolean;
77
+ } = {},
78
+ ): void {
79
+ for (let attempt = 0; attempt < 25; attempt += 1) {
80
+ runtime.beginRender();
81
+ let committed = false;
82
+
83
+ try {
84
+ for (const portalContainer of runtime.portalContainers) {
85
+ portalContainer.replaceChildren();
86
+ }
87
+ runtime.portalContainers.clear();
88
+
89
+ const scope = getHydrationScope(container, options.resumeId);
90
+ const renderOptions = { ...options, eventRoot: container };
91
+ const nodes = reconcileNodeList(
92
+ scope.parent,
93
+ scope.previousNodes,
94
+ element as ReactCompatNode,
95
+ runtime,
96
+ "0",
97
+ renderOptions,
98
+ );
99
+
100
+ if (!hasStableExternalStores(runtime)) {
101
+ continue;
102
+ }
103
+
104
+ syncScopedChildNodes(scope.parent, scope.before, scope.after, nodes);
105
+
106
+ if (options.consumeResumeMarkers === true) {
107
+ scope.before?.parentNode?.removeChild(scope.before);
108
+ scope.after?.parentNode?.removeChild(scope.after);
109
+ }
110
+ committed = true;
111
+ } finally {
112
+ runtime.endRender(committed);
113
+ }
114
+
115
+ runtime.flushEffects();
116
+ commitDevToolsRoot(container, element as ReactCompatNode);
117
+ return;
118
+ }
119
+
120
+ throw new Error("Store unstable.");
121
+ }
122
+
123
+ function reconcileNodeList(
124
+ parent: ParentNode,
125
+ previousNodes: readonly Node[],
126
+ node: ReactCompatNode,
127
+ runtime: RootRuntime,
128
+ path: string,
129
+ options: RenderOptions = {},
130
+ ): Node[] {
131
+ const result = reconcileNode(parent, previousNodes, node, runtime, path, options);
132
+ return result.nodes;
133
+ }
134
+
135
+ function reconcileNode(
136
+ parent: ParentNode,
137
+ previousNodes: readonly Node[],
138
+ node: ReactCompatNode,
139
+ runtime: RootRuntime,
140
+ path: string,
141
+ options: RenderOptions = {},
142
+ ): ReconcileResult {
143
+ if (node === null || node === undefined || typeof node === "boolean") {
144
+ return { nodes: [], consumed: 0 };
145
+ }
146
+
147
+ if (typeof node === "string" || typeof node === "number") {
148
+ const existing = previousNodes[0];
149
+ const text =
150
+ existing instanceof Text ? existing : document.createTextNode("");
151
+ if (existing === undefined) {
152
+ reportMissingHydrationNode(options, path);
153
+ } else if (!(existing instanceof Text)) {
154
+ reportHydrationNodeTypeMismatch(options, path, "text", existing);
155
+ }
156
+ if (existing instanceof Text && existing.data !== String(node)) {
157
+ reportRecoverable(
158
+ options,
159
+ "text",
160
+ path,
161
+ new Error("Hydration text mismatch."),
162
+ );
163
+ }
164
+ text.data = String(node);
165
+ return { nodes: [text], consumed: existing instanceof Text ? 1 : 0 };
166
+ }
167
+
168
+ if (Array.isArray(node)) {
169
+ return reconcileSequence(parent, previousNodes, node, runtime, path, options);
170
+ }
171
+
172
+ if (!isReactCompatElement(node)) {
173
+ if (isReactCompatPortal(node)) {
174
+ return reconcilePortal(node, parent, runtime, path, options);
175
+ }
176
+
177
+ throw new Error("Invalid react-compat element.");
178
+ }
179
+
180
+ return reconcileElement(parent, previousNodes, node, runtime, path, options);
181
+ }
182
+
183
+ function reconcilePortal(
184
+ portal: ReactCompatPortal,
185
+ parent: ParentNode,
186
+ runtime: RootRuntime,
187
+ path: string,
188
+ options: RenderOptions = {},
189
+ ): ReconcileResult {
190
+ runtime.portalContainers.add(portal.container);
191
+ setLogicalEventParent(portal.container, parent);
192
+ const nodes = reconcileNodeList(
193
+ portal.container,
194
+ Array.from(portal.container.childNodes),
195
+ portal.children,
196
+ runtime,
197
+ `${path}.portal`,
198
+ { ...options, eventRoot: portal.container },
199
+ );
200
+ syncChildNodes(portal.container, nodes);
201
+ return { nodes: [], consumed: 0 };
202
+ }
203
+
204
+ function reconcileSequence(
205
+ parent: ParentNode,
206
+ previousNodes: readonly Node[],
207
+ children: readonly ReactCompatNode[],
208
+ runtime: RootRuntime,
209
+ path: string,
210
+ options: RenderOptions = {},
211
+ ): ReconcileResult {
212
+ const keyedNodes = collectKeyedNodes(previousNodes);
213
+ const nodes: Node[] = [];
214
+ let previousIndex = 0;
215
+
216
+ children.forEach((child, index) => {
217
+ const key = getNodeKey(child);
218
+ const previousForChild =
219
+ key === undefined
220
+ ? previousNodes.slice(previousIndex)
221
+ : keyedNodes.size === 0
222
+ ? previousNodes.slice(previousIndex)
223
+ : keyedNodes.get(key) === undefined
224
+ ? []
225
+ : [keyedNodes.get(key) as Node];
226
+ const result = reconcileNode(
227
+ parent,
228
+ previousForChild,
229
+ child,
230
+ runtime,
231
+ `${path}.${getNodePathSegment(child, index)}`,
232
+ options,
233
+ );
234
+
235
+ if (key === undefined || keyedNodes.size === 0) {
236
+ previousIndex += result.consumed;
237
+ }
238
+
239
+ for (const childNode of result.nodes) {
240
+ if (key !== undefined) {
241
+ nodeKeys.set(childNode, key);
242
+ }
243
+
244
+ nodes.push(childNode);
245
+ }
246
+ });
247
+
248
+ return { nodes, consumed: nodes.length };
249
+ }
250
+
251
+ function reconcileElement(
252
+ parent: ParentNode,
253
+ previousNodes: readonly Node[],
254
+ element: ReactCompatElement,
255
+ runtime: RootRuntime,
256
+ path: string,
257
+ options: RenderOptions = {},
258
+ ): ReconcileResult {
259
+ if (element.type === Fragment) {
260
+ return reconcileNode(
261
+ parent,
262
+ previousNodes,
263
+ element.props.children,
264
+ runtime,
265
+ `${path}.f`,
266
+ options,
267
+ );
268
+ }
269
+
270
+ if (element.type === Activity) {
271
+ const children =
272
+ (element.props as { mode?: unknown }).mode === "hidden"
273
+ ? null
274
+ : element.props.children;
275
+ return reconcileNode(
276
+ parent,
277
+ previousNodes,
278
+ children,
279
+ runtime,
280
+ `${path}.activity`,
281
+ options,
282
+ );
283
+ }
284
+
285
+ if (element.type === Profiler) {
286
+ return renderWithProfiler(
287
+ runtime,
288
+ `${path}.profiler`,
289
+ element.props,
290
+ () =>
291
+ reconcileNode(
292
+ parent,
293
+ previousNodes,
294
+ element.props.children,
295
+ runtime,
296
+ `${path}.profiler`,
297
+ options,
298
+ ),
299
+ );
300
+ }
301
+
302
+ if (element.type === STRICT_MODE_TYPE) {
303
+ const snapshot = takeRuntimeSnapshot(runtime);
304
+ try {
305
+ reconcileNode(
306
+ parent,
307
+ [],
308
+ element.props.children,
309
+ runtime,
310
+ `${path}.strict.preview`,
311
+ options,
312
+ );
313
+ } finally {
314
+ restoreRuntimeSnapshot(runtime, snapshot);
315
+ }
316
+
317
+ return renderWithStrictMode(runtime, () =>
318
+ reconcileNode(
319
+ parent,
320
+ previousNodes,
321
+ element.props.children,
322
+ runtime,
323
+ `${path}.strict`,
324
+ options,
325
+ ),
326
+ );
327
+ }
328
+
329
+ if (element.type === Suspense) {
330
+ return reconcileSuspense(
331
+ parent,
332
+ previousNodes,
333
+ element,
334
+ runtime,
335
+ path,
336
+ options,
337
+ reconcileNode,
338
+ );
339
+ }
340
+
341
+ if (element.type === SuspenseList) {
342
+ return reconcileSuspenseList(
343
+ parent,
344
+ previousNodes,
345
+ element,
346
+ runtime,
347
+ path,
348
+ options,
349
+ reconcileNode,
350
+ reconcileSequence,
351
+ );
352
+ }
353
+
354
+ if (element.type === ERROR_BOUNDARY_TYPE) {
355
+ return reconcileErrorBoundary(
356
+ parent,
357
+ previousNodes,
358
+ element,
359
+ runtime,
360
+ path,
361
+ options,
362
+ reconcileNode,
363
+ );
364
+ }
365
+
366
+ const elementType = element.type;
367
+
368
+ if (isReactCompatProvider(elementType)) {
369
+ return renderWithContextProvider(
370
+ elementType,
371
+ element.props.value,
372
+ () =>
373
+ reconcileNode(
374
+ parent,
375
+ previousNodes,
376
+ element.props.children,
377
+ runtime,
378
+ `${path}.p`,
379
+ options,
380
+ ),
381
+ );
382
+ }
383
+
384
+ if (isReactCompatConsumer(elementType)) {
385
+ const children = element.props.children;
386
+ const render =
387
+ typeof children === "function"
388
+ ? (children as (value: unknown) => ReactCompatNode)
389
+ : () => null;
390
+ return reconcileNode(
391
+ parent,
392
+ previousNodes,
393
+ render(useContext(elementType.context)),
394
+ runtime,
395
+ `${path}.consumer`,
396
+ options,
397
+ );
398
+ }
399
+
400
+ if (isForwardRefType(elementType)) {
401
+ return renderWithRootRuntime(runtime, path, () =>
402
+ reconcileNode(
403
+ parent,
404
+ previousNodes,
405
+ elementType.render(element.props, element.ref),
406
+ runtime,
407
+ `${path}.forwardRef`,
408
+ options,
409
+ ),
410
+ );
411
+ }
412
+
413
+ if (isMemoType(elementType)) {
414
+ const memoPath = `${path}.memo`;
415
+ const memoStates = getMemoRenderStates(runtime);
416
+ const previousMemoState = memoStates.get(memoPath);
417
+
418
+ if (
419
+ previousMemoState !== undefined &&
420
+ previousNodes.length >= previousMemoState.nodeCount &&
421
+ !hasDirtyInstance(runtime, previousMemoState.instanceKeys) &&
422
+ areMemoPropsEqual(elementType, previousMemoState.props, element.props)
423
+ ) {
424
+ markActiveInstanceKeys(runtime, previousMemoState.instanceKeys);
425
+ return {
426
+ nodes: previousNodes.slice(0, previousMemoState.nodeCount),
427
+ consumed: previousMemoState.nodeCount,
428
+ };
429
+ }
430
+
431
+ const result = reconcileElement(
432
+ parent,
433
+ previousNodes,
434
+ {
435
+ ...element,
436
+ type: elementType.type,
437
+ },
438
+ runtime,
439
+ memoPath,
440
+ options,
441
+ );
442
+ memoStates.set(memoPath, {
443
+ props: { ...element.props },
444
+ nodeCount: result.nodes.length,
445
+ instanceKeys: collectInstanceKeys(runtime, memoPath),
446
+ });
447
+ return result;
448
+ }
449
+
450
+ if (isLazyType(elementType)) {
451
+ if (elementType.status === "resolved" && elementType.resolved !== undefined) {
452
+ return reconcileElement(
453
+ parent,
454
+ previousNodes,
455
+ { ...element, type: elementType.resolved },
456
+ runtime,
457
+ `${path}.lazy`,
458
+ options,
459
+ );
460
+ }
461
+
462
+ if (elementType.status === "rejected") {
463
+ throw elementType.error;
464
+ }
465
+
466
+ if (elementType.status === "uninitialized") {
467
+ elementType.status = "pending";
468
+ elementType.promise = elementType
469
+ .load()
470
+ .then((module) => {
471
+ elementType.status = "resolved";
472
+ elementType.resolved = module.default;
473
+ runtime.rerender();
474
+ })
475
+ .catch((error: unknown) => {
476
+ elementType.status = "rejected";
477
+ elementType.error = error;
478
+ runtime.rerender();
479
+ });
480
+ }
481
+
482
+ if (isSuspensePrimaryRenderActive()) {
483
+ throw elementType.promise;
484
+ }
485
+
486
+ return { nodes: [], consumed: 0 };
487
+ }
488
+
489
+ if (isClassComponentType(elementType)) {
490
+ return reconcileClassComponent(
491
+ parent,
492
+ previousNodes,
493
+ elementType,
494
+ element.props,
495
+ runtime,
496
+ path,
497
+ options,
498
+ reconcileNode,
499
+ (instance) => {
500
+ applyRef(element.ref, instance);
501
+ },
502
+ );
503
+ }
504
+
505
+ if (typeof elementType === "function") {
506
+ const functionComponent = elementType as (
507
+ props: Record<string, unknown>,
508
+ ) => ReactCompatNode;
509
+ return renderWithRootRuntime(runtime, path, () =>
510
+ reconcileNode(
511
+ parent,
512
+ previousNodes,
513
+ functionComponent(element.props),
514
+ runtime,
515
+ `${path}.0`,
516
+ withHydrationComponentStack(options, getComponentName(functionComponent)),
517
+ ),
518
+ );
519
+ }
520
+
521
+ if (typeof elementType !== "string") {
522
+ throw new Error("Invalid react-compat element type.");
523
+ }
524
+
525
+ const existing = previousNodes[0];
526
+ if (existing === undefined) {
527
+ reportMissingHydrationNode(options, path);
528
+ } else if (!(existing instanceof HTMLElement)) {
529
+ reportHydrationNodeTypeMismatch(options, path, `<${elementType}>`, existing);
530
+ }
531
+ if (
532
+ existing instanceof HTMLElement &&
533
+ existing.tagName.toLowerCase() !== elementType
534
+ ) {
535
+ reportRecoverable(
536
+ options,
537
+ "tag",
538
+ path,
539
+ new Error(
540
+ `Hydration tag mismatch: expected <${elementType}> but found <${existing.tagName.toLowerCase()}>.`,
541
+ ),
542
+ );
543
+ reportElementTextMismatch(options, `${path}.c`, existing, element.props.children);
544
+ }
545
+ const domElement =
546
+ existing instanceof HTMLElement &&
547
+ existing.tagName.toLowerCase() === elementType
548
+ ? existing
549
+ : document.createElement(elementType);
550
+
551
+ applyProps(domElement, element.props, path, {
552
+ ...options,
553
+ preserveHydrationAttributes:
554
+ options.hydration !== undefined &&
555
+ existing instanceof HTMLElement &&
556
+ existing.tagName.toLowerCase() === elementType,
557
+ });
558
+ const previousChildNodes = Array.from(domElement.childNodes);
559
+ const childResult = reconcileNode(
560
+ domElement,
561
+ previousChildNodes,
562
+ element.props.children,
563
+ runtime,
564
+ `${path}.c`,
565
+ options,
566
+ );
567
+ reportExtraHydrationNodes(
568
+ options,
569
+ `${path}.c`,
570
+ previousChildNodes,
571
+ childResult.consumed,
572
+ );
573
+ syncChildNodes(domElement, childResult.nodes);
574
+ applyPostChildFormProps(domElement, element.props);
575
+ applyRef(element.ref, domElement);
576
+ return { nodes: [domElement], consumed: existing === undefined ? 0 : 1 };
577
+ }
578
+
579
+ function collectKeyedNodes(nodes: readonly Node[]): Map<string, Node> {
580
+ const keyedNodes = new Map<string, Node>();
581
+
582
+ for (const node of nodes) {
583
+ const key = nodeKeys.get(node);
584
+
585
+ if (key !== undefined) {
586
+ keyedNodes.set(key, node);
587
+ }
588
+ }
589
+
590
+ return keyedNodes;
591
+ }
592
+
593
+ function getNodePathSegment(node: ReactCompatNode, index: number): string {
594
+ const key = getNodeKey(node);
595
+ return key === undefined ? String(index) : `k:${key}`;
596
+ }
597
+
598
+ function getNodeKey(node: ReactCompatNode): string | undefined {
599
+ return isReactCompatElement(node) && node.key !== null
600
+ ? node.key
601
+ : undefined;
602
+ }
603
+
604
+ function getComponentName(component: Function): string {
605
+ return component.name === "" ? "Anonymous" : component.name;
606
+ }
607
+
608
+ function isForwardRefType(
609
+ value: unknown,
610
+ ): value is { $$typeof: typeof FORWARD_REF_TYPE; render: (props: Record<string, unknown>, ref: unknown) => ReactCompatNode } {
611
+ return (
612
+ typeof value === "object" &&
613
+ value !== null &&
614
+ (value as { $$typeof?: unknown }).$$typeof === FORWARD_REF_TYPE
615
+ );
616
+ }
617
+
618
+ function isMemoType(
619
+ value: unknown,
620
+ ): value is {
621
+ $$typeof: typeof MEMO_TYPE;
622
+ type: ReactCompatElement["type"];
623
+ compare?: (
624
+ previous: Record<string, unknown>,
625
+ next: Record<string, unknown>,
626
+ ) => boolean;
627
+ } {
628
+ return (
629
+ typeof value === "object" &&
630
+ value !== null &&
631
+ (value as { $$typeof?: unknown }).$$typeof === MEMO_TYPE
632
+ );
633
+ }
634
+
635
+ function getMemoRenderStates(runtime: RootRuntime): Map<string, MemoRenderState> {
636
+ const existing = memoRenderStates.get(runtime);
637
+
638
+ if (existing !== undefined) {
639
+ return existing;
640
+ }
641
+
642
+ const created = new Map<string, MemoRenderState>();
643
+ memoRenderStates.set(runtime, created);
644
+ return created;
645
+ }
646
+
647
+ function collectInstanceKeys(runtime: RootRuntime, prefix: string): string[] {
648
+ return Array.from(runtime.instances.keys()).filter((key) =>
649
+ key === prefix || key.startsWith(`${prefix}.`),
650
+ );
651
+ }
652
+
653
+ function markActiveInstanceKeys(runtime: RootRuntime, keys: readonly string[]): void {
654
+ for (const key of keys) {
655
+ runtime.activeInstanceKeys?.add(key);
656
+ }
657
+ }
658
+
659
+ function hasDirtyInstance(runtime: RootRuntime, keys: readonly string[]): boolean {
660
+ return keys.some(
661
+ (key) =>
662
+ (runtime.instances.get(key) as { dirty?: boolean } | undefined)?.dirty === true,
663
+ );
664
+ }
665
+
666
+ function isLazyType(
667
+ value: unknown,
668
+ ): value is {
669
+ $$typeof: typeof LAZY_TYPE;
670
+ load: () => Promise<{ default: ReactCompatElement["type"] }>;
671
+ status: "uninitialized" | "pending" | "resolved" | "rejected";
672
+ promise?: Promise<void>;
673
+ resolved?: ReactCompatElement["type"];
674
+ error?: unknown;
675
+ } {
676
+ return (
677
+ typeof value === "object" &&
678
+ value !== null &&
679
+ (value as { $$typeof?: unknown }).$$typeof === LAZY_TYPE
680
+ );
681
+ }
682
+
683
+ function applyRef(ref: unknown, node: unknown): void {
684
+ if (typeof ref === "function") {
685
+ ref(node);
686
+ return;
687
+ }
688
+
689
+ if (typeof ref === "object" && ref !== null && "current" in ref) {
690
+ (ref as { current: unknown }).current = node;
691
+ }
692
+ }
package/src/render.ts ADDED
@@ -0,0 +1,29 @@
1
+ export {
2
+ createRoot,
3
+ createStreamingHydrationRoot,
4
+ flushSync,
5
+ hydrateRoot,
6
+ render,
7
+ unmountComponentAtNode,
8
+ } from "./root.js";
9
+ export type {
10
+ HydrateRootOptions,
11
+ Root,
12
+ RootOptions,
13
+ SelectiveHydrationBoundary,
14
+ SelectiveHydrationOptions,
15
+ StreamingHydrationRoot,
16
+ StreamingHydrationRootOptions,
17
+ } from "./root.js";
18
+ export { applyStreamingHydrationFragments } from "./hydration.js";
19
+ export {
20
+ enableEventHydrationManifestReplay,
21
+ enableHydrationEventReplay,
22
+ queueHydrationEvent,
23
+ readEventHydrationManifest,
24
+ } from "./event-replay.js";
25
+ export type {
26
+ EventHydrationManifest,
27
+ EventHydrationManifestEntry,
28
+ } from "./event-replay.js";
29
+ export type { HydrationRecoverableErrorInfo } from "./hydration.js";