@reckona/mreact-compat 0.0.171 → 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.
- package/dist/devtools.js +2 -0
- package/dist/devtools.js.map +1 -1
- package/dist/dom-host-rules.d.ts.map +1 -1
- package/dist/dom-host-rules.js +4 -1
- package/dist/dom-host-rules.js.map +1 -1
- package/dist/dom-props.js +2 -1
- package/dist/dom-props.js.map +1 -1
- package/dist/element.d.ts +13 -1
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +30 -2
- package/dist/element.js.map +1 -1
- package/dist/fiber-commit.js +2 -1
- package/dist/fiber-commit.js.map +1 -1
- package/dist/fiber-host.d.ts +1 -1
- package/dist/fiber-host.d.ts.map +1 -1
- package/dist/fiber-host.js +1 -1
- package/dist/fiber-host.js.map +1 -1
- package/dist/fiber.d.ts +2 -1
- package/dist/fiber.d.ts.map +1 -1
- package/dist/fiber.js +2 -0
- package/dist/fiber.js.map +1 -1
- package/dist/hooks.d.ts +7 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +122 -41
- package/dist/hooks.js.map +1 -1
- package/dist/host-reconciler.d.ts +1 -0
- package/dist/host-reconciler.d.ts.map +1 -1
- package/dist/host-reconciler.js +659 -50
- package/dist/host-reconciler.js.map +1 -1
- package/dist/jsx-dev-runtime.d.ts +3 -1
- package/dist/jsx-dev-runtime.d.ts.map +1 -1
- package/dist/jsx-dev-runtime.js +3 -1
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.d.ts +4 -2
- package/dist/jsx-runtime.d.ts.map +1 -1
- package/dist/jsx-runtime.js +9 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/reactive-prop-cell.d.ts +13 -0
- package/dist/reactive-prop-cell.d.ts.map +1 -0
- package/dist/reactive-prop-cell.js +36 -0
- package/dist/reactive-prop-cell.js.map +1 -0
- package/dist/root.d.ts.map +1 -1
- package/dist/root.js +3 -1
- package/dist/root.js.map +1 -1
- package/package.json +3 -3
- package/src/devtools.ts +2 -0
- package/src/dom-host-rules.ts +4 -1
- package/src/dom-props.ts +2 -1
- package/src/element.ts +60 -3
- package/src/fiber-commit.ts +3 -1
- package/src/fiber-host.ts +1 -0
- package/src/fiber.ts +4 -0
- package/src/hooks.ts +173 -41
- package/src/host-reconciler.ts +979 -64
- package/src/jsx-dev-runtime.ts +4 -0
- package/src/jsx-runtime.ts +16 -0
- package/src/reactive-prop-cell.ts +67 -0
- package/src/root.ts +3 -0
package/dist/host-reconciler.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
274
|
+
matchedCurrent = matched;
|
|
230
275
|
canReuseMatchedCurrentFiber = false;
|
|
231
|
-
currentKeyed =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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:
|
|
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
|
|
1021
|
-
|
|
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
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
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
|
-
:
|
|
1042
|
-
|
|
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
|
|
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 (
|
|
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 (!(
|
|
1159
|
-
!(
|
|
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
|
-
|
|
1688
|
+
commitHostDirtyChildrenOf(fiber, fiber.child, element, eventRoot, `${path}.c`, options);
|
|
1165
1689
|
}
|
|
1166
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 (
|
|
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 (!(
|
|
1483
|
-
!(
|
|
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 (
|
|
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
|
-
|
|
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) {
|