@reckona/mreact-compat 0.0.171 → 0.0.173

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 (58) hide show
  1. package/dist/devtools.js +2 -0
  2. package/dist/devtools.js.map +1 -1
  3. package/dist/dom-host-rules.d.ts.map +1 -1
  4. package/dist/dom-host-rules.js +4 -1
  5. package/dist/dom-host-rules.js.map +1 -1
  6. package/dist/dom-props.js +2 -1
  7. package/dist/dom-props.js.map +1 -1
  8. package/dist/element.d.ts +13 -1
  9. package/dist/element.d.ts.map +1 -1
  10. package/dist/element.js +30 -2
  11. package/dist/element.js.map +1 -1
  12. package/dist/fiber-commit.js +2 -1
  13. package/dist/fiber-commit.js.map +1 -1
  14. package/dist/fiber-host.d.ts +1 -1
  15. package/dist/fiber-host.d.ts.map +1 -1
  16. package/dist/fiber-host.js +1 -1
  17. package/dist/fiber-host.js.map +1 -1
  18. package/dist/fiber.d.ts +2 -1
  19. package/dist/fiber.d.ts.map +1 -1
  20. package/dist/fiber.js +2 -0
  21. package/dist/fiber.js.map +1 -1
  22. package/dist/hooks.d.ts +9 -0
  23. package/dist/hooks.d.ts.map +1 -1
  24. package/dist/hooks.js +127 -44
  25. package/dist/hooks.js.map +1 -1
  26. package/dist/host-reconciler.d.ts +2 -0
  27. package/dist/host-reconciler.d.ts.map +1 -1
  28. package/dist/host-reconciler.js +696 -55
  29. package/dist/host-reconciler.js.map +1 -1
  30. package/dist/jsx-dev-runtime.d.ts +3 -1
  31. package/dist/jsx-dev-runtime.d.ts.map +1 -1
  32. package/dist/jsx-dev-runtime.js +3 -1
  33. package/dist/jsx-dev-runtime.js.map +1 -1
  34. package/dist/jsx-runtime.d.ts +4 -2
  35. package/dist/jsx-runtime.d.ts.map +1 -1
  36. package/dist/jsx-runtime.js +9 -1
  37. package/dist/jsx-runtime.js.map +1 -1
  38. package/dist/reactive-prop-cell.d.ts +13 -0
  39. package/dist/reactive-prop-cell.d.ts.map +1 -0
  40. package/dist/reactive-prop-cell.js +36 -0
  41. package/dist/reactive-prop-cell.js.map +1 -0
  42. package/dist/root.d.ts.map +1 -1
  43. package/dist/root.js +8 -5
  44. package/dist/root.js.map +1 -1
  45. package/package.json +3 -3
  46. package/src/devtools.ts +2 -0
  47. package/src/dom-host-rules.ts +4 -1
  48. package/src/dom-props.ts +2 -1
  49. package/src/element.ts +60 -3
  50. package/src/fiber-commit.ts +3 -1
  51. package/src/fiber-host.ts +2 -0
  52. package/src/fiber.ts +4 -0
  53. package/src/hooks.ts +187 -46
  54. package/src/host-reconciler.ts +1031 -70
  55. package/src/jsx-dev-runtime.ts +4 -0
  56. package/src/jsx-runtime.ts +16 -0
  57. package/src/reactive-prop-cell.ts +67 -0
  58. package/src/root.ts +14 -4
@@ -1,4 +1,5 @@
1
- import { Activity, ERROR_BOUNDARY_TYPE, FORWARD_REF_TYPE, Fragment, HOST_CHILDREN_ONLY_PROPS_META, LAZY_TYPE, MEMO_TYPE, Profiler, REACTIVE_TEXT_BINDING_META, STRICT_MODE_TYPE, Suspense, SuspenseList, isReactCompatElement, isReactCompatPortal, } from "./element.js";
1
+ import { Activity, ERROR_BOUNDARY_TYPE, FORWARD_REF_TYPE, Fragment, HOST_CHILDREN_ONLY_PROPS_META, LAZY_TYPE, MEMO_TYPE, Profiler, REACTIVE_DOM_BLOCK_TYPE, REACTIVE_TEXT_BINDING_META, STRICT_MODE_TYPE, Suspense, SuspenseList, isReactCompatElement, isReactCompatPortal, } from "./element.js";
2
+ import { createReactivePropCell, createReactivePropProxy, setReactivePropCell, } from "./reactive-prop-cell.js";
2
3
  import { consumerContext, isReactCompatConsumer, isReactCompatProvider, renderWithContextProvider, useContext, } from "./context.js";
3
4
  import { applyPostChildFormProps, applyProps } from "./dom-props.js";
4
5
  import { syncChildNodes, syncOwnedChildNodes, syncScopedChildNodes } from "./dom-children.js";
@@ -13,6 +14,7 @@ import { areMemoPropsEqual, getPendingProps, shallowEqual } from "./prop-compari
13
14
  import { reportElementTextMismatch, reportExtraHydrationNodes, reportHydrationNodeTypeMismatch, reportMissingHydrationNode, reportReactSuspenseServerError, reportRecoverable, withHydrationComponentStack, } from "./hydration.js";
14
15
  const committedPortalContainers = new Set();
15
16
  const pendingHostRefUpdates = [];
17
+ const emptyInstanceKeys = [];
16
18
  const SKIP_COMMIT_PATH = "\0";
17
19
  const hasOwnProperty = Object.prototype.hasOwnProperty;
18
20
  let suspensePrimaryRenderDepth = 0;
@@ -49,6 +51,9 @@ export function canRenderHostFiber(node) {
49
51
  if (node.type === ERROR_BOUNDARY_TYPE) {
50
52
  return true;
51
53
  }
54
+ if (node.type === REACTIVE_DOM_BLOCK_TYPE) {
55
+ return true;
56
+ }
52
57
  if (isReactCompatProvider(node.type)) {
53
58
  return canRenderHostFiber(node.props.children);
54
59
  }
@@ -100,7 +105,12 @@ export function commitHostFiberRoot(root, finishedWork, options = {}) {
100
105
  pendingHostRefUpdates.length = 0;
101
106
  const commitPath = getRootCommitPath(options);
102
107
  if (!hasChildListMutation(finishedWork)) {
103
- commitHostDirtyChildren(finishedWork.child, root.container, root.container, commitPath, options);
108
+ commitHostDirtyChildrenOf(finishedWork, finishedWork.child, root.container, root.container, commitPath, options);
109
+ committed = true;
110
+ return;
111
+ }
112
+ if (finishedWork.childListChanged &&
113
+ commitHostKeyedChildListMutationFiber(finishedWork, root.container, root.container, commitPath, options)) {
104
114
  committed = true;
105
115
  return;
106
116
  }
@@ -151,6 +161,53 @@ export function commitHydratingHostFiberRoot(root, finishedWork, scope, options
151
161
  scope.after?.parentNode?.removeChild(scope.after);
152
162
  }
153
163
  }
164
+ export function disposeHostFiberResources(fiber) {
165
+ if (fiber === undefined || fiber.hasDisposableResources !== true) {
166
+ return;
167
+ }
168
+ // Dispose this fiber and its SUBTREE only — not its siblings. This is called
169
+ // once per deleted fiber, and deleted siblings are disposed by their own
170
+ // calls; walking siblings here re-walked the whole deleted list per deletion
171
+ // (O(n^2) on a cleared 1k-row reactive list). The dedupe set is allocated
172
+ // once for the subtree walk.
173
+ const seen = new Set();
174
+ if (fiber.tag === "reactive-dom-block") {
175
+ disposeReactiveDomBlockState(fiber.stateNode, seen);
176
+ }
177
+ disposeHostFiberChildResources(fiber.child, seen);
178
+ }
179
+ export function disposeUnretainedHostFiberResources(fiber, retained) {
180
+ if (fiber === undefined || fiber.hasDisposableResources !== true || retained.has(fiber)) {
181
+ return;
182
+ }
183
+ const seen = new Set();
184
+ disposeUnretainedHostFiberSubtreeResources(fiber, seen, retained);
185
+ }
186
+ function disposeHostFiberChildResources(fiber, seen) {
187
+ let cursor = fiber;
188
+ while (cursor !== undefined) {
189
+ if (cursor.hasDisposableResources === true) {
190
+ if (cursor.tag === "reactive-dom-block") {
191
+ disposeReactiveDomBlockState(cursor.stateNode, seen);
192
+ }
193
+ disposeHostFiberChildResources(cursor.child, seen);
194
+ }
195
+ cursor = cursor.sibling;
196
+ }
197
+ }
198
+ function disposeUnretainedHostFiberSubtreeResources(fiber, seen, retained) {
199
+ if (fiber === undefined || retained.has(fiber) || fiber.hasDisposableResources !== true) {
200
+ return;
201
+ }
202
+ if (fiber.tag === "reactive-dom-block") {
203
+ disposeReactiveDomBlockState(fiber.stateNode, seen);
204
+ }
205
+ let child = fiber.child;
206
+ while (child !== undefined) {
207
+ disposeUnretainedHostFiberSubtreeResources(child, seen, retained);
208
+ child = child.sibling;
209
+ }
210
+ }
154
211
  function reconcileHostChild(parent, currentFirstChild, node, runtime, path, options = {}) {
155
212
  resetFiberRefSubtree(parent);
156
213
  parent.subtreeFlags = NoFlags;
@@ -166,7 +223,8 @@ function reconcileHostChild(parent, currentFirstChild, node, runtime, path, opti
166
223
  const children = Array.isArray(node) ? node : undefined;
167
224
  const rowResult = children === undefined
168
225
  ? undefined
169
- : reconcileKeyedRowHostChildren(parent, currentFirstChild, children, options);
226
+ : reconcileKeyedRowHostChildren(parent, currentFirstChild, children, options) ??
227
+ reconcileKeyedMemoRowHostChildren(parent, currentFirstChild, children, runtime, path, options);
170
228
  if (rowResult !== undefined) {
171
229
  return rowResult;
172
230
  }
@@ -182,6 +240,7 @@ function reconcileHostChild(parent, currentFirstChild, node, runtime, path, opti
182
240
  let skipRemainingKeyedLookup = false;
183
241
  let sequentialCurrentCursor = currentFirstChild;
184
242
  let childListOrderChanged = false;
243
+ let dirtyChildren;
185
244
  let usedCurrentChildren = currentFirstChild === undefined || hasKeyedChildren ? undefined : new Set();
186
245
  const ensureUsedCurrentChildren = () => {
187
246
  if (usedCurrentChildren === undefined) {
@@ -224,11 +283,17 @@ function reconcileHostChild(parent, currentFirstChild, node, runtime, path, opti
224
283
  else if (children !== undefined &&
225
284
  currentKeyed?.sibling?.key === key &&
226
285
  canSkipSingleDeletedKeyedFiber(children, index, currentKeyed.sibling)) {
286
+ const deleted = currentKeyed;
287
+ const matched = currentKeyed.sibling;
288
+ const suffixResult = tryReuseDependencyFreeMemoRemovalSuffix(parent, children, index, deleted, matched, runtime, options, first, previous, consumed);
289
+ if (suffixResult !== undefined) {
290
+ return suffixResult;
291
+ }
227
292
  childListOrderChanged = true;
228
293
  ensureUsedCurrentChildren();
229
- matchedCurrent = currentKeyed.sibling;
294
+ matchedCurrent = matched;
230
295
  canReuseMatchedCurrentFiber = false;
231
- currentKeyed = currentKeyed.sibling.sibling;
296
+ currentKeyed = matched.sibling;
232
297
  }
233
298
  else {
234
299
  if (children !== undefined &&
@@ -287,6 +352,13 @@ function reconcileHostChild(parent, currentFirstChild, node, runtime, path, opti
287
352
  && fiber.memoizedState === undefined) {
288
353
  fiber.memoizedState = index;
289
354
  }
355
+ else if (fiber.tag === "memo" &&
356
+ (fiber.stateNode === undefined || typeof fiber.stateNode === "number")) {
357
+ fiber.stateNode = index;
358
+ }
359
+ if (hasHostCommitWork(fiber)) {
360
+ (dirtyChildren ??= []).push(fiber);
361
+ }
290
362
  previous = fiber;
291
363
  }
292
364
  if (usedCurrentChildren === undefined && hasKeyedChildren && currentKeyed !== undefined) {
@@ -297,6 +369,7 @@ function reconcileHostChild(parent, currentFirstChild, node, runtime, path, opti
297
369
  }
298
370
  parent.childListChanged =
299
371
  childListOrderChanged || childFiberListShapeChanged(currentFirstChild, first);
372
+ recordDirtyChildCommitHints(parent, dirtyChildren);
300
373
  return { fiber: first, consumed };
301
374
  }
302
375
  function reconcileKeyedRowHostChildren(parent, currentFirstChild, children, options) {
@@ -315,6 +388,7 @@ function reconcileKeyedRowHostChildren(parent, currentFirstChild, children, opti
315
388
  let subtreeFlags = NoFlags;
316
389
  let subtreeChildListChanged = false;
317
390
  let hasRefSubtree = false;
391
+ let hasDisposableResources = false;
318
392
  let appendSuffix;
319
393
  const canReuseUnchangedRows = hasSameKeyOrderPrefix(currentFirstChild, children);
320
394
  const row = createKeyedRowHostElementScratch();
@@ -375,6 +449,9 @@ function reconcileKeyedRowHostChildren(parent, currentFirstChild, children, opti
375
449
  if (fiber.hasRefSubtree) {
376
450
  hasRefSubtree = true;
377
451
  }
452
+ if (fiber.hasDisposableResources) {
453
+ hasDisposableResources = true;
454
+ }
378
455
  subtreeFlags |= fiber.flags | fiber.subtreeFlags;
379
456
  subtreeChildListChanged =
380
457
  subtreeChildListChanged ||
@@ -390,6 +467,7 @@ function reconcileKeyedRowHostChildren(parent, currentFirstChild, children, opti
390
467
  markOptimizedChildrenForDeletion(parent, currentKeyed);
391
468
  }
392
469
  parent.hasRefSubtree = hasRefSubtree;
470
+ parent.hasDisposableResources = hasDisposableResources;
393
471
  parent.subtreeFlags = subtreeFlags;
394
472
  parent.subtreeChildListChanged = subtreeChildListChanged;
395
473
  parent.childListChanged = listShapeChanged;
@@ -398,6 +476,270 @@ function reconcileKeyedRowHostChildren(parent, currentFirstChild, children, opti
398
476
  }
399
477
  return { fiber: first, consumed: 0 };
400
478
  }
479
+ function isKeyedMemoRowCandidate(node) {
480
+ return (isReactCompatElement(node) &&
481
+ node.key !== null &&
482
+ node.ref === null &&
483
+ isMemoType(node.type));
484
+ }
485
+ // Marker the compiler stamps on a lowered host-only component that returns its
486
+ // props verbatim as a reactive block (`createReactiveDomBlock(render, props)`).
487
+ // Such a component is pure and structurally static, so on a props change the
488
+ // reconciler can drive its committed block straight through the prop cell instead
489
+ // of re-invoking the component (the block's bound DOM updates via subscriptions).
490
+ const STATIC_REACTIVE_BLOCK_MARKER = "__mreactStaticBlock";
491
+ function isStaticReactiveBlockComponent(type) {
492
+ return (typeof type === "function" &&
493
+ type[STATIC_REACTIVE_BLOCK_MARKER] === true);
494
+ }
495
+ // In-place bailout for a keyed memo row whose props are unchanged and which has
496
+ // no hook/context/effect dependencies. The caller has proven the key order is
497
+ // unchanged (hasSameKeyOrderPrefix), so the fiber keeps its position and can be
498
+ // reused WITHOUT createWorkInProgress — no allocation, and the retained subtree
499
+ // (including any reactive-dom-block subscriptions) is left untouched. Returns
500
+ // undefined when the row must re-render or has dependencies (handled by the
501
+ // general per-child path instead). Mirrors tryReuseDependencyFreeMemoBailout +
502
+ // getMemoBailoutFiber's in-place branch, but is safe to reuse a fiber with
503
+ // disposable resources because a stable position means nothing is disposed.
504
+ function tryReuseDependencyFreeKeyedMemoRow(matched, child) {
505
+ if (matched.tag !== "memo" ||
506
+ matched.type !== child.type ||
507
+ matched.hasRefSubtree === true ||
508
+ matched.hydrateExisting === true) {
509
+ return undefined;
510
+ }
511
+ const state = matched.memoizedState;
512
+ if (state === undefined ||
513
+ state.hasDirtyInstanceDependencies !== false ||
514
+ state.hasUnflushedEffectDependencies !== false ||
515
+ state.hasRetainedInstanceDependencies !== false ||
516
+ !areMemoPropsEqual(child.type, state.props, child.props)) {
517
+ return undefined;
518
+ }
519
+ matched.pendingProps = child.props;
520
+ matched.flags = NoFlags;
521
+ matched.subtreeFlags = NoFlags;
522
+ matched.childListChanged = false;
523
+ matched.subtreeChildListChanged = false;
524
+ matched.hostChildListChanged = false;
525
+ matched.child = getSkippedChild(matched);
526
+ return matched;
527
+ }
528
+ // In-place CHANGED-row update for a keyed memo row whose inner component is a
529
+ // compiler-marked static reactive block (returns its props verbatim as the block
530
+ // props). Instead of re-invoking the component to rebuild a throwaway block
531
+ // element, push the new props straight into the committed block's prop cell — the
532
+ // bound DOM updates via subscriptions, exactly as the normal block re-render path
533
+ // would, but without the memo/component render machinery. Same-order is proven by
534
+ // the caller, so the fiber is reused in place. Returns undefined when this is not
535
+ // a marked, dependency-free, single-block memo row (the general path handles it).
536
+ function tryCellUpdateStaticBlockMemoRow(matched, child) {
537
+ if (matched.tag !== "memo" ||
538
+ matched.type !== child.type ||
539
+ matched.hasRefSubtree === true ||
540
+ matched.hydrateExisting === true ||
541
+ !isStaticReactiveBlockComponent(matched.type.type)) {
542
+ return undefined;
543
+ }
544
+ const state = matched.memoizedState;
545
+ if (state === undefined ||
546
+ state.hasDirtyInstanceDependencies !== false ||
547
+ state.hasUnflushedEffectDependencies !== false ||
548
+ state.hasRetainedInstanceDependencies !== false) {
549
+ return undefined;
550
+ }
551
+ // Navigate memo -> component fiber -> reactive-dom-block fiber. The marked
552
+ // component renders exactly one static block, so anything else is unexpected.
553
+ const componentFiber = matched.child;
554
+ if (componentFiber === undefined || componentFiber.sibling !== undefined) {
555
+ return undefined;
556
+ }
557
+ const blockFiber = componentFiber.child;
558
+ if (blockFiber === undefined ||
559
+ blockFiber.tag !== "reactive-dom-block" ||
560
+ blockFiber.sibling !== undefined) {
561
+ return undefined;
562
+ }
563
+ const blockState = blockFiber.stateNode;
564
+ if (blockState?.propCell === undefined) {
565
+ return undefined;
566
+ }
567
+ // Drive the bound DOM through the prop cell (the marker guarantees the block's
568
+ // props are the component's props verbatim), then reuse the memo fiber in place.
569
+ setReactivePropCell(blockState.propCell, child.props);
570
+ matched.memoizedState = { ...state, props: child.props };
571
+ matched.pendingProps = child.props;
572
+ matched.flags = NoFlags;
573
+ matched.subtreeFlags = NoFlags;
574
+ matched.childListChanged = false;
575
+ matched.subtreeChildListChanged = false;
576
+ matched.hostChildListChanged = false;
577
+ matched.child = getSkippedChild(matched);
578
+ return matched;
579
+ }
580
+ // Fast path for a keyed list of memo-wrapped rows (e.g. `<RowMemo key={id} />`)
581
+ // whose key order is UNCHANGED between renders — the js-framework-benchmark
582
+ // "select row" and "partial update" shapes. Walks current fibers and new
583
+ // children in lockstep: unchanged rows bail in place (no allocation, no general
584
+ // keyed-reconcile machinery), changed rows re-render through the proven
585
+ // per-child path. Any reorder / insert / delete / non-memo child / length change
586
+ // makes it return undefined so the general reconcile takes over.
587
+ function reconcileKeyedMemoRowHostChildren(parent, currentFirstChild, children, runtime, path, options) {
588
+ if (children.length === 0 ||
589
+ currentFirstChild === undefined ||
590
+ runtime === undefined ||
591
+ options.previousNodes !== undefined ||
592
+ !isKeyedMemoRowCandidate(children[0]) ||
593
+ !hasSameKeyOrderPrefix(currentFirstChild, children)) {
594
+ return undefined;
595
+ }
596
+ let currentKeyed = currentFirstChild;
597
+ let first;
598
+ let previous;
599
+ let subtreeFlags = NoFlags;
600
+ let subtreeChildListChanged = false;
601
+ let hasRefSubtree = false;
602
+ let hasDisposableResources = false;
603
+ let dirtyChildren;
604
+ for (let index = 0; index < children.length; index += 1) {
605
+ const child = children[index];
606
+ if (currentKeyed === undefined || !isKeyedMemoRowCandidate(child)) {
607
+ return undefined;
608
+ }
609
+ const matched = currentKeyed;
610
+ currentKeyed = currentKeyed.sibling;
611
+ const childElement = child;
612
+ let fiber = tryReuseDependencyFreeKeyedMemoRow(matched, childElement) ??
613
+ tryCellUpdateStaticBlockMemoRow(matched, childElement);
614
+ if (fiber === undefined) {
615
+ const result = createHostFiber(parent, matched, child, childElement.key ?? undefined, runtime, getReconcileChildPath(path, child, index, options), options, false);
616
+ fiber = result.fiber;
617
+ if (fiber === undefined) {
618
+ return undefined;
619
+ }
620
+ if (fiber !== matched && fiber.alternate !== matched) {
621
+ markOptimizedChildForDeletion(parent, matched);
622
+ }
623
+ }
624
+ if (first === undefined) {
625
+ first = fiber;
626
+ }
627
+ else if (previous !== undefined) {
628
+ previous.sibling = fiber;
629
+ }
630
+ fiber.return = parent;
631
+ fiber.sibling = undefined;
632
+ if (fiber.hasRefSubtree) {
633
+ hasRefSubtree = true;
634
+ }
635
+ if (fiber.hasDisposableResources) {
636
+ hasDisposableResources = true;
637
+ }
638
+ subtreeFlags |= fiber.flags | fiber.subtreeFlags;
639
+ subtreeChildListChanged =
640
+ subtreeChildListChanged || fiber.childListChanged || fiber.subtreeChildListChanged;
641
+ if (fiber.tag === "memo" &&
642
+ (fiber.stateNode === undefined || typeof fiber.stateNode === "number")) {
643
+ fiber.stateNode = index;
644
+ }
645
+ if (hasHostCommitWork(fiber)) {
646
+ (dirtyChildren ??= []).push(fiber);
647
+ }
648
+ previous = fiber;
649
+ }
650
+ if (currentKeyed !== undefined) {
651
+ return undefined;
652
+ }
653
+ parent.hasRefSubtree = hasRefSubtree;
654
+ parent.hasDisposableResources = hasDisposableResources;
655
+ parent.subtreeFlags = subtreeFlags;
656
+ parent.subtreeChildListChanged = subtreeChildListChanged;
657
+ parent.childListChanged = false;
658
+ recordDirtyChildCommitHints(parent, dirtyChildren);
659
+ return { fiber: first, consumed: 0 };
660
+ }
661
+ function canReuseDependencyFreeMemoAtKey(current, node, key) {
662
+ return (current !== undefined &&
663
+ current.key === key &&
664
+ current.tag === "memo" &&
665
+ current.type === node.type &&
666
+ current.hasRefSubtree !== true &&
667
+ current.hasDisposableResources !== true &&
668
+ current.hydrateExisting !== true &&
669
+ isMemoType(node.type));
670
+ }
671
+ function tryReuseDependencyFreeMemoRemovalSuffix(parent, children, startIndex, removed, matchedCurrent, runtime, options, prefixFirst, prefixPrevious, consumed) {
672
+ if (runtime === undefined ||
673
+ options.previousNodes !== undefined ||
674
+ removed.hasRefSubtree) {
675
+ return undefined;
676
+ }
677
+ const suffix = [];
678
+ let current = matchedCurrent;
679
+ for (let index = startIndex; index < children.length; index += 1) {
680
+ const child = children[index];
681
+ const key = getNodeKey(child);
682
+ if (key === undefined || !isReactCompatElement(child) || !isMemoType(child.type)) {
683
+ return undefined;
684
+ }
685
+ if (!canReuseDependencyFreeMemoAtKey(current, child, key)) {
686
+ return undefined;
687
+ }
688
+ if (current === undefined) {
689
+ return undefined;
690
+ }
691
+ const matched = current;
692
+ const fiber = reuseDependencyFreeMemoFiber(matched, child);
693
+ if (fiber === undefined) {
694
+ return undefined;
695
+ }
696
+ suffix.push(fiber);
697
+ current = matched.sibling;
698
+ }
699
+ if (current !== undefined) {
700
+ return undefined;
701
+ }
702
+ let first = prefixFirst;
703
+ let previous = prefixPrevious;
704
+ for (const fiber of suffix) {
705
+ if (first === undefined) {
706
+ first = fiber;
707
+ }
708
+ else if (previous !== undefined) {
709
+ previous.sibling = fiber;
710
+ }
711
+ fiber.return = parent;
712
+ fiber.sibling = undefined;
713
+ previous = fiber;
714
+ }
715
+ parent.childListChanged = true;
716
+ parent.deletions = [removed];
717
+ markOptimizedChildForDeletion(parent, removed);
718
+ return { fiber: first, consumed };
719
+ }
720
+ function reuseDependencyFreeMemoFiber(current, node) {
721
+ if (!isMemoType(node.type)) {
722
+ return undefined;
723
+ }
724
+ const previousMemoState = current.memoizedState;
725
+ if (previousMemoState === undefined ||
726
+ previousMemoState.hasDirtyInstanceDependencies !== false ||
727
+ previousMemoState.hasUnflushedEffectDependencies !== false ||
728
+ previousMemoState.hasRetainedInstanceDependencies !== false ||
729
+ !areMemoPropsEqual(node.type, previousMemoState.props, node.props)) {
730
+ return undefined;
731
+ }
732
+ current.pendingProps = node.props;
733
+ current.flags = NoFlags;
734
+ current.subtreeFlags = NoFlags;
735
+ current.childListChanged = false;
736
+ current.subtreeChildListChanged = false;
737
+ current.hostChildListChanged = false;
738
+ current.type = node.type;
739
+ current.child = getSkippedChild(current);
740
+ current.memoizedState = previousMemoState;
741
+ return current;
742
+ }
401
743
  function canStoreAppendSuffixCommitHint(parent) {
402
744
  return (parent.tag === "fragment" ||
403
745
  parent.tag === "host-component" ||
@@ -459,6 +801,7 @@ function getReusableKeyedRowHostFiber(current, row) {
459
801
  current.subtreeChildListChanged = false;
460
802
  current.hostChildListChanged = false;
461
803
  current.hasRefSubtree = false;
804
+ current.hasDisposableResources = false;
462
805
  return current;
463
806
  }
464
807
  function createKeyedRowHostElementScratch() {
@@ -512,6 +855,7 @@ function createKeyedRowHostFiber(parent, current, row, options) {
512
855
  fiber.pendingProps = node.props;
513
856
  fiber.hostChildListChanged = false;
514
857
  fiber.hasRefSubtree = false;
858
+ fiber.hasDisposableResources = false;
515
859
  if (current === undefined || fiber.alternate !== current) {
516
860
  fiber.flags |= Placement;
517
861
  fiber.hostChildListChanged = true;
@@ -617,14 +961,25 @@ function bubbleHostChild(parent, child) {
617
961
  if (child.hasRefSubtree) {
618
962
  parent.hasRefSubtree = true;
619
963
  }
964
+ if (child.hasDisposableResources) {
965
+ parent.hasDisposableResources = true;
966
+ }
620
967
  parent.subtreeFlags |= child.flags | child.subtreeFlags;
621
968
  parent.subtreeChildListChanged =
622
969
  parent.subtreeChildListChanged ||
623
970
  child.childListChanged ||
624
971
  child.subtreeChildListChanged;
625
972
  }
973
+ function recordDirtyChildCommitHints(parent, dirtyChildren) {
974
+ if (parent.childListChanged || dirtyChildren === undefined || dirtyChildren.length === 0) {
975
+ return;
976
+ }
977
+ // Reuse the effect-list slot only when there are no child-list deletions.
978
+ parent.deletions = dirtyChildren;
979
+ }
626
980
  function resetFiberRefSubtree(fiber) {
627
981
  fiber.hasRefSubtree = false;
982
+ fiber.hasDisposableResources = false;
628
983
  }
629
984
  function includeNodeRef(fiber, node) {
630
985
  fiber.hasRefSubtree =
@@ -722,6 +1077,12 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
722
1077
  }
723
1078
  return { fiber: undefined, consumed: 0 };
724
1079
  }
1080
+ // Host elements (string type) are by far the most common node. Dispatch them
1081
+ // before the component-type checks below, none of which a string can match,
1082
+ // so each reconciled host element skips ~12 type comparisons / probes.
1083
+ if (typeof node.type === "string") {
1084
+ return createHostComponentFiber(parent, current, node, key, runtime, path, options);
1085
+ }
725
1086
  if (node.type === Fragment) {
726
1087
  const fiber = current?.tag === "fragment"
727
1088
  ? createWorkInProgress(current, node.props.children)
@@ -790,6 +1151,36 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
790
1151
  }
791
1152
  return { fiber, consumed: options.previousNodes?.length ?? 0 };
792
1153
  }
1154
+ if (node.type === REACTIVE_DOM_BLOCK_TYPE) {
1155
+ const blockProps = node.props.blockProps;
1156
+ if (current?.tag === "reactive-dom-block") {
1157
+ // Re-render: reuse the committed DOM/subscriptions and push the new props
1158
+ // into the prop cell instead of re-running render(). Bound text/attributes
1159
+ // update via their reactive subscriptions; the subtree is never reconciled.
1160
+ const fiber = createWorkInProgress(current, node.props);
1161
+ fiber.type = node.type;
1162
+ fiber.hasDisposableResources = true;
1163
+ const previousState = current.stateNode;
1164
+ if (previousState?.propCell !== undefined && blockProps !== undefined) {
1165
+ setReactivePropCell(previousState.propCell, blockProps);
1166
+ }
1167
+ fiber.stateNode = previousState;
1168
+ return { fiber, consumed: options.previousNodes?.length ?? 0 };
1169
+ }
1170
+ const fiber = createFiber("reactive-dom-block", node.props, key);
1171
+ fiber.type = node.type;
1172
+ fiber.hasDisposableResources = true;
1173
+ const render = node.props.render;
1174
+ if (blockProps !== undefined) {
1175
+ const propCell = createReactivePropCell(blockProps);
1176
+ const result = render(createReactivePropProxy(propCell));
1177
+ fiber.stateNode = { node: result.node, dispose: result.dispose, propCell };
1178
+ }
1179
+ else {
1180
+ fiber.stateNode = render();
1181
+ }
1182
+ return { fiber, consumed: options.previousNodes?.length ?? 0 };
1183
+ }
793
1184
  if (isReactCompatProvider(node.type)) {
794
1185
  const fiber = current?.tag === "context-provider" && current.type === node.type
795
1186
  ? createWorkInProgress(current, node.props)
@@ -858,8 +1249,11 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
858
1249
  : createFiber("memo", node.props, key);
859
1250
  fiber.type = memoType;
860
1251
  const renderedElement = {
861
- ...node,
1252
+ $$typeof: node.$$typeof,
862
1253
  type: memoType.type,
1254
+ key: node.key,
1255
+ ref: node.ref,
1256
+ props: node.props,
863
1257
  };
864
1258
  const childResult = createHostFiber(fiber, current?.tag === "memo" ? current.child : undefined, renderedElement, key, runtime, memoPath, options);
865
1259
  fiber.child = childResult.fiber;
@@ -868,7 +1262,7 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
868
1262
  fiber.child.sibling = undefined;
869
1263
  bubbleHostChild(fiber, fiber.child);
870
1264
  }
871
- const instanceKeys = collectInstanceKeys(runtime, memoPath);
1265
+ const instanceKeys = collectMemoInstanceKeys(runtime, memoPath);
872
1266
  const hasClassDescendant = hasClassComponentDescendant(fiber.child);
873
1267
  fiber.memoizedState = {
874
1268
  props: node.props,
@@ -890,8 +1284,11 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
890
1284
  fiber.type = lazyType;
891
1285
  if (lazyType.status === "resolved" && lazyType.resolved !== undefined) {
892
1286
  const renderedElement = {
893
- ...node,
1287
+ $$typeof: node.$$typeof,
894
1288
  type: lazyType.resolved,
1289
+ key: node.key,
1290
+ ref: node.ref,
1291
+ props: node.props,
895
1292
  };
896
1293
  const childResult = createHostFiber(fiber, current?.tag === "lazy" ? current.child : undefined, renderedElement, key, runtime, `${path}.lazy`, options);
897
1294
  fiber.child = childResult.fiber;
@@ -1006,45 +1403,64 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
1006
1403
  const instanceKeys = collectInstanceKeys(runtime, path);
1007
1404
  fiber.stateNode = {
1008
1405
  element: node,
1009
- props: { ...node.props },
1406
+ props: node.props,
1010
1407
  instanceKeys,
1011
1408
  hasContextDependencies: hasContextDependency(runtime, instanceKeys),
1012
1409
  };
1013
1410
  return { fiber, consumed: childResult.consumed };
1014
1411
  }
1412
+ return { fiber: undefined, consumed: 0 };
1413
+ }
1414
+ // The host-component reconcile, split out of createHostFiberImpl so host
1415
+ // elements can be dispatched before the component-type checks. Only called with
1416
+ // a string element type.
1417
+ function createHostComponentFiber(parent, current, node, key, runtime, path, options) {
1015
1418
  if (typeof node.type !== "string") {
1016
1419
  return { fiber: undefined, consumed: 0 };
1017
1420
  }
1421
+ const initialHostOnlyFiber = tryCreateInitialHostOnlyFiber(current, node, key, options);
1422
+ if (initialHostOnlyFiber !== undefined) {
1423
+ return { fiber: initialHostOnlyFiber, consumed: 0 };
1424
+ }
1018
1425
  const elementNamespace = namespaceForHostElement(options.namespace ?? "html", node.type);
1019
1426
  const childNamespace = namespaceForHostChildren(elementNamespace, node.type);
1020
- const fiber = current?.tag === "host-component" && current.type === node.type
1021
- ? createWorkInProgress(current, node.props)
1427
+ const reusableCurrent = current?.tag === "host-component" && current.type === node.type ? current : undefined;
1428
+ const fiber = reusableCurrent !== undefined
1429
+ ? createWorkInProgress(reusableCurrent, node.props)
1022
1430
  : createFiber("host-component", node.props, key);
1431
+ // The hydration node bookkeeping only matters when hydrating. Skipping it for
1432
+ // the common (non-hydration) render avoids an isHostElement(undefined) probe
1433
+ // and the mismatch checks on every reconciled element.
1023
1434
  const existing = options.previousNodes?.[0];
1024
- const existingElement = isHostElement(existing) ? existing : undefined;
1025
- const tagMatches = existingElement !== undefined &&
1026
- hostElementMatches(existingElement, node.type, elementNamespace);
1027
- if (existing === undefined && options.previousNodes !== undefined) {
1028
- reportMissingHydrationNode(options, path);
1029
- }
1030
- else if (existing !== undefined && !isHostElement(existing)) {
1031
- reportHydrationNodeTypeMismatch(options, path, `<${node.type}>`, existing);
1032
- }
1033
- if (existingElement !== undefined && !tagMatches) {
1034
- reportRecoverable(options, "tag", path, new Error(`Hydration tag mismatch: expected <${node.type}> but found <${existingElement.tagName.toLowerCase()}>.`));
1035
- reportElementTextMismatch(options, `${path}.c`, existingElement, node.props.children);
1435
+ let existingElement;
1436
+ let tagMatches = false;
1437
+ if (options.previousNodes !== undefined) {
1438
+ existingElement = isHostElement(existing) ? existing : undefined;
1439
+ tagMatches =
1440
+ existingElement !== undefined &&
1441
+ hostElementMatches(existingElement, node.type, elementNamespace);
1442
+ if (existing === undefined) {
1443
+ reportMissingHydrationNode(options, path);
1444
+ }
1445
+ else if (!isHostElement(existing)) {
1446
+ reportHydrationNodeTypeMismatch(options, path, `<${node.type}>`, existing);
1447
+ }
1448
+ if (existingElement !== undefined && !tagMatches) {
1449
+ reportRecoverable(options, "tag", path, new Error(`Hydration tag mismatch: expected <${node.type}> but found <${existingElement.tagName.toLowerCase()}>.`));
1450
+ reportElementTextMismatch(options, `${path}.c`, existingElement, node.props.children);
1451
+ }
1036
1452
  }
1037
1453
  fiber.type = node.type;
1454
+ // When reusing a same-type current fiber, its stateNode was created for this
1455
+ // exact tag and namespace (same tree position), so the hostElementMatches
1456
+ // re-check (localName + namespaceURI reads) is redundant.
1038
1457
  fiber.stateNode =
1039
1458
  tagMatches
1040
1459
  ? existingElement
1041
- : current?.tag === "host-component" &&
1042
- current.type === node.type &&
1043
- isHostElement(current.stateNode) &&
1044
- hostElementMatches(current.stateNode, node.type, elementNamespace)
1045
- ? current.stateNode
1460
+ : reusableCurrent !== undefined && isHostElement(reusableCurrent.stateNode)
1461
+ ? reusableCurrent.stateNode
1046
1462
  : createHostElement(getDocumentRef(options), node.type, options.namespace ?? "html");
1047
- fiber.hydrateExisting = tagMatches && options.previousNodes !== undefined;
1463
+ fiber.hydrateExisting = tagMatches;
1048
1464
  const previousChildNodes = tagMatches && existingElement !== undefined
1049
1465
  ? Array.from(existingElement.childNodes)
1050
1466
  : undefined;
@@ -1069,11 +1485,7 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
1069
1485
  parent.child ??= fiber;
1070
1486
  return { fiber, consumed: existing === undefined ? 0 : 1 };
1071
1487
  }
1072
- const childResult = reconcileHostChild(fiber, current?.tag === "host-component" ? current.child : undefined, node.props.children, runtime, `${path}.c`, {
1073
- ...options,
1074
- namespace: childNamespace,
1075
- ...(previousChildNodes === undefined ? {} : { previousNodes: previousChildNodes }),
1076
- });
1488
+ const childResult = reconcileHostChild(fiber, current?.tag === "host-component" ? current.child : undefined, node.props.children, runtime, `${path}.c`, getHostChildFiberOptions(options, childNamespace, previousChildNodes));
1077
1489
  fiber.child = childResult.fiber;
1078
1490
  if (previousChildNodes !== undefined) {
1079
1491
  reportExtraHydrationNodes(options, `${path}.c`, previousChildNodes, childResult.consumed);
@@ -1086,6 +1498,114 @@ function isFunctionComponentType(value) {
1086
1498
  typeof value.prototype?.render !==
1087
1499
  "function");
1088
1500
  }
1501
+ function tryCreateInitialHostOnlyFiber(current, node, key, options) {
1502
+ if (current !== undefined ||
1503
+ options.previousNodes !== undefined ||
1504
+ !shouldUseDirectHostTextChild() ||
1505
+ typeof node.type !== "string" ||
1506
+ !canCreateInitialHostOnlyNode(node)) {
1507
+ return undefined;
1508
+ }
1509
+ return createInitialHostOnlyElementFiber(node, key, options.namespace ?? "html", getDocumentRef(options));
1510
+ }
1511
+ function createInitialHostOnlyElementFiber(node, key, namespace, documentRef) {
1512
+ const elementType = node.type;
1513
+ const elementNamespace = namespaceForHostElement(namespace, elementType);
1514
+ const childNamespace = namespaceForHostChildren(elementNamespace, elementType);
1515
+ const fiber = createFiber("host-component", node.props, key);
1516
+ fiber.type = elementType;
1517
+ fiber.stateNode = createHostElement(documentRef, elementType, namespace);
1518
+ fiber.flags |= Placement;
1519
+ fiber.hostChildListChanged = true;
1520
+ if (getDirectHostTextChild(node.props.children) === undefined) {
1521
+ fiber.child = createInitialHostOnlyChildList(fiber, node.props.children, childNamespace, documentRef);
1522
+ }
1523
+ return fiber;
1524
+ }
1525
+ function createInitialHostOnlyChildList(parent, children, namespace, documentRef) {
1526
+ const normalized = normalizeChildren(children);
1527
+ let first;
1528
+ let previous;
1529
+ for (let index = 0; index < normalized.length; index += 1) {
1530
+ const child = normalized[index];
1531
+ const fiber = createInitialHostOnlyChildFiber(child, namespace, documentRef);
1532
+ if (fiber === undefined) {
1533
+ continue;
1534
+ }
1535
+ fiber.return = parent;
1536
+ fiber.memoizedState = index;
1537
+ if (first === undefined) {
1538
+ first = fiber;
1539
+ }
1540
+ else if (previous !== undefined) {
1541
+ previous.sibling = fiber;
1542
+ }
1543
+ bubbleHostChild(parent, fiber);
1544
+ previous = fiber;
1545
+ }
1546
+ return first;
1547
+ }
1548
+ function createInitialHostOnlyChildFiber(node, namespace, documentRef) {
1549
+ if (node === null || node === undefined || typeof node === "boolean") {
1550
+ return undefined;
1551
+ }
1552
+ if (typeof node === "string" || typeof node === "number") {
1553
+ const fiber = createFiber("host-text", String(node));
1554
+ fiber.stateNode = createHostTextNode(documentRef);
1555
+ fiber.flags |= Placement;
1556
+ return fiber;
1557
+ }
1558
+ if (Array.isArray(node)) {
1559
+ const fiber = createFiber("fragment", node);
1560
+ fiber.child = createInitialHostOnlyChildList(fiber, node, namespace, documentRef);
1561
+ return fiber.child === undefined ? undefined : fiber;
1562
+ }
1563
+ if (!isReactCompatElement(node) || typeof node.type !== "string") {
1564
+ return undefined;
1565
+ }
1566
+ return createInitialHostOnlyElementFiber(node, node.key === null ? undefined : node.key, namespace, documentRef);
1567
+ }
1568
+ function canCreateInitialHostOnlyNode(node) {
1569
+ if (node === null ||
1570
+ node === undefined ||
1571
+ typeof node === "boolean" ||
1572
+ typeof node === "string" ||
1573
+ typeof node === "number") {
1574
+ return true;
1575
+ }
1576
+ if (Array.isArray(node)) {
1577
+ return node.every(canCreateInitialHostOnlyNode);
1578
+ }
1579
+ if (!isReactCompatElement(node) ||
1580
+ typeof node.type !== "string" ||
1581
+ node.ref !== null ||
1582
+ hasInitialHostOnlyExcludedProps(node.props)) {
1583
+ return false;
1584
+ }
1585
+ return canCreateInitialHostOnlyNode(node.props.children);
1586
+ }
1587
+ function hasInitialHostOnlyExcludedProps(props) {
1588
+ return (hasOwnProperty.call(props, REACTIVE_TEXT_BINDING_META) ||
1589
+ props.dangerouslySetInnerHTML !== undefined ||
1590
+ props.contentEditable === true ||
1591
+ props.suppressContentEditableWarning === true ||
1592
+ props.value !== undefined ||
1593
+ props.defaultValue !== undefined ||
1594
+ props.checked !== undefined ||
1595
+ props.defaultChecked !== undefined);
1596
+ }
1597
+ function getHostChildFiberOptions(options, namespace, previousNodes) {
1598
+ const namespaceUnchanged = options.namespace === namespace ||
1599
+ (options.namespace === undefined && namespace === "html");
1600
+ if (previousNodes === undefined && namespaceUnchanged) {
1601
+ return options;
1602
+ }
1603
+ return {
1604
+ ...options,
1605
+ namespace,
1606
+ ...(previousNodes === undefined ? {} : { previousNodes }),
1607
+ };
1608
+ }
1089
1609
  function commitHostChildren(fiber, parent, eventRoot, path, options = {}) {
1090
1610
  const nodes = [];
1091
1611
  let cursor = fiber;
@@ -1110,6 +1630,29 @@ function commitHostDirtyChildren(fiber, parent, eventRoot, path, options = {}) {
1110
1630
  index += 1;
1111
1631
  }
1112
1632
  }
1633
+ function commitHostDirtyChildrenOf(owner, fiber, parent, eventRoot, path, options = {}) {
1634
+ const dirtyChildren = readDirtyChildCommitHints(owner);
1635
+ if (dirtyChildren === undefined) {
1636
+ commitHostDirtyChildren(fiber, parent, eventRoot, path, options);
1637
+ return;
1638
+ }
1639
+ for (let index = 0; index < dirtyChildren.length; index += 1) {
1640
+ const dirtyChild = dirtyChildren[index];
1641
+ if (dirtyChild !== undefined && hasHostCommitWork(dirtyChild)) {
1642
+ commitHostDirtyFiber(dirtyChild, parent, eventRoot, joinCommitPath(path, String(getDirtyChildCommitIndex(dirtyChild, index))), options);
1643
+ }
1644
+ }
1645
+ owner.deletions = undefined;
1646
+ }
1647
+ function readDirtyChildCommitHints(fiber) {
1648
+ return fiber.childListChanged ? undefined : fiber.deletions;
1649
+ }
1650
+ function getDirtyChildCommitIndex(fiber, fallback) {
1651
+ if (typeof fiber.memoizedState === "number") {
1652
+ return fiber.memoizedState;
1653
+ }
1654
+ return typeof fiber.stateNode === "number" ? fiber.stateNode : fallback;
1655
+ }
1113
1656
  function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1114
1657
  if (fiber.tag === "host-text") {
1115
1658
  commitHostFiber(fiber, parent, eventRoot, path, options);
@@ -1121,6 +1664,7 @@ function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1121
1664
  finishCommittedFiber(fiber);
1122
1665
  return;
1123
1666
  }
1667
+ const isDomElement = isDomHostElement(element);
1124
1668
  const props = fiber.pendingProps;
1125
1669
  const previousProps = fiber.memoizedProps;
1126
1670
  const directTextChild = fiber.child === undefined && fiber.hydrateExisting !== true
@@ -1140,7 +1684,7 @@ function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1140
1684
  !propsAreChildrenOnly &&
1141
1685
  fiber.hydrateExisting !== true &&
1142
1686
  isRowTextOnlyUpdate(fiber.memoizedProps, props);
1143
- if (isDomHostElement(element) && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
1687
+ if (isDomElement && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
1144
1688
  applyProps(element, props, path, {
1145
1689
  ...options,
1146
1690
  eventRoot,
@@ -1155,15 +1699,15 @@ function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1155
1699
  fiber.childListChanged ||
1156
1700
  fiber.subtreeChildListChanged) {
1157
1701
  const childNodes = commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
1158
- if (!(isDomHostElement(element) && childNodes.length === 0 && committedPortalContainers.has(element)) &&
1159
- !(isDomHostElement(element) && shouldPreserveContentEditableChildren(element, props, childNodes))) {
1702
+ if (!(isDomElement && childNodes.length === 0 && committedPortalContainers.has(element)) &&
1703
+ !(isDomElement && shouldPreserveContentEditableChildren(element, props, childNodes))) {
1160
1704
  syncChildNodes(element, childNodes);
1161
1705
  }
1162
1706
  }
1163
1707
  else if (fiber.subtreeFlags !== NoFlags) {
1164
- commitHostDirtyChildren(fiber.child, element, eventRoot, `${path}.c`, options);
1708
+ commitHostDirtyChildrenOf(fiber, fiber.child, element, eventRoot, `${path}.c`, options);
1165
1709
  }
1166
- if (isDomHostElement(element)) {
1710
+ if (isDomElement && isFormHostType(fiber.type)) {
1167
1711
  applyPostChildFormProps(element, props, previousProps);
1168
1712
  }
1169
1713
  applyChangedRef(previousProps?.ref, props.ref, element);
@@ -1182,7 +1726,7 @@ function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1182
1726
  : container instanceof Element
1183
1727
  ? container
1184
1728
  : eventRoot;
1185
- const portalOptions = withPortalDocumentRef(options, container);
1729
+ const portalOptions = withPortalHostOptions(options, container);
1186
1730
  if (fiber.childListChanged ||
1187
1731
  fiber.subtreeChildListChanged ||
1188
1732
  (fiber.subtreeFlags & Placement) !== NoFlags) {
@@ -1192,15 +1736,20 @@ function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1192
1736
  fiber.memoizedState = childNodes;
1193
1737
  }
1194
1738
  else {
1195
- commitHostDirtyChildren(fiber.child, container, portalEventRoot, `${path}.portal`, portalOptions);
1739
+ commitHostDirtyChildrenOf(fiber, fiber.child, container, portalEventRoot, `${path}.portal`, portalOptions);
1196
1740
  }
1197
1741
  }
1198
1742
  fiber.memoizedProps = fiber.pendingProps;
1199
1743
  finishCommittedFiber(fiber);
1200
1744
  return;
1201
1745
  }
1746
+ if (fiber.tag === "reactive-dom-block") {
1747
+ fiber.memoizedProps = fiber.pendingProps;
1748
+ finishCommittedFiber(fiber);
1749
+ return;
1750
+ }
1202
1751
  if (fiber.subtreeFlags !== NoFlags) {
1203
- commitHostDirtyChildren(fiber.child, parent, eventRoot, path, options);
1752
+ commitHostDirtyChildrenOf(fiber, fiber.child, parent, eventRoot, path, options);
1204
1753
  }
1205
1754
  fiber.memoizedProps = fiber.pendingProps;
1206
1755
  finishCommittedFiber(fiber);
@@ -1307,7 +1856,9 @@ function readAppendSuffixCommitHint(value) {
1307
1856
  : undefined;
1308
1857
  }
1309
1858
  function commitHostSingleRemoval(fiber, parent) {
1310
- const removed = getSingleRemovedFiber(fiber.alternate?.child, fiber.child);
1859
+ const removed = fiber.deletions?.length === 1
1860
+ ? fiber.deletions[0]
1861
+ : getSingleRemovedFiber(fiber.alternate?.child, fiber.child);
1311
1862
  if (removed === undefined) {
1312
1863
  return false;
1313
1864
  }
@@ -1319,6 +1870,9 @@ function commitHostSingleRemoval(fiber, parent) {
1319
1870
  parent.removeChild(node);
1320
1871
  removedAny = true;
1321
1872
  }
1873
+ if (removedAny) {
1874
+ fiber.deletions = undefined;
1875
+ }
1322
1876
  return removedAny;
1323
1877
  }
1324
1878
  function getAppendSuffix(current, next) {
@@ -1372,6 +1926,10 @@ function isSameFiberSlot(current, next) {
1372
1926
  current.key === next.key);
1373
1927
  }
1374
1928
  function collectCommittedHostNodes(fiber) {
1929
+ if (fiber.tag === "reactive-dom-block") {
1930
+ const node = getReactiveDomBlockNode(fiber.stateNode);
1931
+ return node === undefined ? [] : [node];
1932
+ }
1375
1933
  if ((fiber.tag === "host-component" || fiber.tag === "host-text") &&
1376
1934
  fiber.stateNode instanceof Node) {
1377
1935
  return [fiber.stateNode];
@@ -1385,6 +1943,9 @@ function collectCommittedHostNodes(fiber) {
1385
1943
  return nodes;
1386
1944
  }
1387
1945
  function hasCommittedHostNode(fiber) {
1946
+ if (fiber.tag === "reactive-dom-block") {
1947
+ return getReactiveDomBlockNode(fiber.stateNode) !== undefined;
1948
+ }
1388
1949
  if ((fiber.tag === "host-component" || fiber.tag === "host-text") &&
1389
1950
  fiber.stateNode instanceof Node) {
1390
1951
  return true;
@@ -1428,6 +1989,12 @@ function commitHostFiber(fiber, parent, eventRoot, path, options = {}) {
1428
1989
  finishCommittedFiber(fiber);
1429
1990
  return [text];
1430
1991
  }
1992
+ if (fiber.tag === "reactive-dom-block") {
1993
+ const node = getReactiveDomBlockNode(fiber.stateNode);
1994
+ fiber.memoizedProps = fiber.pendingProps;
1995
+ finishCommittedFiber(fiber);
1996
+ return node === undefined ? [] : [node];
1997
+ }
1431
1998
  if (fiber.tag === "host-component") {
1432
1999
  const element = fiber.stateNode;
1433
2000
  if (!isHostElement(element)) {
@@ -1443,6 +2010,7 @@ function commitHostFiber(fiber, parent, eventRoot, path, options = {}) {
1443
2010
  fiber.memoizedProps = fiber.pendingProps;
1444
2011
  return [element];
1445
2012
  }
2013
+ const isDomElement = isDomHostElement(element);
1446
2014
  const props = fiber.pendingProps;
1447
2015
  const previousProps = fiber.memoizedProps;
1448
2016
  const directTextChild = fiber.child === undefined && fiber.hydrateExisting !== true
@@ -1462,7 +2030,7 @@ function commitHostFiber(fiber, parent, eventRoot, path, options = {}) {
1462
2030
  !propsAreChildrenOnly &&
1463
2031
  fiber.hydrateExisting !== true &&
1464
2032
  isRowTextOnlyUpdate(fiber.memoizedProps, props);
1465
- if (isDomHostElement(element) && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
2033
+ if (isDomElement && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
1466
2034
  applyProps(element, props, path, {
1467
2035
  ...options,
1468
2036
  eventRoot,
@@ -1479,15 +2047,15 @@ function commitHostFiber(fiber, parent, eventRoot, path, options = {}) {
1479
2047
  fiber.hydrateExisting === true ||
1480
2048
  (fiber.subtreeFlags & Placement) !== NoFlags) {
1481
2049
  const childNodes = commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
1482
- if (!(isDomHostElement(element) && childNodes.length === 0 && committedPortalContainers.has(element)) &&
1483
- !(isDomHostElement(element) && shouldPreserveContentEditableChildren(element, props, childNodes))) {
2050
+ if (!(isDomElement && childNodes.length === 0 && committedPortalContainers.has(element)) &&
2051
+ !(isDomElement && shouldPreserveContentEditableChildren(element, props, childNodes))) {
1484
2052
  syncChildNodes(element, childNodes);
1485
2053
  }
1486
2054
  }
1487
2055
  else if (fiber.subtreeFlags !== NoFlags) {
1488
2056
  commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
1489
2057
  }
1490
- if (isDomHostElement(element)) {
2058
+ if (isDomElement && isFormHostType(fiber.type)) {
1491
2059
  applyPostChildFormProps(element, props, previousProps);
1492
2060
  }
1493
2061
  applyChangedRef(previousProps?.ref, props.ref, element);
@@ -1581,7 +2149,7 @@ function commitHostFiber(fiber, parent, eventRoot, path, options = {}) {
1581
2149
  : container instanceof Element
1582
2150
  ? container
1583
2151
  : eventRoot;
1584
- const portalOptions = withPortalDocumentRef(options, container);
2152
+ const portalOptions = withPortalHostOptions(options, container);
1585
2153
  const childNodes = commitHostChildren(fiber.child, container, portalEventRoot, `${path}.portal`, portalOptions);
1586
2154
  const previousNodes = committedHostNodesFromState(fiber.alternate?.memoizedState);
1587
2155
  syncOwnedChildNodes(container, previousNodes, childNodes);
@@ -1600,6 +2168,26 @@ function finishCommittedFiber(fiber) {
1600
2168
  fiber.subtreeChildListChanged = false;
1601
2169
  fiber.hostChildListChanged = false;
1602
2170
  }
2171
+ function getReactiveDomBlockNode(state) {
2172
+ if (typeof state === "object" &&
2173
+ state !== null &&
2174
+ "node" in state &&
2175
+ state.node instanceof Node) {
2176
+ return state.node;
2177
+ }
2178
+ return undefined;
2179
+ }
2180
+ function disposeReactiveDomBlockState(state, seen) {
2181
+ if (typeof state !== "object" || state === null || seen.has(state)) {
2182
+ return;
2183
+ }
2184
+ seen.add(state);
2185
+ const dispose = state.dispose;
2186
+ if (typeof dispose === "function") {
2187
+ dispose();
2188
+ state.dispose = undefined;
2189
+ }
2190
+ }
1603
2191
  function hasChildListMutation(fiber) {
1604
2192
  return fiber.childListChanged || fiber.subtreeChildListChanged;
1605
2193
  }
@@ -1746,6 +2334,12 @@ function getDirectHostTextChild(children) {
1746
2334
  ? String(children)
1747
2335
  : undefined;
1748
2336
  }
2337
+ // Only these host tags carry post-child form value/checked semantics. Gating on
2338
+ // the (cheap, already-known) type string lets the commit skip applyPostChildFormProps
2339
+ // and its instanceof probes for every other element.
2340
+ function isFormHostType(type) {
2341
+ return type === "input" || type === "textarea" || type === "select";
2342
+ }
1749
2343
  const hostFastPathMode = (() => {
1750
2344
  try {
1751
2345
  // The literal process.env.NODE_ENV member expression is what bundler
@@ -2016,7 +2610,23 @@ function tryReuseMemoBailout(current, node, runtime, path, options, canReuseCurr
2016
2610
  !areMemoPropsEqual(node.type, previousMemoState.props, node.props)) {
2017
2611
  return undefined;
2018
2612
  }
2019
- const fiber = getMemoBailoutFiber(runtime, current, node.props, previousMemoState, canReuseCurrentFiber);
2613
+ if (memoStateNeedsActiveInstanceMark(previousMemoState)) {
2614
+ markActiveInstanceKeys(runtime, previousMemoState.instanceKeys);
2615
+ }
2616
+ const fiber = canReuseCurrentFiber &&
2617
+ current.hasRefSubtree !== true &&
2618
+ current.hasDisposableResources !== true &&
2619
+ current.hydrateExisting !== true
2620
+ ? current
2621
+ : createWorkInProgress(current, node.props);
2622
+ if (fiber === current) {
2623
+ current.pendingProps = node.props;
2624
+ current.flags = NoFlags;
2625
+ current.subtreeFlags = NoFlags;
2626
+ current.childListChanged = false;
2627
+ current.subtreeChildListChanged = false;
2628
+ current.hostChildListChanged = false;
2629
+ }
2020
2630
  fiber.type = node.type;
2021
2631
  fiber.child = getSkippedChild(current);
2022
2632
  fiber.memoizedState = previousMemoState;
@@ -2139,7 +2749,11 @@ function createPortalFiber(parent, current, portal, key, runtime, path, options
2139
2749
  ? createWorkInProgress(current, portal.children)
2140
2750
  : createFiber("portal", portal.children, key);
2141
2751
  fiber.stateNode = portal.container;
2142
- const childResult = reconcileHostChild(fiber, current?.tag === "portal" ? current.child : undefined, portal.children, runtime, `${path}.portal`, { ...options, documentRef: portal.container.ownerDocument });
2752
+ const childResult = reconcileHostChild(fiber, current?.tag === "portal" ? current.child : undefined, portal.children, runtime, `${path}.portal`, {
2753
+ ...options,
2754
+ documentRef: portal.container.ownerDocument,
2755
+ namespace: namespaceForPortalContainer(portal.container),
2756
+ });
2143
2757
  fiber.child = childResult.fiber;
2144
2758
  fiber.return = parent;
2145
2759
  return { fiber, consumed: childResult.consumed };
@@ -2239,6 +2853,24 @@ function isLazyType(value) {
2239
2853
  function collectInstanceKeys(runtime, prefix) {
2240
2854
  return collectRuntimeInstanceKeys(runtime, prefix);
2241
2855
  }
2856
+ function collectMemoInstanceKeys(runtime, prefix) {
2857
+ return readDependencyFreeMemoInstanceKey(runtime, prefix) === undefined
2858
+ ? collectInstanceKeys(runtime, prefix)
2859
+ : emptyInstanceKeys;
2860
+ }
2861
+ function readDependencyFreeMemoInstanceKey(runtime, prefix) {
2862
+ const keys = runtime.instanceKeysByPrefix.get(prefix);
2863
+ if (keys === undefined || keys.size !== 1 || !keys.has(prefix)) {
2864
+ return undefined;
2865
+ }
2866
+ const instance = runtime.instances.get(prefix);
2867
+ if (instance === undefined ||
2868
+ instance.contextDependencies !== undefined ||
2869
+ (instance.hooks !== undefined && instance.hooks.length > 0)) {
2870
+ return undefined;
2871
+ }
2872
+ return prefix;
2873
+ }
2242
2874
  function markActiveInstanceKeys(runtime, keys) {
2243
2875
  for (const key of keys) {
2244
2876
  runtime.activeInstanceKeys?.add(key);
@@ -2254,6 +2886,9 @@ function memoStateNeedsActiveInstanceMark(state) {
2254
2886
  return state.hasRetainedInstanceDependencies !== false;
2255
2887
  }
2256
2888
  function getMemoBailoutFiber(runtime, current, pendingProps, state, canReuseCurrentFiber) {
2889
+ if (memoStateNeedsActiveInstanceMark(state)) {
2890
+ markActiveInstanceKeys(runtime, state.instanceKeys);
2891
+ }
2257
2892
  if (canReuseCurrentFiber && canReuseMemoBailoutFiber(current, state)) {
2258
2893
  current.pendingProps = pendingProps;
2259
2894
  current.flags = NoFlags;
@@ -2264,14 +2899,12 @@ function getMemoBailoutFiber(runtime, current, pendingProps, state, canReuseCurr
2264
2899
  return current;
2265
2900
  }
2266
2901
  const fiber = createWorkInProgress(current, pendingProps);
2267
- if (memoStateNeedsActiveInstanceMark(state)) {
2268
- markActiveInstanceKeys(runtime, state.instanceKeys);
2269
- }
2270
2902
  return fiber;
2271
2903
  }
2272
2904
  function canReuseMemoBailoutFiber(current, state) {
2273
2905
  return (state.hasRetainedInstanceDependencies === false &&
2274
2906
  current.hasRefSubtree !== true &&
2907
+ current.hasDisposableResources !== true &&
2275
2908
  current.hydrateExisting !== true);
2276
2909
  }
2277
2910
  function hasDirtyInstanceDependencies(runtime, keys) {
@@ -2413,7 +3046,8 @@ function isPortalHostContainer(value) {
2413
3046
  typeof candidate.removeChild === "function" &&
2414
3047
  typeof candidate.ownerDocument?.createElement === "function");
2415
3048
  }
2416
- function withPortalDocumentRef(options, container) {
3049
+ function withPortalHostOptions(options, container) {
3050
+ const namespace = namespaceForPortalContainer(container);
2417
3051
  const ownerDocument = container.ownerDocument;
2418
3052
  if (typeof ownerDocument === "object" &&
2419
3053
  ownerDocument !== null &&
@@ -2421,9 +3055,16 @@ function withPortalDocumentRef(options, container) {
2421
3055
  return {
2422
3056
  ...options,
2423
3057
  documentRef: ownerDocument,
3058
+ namespace,
2424
3059
  };
2425
3060
  }
2426
- return options;
3061
+ return { ...options, namespace };
3062
+ }
3063
+ function namespaceForPortalContainer(container) {
3064
+ if (container instanceof SVGElement && container.localName !== "foreignObject") {
3065
+ return "svg";
3066
+ }
3067
+ return "html";
2427
3068
  }
2428
3069
  function committedHostNodesFromState(state) {
2429
3070
  return Array.isArray(state) ? state : [];