@reckona/mreact-compat 0.0.170 → 0.0.172

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 +7 -0
  23. package/dist/hooks.d.ts.map +1 -1
  24. package/dist/hooks.js +122 -41
  25. package/dist/hooks.js.map +1 -1
  26. package/dist/host-reconciler.d.ts +1 -0
  27. package/dist/host-reconciler.d.ts.map +1 -1
  28. package/dist/host-reconciler.js +659 -50
  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 +3 -1
  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 +1 -0
  52. package/src/fiber.ts +4 -0
  53. package/src/hooks.ts +173 -41
  54. package/src/host-reconciler.ts +979 -64
  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 +3 -0
@@ -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,33 @@ 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
+ function disposeHostFiberChildResources(fiber, seen) {
180
+ let cursor = fiber;
181
+ while (cursor !== undefined) {
182
+ if (cursor.hasDisposableResources === true) {
183
+ if (cursor.tag === "reactive-dom-block") {
184
+ disposeReactiveDomBlockState(cursor.stateNode, seen);
185
+ }
186
+ disposeHostFiberChildResources(cursor.child, seen);
187
+ }
188
+ cursor = cursor.sibling;
189
+ }
190
+ }
154
191
  function reconcileHostChild(parent, currentFirstChild, node, runtime, path, options = {}) {
155
192
  resetFiberRefSubtree(parent);
156
193
  parent.subtreeFlags = NoFlags;
@@ -166,7 +203,8 @@ function reconcileHostChild(parent, currentFirstChild, node, runtime, path, opti
166
203
  const children = Array.isArray(node) ? node : undefined;
167
204
  const rowResult = children === undefined
168
205
  ? undefined
169
- : reconcileKeyedRowHostChildren(parent, currentFirstChild, children, options);
206
+ : reconcileKeyedRowHostChildren(parent, currentFirstChild, children, options) ??
207
+ reconcileKeyedMemoRowHostChildren(parent, currentFirstChild, children, runtime, path, options);
170
208
  if (rowResult !== undefined) {
171
209
  return rowResult;
172
210
  }
@@ -182,6 +220,7 @@ function reconcileHostChild(parent, currentFirstChild, node, runtime, path, opti
182
220
  let skipRemainingKeyedLookup = false;
183
221
  let sequentialCurrentCursor = currentFirstChild;
184
222
  let childListOrderChanged = false;
223
+ let dirtyChildren;
185
224
  let usedCurrentChildren = currentFirstChild === undefined || hasKeyedChildren ? undefined : new Set();
186
225
  const ensureUsedCurrentChildren = () => {
187
226
  if (usedCurrentChildren === undefined) {
@@ -224,11 +263,17 @@ function reconcileHostChild(parent, currentFirstChild, node, runtime, path, opti
224
263
  else if (children !== undefined &&
225
264
  currentKeyed?.sibling?.key === key &&
226
265
  canSkipSingleDeletedKeyedFiber(children, index, currentKeyed.sibling)) {
266
+ const deleted = currentKeyed;
267
+ const matched = currentKeyed.sibling;
268
+ const suffixResult = tryReuseDependencyFreeMemoRemovalSuffix(parent, children, index, deleted, matched, runtime, options, first, previous, consumed);
269
+ if (suffixResult !== undefined) {
270
+ return suffixResult;
271
+ }
227
272
  childListOrderChanged = true;
228
273
  ensureUsedCurrentChildren();
229
- matchedCurrent = currentKeyed.sibling;
274
+ matchedCurrent = matched;
230
275
  canReuseMatchedCurrentFiber = false;
231
- currentKeyed = currentKeyed.sibling.sibling;
276
+ currentKeyed = matched.sibling;
232
277
  }
233
278
  else {
234
279
  if (children !== undefined &&
@@ -287,6 +332,13 @@ function reconcileHostChild(parent, currentFirstChild, node, runtime, path, opti
287
332
  && fiber.memoizedState === undefined) {
288
333
  fiber.memoizedState = index;
289
334
  }
335
+ else if (fiber.tag === "memo" &&
336
+ (fiber.stateNode === undefined || typeof fiber.stateNode === "number")) {
337
+ fiber.stateNode = index;
338
+ }
339
+ if (hasHostCommitWork(fiber)) {
340
+ (dirtyChildren ??= []).push(fiber);
341
+ }
290
342
  previous = fiber;
291
343
  }
292
344
  if (usedCurrentChildren === undefined && hasKeyedChildren && currentKeyed !== undefined) {
@@ -297,6 +349,7 @@ function reconcileHostChild(parent, currentFirstChild, node, runtime, path, opti
297
349
  }
298
350
  parent.childListChanged =
299
351
  childListOrderChanged || childFiberListShapeChanged(currentFirstChild, first);
352
+ recordDirtyChildCommitHints(parent, dirtyChildren);
300
353
  return { fiber: first, consumed };
301
354
  }
302
355
  function reconcileKeyedRowHostChildren(parent, currentFirstChild, children, options) {
@@ -315,6 +368,7 @@ function reconcileKeyedRowHostChildren(parent, currentFirstChild, children, opti
315
368
  let subtreeFlags = NoFlags;
316
369
  let subtreeChildListChanged = false;
317
370
  let hasRefSubtree = false;
371
+ let hasDisposableResources = false;
318
372
  let appendSuffix;
319
373
  const canReuseUnchangedRows = hasSameKeyOrderPrefix(currentFirstChild, children);
320
374
  const row = createKeyedRowHostElementScratch();
@@ -375,6 +429,9 @@ function reconcileKeyedRowHostChildren(parent, currentFirstChild, children, opti
375
429
  if (fiber.hasRefSubtree) {
376
430
  hasRefSubtree = true;
377
431
  }
432
+ if (fiber.hasDisposableResources) {
433
+ hasDisposableResources = true;
434
+ }
378
435
  subtreeFlags |= fiber.flags | fiber.subtreeFlags;
379
436
  subtreeChildListChanged =
380
437
  subtreeChildListChanged ||
@@ -390,6 +447,7 @@ function reconcileKeyedRowHostChildren(parent, currentFirstChild, children, opti
390
447
  markOptimizedChildrenForDeletion(parent, currentKeyed);
391
448
  }
392
449
  parent.hasRefSubtree = hasRefSubtree;
450
+ parent.hasDisposableResources = hasDisposableResources;
393
451
  parent.subtreeFlags = subtreeFlags;
394
452
  parent.subtreeChildListChanged = subtreeChildListChanged;
395
453
  parent.childListChanged = listShapeChanged;
@@ -398,6 +456,270 @@ function reconcileKeyedRowHostChildren(parent, currentFirstChild, children, opti
398
456
  }
399
457
  return { fiber: first, consumed: 0 };
400
458
  }
459
+ function isKeyedMemoRowCandidate(node) {
460
+ return (isReactCompatElement(node) &&
461
+ node.key !== null &&
462
+ node.ref === null &&
463
+ isMemoType(node.type));
464
+ }
465
+ // Marker the compiler stamps on a lowered host-only component that returns its
466
+ // props verbatim as a reactive block (`createReactiveDomBlock(render, props)`).
467
+ // Such a component is pure and structurally static, so on a props change the
468
+ // reconciler can drive its committed block straight through the prop cell instead
469
+ // of re-invoking the component (the block's bound DOM updates via subscriptions).
470
+ const STATIC_REACTIVE_BLOCK_MARKER = "__mreactStaticBlock";
471
+ function isStaticReactiveBlockComponent(type) {
472
+ return (typeof type === "function" &&
473
+ type[STATIC_REACTIVE_BLOCK_MARKER] === true);
474
+ }
475
+ // In-place bailout for a keyed memo row whose props are unchanged and which has
476
+ // no hook/context/effect dependencies. The caller has proven the key order is
477
+ // unchanged (hasSameKeyOrderPrefix), so the fiber keeps its position and can be
478
+ // reused WITHOUT createWorkInProgress — no allocation, and the retained subtree
479
+ // (including any reactive-dom-block subscriptions) is left untouched. Returns
480
+ // undefined when the row must re-render or has dependencies (handled by the
481
+ // general per-child path instead). Mirrors tryReuseDependencyFreeMemoBailout +
482
+ // getMemoBailoutFiber's in-place branch, but is safe to reuse a fiber with
483
+ // disposable resources because a stable position means nothing is disposed.
484
+ function tryReuseDependencyFreeKeyedMemoRow(matched, child) {
485
+ if (matched.tag !== "memo" ||
486
+ matched.type !== child.type ||
487
+ matched.hasRefSubtree === true ||
488
+ matched.hydrateExisting === true) {
489
+ return undefined;
490
+ }
491
+ const state = matched.memoizedState;
492
+ if (state === undefined ||
493
+ state.hasDirtyInstanceDependencies !== false ||
494
+ state.hasUnflushedEffectDependencies !== false ||
495
+ state.hasRetainedInstanceDependencies !== false ||
496
+ !areMemoPropsEqual(child.type, state.props, child.props)) {
497
+ return undefined;
498
+ }
499
+ matched.pendingProps = child.props;
500
+ matched.flags = NoFlags;
501
+ matched.subtreeFlags = NoFlags;
502
+ matched.childListChanged = false;
503
+ matched.subtreeChildListChanged = false;
504
+ matched.hostChildListChanged = false;
505
+ matched.child = getSkippedChild(matched);
506
+ return matched;
507
+ }
508
+ // In-place CHANGED-row update for a keyed memo row whose inner component is a
509
+ // compiler-marked static reactive block (returns its props verbatim as the block
510
+ // props). Instead of re-invoking the component to rebuild a throwaway block
511
+ // element, push the new props straight into the committed block's prop cell — the
512
+ // bound DOM updates via subscriptions, exactly as the normal block re-render path
513
+ // would, but without the memo/component render machinery. Same-order is proven by
514
+ // the caller, so the fiber is reused in place. Returns undefined when this is not
515
+ // a marked, dependency-free, single-block memo row (the general path handles it).
516
+ function tryCellUpdateStaticBlockMemoRow(matched, child) {
517
+ if (matched.tag !== "memo" ||
518
+ matched.type !== child.type ||
519
+ matched.hasRefSubtree === true ||
520
+ matched.hydrateExisting === true ||
521
+ !isStaticReactiveBlockComponent(matched.type.type)) {
522
+ return undefined;
523
+ }
524
+ const state = matched.memoizedState;
525
+ if (state === undefined ||
526
+ state.hasDirtyInstanceDependencies !== false ||
527
+ state.hasUnflushedEffectDependencies !== false ||
528
+ state.hasRetainedInstanceDependencies !== false) {
529
+ return undefined;
530
+ }
531
+ // Navigate memo -> component fiber -> reactive-dom-block fiber. The marked
532
+ // component renders exactly one static block, so anything else is unexpected.
533
+ const componentFiber = matched.child;
534
+ if (componentFiber === undefined || componentFiber.sibling !== undefined) {
535
+ return undefined;
536
+ }
537
+ const blockFiber = componentFiber.child;
538
+ if (blockFiber === undefined ||
539
+ blockFiber.tag !== "reactive-dom-block" ||
540
+ blockFiber.sibling !== undefined) {
541
+ return undefined;
542
+ }
543
+ const blockState = blockFiber.stateNode;
544
+ if (blockState?.propCell === undefined) {
545
+ return undefined;
546
+ }
547
+ // Drive the bound DOM through the prop cell (the marker guarantees the block's
548
+ // props are the component's props verbatim), then reuse the memo fiber in place.
549
+ setReactivePropCell(blockState.propCell, child.props);
550
+ matched.memoizedState = { ...state, props: child.props };
551
+ matched.pendingProps = child.props;
552
+ matched.flags = NoFlags;
553
+ matched.subtreeFlags = NoFlags;
554
+ matched.childListChanged = false;
555
+ matched.subtreeChildListChanged = false;
556
+ matched.hostChildListChanged = false;
557
+ matched.child = getSkippedChild(matched);
558
+ return matched;
559
+ }
560
+ // Fast path for a keyed list of memo-wrapped rows (e.g. `<RowMemo key={id} />`)
561
+ // whose key order is UNCHANGED between renders — the js-framework-benchmark
562
+ // "select row" and "partial update" shapes. Walks current fibers and new
563
+ // children in lockstep: unchanged rows bail in place (no allocation, no general
564
+ // keyed-reconcile machinery), changed rows re-render through the proven
565
+ // per-child path. Any reorder / insert / delete / non-memo child / length change
566
+ // makes it return undefined so the general reconcile takes over.
567
+ function reconcileKeyedMemoRowHostChildren(parent, currentFirstChild, children, runtime, path, options) {
568
+ if (children.length === 0 ||
569
+ currentFirstChild === undefined ||
570
+ runtime === undefined ||
571
+ options.previousNodes !== undefined ||
572
+ !isKeyedMemoRowCandidate(children[0]) ||
573
+ !hasSameKeyOrderPrefix(currentFirstChild, children)) {
574
+ return undefined;
575
+ }
576
+ let currentKeyed = currentFirstChild;
577
+ let first;
578
+ let previous;
579
+ let subtreeFlags = NoFlags;
580
+ let subtreeChildListChanged = false;
581
+ let hasRefSubtree = false;
582
+ let hasDisposableResources = false;
583
+ let dirtyChildren;
584
+ for (let index = 0; index < children.length; index += 1) {
585
+ const child = children[index];
586
+ if (currentKeyed === undefined || !isKeyedMemoRowCandidate(child)) {
587
+ return undefined;
588
+ }
589
+ const matched = currentKeyed;
590
+ currentKeyed = currentKeyed.sibling;
591
+ const childElement = child;
592
+ let fiber = tryReuseDependencyFreeKeyedMemoRow(matched, childElement) ??
593
+ tryCellUpdateStaticBlockMemoRow(matched, childElement);
594
+ if (fiber === undefined) {
595
+ const result = createHostFiber(parent, matched, child, childElement.key ?? undefined, runtime, getReconcileChildPath(path, child, index, options), options, false);
596
+ fiber = result.fiber;
597
+ if (fiber === undefined) {
598
+ return undefined;
599
+ }
600
+ if (fiber !== matched && fiber.alternate !== matched) {
601
+ markOptimizedChildForDeletion(parent, matched);
602
+ }
603
+ }
604
+ if (first === undefined) {
605
+ first = fiber;
606
+ }
607
+ else if (previous !== undefined) {
608
+ previous.sibling = fiber;
609
+ }
610
+ fiber.return = parent;
611
+ fiber.sibling = undefined;
612
+ if (fiber.hasRefSubtree) {
613
+ hasRefSubtree = true;
614
+ }
615
+ if (fiber.hasDisposableResources) {
616
+ hasDisposableResources = true;
617
+ }
618
+ subtreeFlags |= fiber.flags | fiber.subtreeFlags;
619
+ subtreeChildListChanged =
620
+ subtreeChildListChanged || fiber.childListChanged || fiber.subtreeChildListChanged;
621
+ if (fiber.tag === "memo" &&
622
+ (fiber.stateNode === undefined || typeof fiber.stateNode === "number")) {
623
+ fiber.stateNode = index;
624
+ }
625
+ if (hasHostCommitWork(fiber)) {
626
+ (dirtyChildren ??= []).push(fiber);
627
+ }
628
+ previous = fiber;
629
+ }
630
+ if (currentKeyed !== undefined) {
631
+ return undefined;
632
+ }
633
+ parent.hasRefSubtree = hasRefSubtree;
634
+ parent.hasDisposableResources = hasDisposableResources;
635
+ parent.subtreeFlags = subtreeFlags;
636
+ parent.subtreeChildListChanged = subtreeChildListChanged;
637
+ parent.childListChanged = false;
638
+ recordDirtyChildCommitHints(parent, dirtyChildren);
639
+ return { fiber: first, consumed: 0 };
640
+ }
641
+ function canReuseDependencyFreeMemoAtKey(current, node, key) {
642
+ return (current !== undefined &&
643
+ current.key === key &&
644
+ current.tag === "memo" &&
645
+ current.type === node.type &&
646
+ current.hasRefSubtree !== true &&
647
+ current.hasDisposableResources !== true &&
648
+ current.hydrateExisting !== true &&
649
+ isMemoType(node.type));
650
+ }
651
+ function tryReuseDependencyFreeMemoRemovalSuffix(parent, children, startIndex, removed, matchedCurrent, runtime, options, prefixFirst, prefixPrevious, consumed) {
652
+ if (runtime === undefined ||
653
+ options.previousNodes !== undefined ||
654
+ removed.hasRefSubtree) {
655
+ return undefined;
656
+ }
657
+ const suffix = [];
658
+ let current = matchedCurrent;
659
+ for (let index = startIndex; index < children.length; index += 1) {
660
+ const child = children[index];
661
+ const key = getNodeKey(child);
662
+ if (key === undefined || !isReactCompatElement(child) || !isMemoType(child.type)) {
663
+ return undefined;
664
+ }
665
+ if (!canReuseDependencyFreeMemoAtKey(current, child, key)) {
666
+ return undefined;
667
+ }
668
+ if (current === undefined) {
669
+ return undefined;
670
+ }
671
+ const matched = current;
672
+ const fiber = reuseDependencyFreeMemoFiber(matched, child);
673
+ if (fiber === undefined) {
674
+ return undefined;
675
+ }
676
+ suffix.push(fiber);
677
+ current = matched.sibling;
678
+ }
679
+ if (current !== undefined) {
680
+ return undefined;
681
+ }
682
+ let first = prefixFirst;
683
+ let previous = prefixPrevious;
684
+ for (const fiber of suffix) {
685
+ if (first === undefined) {
686
+ first = fiber;
687
+ }
688
+ else if (previous !== undefined) {
689
+ previous.sibling = fiber;
690
+ }
691
+ fiber.return = parent;
692
+ fiber.sibling = undefined;
693
+ previous = fiber;
694
+ }
695
+ parent.childListChanged = true;
696
+ parent.deletions = [removed];
697
+ markOptimizedChildForDeletion(parent, removed);
698
+ return { fiber: first, consumed };
699
+ }
700
+ function reuseDependencyFreeMemoFiber(current, node) {
701
+ if (!isMemoType(node.type)) {
702
+ return undefined;
703
+ }
704
+ const previousMemoState = current.memoizedState;
705
+ if (previousMemoState === undefined ||
706
+ previousMemoState.hasDirtyInstanceDependencies !== false ||
707
+ previousMemoState.hasUnflushedEffectDependencies !== false ||
708
+ previousMemoState.hasRetainedInstanceDependencies !== false ||
709
+ !areMemoPropsEqual(node.type, previousMemoState.props, node.props)) {
710
+ return undefined;
711
+ }
712
+ current.pendingProps = node.props;
713
+ current.flags = NoFlags;
714
+ current.subtreeFlags = NoFlags;
715
+ current.childListChanged = false;
716
+ current.subtreeChildListChanged = false;
717
+ current.hostChildListChanged = false;
718
+ current.type = node.type;
719
+ current.child = getSkippedChild(current);
720
+ current.memoizedState = previousMemoState;
721
+ return current;
722
+ }
401
723
  function canStoreAppendSuffixCommitHint(parent) {
402
724
  return (parent.tag === "fragment" ||
403
725
  parent.tag === "host-component" ||
@@ -459,6 +781,7 @@ function getReusableKeyedRowHostFiber(current, row) {
459
781
  current.subtreeChildListChanged = false;
460
782
  current.hostChildListChanged = false;
461
783
  current.hasRefSubtree = false;
784
+ current.hasDisposableResources = false;
462
785
  return current;
463
786
  }
464
787
  function createKeyedRowHostElementScratch() {
@@ -512,6 +835,7 @@ function createKeyedRowHostFiber(parent, current, row, options) {
512
835
  fiber.pendingProps = node.props;
513
836
  fiber.hostChildListChanged = false;
514
837
  fiber.hasRefSubtree = false;
838
+ fiber.hasDisposableResources = false;
515
839
  if (current === undefined || fiber.alternate !== current) {
516
840
  fiber.flags |= Placement;
517
841
  fiber.hostChildListChanged = true;
@@ -617,14 +941,25 @@ function bubbleHostChild(parent, child) {
617
941
  if (child.hasRefSubtree) {
618
942
  parent.hasRefSubtree = true;
619
943
  }
944
+ if (child.hasDisposableResources) {
945
+ parent.hasDisposableResources = true;
946
+ }
620
947
  parent.subtreeFlags |= child.flags | child.subtreeFlags;
621
948
  parent.subtreeChildListChanged =
622
949
  parent.subtreeChildListChanged ||
623
950
  child.childListChanged ||
624
951
  child.subtreeChildListChanged;
625
952
  }
953
+ function recordDirtyChildCommitHints(parent, dirtyChildren) {
954
+ if (parent.childListChanged || dirtyChildren === undefined || dirtyChildren.length === 0) {
955
+ return;
956
+ }
957
+ // Reuse the effect-list slot only when there are no child-list deletions.
958
+ parent.deletions = dirtyChildren;
959
+ }
626
960
  function resetFiberRefSubtree(fiber) {
627
961
  fiber.hasRefSubtree = false;
962
+ fiber.hasDisposableResources = false;
628
963
  }
629
964
  function includeNodeRef(fiber, node) {
630
965
  fiber.hasRefSubtree =
@@ -722,6 +1057,12 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
722
1057
  }
723
1058
  return { fiber: undefined, consumed: 0 };
724
1059
  }
1060
+ // Host elements (string type) are by far the most common node. Dispatch them
1061
+ // before the component-type checks below, none of which a string can match,
1062
+ // so each reconciled host element skips ~12 type comparisons / probes.
1063
+ if (typeof node.type === "string") {
1064
+ return createHostComponentFiber(parent, current, node, key, runtime, path, options);
1065
+ }
725
1066
  if (node.type === Fragment) {
726
1067
  const fiber = current?.tag === "fragment"
727
1068
  ? createWorkInProgress(current, node.props.children)
@@ -790,6 +1131,36 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
790
1131
  }
791
1132
  return { fiber, consumed: options.previousNodes?.length ?? 0 };
792
1133
  }
1134
+ if (node.type === REACTIVE_DOM_BLOCK_TYPE) {
1135
+ const blockProps = node.props.blockProps;
1136
+ if (current?.tag === "reactive-dom-block") {
1137
+ // Re-render: reuse the committed DOM/subscriptions and push the new props
1138
+ // into the prop cell instead of re-running render(). Bound text/attributes
1139
+ // update via their reactive subscriptions; the subtree is never reconciled.
1140
+ const fiber = createWorkInProgress(current, node.props);
1141
+ fiber.type = node.type;
1142
+ fiber.hasDisposableResources = true;
1143
+ const previousState = current.stateNode;
1144
+ if (previousState?.propCell !== undefined && blockProps !== undefined) {
1145
+ setReactivePropCell(previousState.propCell, blockProps);
1146
+ }
1147
+ fiber.stateNode = previousState;
1148
+ return { fiber, consumed: options.previousNodes?.length ?? 0 };
1149
+ }
1150
+ const fiber = createFiber("reactive-dom-block", node.props, key);
1151
+ fiber.type = node.type;
1152
+ fiber.hasDisposableResources = true;
1153
+ const render = node.props.render;
1154
+ if (blockProps !== undefined) {
1155
+ const propCell = createReactivePropCell(blockProps);
1156
+ const result = render(createReactivePropProxy(propCell));
1157
+ fiber.stateNode = { node: result.node, dispose: result.dispose, propCell };
1158
+ }
1159
+ else {
1160
+ fiber.stateNode = render();
1161
+ }
1162
+ return { fiber, consumed: options.previousNodes?.length ?? 0 };
1163
+ }
793
1164
  if (isReactCompatProvider(node.type)) {
794
1165
  const fiber = current?.tag === "context-provider" && current.type === node.type
795
1166
  ? createWorkInProgress(current, node.props)
@@ -858,8 +1229,11 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
858
1229
  : createFiber("memo", node.props, key);
859
1230
  fiber.type = memoType;
860
1231
  const renderedElement = {
861
- ...node,
1232
+ $$typeof: node.$$typeof,
862
1233
  type: memoType.type,
1234
+ key: node.key,
1235
+ ref: node.ref,
1236
+ props: node.props,
863
1237
  };
864
1238
  const childResult = createHostFiber(fiber, current?.tag === "memo" ? current.child : undefined, renderedElement, key, runtime, memoPath, options);
865
1239
  fiber.child = childResult.fiber;
@@ -868,7 +1242,7 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
868
1242
  fiber.child.sibling = undefined;
869
1243
  bubbleHostChild(fiber, fiber.child);
870
1244
  }
871
- const instanceKeys = collectInstanceKeys(runtime, memoPath);
1245
+ const instanceKeys = collectMemoInstanceKeys(runtime, memoPath);
872
1246
  const hasClassDescendant = hasClassComponentDescendant(fiber.child);
873
1247
  fiber.memoizedState = {
874
1248
  props: node.props,
@@ -890,8 +1264,11 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
890
1264
  fiber.type = lazyType;
891
1265
  if (lazyType.status === "resolved" && lazyType.resolved !== undefined) {
892
1266
  const renderedElement = {
893
- ...node,
1267
+ $$typeof: node.$$typeof,
894
1268
  type: lazyType.resolved,
1269
+ key: node.key,
1270
+ ref: node.ref,
1271
+ props: node.props,
895
1272
  };
896
1273
  const childResult = createHostFiber(fiber, current?.tag === "lazy" ? current.child : undefined, renderedElement, key, runtime, `${path}.lazy`, options);
897
1274
  fiber.child = childResult.fiber;
@@ -1006,45 +1383,64 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
1006
1383
  const instanceKeys = collectInstanceKeys(runtime, path);
1007
1384
  fiber.stateNode = {
1008
1385
  element: node,
1009
- props: { ...node.props },
1386
+ props: node.props,
1010
1387
  instanceKeys,
1011
1388
  hasContextDependencies: hasContextDependency(runtime, instanceKeys),
1012
1389
  };
1013
1390
  return { fiber, consumed: childResult.consumed };
1014
1391
  }
1392
+ return { fiber: undefined, consumed: 0 };
1393
+ }
1394
+ // The host-component reconcile, split out of createHostFiberImpl so host
1395
+ // elements can be dispatched before the component-type checks. Only called with
1396
+ // a string element type.
1397
+ function createHostComponentFiber(parent, current, node, key, runtime, path, options) {
1015
1398
  if (typeof node.type !== "string") {
1016
1399
  return { fiber: undefined, consumed: 0 };
1017
1400
  }
1401
+ const initialHostOnlyFiber = tryCreateInitialHostOnlyFiber(current, node, key, options);
1402
+ if (initialHostOnlyFiber !== undefined) {
1403
+ return { fiber: initialHostOnlyFiber, consumed: 0 };
1404
+ }
1018
1405
  const elementNamespace = namespaceForHostElement(options.namespace ?? "html", node.type);
1019
1406
  const childNamespace = namespaceForHostChildren(elementNamespace, node.type);
1020
- const fiber = current?.tag === "host-component" && current.type === node.type
1021
- ? createWorkInProgress(current, node.props)
1407
+ const reusableCurrent = current?.tag === "host-component" && current.type === node.type ? current : undefined;
1408
+ const fiber = reusableCurrent !== undefined
1409
+ ? createWorkInProgress(reusableCurrent, node.props)
1022
1410
  : createFiber("host-component", node.props, key);
1411
+ // The hydration node bookkeeping only matters when hydrating. Skipping it for
1412
+ // the common (non-hydration) render avoids an isHostElement(undefined) probe
1413
+ // and the mismatch checks on every reconciled element.
1023
1414
  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);
1415
+ let existingElement;
1416
+ let tagMatches = false;
1417
+ if (options.previousNodes !== undefined) {
1418
+ existingElement = isHostElement(existing) ? existing : undefined;
1419
+ tagMatches =
1420
+ existingElement !== undefined &&
1421
+ hostElementMatches(existingElement, node.type, elementNamespace);
1422
+ if (existing === undefined) {
1423
+ reportMissingHydrationNode(options, path);
1424
+ }
1425
+ else if (!isHostElement(existing)) {
1426
+ reportHydrationNodeTypeMismatch(options, path, `<${node.type}>`, existing);
1427
+ }
1428
+ if (existingElement !== undefined && !tagMatches) {
1429
+ reportRecoverable(options, "tag", path, new Error(`Hydration tag mismatch: expected <${node.type}> but found <${existingElement.tagName.toLowerCase()}>.`));
1430
+ reportElementTextMismatch(options, `${path}.c`, existingElement, node.props.children);
1431
+ }
1036
1432
  }
1037
1433
  fiber.type = node.type;
1434
+ // When reusing a same-type current fiber, its stateNode was created for this
1435
+ // exact tag and namespace (same tree position), so the hostElementMatches
1436
+ // re-check (localName + namespaceURI reads) is redundant.
1038
1437
  fiber.stateNode =
1039
1438
  tagMatches
1040
1439
  ? 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
1440
+ : reusableCurrent !== undefined && isHostElement(reusableCurrent.stateNode)
1441
+ ? reusableCurrent.stateNode
1046
1442
  : createHostElement(getDocumentRef(options), node.type, options.namespace ?? "html");
1047
- fiber.hydrateExisting = tagMatches && options.previousNodes !== undefined;
1443
+ fiber.hydrateExisting = tagMatches;
1048
1444
  const previousChildNodes = tagMatches && existingElement !== undefined
1049
1445
  ? Array.from(existingElement.childNodes)
1050
1446
  : undefined;
@@ -1069,11 +1465,7 @@ function createHostFiberImpl(parent, current, node, key, runtime, path, options
1069
1465
  parent.child ??= fiber;
1070
1466
  return { fiber, consumed: existing === undefined ? 0 : 1 };
1071
1467
  }
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
- });
1468
+ const childResult = reconcileHostChild(fiber, current?.tag === "host-component" ? current.child : undefined, node.props.children, runtime, `${path}.c`, getHostChildFiberOptions(options, childNamespace, previousChildNodes));
1077
1469
  fiber.child = childResult.fiber;
1078
1470
  if (previousChildNodes !== undefined) {
1079
1471
  reportExtraHydrationNodes(options, `${path}.c`, previousChildNodes, childResult.consumed);
@@ -1086,6 +1478,114 @@ function isFunctionComponentType(value) {
1086
1478
  typeof value.prototype?.render !==
1087
1479
  "function");
1088
1480
  }
1481
+ function tryCreateInitialHostOnlyFiber(current, node, key, options) {
1482
+ if (current !== undefined ||
1483
+ options.previousNodes !== undefined ||
1484
+ !shouldUseDirectHostTextChild() ||
1485
+ typeof node.type !== "string" ||
1486
+ !canCreateInitialHostOnlyNode(node)) {
1487
+ return undefined;
1488
+ }
1489
+ return createInitialHostOnlyElementFiber(node, key, options.namespace ?? "html", getDocumentRef(options));
1490
+ }
1491
+ function createInitialHostOnlyElementFiber(node, key, namespace, documentRef) {
1492
+ const elementType = node.type;
1493
+ const elementNamespace = namespaceForHostElement(namespace, elementType);
1494
+ const childNamespace = namespaceForHostChildren(elementNamespace, elementType);
1495
+ const fiber = createFiber("host-component", node.props, key);
1496
+ fiber.type = elementType;
1497
+ fiber.stateNode = createHostElement(documentRef, elementType, namespace);
1498
+ fiber.flags |= Placement;
1499
+ fiber.hostChildListChanged = true;
1500
+ if (getDirectHostTextChild(node.props.children) === undefined) {
1501
+ fiber.child = createInitialHostOnlyChildList(fiber, node.props.children, childNamespace, documentRef);
1502
+ }
1503
+ return fiber;
1504
+ }
1505
+ function createInitialHostOnlyChildList(parent, children, namespace, documentRef) {
1506
+ const normalized = normalizeChildren(children);
1507
+ let first;
1508
+ let previous;
1509
+ for (let index = 0; index < normalized.length; index += 1) {
1510
+ const child = normalized[index];
1511
+ const fiber = createInitialHostOnlyChildFiber(child, namespace, documentRef);
1512
+ if (fiber === undefined) {
1513
+ continue;
1514
+ }
1515
+ fiber.return = parent;
1516
+ fiber.memoizedState = index;
1517
+ if (first === undefined) {
1518
+ first = fiber;
1519
+ }
1520
+ else if (previous !== undefined) {
1521
+ previous.sibling = fiber;
1522
+ }
1523
+ bubbleHostChild(parent, fiber);
1524
+ previous = fiber;
1525
+ }
1526
+ return first;
1527
+ }
1528
+ function createInitialHostOnlyChildFiber(node, namespace, documentRef) {
1529
+ if (node === null || node === undefined || typeof node === "boolean") {
1530
+ return undefined;
1531
+ }
1532
+ if (typeof node === "string" || typeof node === "number") {
1533
+ const fiber = createFiber("host-text", String(node));
1534
+ fiber.stateNode = createHostTextNode(documentRef);
1535
+ fiber.flags |= Placement;
1536
+ return fiber;
1537
+ }
1538
+ if (Array.isArray(node)) {
1539
+ const fiber = createFiber("fragment", node);
1540
+ fiber.child = createInitialHostOnlyChildList(fiber, node, namespace, documentRef);
1541
+ return fiber.child === undefined ? undefined : fiber;
1542
+ }
1543
+ if (!isReactCompatElement(node) || typeof node.type !== "string") {
1544
+ return undefined;
1545
+ }
1546
+ return createInitialHostOnlyElementFiber(node, node.key === null ? undefined : node.key, namespace, documentRef);
1547
+ }
1548
+ function canCreateInitialHostOnlyNode(node) {
1549
+ if (node === null ||
1550
+ node === undefined ||
1551
+ typeof node === "boolean" ||
1552
+ typeof node === "string" ||
1553
+ typeof node === "number") {
1554
+ return true;
1555
+ }
1556
+ if (Array.isArray(node)) {
1557
+ return node.every(canCreateInitialHostOnlyNode);
1558
+ }
1559
+ if (!isReactCompatElement(node) ||
1560
+ typeof node.type !== "string" ||
1561
+ node.ref !== null ||
1562
+ hasInitialHostOnlyExcludedProps(node.props)) {
1563
+ return false;
1564
+ }
1565
+ return canCreateInitialHostOnlyNode(node.props.children);
1566
+ }
1567
+ function hasInitialHostOnlyExcludedProps(props) {
1568
+ return (hasOwnProperty.call(props, REACTIVE_TEXT_BINDING_META) ||
1569
+ props.dangerouslySetInnerHTML !== undefined ||
1570
+ props.contentEditable === true ||
1571
+ props.suppressContentEditableWarning === true ||
1572
+ props.value !== undefined ||
1573
+ props.defaultValue !== undefined ||
1574
+ props.checked !== undefined ||
1575
+ props.defaultChecked !== undefined);
1576
+ }
1577
+ function getHostChildFiberOptions(options, namespace, previousNodes) {
1578
+ const namespaceUnchanged = options.namespace === namespace ||
1579
+ (options.namespace === undefined && namespace === "html");
1580
+ if (previousNodes === undefined && namespaceUnchanged) {
1581
+ return options;
1582
+ }
1583
+ return {
1584
+ ...options,
1585
+ namespace,
1586
+ ...(previousNodes === undefined ? {} : { previousNodes }),
1587
+ };
1588
+ }
1089
1589
  function commitHostChildren(fiber, parent, eventRoot, path, options = {}) {
1090
1590
  const nodes = [];
1091
1591
  let cursor = fiber;
@@ -1110,6 +1610,29 @@ function commitHostDirtyChildren(fiber, parent, eventRoot, path, options = {}) {
1110
1610
  index += 1;
1111
1611
  }
1112
1612
  }
1613
+ function commitHostDirtyChildrenOf(owner, fiber, parent, eventRoot, path, options = {}) {
1614
+ const dirtyChildren = readDirtyChildCommitHints(owner);
1615
+ if (dirtyChildren === undefined) {
1616
+ commitHostDirtyChildren(fiber, parent, eventRoot, path, options);
1617
+ return;
1618
+ }
1619
+ for (let index = 0; index < dirtyChildren.length; index += 1) {
1620
+ const dirtyChild = dirtyChildren[index];
1621
+ if (dirtyChild !== undefined && hasHostCommitWork(dirtyChild)) {
1622
+ commitHostDirtyFiber(dirtyChild, parent, eventRoot, joinCommitPath(path, String(getDirtyChildCommitIndex(dirtyChild, index))), options);
1623
+ }
1624
+ }
1625
+ owner.deletions = undefined;
1626
+ }
1627
+ function readDirtyChildCommitHints(fiber) {
1628
+ return fiber.childListChanged ? undefined : fiber.deletions;
1629
+ }
1630
+ function getDirtyChildCommitIndex(fiber, fallback) {
1631
+ if (typeof fiber.memoizedState === "number") {
1632
+ return fiber.memoizedState;
1633
+ }
1634
+ return typeof fiber.stateNode === "number" ? fiber.stateNode : fallback;
1635
+ }
1113
1636
  function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1114
1637
  if (fiber.tag === "host-text") {
1115
1638
  commitHostFiber(fiber, parent, eventRoot, path, options);
@@ -1121,6 +1644,7 @@ function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1121
1644
  finishCommittedFiber(fiber);
1122
1645
  return;
1123
1646
  }
1647
+ const isDomElement = isDomHostElement(element);
1124
1648
  const props = fiber.pendingProps;
1125
1649
  const previousProps = fiber.memoizedProps;
1126
1650
  const directTextChild = fiber.child === undefined && fiber.hydrateExisting !== true
@@ -1140,7 +1664,7 @@ function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1140
1664
  !propsAreChildrenOnly &&
1141
1665
  fiber.hydrateExisting !== true &&
1142
1666
  isRowTextOnlyUpdate(fiber.memoizedProps, props);
1143
- if (isDomHostElement(element) && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
1667
+ if (isDomElement && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
1144
1668
  applyProps(element, props, path, {
1145
1669
  ...options,
1146
1670
  eventRoot,
@@ -1155,15 +1679,15 @@ function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1155
1679
  fiber.childListChanged ||
1156
1680
  fiber.subtreeChildListChanged) {
1157
1681
  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))) {
1682
+ if (!(isDomElement && childNodes.length === 0 && committedPortalContainers.has(element)) &&
1683
+ !(isDomElement && shouldPreserveContentEditableChildren(element, props, childNodes))) {
1160
1684
  syncChildNodes(element, childNodes);
1161
1685
  }
1162
1686
  }
1163
1687
  else if (fiber.subtreeFlags !== NoFlags) {
1164
- commitHostDirtyChildren(fiber.child, element, eventRoot, `${path}.c`, options);
1688
+ commitHostDirtyChildrenOf(fiber, fiber.child, element, eventRoot, `${path}.c`, options);
1165
1689
  }
1166
- if (isDomHostElement(element)) {
1690
+ if (isDomElement && isFormHostType(fiber.type)) {
1167
1691
  applyPostChildFormProps(element, props, previousProps);
1168
1692
  }
1169
1693
  applyChangedRef(previousProps?.ref, props.ref, element);
@@ -1192,15 +1716,20 @@ function commitHostDirtyFiber(fiber, parent, eventRoot, path, options = {}) {
1192
1716
  fiber.memoizedState = childNodes;
1193
1717
  }
1194
1718
  else {
1195
- commitHostDirtyChildren(fiber.child, container, portalEventRoot, `${path}.portal`, portalOptions);
1719
+ commitHostDirtyChildrenOf(fiber, fiber.child, container, portalEventRoot, `${path}.portal`, portalOptions);
1196
1720
  }
1197
1721
  }
1198
1722
  fiber.memoizedProps = fiber.pendingProps;
1199
1723
  finishCommittedFiber(fiber);
1200
1724
  return;
1201
1725
  }
1726
+ if (fiber.tag === "reactive-dom-block") {
1727
+ fiber.memoizedProps = fiber.pendingProps;
1728
+ finishCommittedFiber(fiber);
1729
+ return;
1730
+ }
1202
1731
  if (fiber.subtreeFlags !== NoFlags) {
1203
- commitHostDirtyChildren(fiber.child, parent, eventRoot, path, options);
1732
+ commitHostDirtyChildrenOf(fiber, fiber.child, parent, eventRoot, path, options);
1204
1733
  }
1205
1734
  fiber.memoizedProps = fiber.pendingProps;
1206
1735
  finishCommittedFiber(fiber);
@@ -1307,7 +1836,9 @@ function readAppendSuffixCommitHint(value) {
1307
1836
  : undefined;
1308
1837
  }
1309
1838
  function commitHostSingleRemoval(fiber, parent) {
1310
- const removed = getSingleRemovedFiber(fiber.alternate?.child, fiber.child);
1839
+ const removed = fiber.deletions?.length === 1
1840
+ ? fiber.deletions[0]
1841
+ : getSingleRemovedFiber(fiber.alternate?.child, fiber.child);
1311
1842
  if (removed === undefined) {
1312
1843
  return false;
1313
1844
  }
@@ -1319,6 +1850,9 @@ function commitHostSingleRemoval(fiber, parent) {
1319
1850
  parent.removeChild(node);
1320
1851
  removedAny = true;
1321
1852
  }
1853
+ if (removedAny) {
1854
+ fiber.deletions = undefined;
1855
+ }
1322
1856
  return removedAny;
1323
1857
  }
1324
1858
  function getAppendSuffix(current, next) {
@@ -1372,6 +1906,10 @@ function isSameFiberSlot(current, next) {
1372
1906
  current.key === next.key);
1373
1907
  }
1374
1908
  function collectCommittedHostNodes(fiber) {
1909
+ if (fiber.tag === "reactive-dom-block") {
1910
+ const node = getReactiveDomBlockNode(fiber.stateNode);
1911
+ return node === undefined ? [] : [node];
1912
+ }
1375
1913
  if ((fiber.tag === "host-component" || fiber.tag === "host-text") &&
1376
1914
  fiber.stateNode instanceof Node) {
1377
1915
  return [fiber.stateNode];
@@ -1385,6 +1923,9 @@ function collectCommittedHostNodes(fiber) {
1385
1923
  return nodes;
1386
1924
  }
1387
1925
  function hasCommittedHostNode(fiber) {
1926
+ if (fiber.tag === "reactive-dom-block") {
1927
+ return getReactiveDomBlockNode(fiber.stateNode) !== undefined;
1928
+ }
1388
1929
  if ((fiber.tag === "host-component" || fiber.tag === "host-text") &&
1389
1930
  fiber.stateNode instanceof Node) {
1390
1931
  return true;
@@ -1428,6 +1969,12 @@ function commitHostFiber(fiber, parent, eventRoot, path, options = {}) {
1428
1969
  finishCommittedFiber(fiber);
1429
1970
  return [text];
1430
1971
  }
1972
+ if (fiber.tag === "reactive-dom-block") {
1973
+ const node = getReactiveDomBlockNode(fiber.stateNode);
1974
+ fiber.memoizedProps = fiber.pendingProps;
1975
+ finishCommittedFiber(fiber);
1976
+ return node === undefined ? [] : [node];
1977
+ }
1431
1978
  if (fiber.tag === "host-component") {
1432
1979
  const element = fiber.stateNode;
1433
1980
  if (!isHostElement(element)) {
@@ -1443,6 +1990,7 @@ function commitHostFiber(fiber, parent, eventRoot, path, options = {}) {
1443
1990
  fiber.memoizedProps = fiber.pendingProps;
1444
1991
  return [element];
1445
1992
  }
1993
+ const isDomElement = isDomHostElement(element);
1446
1994
  const props = fiber.pendingProps;
1447
1995
  const previousProps = fiber.memoizedProps;
1448
1996
  const directTextChild = fiber.child === undefined && fiber.hydrateExisting !== true
@@ -1462,7 +2010,7 @@ function commitHostFiber(fiber, parent, eventRoot, path, options = {}) {
1462
2010
  !propsAreChildrenOnly &&
1463
2011
  fiber.hydrateExisting !== true &&
1464
2012
  isRowTextOnlyUpdate(fiber.memoizedProps, props);
1465
- if (isDomHostElement(element) && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
2013
+ if (isDomElement && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
1466
2014
  applyProps(element, props, path, {
1467
2015
  ...options,
1468
2016
  eventRoot,
@@ -1479,15 +2027,15 @@ function commitHostFiber(fiber, parent, eventRoot, path, options = {}) {
1479
2027
  fiber.hydrateExisting === true ||
1480
2028
  (fiber.subtreeFlags & Placement) !== NoFlags) {
1481
2029
  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))) {
2030
+ if (!(isDomElement && childNodes.length === 0 && committedPortalContainers.has(element)) &&
2031
+ !(isDomElement && shouldPreserveContentEditableChildren(element, props, childNodes))) {
1484
2032
  syncChildNodes(element, childNodes);
1485
2033
  }
1486
2034
  }
1487
2035
  else if (fiber.subtreeFlags !== NoFlags) {
1488
2036
  commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
1489
2037
  }
1490
- if (isDomHostElement(element)) {
2038
+ if (isDomElement && isFormHostType(fiber.type)) {
1491
2039
  applyPostChildFormProps(element, props, previousProps);
1492
2040
  }
1493
2041
  applyChangedRef(previousProps?.ref, props.ref, element);
@@ -1600,6 +2148,26 @@ function finishCommittedFiber(fiber) {
1600
2148
  fiber.subtreeChildListChanged = false;
1601
2149
  fiber.hostChildListChanged = false;
1602
2150
  }
2151
+ function getReactiveDomBlockNode(state) {
2152
+ if (typeof state === "object" &&
2153
+ state !== null &&
2154
+ "node" in state &&
2155
+ state.node instanceof Node) {
2156
+ return state.node;
2157
+ }
2158
+ return undefined;
2159
+ }
2160
+ function disposeReactiveDomBlockState(state, seen) {
2161
+ if (typeof state !== "object" || state === null || seen.has(state)) {
2162
+ return;
2163
+ }
2164
+ seen.add(state);
2165
+ const dispose = state.dispose;
2166
+ if (typeof dispose === "function") {
2167
+ dispose();
2168
+ state.dispose = undefined;
2169
+ }
2170
+ }
1603
2171
  function hasChildListMutation(fiber) {
1604
2172
  return fiber.childListChanged || fiber.subtreeChildListChanged;
1605
2173
  }
@@ -1746,6 +2314,12 @@ function getDirectHostTextChild(children) {
1746
2314
  ? String(children)
1747
2315
  : undefined;
1748
2316
  }
2317
+ // Only these host tags carry post-child form value/checked semantics. Gating on
2318
+ // the (cheap, already-known) type string lets the commit skip applyPostChildFormProps
2319
+ // and its instanceof probes for every other element.
2320
+ function isFormHostType(type) {
2321
+ return type === "input" || type === "textarea" || type === "select";
2322
+ }
1749
2323
  const hostFastPathMode = (() => {
1750
2324
  try {
1751
2325
  // The literal process.env.NODE_ENV member expression is what bundler
@@ -2016,7 +2590,23 @@ function tryReuseMemoBailout(current, node, runtime, path, options, canReuseCurr
2016
2590
  !areMemoPropsEqual(node.type, previousMemoState.props, node.props)) {
2017
2591
  return undefined;
2018
2592
  }
2019
- const fiber = getMemoBailoutFiber(runtime, current, node.props, previousMemoState, canReuseCurrentFiber);
2593
+ if (memoStateNeedsActiveInstanceMark(previousMemoState)) {
2594
+ markActiveInstanceKeys(runtime, previousMemoState.instanceKeys);
2595
+ }
2596
+ const fiber = canReuseCurrentFiber &&
2597
+ current.hasRefSubtree !== true &&
2598
+ current.hasDisposableResources !== true &&
2599
+ current.hydrateExisting !== true
2600
+ ? current
2601
+ : createWorkInProgress(current, node.props);
2602
+ if (fiber === current) {
2603
+ current.pendingProps = node.props;
2604
+ current.flags = NoFlags;
2605
+ current.subtreeFlags = NoFlags;
2606
+ current.childListChanged = false;
2607
+ current.subtreeChildListChanged = false;
2608
+ current.hostChildListChanged = false;
2609
+ }
2020
2610
  fiber.type = node.type;
2021
2611
  fiber.child = getSkippedChild(current);
2022
2612
  fiber.memoizedState = previousMemoState;
@@ -2239,6 +2829,24 @@ function isLazyType(value) {
2239
2829
  function collectInstanceKeys(runtime, prefix) {
2240
2830
  return collectRuntimeInstanceKeys(runtime, prefix);
2241
2831
  }
2832
+ function collectMemoInstanceKeys(runtime, prefix) {
2833
+ return readDependencyFreeMemoInstanceKey(runtime, prefix) === undefined
2834
+ ? collectInstanceKeys(runtime, prefix)
2835
+ : emptyInstanceKeys;
2836
+ }
2837
+ function readDependencyFreeMemoInstanceKey(runtime, prefix) {
2838
+ const keys = runtime.instanceKeysByPrefix.get(prefix);
2839
+ if (keys === undefined || keys.size !== 1 || !keys.has(prefix)) {
2840
+ return undefined;
2841
+ }
2842
+ const instance = runtime.instances.get(prefix);
2843
+ if (instance === undefined ||
2844
+ instance.contextDependencies !== undefined ||
2845
+ (instance.hooks !== undefined && instance.hooks.length > 0)) {
2846
+ return undefined;
2847
+ }
2848
+ return prefix;
2849
+ }
2242
2850
  function markActiveInstanceKeys(runtime, keys) {
2243
2851
  for (const key of keys) {
2244
2852
  runtime.activeInstanceKeys?.add(key);
@@ -2254,6 +2862,9 @@ function memoStateNeedsActiveInstanceMark(state) {
2254
2862
  return state.hasRetainedInstanceDependencies !== false;
2255
2863
  }
2256
2864
  function getMemoBailoutFiber(runtime, current, pendingProps, state, canReuseCurrentFiber) {
2865
+ if (memoStateNeedsActiveInstanceMark(state)) {
2866
+ markActiveInstanceKeys(runtime, state.instanceKeys);
2867
+ }
2257
2868
  if (canReuseCurrentFiber && canReuseMemoBailoutFiber(current, state)) {
2258
2869
  current.pendingProps = pendingProps;
2259
2870
  current.flags = NoFlags;
@@ -2264,14 +2875,12 @@ function getMemoBailoutFiber(runtime, current, pendingProps, state, canReuseCurr
2264
2875
  return current;
2265
2876
  }
2266
2877
  const fiber = createWorkInProgress(current, pendingProps);
2267
- if (memoStateNeedsActiveInstanceMark(state)) {
2268
- markActiveInstanceKeys(runtime, state.instanceKeys);
2269
- }
2270
2878
  return fiber;
2271
2879
  }
2272
2880
  function canReuseMemoBailoutFiber(current, state) {
2273
2881
  return (state.hasRetainedInstanceDependencies === false &&
2274
2882
  current.hasRefSubtree !== true &&
2883
+ current.hasDisposableResources !== true &&
2275
2884
  current.hydrateExisting !== true);
2276
2885
  }
2277
2886
  function hasDirtyInstanceDependencies(runtime, keys) {