@reckona/mreact-compat 0.0.90 → 0.0.92
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/README.md +1 -0
- package/dist/class-component.d.ts +20 -6
- package/dist/class-component.d.ts.map +1 -1
- package/dist/class-component.js +94 -51
- package/dist/class-component.js.map +1 -1
- package/dist/context.d.ts +19 -3
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +55 -6
- package/dist/context.js.map +1 -1
- package/dist/dom-children.d.ts +2 -0
- package/dist/dom-children.d.ts.map +1 -1
- package/dist/dom-children.js +103 -1
- package/dist/dom-children.js.map +1 -1
- package/dist/dom-host-rules.d.ts +10 -0
- package/dist/dom-host-rules.d.ts.map +1 -0
- package/dist/dom-host-rules.js +86 -0
- package/dist/dom-host-rules.js.map +1 -0
- package/dist/dom-props.d.ts +3 -2
- package/dist/dom-props.d.ts.map +1 -1
- package/dist/dom-props.js +229 -33
- package/dist/dom-props.js.map +1 -1
- package/dist/element.d.ts +9 -4
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +101 -26
- package/dist/element.js.map +1 -1
- package/dist/event-listeners.d.ts +4 -4
- package/dist/event-listeners.d.ts.map +1 -1
- package/dist/event-listeners.js +1 -1
- package/dist/event-listeners.js.map +1 -1
- package/dist/event-types.d.ts +10 -0
- package/dist/event-types.d.ts.map +1 -1
- package/dist/event-types.js.map +1 -1
- package/dist/events.js +22 -1
- package/dist/events.js.map +1 -1
- package/dist/fiber-commit.d.ts +2 -1
- package/dist/fiber-commit.d.ts.map +1 -1
- package/dist/fiber-commit.js +13 -1
- package/dist/fiber-commit.js.map +1 -1
- package/dist/fiber-reconciler.d.ts.map +1 -1
- package/dist/fiber-reconciler.js +28 -7
- package/dist/fiber-reconciler.js.map +1 -1
- package/dist/fiber-work-loop.d.ts.map +1 -1
- package/dist/fiber-work-loop.js +4 -3
- package/dist/fiber-work-loop.js.map +1 -1
- package/dist/fiber.d.ts +5 -0
- package/dist/fiber.d.ts.map +1 -1
- package/dist/fiber.js +9 -0
- package/dist/fiber.js.map +1 -1
- package/dist/hooks-entry.d.ts +3 -0
- package/dist/hooks-entry.d.ts.map +1 -0
- package/dist/hooks-entry.js +2 -0
- package/dist/hooks-entry.js.map +1 -0
- package/dist/hooks.d.ts +39 -5
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +373 -326
- package/dist/hooks.js.map +1 -1
- package/dist/host-reconciler.d.ts +3 -0
- package/dist/host-reconciler.d.ts.map +1 -1
- package/dist/host-reconciler.js +1152 -64
- package/dist/host-reconciler.js.map +1 -1
- package/dist/hydration.d.ts +1 -1
- package/dist/hydration.d.ts.map +1 -1
- package/dist/hydration.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/react-default.d.ts +4 -4
- package/dist/react-default.d.ts.map +1 -1
- package/dist/react-default.js +2 -1
- package/dist/react-default.js.map +1 -1
- package/dist/reconciler.d.ts.map +1 -1
- package/dist/reconciler.js +38 -22
- package/dist/reconciler.js.map +1 -1
- package/dist/root.d.ts.map +1 -1
- package/dist/root.js +48 -13
- package/dist/root.js.map +1 -1
- package/dist/server-render.d.ts +6 -0
- package/dist/server-render.d.ts.map +1 -0
- package/dist/server-render.js +307 -0
- package/dist/server-render.js.map +1 -0
- package/package.json +6 -2
- package/src/class-component.ts +216 -51
- package/src/context.ts +108 -9
- package/src/dom-children.ts +155 -1
- package/src/dom-host-rules.ts +115 -0
- package/src/dom-props.ts +297 -46
- package/src/element.ts +141 -31
- package/src/event-listeners.ts +6 -6
- package/src/event-types.ts +10 -0
- package/src/events.ts +32 -10
- package/src/fiber-commit.ts +16 -1
- package/src/fiber-reconciler.ts +39 -6
- package/src/fiber-work-loop.ts +4 -3
- package/src/fiber.ts +14 -0
- package/src/hooks-entry.ts +24 -0
- package/src/hooks.ts +482 -479
- package/src/host-reconciler.ts +1628 -94
- package/src/hydration.ts +1 -1
- package/src/index.ts +1 -1
- package/src/react-default.ts +1 -1
- package/src/reconciler.ts +61 -22
- package/src/root.ts +55 -12
- package/src/server-render.ts +478 -0
package/src/host-reconciler.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ERROR_BOUNDARY_TYPE,
|
|
4
4
|
FORWARD_REF_TYPE,
|
|
5
5
|
Fragment,
|
|
6
|
+
HOST_OWN_PROPS_META,
|
|
6
7
|
LAZY_TYPE,
|
|
7
8
|
MEMO_TYPE,
|
|
8
9
|
Profiler,
|
|
@@ -16,22 +17,36 @@ import {
|
|
|
16
17
|
type ReactCompatNode,
|
|
17
18
|
} from "./element.js";
|
|
18
19
|
import {
|
|
20
|
+
consumerContext,
|
|
19
21
|
isReactCompatConsumer,
|
|
20
22
|
isReactCompatProvider,
|
|
21
23
|
renderWithContextProvider,
|
|
22
24
|
useContext,
|
|
23
25
|
} from "./context.js";
|
|
24
26
|
import { applyPostChildFormProps, applyProps } from "./dom-props.js";
|
|
25
|
-
import { syncChildNodes, syncScopedChildNodes } from "./dom-children.js";
|
|
27
|
+
import { syncChildNodes, syncOwnedChildNodes, syncScopedChildNodes } from "./dom-children.js";
|
|
26
28
|
import { setLogicalEventParent } from "./host-event-binder.js";
|
|
29
|
+
import { NoFlags, Placement, Update } from "./fiber-flags.js";
|
|
30
|
+
import {
|
|
31
|
+
createHostElement,
|
|
32
|
+
hostElementMatches,
|
|
33
|
+
isHostElement,
|
|
34
|
+
namespaceForHostChildren,
|
|
35
|
+
namespaceForHostElement,
|
|
36
|
+
type HostNamespace,
|
|
37
|
+
} from "./dom-host-rules.js";
|
|
27
38
|
import { createFiber, createWorkInProgress, type Fiber, type FiberRoot } from "./fiber.js";
|
|
28
39
|
import {
|
|
29
40
|
renderWithRootRuntime,
|
|
30
41
|
renderWithProfiler,
|
|
31
|
-
|
|
42
|
+
renderWithStrictModeMemoCapture,
|
|
43
|
+
renderStrictModeReplay,
|
|
44
|
+
runWithHostCommit,
|
|
32
45
|
restoreRuntimeSnapshot,
|
|
33
46
|
takeRuntimeSnapshot,
|
|
34
47
|
getDevToolsHookState,
|
|
48
|
+
hasContextDependency,
|
|
49
|
+
hasChangedContextDependency,
|
|
35
50
|
type RootRuntime,
|
|
36
51
|
} from "./hooks.js";
|
|
37
52
|
import { isThenable } from "./thenable.js";
|
|
@@ -40,7 +55,7 @@ import {
|
|
|
40
55
|
recoverClassComponentError,
|
|
41
56
|
renderClassComponentWithRuntime,
|
|
42
57
|
} from "./class-component.js";
|
|
43
|
-
import { areMemoPropsEqual, getPendingProps } from "./prop-comparison.js";
|
|
58
|
+
import { areMemoPropsEqual, getPendingProps, shallowEqual } from "./prop-comparison.js";
|
|
44
59
|
import {
|
|
45
60
|
reportElementTextMismatch,
|
|
46
61
|
reportExtraHydrationNodes,
|
|
@@ -58,16 +73,30 @@ interface MemoFiberState {
|
|
|
58
73
|
instanceKeys: string[];
|
|
59
74
|
}
|
|
60
75
|
|
|
76
|
+
interface FunctionFiberState {
|
|
77
|
+
element: ReactCompatElement;
|
|
78
|
+
props: Record<string, unknown>;
|
|
79
|
+
instanceKeys: string[];
|
|
80
|
+
hasContextDependencies: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
61
83
|
interface SuspenseFiberState {
|
|
62
84
|
didSuspend: boolean;
|
|
63
85
|
}
|
|
64
86
|
|
|
87
|
+
const committedPortalContainers = new Set<Element>();
|
|
88
|
+
|
|
65
89
|
interface FiberHydrationOptions extends RenderOptions {
|
|
66
90
|
previousNodes?: readonly Node[];
|
|
67
91
|
resumeId?: string;
|
|
68
92
|
consumeResumeMarkers?: boolean;
|
|
93
|
+
namespace?: HostNamespace;
|
|
94
|
+
documentRef?: Document;
|
|
69
95
|
}
|
|
70
96
|
|
|
97
|
+
const SKIP_COMMIT_PATH = "\0";
|
|
98
|
+
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
99
|
+
|
|
71
100
|
interface FiberReconcileResult {
|
|
72
101
|
fiber: Fiber | undefined;
|
|
73
102
|
consumed: number;
|
|
@@ -166,16 +195,18 @@ export function renderHostFiberRoot(
|
|
|
166
195
|
options: FiberHydrationOptions = {},
|
|
167
196
|
): Fiber {
|
|
168
197
|
const workInProgress = createWorkInProgress(root.current, { children: element });
|
|
198
|
+
const rootDocument = root.container.ownerDocument;
|
|
169
199
|
const result = reconcileHostChild(
|
|
170
200
|
workInProgress,
|
|
171
201
|
root.current.child,
|
|
172
202
|
element,
|
|
173
203
|
runtime,
|
|
174
204
|
options.previousNodes === undefined ? "0" : "",
|
|
175
|
-
options,
|
|
205
|
+
{ ...options, documentRef: options.documentRef ?? rootDocument },
|
|
176
206
|
);
|
|
177
207
|
workInProgress.child = result.fiber;
|
|
178
208
|
workInProgress.memoizedProps = { children: element };
|
|
209
|
+
root.refCleanupKnown = true;
|
|
179
210
|
return workInProgress;
|
|
180
211
|
}
|
|
181
212
|
|
|
@@ -204,8 +235,29 @@ export function commitHostFiberRoot(
|
|
|
204
235
|
finishedWork: Fiber,
|
|
205
236
|
options: RenderOptions = {},
|
|
206
237
|
): void {
|
|
207
|
-
|
|
208
|
-
|
|
238
|
+
runWithHostCommit(() => {
|
|
239
|
+
try {
|
|
240
|
+
committedPortalContainers.clear();
|
|
241
|
+
const commitPath = getRootCommitPath(options);
|
|
242
|
+
if (!hasChildListMutation(finishedWork)) {
|
|
243
|
+
commitHostDirtyChildren(finishedWork.child, root.container, root.container, commitPath, options);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (
|
|
248
|
+
!finishedWork.childListChanged &&
|
|
249
|
+
finishedWork.subtreeChildListChanged &&
|
|
250
|
+
commitHostKeyedChildListMutation(finishedWork.child, root.container, root.container, commitPath, options)
|
|
251
|
+
) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const nodes = commitHostChildren(finishedWork.child, root.container, root.container, commitPath, options);
|
|
256
|
+
syncChildNodes(root.container, nodes);
|
|
257
|
+
} finally {
|
|
258
|
+
committedPortalContainers.clear();
|
|
259
|
+
}
|
|
260
|
+
});
|
|
209
261
|
}
|
|
210
262
|
|
|
211
263
|
export function commitHydratingHostFiberRoot(
|
|
@@ -214,9 +266,16 @@ export function commitHydratingHostFiberRoot(
|
|
|
214
266
|
scope: HydrationScope,
|
|
215
267
|
options: FiberHydrationOptions = {},
|
|
216
268
|
): void {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
269
|
+
runWithHostCommit(() => {
|
|
270
|
+
try {
|
|
271
|
+
committedPortalContainers.clear();
|
|
272
|
+
const eventRoot = root.container;
|
|
273
|
+
const nodes = commitHostChildren(finishedWork.child, scope.parent, eventRoot, "", options);
|
|
274
|
+
syncScopedChildNodes(scope.parent, scope.before, scope.after, nodes);
|
|
275
|
+
} finally {
|
|
276
|
+
committedPortalContainers.clear();
|
|
277
|
+
}
|
|
278
|
+
});
|
|
220
279
|
|
|
221
280
|
if (options.consumeResumeMarkers === true) {
|
|
222
281
|
scope.before?.parentNode?.removeChild(scope.before);
|
|
@@ -232,17 +291,70 @@ function reconcileHostChild(
|
|
|
232
291
|
path: string,
|
|
233
292
|
options: FiberHydrationOptions = {},
|
|
234
293
|
): FiberReconcileResult {
|
|
235
|
-
|
|
236
|
-
|
|
294
|
+
resetFiberRefSubtree(parent);
|
|
295
|
+
parent.subtreeFlags = NoFlags;
|
|
296
|
+
parent.childListChanged = false;
|
|
297
|
+
parent.subtreeChildListChanged = false;
|
|
298
|
+
|
|
299
|
+
if (node === null || node === undefined || typeof node === "boolean") {
|
|
300
|
+
parent.childListChanged = currentFirstChild !== undefined;
|
|
301
|
+
return { fiber: undefined, consumed: 0 };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const children = Array.isArray(node) ? node : undefined;
|
|
305
|
+
const rowResult =
|
|
306
|
+
children === undefined
|
|
307
|
+
? undefined
|
|
308
|
+
: reconcileKeyedRowHostChildren(parent, currentFirstChild, children, options);
|
|
309
|
+
if (rowResult !== undefined) {
|
|
310
|
+
return rowResult;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const childCount = children === undefined ? 1 : children.length;
|
|
314
|
+
const hasKeyedChildren = children !== undefined && hasKeyedChild(children);
|
|
315
|
+
let existingByKey: Map<string, Fiber> | undefined;
|
|
316
|
+
let currentKeyed: Fiber | undefined = currentFirstChild;
|
|
237
317
|
let currentUnkeyed = currentFirstChild;
|
|
238
318
|
let first: Fiber | undefined;
|
|
239
319
|
let previous: Fiber | undefined;
|
|
240
320
|
let consumed = 0;
|
|
321
|
+
let skipRemainingKeyedLookup = false;
|
|
241
322
|
|
|
242
|
-
|
|
323
|
+
for (let index = 0; index < childCount; index += 1) {
|
|
324
|
+
const child = children === undefined ? node : children[index];
|
|
243
325
|
const key = getNodeKey(child);
|
|
244
|
-
|
|
245
|
-
|
|
326
|
+
let matchedCurrent: Fiber | undefined;
|
|
327
|
+
|
|
328
|
+
if (key === undefined) {
|
|
329
|
+
matchedCurrent = currentUnkeyed;
|
|
330
|
+
} else if (skipRemainingKeyedLookup) {
|
|
331
|
+
matchedCurrent = undefined;
|
|
332
|
+
} else if (existingByKey !== undefined) {
|
|
333
|
+
matchedCurrent = existingByKey.get(key);
|
|
334
|
+
} else if (currentKeyed?.key === key) {
|
|
335
|
+
matchedCurrent = currentKeyed;
|
|
336
|
+
currentKeyed = currentKeyed.sibling;
|
|
337
|
+
} else if (
|
|
338
|
+
children !== undefined &&
|
|
339
|
+
currentKeyed?.sibling?.key === key &&
|
|
340
|
+
canSkipSingleDeletedKeyedFiber(children, index, currentKeyed.sibling)
|
|
341
|
+
) {
|
|
342
|
+
matchedCurrent = currentKeyed.sibling;
|
|
343
|
+
currentKeyed = currentKeyed.sibling.sibling;
|
|
344
|
+
} else {
|
|
345
|
+
if (
|
|
346
|
+
children !== undefined &&
|
|
347
|
+
hasKeyedChildren &&
|
|
348
|
+
canSkipRemainingKeyedLookup(currentKeyed, children, index)
|
|
349
|
+
) {
|
|
350
|
+
skipRemainingKeyedLookup = true;
|
|
351
|
+
currentKeyed = undefined;
|
|
352
|
+
} else if (hasKeyedChildren) {
|
|
353
|
+
existingByKey = collectExistingKeyedFibers(currentKeyed);
|
|
354
|
+
matchedCurrent = existingByKey.get(key);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
246
358
|
const previousNodes =
|
|
247
359
|
options.previousNodes === undefined
|
|
248
360
|
? undefined
|
|
@@ -253,13 +365,13 @@ function reconcileHostChild(
|
|
|
253
365
|
child,
|
|
254
366
|
key,
|
|
255
367
|
runtime,
|
|
256
|
-
|
|
368
|
+
getReconcileChildPath(path, child, index, options),
|
|
257
369
|
previousNodes === undefined ? options : { ...options, previousNodes },
|
|
258
370
|
);
|
|
259
371
|
const fiber = result.fiber;
|
|
260
372
|
|
|
261
373
|
if (fiber === undefined) {
|
|
262
|
-
|
|
374
|
+
continue;
|
|
263
375
|
}
|
|
264
376
|
|
|
265
377
|
if (key === undefined) {
|
|
@@ -275,7 +387,7 @@ function reconcileHostChild(
|
|
|
275
387
|
|
|
276
388
|
fiber.return = parent;
|
|
277
389
|
fiber.sibling = undefined;
|
|
278
|
-
fiber
|
|
390
|
+
bubbleHostChild(parent, fiber);
|
|
279
391
|
if (
|
|
280
392
|
fiber.tag !== "memo" &&
|
|
281
393
|
fiber.tag !== "function-component" &&
|
|
@@ -288,11 +400,479 @@ function reconcileHostChild(
|
|
|
288
400
|
fiber.memoizedState = index;
|
|
289
401
|
}
|
|
290
402
|
previous = fiber;
|
|
291
|
-
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
parent.childListChanged = childFiberListShapeChanged(currentFirstChild, first);
|
|
292
406
|
|
|
293
407
|
return { fiber: first, consumed };
|
|
294
408
|
}
|
|
295
409
|
|
|
410
|
+
function reconcileKeyedRowHostChildren(
|
|
411
|
+
parent: Fiber,
|
|
412
|
+
currentFirstChild: Fiber | undefined,
|
|
413
|
+
children: readonly ReactCompatNode[],
|
|
414
|
+
options: FiberHydrationOptions,
|
|
415
|
+
): FiberReconcileResult | undefined {
|
|
416
|
+
if (
|
|
417
|
+
children.length === 0 ||
|
|
418
|
+
currentFirstChild === undefined ||
|
|
419
|
+
options.previousNodes !== undefined ||
|
|
420
|
+
!shouldUseDirectHostTextChild()
|
|
421
|
+
) {
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
let currentKeyed: Fiber | undefined = currentFirstChild;
|
|
426
|
+
let first: Fiber | undefined;
|
|
427
|
+
let previous: Fiber | undefined;
|
|
428
|
+
let listShapeChanged = currentFirstChild === undefined;
|
|
429
|
+
let skipRemainingKeyedLookup = false;
|
|
430
|
+
let subtreeFlags = NoFlags;
|
|
431
|
+
let subtreeChildListChanged = false;
|
|
432
|
+
let hasRefSubtree = false;
|
|
433
|
+
const currentSiblingCount = countFiberSiblings(currentFirstChild);
|
|
434
|
+
const canReuseUnchangedRows =
|
|
435
|
+
currentSiblingCount === children.length ||
|
|
436
|
+
isKeyedAppendOnly(currentFirstChild, children, currentSiblingCount);
|
|
437
|
+
const row = createKeyedRowHostElementScratch();
|
|
438
|
+
|
|
439
|
+
for (let index = 0; index < children.length; index += 1) {
|
|
440
|
+
const child = children[index];
|
|
441
|
+
|
|
442
|
+
if (!readKeyedRowHostElement(child, row)) {
|
|
443
|
+
return undefined;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
let matchedCurrent: Fiber | undefined;
|
|
447
|
+
|
|
448
|
+
if (skipRemainingKeyedLookup) {
|
|
449
|
+
matchedCurrent = undefined;
|
|
450
|
+
} else if (currentKeyed === undefined) {
|
|
451
|
+
listShapeChanged = true;
|
|
452
|
+
skipRemainingKeyedLookup = true;
|
|
453
|
+
matchedCurrent = undefined;
|
|
454
|
+
} else if (currentKeyed?.key === row.key) {
|
|
455
|
+
matchedCurrent = currentKeyed;
|
|
456
|
+
currentKeyed = currentKeyed.sibling;
|
|
457
|
+
} else if (
|
|
458
|
+
currentKeyed?.sibling?.key === row.key &&
|
|
459
|
+
canSkipSingleDeletedKeyedFiber(children, index, currentKeyed.sibling)
|
|
460
|
+
) {
|
|
461
|
+
listShapeChanged = true;
|
|
462
|
+
matchedCurrent = currentKeyed.sibling;
|
|
463
|
+
currentKeyed = currentKeyed.sibling.sibling;
|
|
464
|
+
} else if (canSkipRemainingKeyedLookup(currentKeyed, children, index)) {
|
|
465
|
+
listShapeChanged = true;
|
|
466
|
+
skipRemainingKeyedLookup = true;
|
|
467
|
+
currentKeyed = undefined;
|
|
468
|
+
matchedCurrent = undefined;
|
|
469
|
+
} else {
|
|
470
|
+
return undefined;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const fiber =
|
|
474
|
+
matchedCurrent === undefined
|
|
475
|
+
? createKeyedRowHostFiber(parent, undefined, row, options)
|
|
476
|
+
: (canReuseUnchangedRows ? getReusableKeyedRowHostFiber(matchedCurrent, row) : undefined) ??
|
|
477
|
+
createKeyedRowHostFiber(parent, matchedCurrent, row, options);
|
|
478
|
+
|
|
479
|
+
if (first === undefined) {
|
|
480
|
+
first = fiber;
|
|
481
|
+
} else if (previous !== undefined) {
|
|
482
|
+
previous.sibling = fiber;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
fiber.return = parent;
|
|
486
|
+
fiber.sibling = undefined;
|
|
487
|
+
if (fiber.hasRefSubtree) {
|
|
488
|
+
hasRefSubtree = true;
|
|
489
|
+
}
|
|
490
|
+
subtreeFlags |= fiber.flags | fiber.subtreeFlags;
|
|
491
|
+
subtreeChildListChanged =
|
|
492
|
+
subtreeChildListChanged ||
|
|
493
|
+
fiber.childListChanged ||
|
|
494
|
+
fiber.subtreeChildListChanged;
|
|
495
|
+
if (fiber.memoizedState === undefined) {
|
|
496
|
+
fiber.memoizedState = index;
|
|
497
|
+
}
|
|
498
|
+
previous = fiber;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (currentKeyed !== undefined) {
|
|
502
|
+
listShapeChanged = true;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
parent.hasRefSubtree = hasRefSubtree;
|
|
506
|
+
parent.subtreeFlags = subtreeFlags;
|
|
507
|
+
parent.subtreeChildListChanged = subtreeChildListChanged;
|
|
508
|
+
parent.childListChanged = listShapeChanged;
|
|
509
|
+
return { fiber: first, consumed: 0 };
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function countFiberSiblings(first: Fiber): number {
|
|
513
|
+
let count = 0;
|
|
514
|
+
let cursor: Fiber | undefined = first;
|
|
515
|
+
|
|
516
|
+
while (cursor !== undefined) {
|
|
517
|
+
count += 1;
|
|
518
|
+
cursor = cursor.sibling;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return count;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function isKeyedAppendOnly(
|
|
525
|
+
currentFirstChild: Fiber,
|
|
526
|
+
children: readonly ReactCompatNode[],
|
|
527
|
+
currentSiblingCount: number,
|
|
528
|
+
): boolean {
|
|
529
|
+
if (children.length <= currentSiblingCount) {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
let current: Fiber | undefined = currentFirstChild;
|
|
534
|
+
|
|
535
|
+
for (let index = 0; index < currentSiblingCount; index += 1) {
|
|
536
|
+
if (current === undefined || current.key !== getNodeKey(children[index])) {
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
current = current.sibling;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return current === undefined;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function getReusableKeyedRowHostFiber(
|
|
546
|
+
current: Fiber,
|
|
547
|
+
row: KeyedRowHostElement,
|
|
548
|
+
): Fiber | undefined {
|
|
549
|
+
if (
|
|
550
|
+
current.tag !== "host-component" ||
|
|
551
|
+
current.type !== row.type ||
|
|
552
|
+
current.hydrateExisting ||
|
|
553
|
+
current.child !== undefined ||
|
|
554
|
+
!isHostElement(current.stateNode)
|
|
555
|
+
) {
|
|
556
|
+
return undefined;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const previousProps = current.memoizedProps ?? current.pendingProps;
|
|
560
|
+
|
|
561
|
+
if (typeof previousProps !== "object" || previousProps === null) {
|
|
562
|
+
return undefined;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const previousRecord = previousProps as Record<string, unknown>;
|
|
566
|
+
|
|
567
|
+
if (
|
|
568
|
+
getHostOwnPropsMeta(previousRecord) !== row.meta ||
|
|
569
|
+
getDirectHostTextChild(previousRecord.children) !== row.text
|
|
570
|
+
) {
|
|
571
|
+
return undefined;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
current.pendingProps = row.element.props;
|
|
575
|
+
current.flags = NoFlags;
|
|
576
|
+
current.subtreeFlags = NoFlags;
|
|
577
|
+
current.childListChanged = false;
|
|
578
|
+
current.subtreeChildListChanged = false;
|
|
579
|
+
current.hostChildListChanged = false;
|
|
580
|
+
current.hasRefSubtree = false;
|
|
581
|
+
return current;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
interface KeyedRowHostElement {
|
|
585
|
+
element: ReactCompatElement;
|
|
586
|
+
key: string;
|
|
587
|
+
type: string;
|
|
588
|
+
meta: number;
|
|
589
|
+
text: string;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function createKeyedRowHostElementScratch(): KeyedRowHostElement {
|
|
593
|
+
return {
|
|
594
|
+
element: undefined as unknown as ReactCompatElement,
|
|
595
|
+
key: "",
|
|
596
|
+
type: "",
|
|
597
|
+
meta: 0,
|
|
598
|
+
text: "",
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function readKeyedRowHostElement(
|
|
603
|
+
node: ReactCompatNode,
|
|
604
|
+
row: KeyedRowHostElement,
|
|
605
|
+
): boolean {
|
|
606
|
+
if (
|
|
607
|
+
!isReactCompatElement(node) ||
|
|
608
|
+
typeof node.type !== "string" ||
|
|
609
|
+
node.key === null ||
|
|
610
|
+
node.ref !== null
|
|
611
|
+
) {
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const props = node.props as Record<string, unknown>;
|
|
616
|
+
const meta = getHostOwnPropsMeta(props);
|
|
617
|
+
const text = meta === undefined ? undefined : getDirectHostTextChild(props.children);
|
|
618
|
+
|
|
619
|
+
if (meta === undefined || text === undefined) {
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
row.element = node;
|
|
624
|
+
row.key = node.key;
|
|
625
|
+
row.type = node.type;
|
|
626
|
+
row.meta = meta;
|
|
627
|
+
row.text = text;
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function createKeyedRowHostFiber(
|
|
632
|
+
parent: Fiber,
|
|
633
|
+
current: Fiber | undefined,
|
|
634
|
+
row: KeyedRowHostElement,
|
|
635
|
+
options: FiberHydrationOptions,
|
|
636
|
+
): Fiber {
|
|
637
|
+
const node = row.element;
|
|
638
|
+
const elementNamespace = namespaceForHostElement(options.namespace ?? "html", row.type);
|
|
639
|
+
const fiber =
|
|
640
|
+
current?.tag === "host-component" && current.type === row.type
|
|
641
|
+
? createWorkInProgress(current, node.props)
|
|
642
|
+
: createFiber("host-component", node.props, row.key);
|
|
643
|
+
|
|
644
|
+
fiber.type = row.type;
|
|
645
|
+
fiber.stateNode =
|
|
646
|
+
current?.tag === "host-component" &&
|
|
647
|
+
current.type === row.type &&
|
|
648
|
+
isHostElement(current.stateNode) &&
|
|
649
|
+
hostElementMatches(current.stateNode, row.type, elementNamespace)
|
|
650
|
+
? current.stateNode
|
|
651
|
+
: createHostElement(getDocumentRef(options), row.type, options.namespace ?? "html");
|
|
652
|
+
fiber.child = undefined;
|
|
653
|
+
fiber.pendingProps = node.props;
|
|
654
|
+
fiber.hostChildListChanged = false;
|
|
655
|
+
fiber.hasRefSubtree = false;
|
|
656
|
+
|
|
657
|
+
if (current === undefined || fiber.alternate !== current) {
|
|
658
|
+
fiber.flags |= Placement;
|
|
659
|
+
fiber.hostChildListChanged = true;
|
|
660
|
+
return fiber;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const previousProps = current.memoizedProps ?? current.pendingProps;
|
|
664
|
+
const previousMeta =
|
|
665
|
+
typeof previousProps === "object" && previousProps !== null
|
|
666
|
+
? getHostOwnPropsMeta(previousProps as Record<string, unknown>)
|
|
667
|
+
: undefined;
|
|
668
|
+
const previousText = getDirectHostTextChild(hostFiberChildrenProp(previousProps));
|
|
669
|
+
|
|
670
|
+
if (previousMeta !== row.meta || previousText !== row.text) {
|
|
671
|
+
fiber.flags |= Update;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
return fiber;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function canReuseStaticHostSubtree(fiber: Fiber | undefined): boolean {
|
|
678
|
+
let cursor = fiber;
|
|
679
|
+
|
|
680
|
+
while (cursor !== undefined) {
|
|
681
|
+
if (
|
|
682
|
+
cursor.tag !== "host-component" &&
|
|
683
|
+
cursor.tag !== "host-text" &&
|
|
684
|
+
cursor.tag !== "fragment" &&
|
|
685
|
+
cursor.tag !== "strict-mode"
|
|
686
|
+
) {
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (cursor.child !== undefined && !canReuseStaticHostSubtree(cursor.child)) {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
cursor = cursor.sibling;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function canSkipSingleDeletedKeyedFiber(
|
|
701
|
+
children: readonly ReactCompatNode[],
|
|
702
|
+
index: number,
|
|
703
|
+
matched: Fiber,
|
|
704
|
+
): boolean {
|
|
705
|
+
const nextKey = index + 1 < children.length ? getNodeKey(children[index + 1]) : undefined;
|
|
706
|
+
const afterMatched = matched.sibling;
|
|
707
|
+
|
|
708
|
+
return nextKey === undefined ? afterMatched === undefined : afterMatched?.key === nextKey;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function canSkipRemainingKeyedLookup(
|
|
712
|
+
current: Fiber | undefined,
|
|
713
|
+
children: readonly ReactCompatNode[],
|
|
714
|
+
startIndex: number,
|
|
715
|
+
): boolean {
|
|
716
|
+
const currentRange = readContiguousNumericFiberKeyRange(current);
|
|
717
|
+
|
|
718
|
+
if (currentRange === undefined) {
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const nextRange = readContiguousNumericNodeKeyRange(children, startIndex);
|
|
723
|
+
|
|
724
|
+
if (nextRange === undefined) {
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return (
|
|
729
|
+
currentRange.end < nextRange.start ||
|
|
730
|
+
nextRange.end < currentRange.start
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function readContiguousNumericFiberKeyRange(
|
|
735
|
+
fiber: Fiber | undefined,
|
|
736
|
+
): { start: number; end: number } | undefined {
|
|
737
|
+
let cursor = fiber;
|
|
738
|
+
let start: number | undefined;
|
|
739
|
+
let previous: number | undefined;
|
|
740
|
+
|
|
741
|
+
while (cursor !== undefined) {
|
|
742
|
+
const value = parseNumericKey(cursor.key);
|
|
743
|
+
|
|
744
|
+
if (value === undefined || (previous !== undefined && value !== previous + 1)) {
|
|
745
|
+
return undefined;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
start ??= value;
|
|
749
|
+
previous = value;
|
|
750
|
+
cursor = cursor.sibling;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return start === undefined || previous === undefined
|
|
754
|
+
? undefined
|
|
755
|
+
: { start, end: previous };
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function readContiguousNumericNodeKeyRange(
|
|
759
|
+
children: readonly ReactCompatNode[],
|
|
760
|
+
startIndex: number,
|
|
761
|
+
): { start: number; end: number } | undefined {
|
|
762
|
+
let start: number | undefined;
|
|
763
|
+
let previous: number | undefined;
|
|
764
|
+
|
|
765
|
+
for (let index = startIndex; index < children.length; index += 1) {
|
|
766
|
+
const value = parseNumericKey(getNodeKey(children[index]));
|
|
767
|
+
|
|
768
|
+
if (value === undefined || (previous !== undefined && value !== previous + 1)) {
|
|
769
|
+
return undefined;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
start ??= value;
|
|
773
|
+
previous = value;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
return start === undefined || previous === undefined
|
|
777
|
+
? undefined
|
|
778
|
+
: { start, end: previous };
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function parseNumericKey(key: string | undefined): number | undefined {
|
|
782
|
+
if (key === undefined || key.length === 0) {
|
|
783
|
+
return undefined;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
const value = Number(key);
|
|
787
|
+
|
|
788
|
+
return Number.isSafeInteger(value) && String(value) === key ? value : undefined;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function childFiberListShapeChanged(
|
|
792
|
+
current: Fiber | undefined,
|
|
793
|
+
next: Fiber | undefined,
|
|
794
|
+
): boolean {
|
|
795
|
+
let currentCursor = current;
|
|
796
|
+
let nextCursor = next;
|
|
797
|
+
|
|
798
|
+
while (currentCursor !== undefined && nextCursor !== undefined) {
|
|
799
|
+
const isSameSlot =
|
|
800
|
+
nextCursor === currentCursor ||
|
|
801
|
+
nextCursor.alternate === currentCursor;
|
|
802
|
+
|
|
803
|
+
if (
|
|
804
|
+
!isSameSlot ||
|
|
805
|
+
currentCursor.tag !== nextCursor.tag ||
|
|
806
|
+
currentCursor.type !== nextCursor.type ||
|
|
807
|
+
currentCursor.key !== nextCursor.key
|
|
808
|
+
) {
|
|
809
|
+
return true;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
currentCursor = currentCursor.sibling;
|
|
813
|
+
nextCursor = nextCursor.sibling;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
return currentCursor !== undefined || nextCursor !== undefined;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function bubbleHostChild(parent: Fiber, child: Fiber): void {
|
|
820
|
+
if (child.hasRefSubtree) {
|
|
821
|
+
parent.hasRefSubtree = true;
|
|
822
|
+
}
|
|
823
|
+
parent.subtreeFlags |= child.flags | child.subtreeFlags;
|
|
824
|
+
parent.subtreeChildListChanged =
|
|
825
|
+
parent.subtreeChildListChanged ||
|
|
826
|
+
child.childListChanged ||
|
|
827
|
+
child.subtreeChildListChanged;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function resetFiberRefSubtree(fiber: Fiber): void {
|
|
831
|
+
fiber.hasRefSubtree = false;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function includeNodeRef(fiber: Fiber, node: ReactCompatNode): void {
|
|
835
|
+
fiber.hasRefSubtree =
|
|
836
|
+
fiber.hasRefSubtree ||
|
|
837
|
+
(isReactCompatElement(node) && node.ref !== null);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function markHostFiberEffects(
|
|
841
|
+
fiber: Fiber,
|
|
842
|
+
current: Fiber | undefined,
|
|
843
|
+
node: ReactCompatNode,
|
|
844
|
+
): void {
|
|
845
|
+
if (current === undefined || fiber.alternate !== current) {
|
|
846
|
+
fiber.flags |= Placement;
|
|
847
|
+
fiber.hostChildListChanged = true;
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
if (fiber.tag === "host-text") {
|
|
852
|
+
if (!Object.is(current.memoizedProps ?? current.pendingProps, fiber.pendingProps)) {
|
|
853
|
+
fiber.flags |= Update;
|
|
854
|
+
}
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (fiber.tag !== "host-component") {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
const previousProps = current.memoizedProps ?? current.pendingProps;
|
|
863
|
+
const nextProps = fiber.pendingProps as Record<string, unknown>;
|
|
864
|
+
|
|
865
|
+
fiber.hostChildListChanged = hostChildListChanged(previousProps, nextProps);
|
|
866
|
+
|
|
867
|
+
if (!hostOwnPropsEqual(previousProps, nextProps) || hostDirectTextChildChanged(previousProps, nextProps)) {
|
|
868
|
+
fiber.flags |= Update;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (isReactCompatElement(node) && node.ref !== null) {
|
|
872
|
+
fiber.flags |= Update;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
296
876
|
function createHostFiber(
|
|
297
877
|
parent: Fiber,
|
|
298
878
|
current: Fiber | undefined,
|
|
@@ -301,6 +881,48 @@ function createHostFiber(
|
|
|
301
881
|
runtime: RootRuntime | undefined,
|
|
302
882
|
path: string,
|
|
303
883
|
options: FiberHydrationOptions = {},
|
|
884
|
+
): FiberReconcileResult {
|
|
885
|
+
const result = createHostFiberImpl(parent, current, node, key, runtime, path, options);
|
|
886
|
+
|
|
887
|
+
if (result.fiber !== undefined) {
|
|
888
|
+
if (canFinalizeNewHostFiber(result.fiber, current, node, options)) {
|
|
889
|
+
result.fiber.flags |= Placement;
|
|
890
|
+
result.fiber.hostChildListChanged = true;
|
|
891
|
+
return result;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
result.fiber.pendingProps = getPendingProps(node);
|
|
895
|
+
includeNodeRef(result.fiber, node);
|
|
896
|
+
markHostFiberEffects(result.fiber, current, node);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return result;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function canFinalizeNewHostFiber(
|
|
903
|
+
fiber: Fiber,
|
|
904
|
+
current: Fiber | undefined,
|
|
905
|
+
node: ReactCompatNode,
|
|
906
|
+
options: FiberHydrationOptions,
|
|
907
|
+
): boolean {
|
|
908
|
+
return (
|
|
909
|
+
current === undefined &&
|
|
910
|
+
options.previousNodes === undefined &&
|
|
911
|
+
fiber.tag === "host-component" &&
|
|
912
|
+
isReactCompatElement(node) &&
|
|
913
|
+
node.ref === null &&
|
|
914
|
+
typeof node.type === "string"
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
function createHostFiberImpl(
|
|
919
|
+
parent: Fiber,
|
|
920
|
+
current: Fiber | undefined,
|
|
921
|
+
node: ReactCompatNode,
|
|
922
|
+
key: string | undefined,
|
|
923
|
+
runtime: RootRuntime | undefined,
|
|
924
|
+
path: string,
|
|
925
|
+
options: FiberHydrationOptions = {},
|
|
304
926
|
): FiberReconcileResult {
|
|
305
927
|
if (node === null || node === undefined || typeof node === "boolean") {
|
|
306
928
|
return { fiber: undefined, consumed: 0 };
|
|
@@ -322,7 +944,7 @@ function createHostFiber(
|
|
|
322
944
|
? existing
|
|
323
945
|
: current?.tag === "host-text" && current.stateNode instanceof Text
|
|
324
946
|
? current.stateNode
|
|
325
|
-
:
|
|
947
|
+
: getDocumentRef(options).createTextNode("");
|
|
326
948
|
|
|
327
949
|
if (existing instanceof Text && existing.data !== String(node)) {
|
|
328
950
|
reportRecoverable(
|
|
@@ -522,7 +1144,7 @@ function createHostFiber(
|
|
|
522
1144
|
const childResult = reconcileHostChild(
|
|
523
1145
|
fiber,
|
|
524
1146
|
current?.tag === "context-consumer" ? current.child : undefined,
|
|
525
|
-
render(useContext(node.type
|
|
1147
|
+
render(useContext(consumerContext(node.type))),
|
|
526
1148
|
runtime,
|
|
527
1149
|
`${path}.consumer`,
|
|
528
1150
|
options,
|
|
@@ -544,6 +1166,7 @@ function createHostFiber(
|
|
|
544
1166
|
fiber.type = forwardRefType;
|
|
545
1167
|
const rendered = renderWithRootRuntime(runtime, path, () =>
|
|
546
1168
|
forwardRefType.render(node.props, node.ref),
|
|
1169
|
+
forwardRefType,
|
|
547
1170
|
);
|
|
548
1171
|
fiber.memoizedState = getDevToolsHookState(runtime, path);
|
|
549
1172
|
const childOptions = withHydrationComponentStack(
|
|
@@ -582,6 +1205,7 @@ function createHostFiber(
|
|
|
582
1205
|
if (
|
|
583
1206
|
previousMemoState !== undefined &&
|
|
584
1207
|
!hasDirtyInstance(runtime, previousMemoState.instanceKeys) &&
|
|
1208
|
+
!hasUnflushedMountEffectInstance(runtime, previousMemoState.instanceKeys) &&
|
|
585
1209
|
areMemoPropsEqual(memoType, previousMemoState.props, node.props)
|
|
586
1210
|
) {
|
|
587
1211
|
markActiveInstanceKeys(runtime, previousMemoState.instanceKeys);
|
|
@@ -604,6 +1228,11 @@ function createHostFiber(
|
|
|
604
1228
|
options,
|
|
605
1229
|
);
|
|
606
1230
|
fiber.child = childResult.fiber;
|
|
1231
|
+
if (fiber.child !== undefined) {
|
|
1232
|
+
fiber.child.return = fiber;
|
|
1233
|
+
fiber.child.sibling = undefined;
|
|
1234
|
+
bubbleHostChild(fiber, fiber.child);
|
|
1235
|
+
}
|
|
607
1236
|
fiber.memoizedState = {
|
|
608
1237
|
props: { ...node.props },
|
|
609
1238
|
instanceKeys: collectInstanceKeys(runtime, memoPath),
|
|
@@ -638,6 +1267,11 @@ function createHostFiber(
|
|
|
638
1267
|
options,
|
|
639
1268
|
);
|
|
640
1269
|
fiber.child = childResult.fiber;
|
|
1270
|
+
if (fiber.child !== undefined) {
|
|
1271
|
+
fiber.child.return = fiber;
|
|
1272
|
+
fiber.child.sibling = undefined;
|
|
1273
|
+
bubbleHostChild(fiber, fiber.child);
|
|
1274
|
+
}
|
|
641
1275
|
return { fiber, consumed: childResult.consumed };
|
|
642
1276
|
}
|
|
643
1277
|
|
|
@@ -680,11 +1314,13 @@ function createHostFiber(
|
|
|
680
1314
|
? createWorkInProgress(current, node.props)
|
|
681
1315
|
: createFiber("class-component", node.props, key);
|
|
682
1316
|
fiber.type = classType;
|
|
1317
|
+
const previousClassChildKeys = collectInstanceKeys(runtime, `${path}.class`);
|
|
683
1318
|
const rendered = renderClassComponentWithRuntime(
|
|
684
1319
|
classType,
|
|
685
1320
|
node.props,
|
|
686
1321
|
runtime,
|
|
687
1322
|
path,
|
|
1323
|
+
{ hasDirtyDescendant: hasDirtyInstance(runtime, previousClassChildKeys) },
|
|
688
1324
|
);
|
|
689
1325
|
applyRef(node.ref, rendered.kind === "skip" ? current?.stateNode : rendered.instance);
|
|
690
1326
|
|
|
@@ -738,13 +1374,44 @@ function createHostFiber(
|
|
|
738
1374
|
return { fiber: undefined, consumed: 0 };
|
|
739
1375
|
}
|
|
740
1376
|
|
|
1377
|
+
const previousFunctionState =
|
|
1378
|
+
current?.tag === "function-component"
|
|
1379
|
+
? (current.stateNode as FunctionFiberState | undefined)
|
|
1380
|
+
: undefined;
|
|
741
1381
|
const fiber =
|
|
742
1382
|
current?.tag === "function-component" && current.type === node.type
|
|
743
1383
|
? createWorkInProgress(current, node.props)
|
|
744
1384
|
: createFiber("function-component", node.props, key);
|
|
745
1385
|
fiber.type = node.type;
|
|
1386
|
+
|
|
1387
|
+
const canReuseSameElement =
|
|
1388
|
+
previousFunctionState !== undefined &&
|
|
1389
|
+
previousFunctionState.hasContextDependencies !== true &&
|
|
1390
|
+
previousFunctionState.element === node;
|
|
1391
|
+
const canReuseExternalStoreSnapshot =
|
|
1392
|
+
previousFunctionState !== undefined &&
|
|
1393
|
+
runtime.externalStoreUpdate &&
|
|
1394
|
+
shallowEqual(previousFunctionState.props, node.props) &&
|
|
1395
|
+
!hasChangedContextDependency(runtime, previousFunctionState.instanceKeys);
|
|
1396
|
+
|
|
1397
|
+
if (
|
|
1398
|
+
runtime.strictReplayDepth === 0 &&
|
|
1399
|
+
previousFunctionState !== undefined &&
|
|
1400
|
+
(canReuseSameElement || canReuseExternalStoreSnapshot) &&
|
|
1401
|
+
!hasDirtyInstance(runtime, previousFunctionState.instanceKeys) &&
|
|
1402
|
+
!hasUnflushedMountEffectInstance(runtime, previousFunctionState.instanceKeys) &&
|
|
1403
|
+
!hasPendingAsyncChild(current?.child)
|
|
1404
|
+
) {
|
|
1405
|
+
markActiveInstanceKeys(runtime, previousFunctionState.instanceKeys);
|
|
1406
|
+
fiber.child = current?.child;
|
|
1407
|
+
fiber.memoizedState = current?.memoizedState;
|
|
1408
|
+
fiber.stateNode = previousFunctionState;
|
|
1409
|
+
return { fiber, consumed: options.previousNodes?.length ?? 0 };
|
|
1410
|
+
}
|
|
1411
|
+
|
|
746
1412
|
const rendered = renderWithRootRuntime(runtime, path, () =>
|
|
747
1413
|
(node.type as (props: Record<string, unknown>) => ReactCompatNode)(node.props),
|
|
1414
|
+
node.type,
|
|
748
1415
|
);
|
|
749
1416
|
fiber.memoizedState = getDevToolsHookState(runtime, path);
|
|
750
1417
|
const childOptions = withHydrationComponentStack(
|
|
@@ -760,6 +1427,13 @@ function createHostFiber(
|
|
|
760
1427
|
childOptions,
|
|
761
1428
|
);
|
|
762
1429
|
fiber.child = childResult.fiber;
|
|
1430
|
+
const instanceKeys = collectInstanceKeys(runtime, path);
|
|
1431
|
+
fiber.stateNode = {
|
|
1432
|
+
element: node,
|
|
1433
|
+
props: { ...node.props },
|
|
1434
|
+
instanceKeys,
|
|
1435
|
+
hasContextDependencies: hasContextDependency(runtime, instanceKeys),
|
|
1436
|
+
} satisfies FunctionFiberState;
|
|
763
1437
|
return { fiber, consumed: childResult.consumed };
|
|
764
1438
|
}
|
|
765
1439
|
|
|
@@ -767,19 +1441,21 @@ function createHostFiber(
|
|
|
767
1441
|
return { fiber: undefined, consumed: 0 };
|
|
768
1442
|
}
|
|
769
1443
|
|
|
1444
|
+
const elementNamespace = namespaceForHostElement(options.namespace ?? "html", node.type);
|
|
1445
|
+
const childNamespace = namespaceForHostChildren(elementNamespace, node.type);
|
|
770
1446
|
const fiber =
|
|
771
1447
|
current?.tag === "host-component" && current.type === node.type
|
|
772
1448
|
? createWorkInProgress(current, node.props)
|
|
773
1449
|
: createFiber("host-component", node.props, key);
|
|
774
1450
|
const existing = options.previousNodes?.[0];
|
|
775
|
-
const existingElement = existing
|
|
1451
|
+
const existingElement = isHostElement(existing) ? existing : undefined;
|
|
776
1452
|
const tagMatches =
|
|
777
1453
|
existingElement !== undefined &&
|
|
778
|
-
existingElement
|
|
1454
|
+
hostElementMatches(existingElement, node.type, elementNamespace);
|
|
779
1455
|
|
|
780
1456
|
if (existing === undefined && options.previousNodes !== undefined) {
|
|
781
1457
|
reportMissingHydrationNode(options, path);
|
|
782
|
-
} else if (existing !== undefined && !(existing
|
|
1458
|
+
} else if (existing !== undefined && !isHostElement(existing)) {
|
|
783
1459
|
reportHydrationNodeTypeMismatch(options, path, `<${node.type}>`, existing);
|
|
784
1460
|
}
|
|
785
1461
|
|
|
@@ -801,62 +1477,474 @@ function createHostFiber(
|
|
|
801
1477
|
? existingElement
|
|
802
1478
|
: current?.tag === "host-component" &&
|
|
803
1479
|
current.type === node.type &&
|
|
804
|
-
current.stateNode
|
|
1480
|
+
isHostElement(current.stateNode) &&
|
|
1481
|
+
hostElementMatches(current.stateNode, node.type, elementNamespace)
|
|
805
1482
|
? current.stateNode
|
|
806
|
-
:
|
|
1483
|
+
: createHostElement(getDocumentRef(options), node.type, options.namespace ?? "html");
|
|
807
1484
|
fiber.hydrateExisting = tagMatches && options.previousNodes !== undefined;
|
|
808
1485
|
const previousChildNodes =
|
|
809
1486
|
tagMatches && existingElement !== undefined
|
|
810
1487
|
? Array.from(existingElement.childNodes)
|
|
811
1488
|
: undefined;
|
|
1489
|
+
const directTextChild =
|
|
1490
|
+
shouldUseDirectHostTextChild() && previousChildNodes === undefined
|
|
1491
|
+
? getDirectHostTextChild(node.props.children)
|
|
1492
|
+
: undefined;
|
|
1493
|
+
if (
|
|
1494
|
+
previousChildNodes === undefined &&
|
|
1495
|
+
current?.tag === "host-component" &&
|
|
1496
|
+
current.type === node.type &&
|
|
1497
|
+
Object.is(hostFiberChildrenProp(current.memoizedProps), node.props.children) &&
|
|
1498
|
+
canReuseStaticHostSubtree(current.child)
|
|
1499
|
+
) {
|
|
1500
|
+
fiber.child = current.child;
|
|
1501
|
+
if (fiber.child !== undefined) {
|
|
1502
|
+
fiber.child.return = fiber;
|
|
1503
|
+
}
|
|
1504
|
+
parent.child ??= fiber;
|
|
1505
|
+
return { fiber, consumed: existing === undefined ? 0 : 1 };
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
if (directTextChild !== undefined) {
|
|
1509
|
+
fiber.child = undefined;
|
|
1510
|
+
parent.child ??= fiber;
|
|
1511
|
+
return { fiber, consumed: existing === undefined ? 0 : 1 };
|
|
1512
|
+
}
|
|
1513
|
+
|
|
812
1514
|
const childResult = reconcileHostChild(
|
|
813
1515
|
fiber,
|
|
814
1516
|
current?.tag === "host-component" ? current.child : undefined,
|
|
815
1517
|
node.props.children as ReactCompatNode,
|
|
816
1518
|
runtime,
|
|
817
1519
|
`${path}.c`,
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
:
|
|
1520
|
+
{
|
|
1521
|
+
...options,
|
|
1522
|
+
namespace: childNamespace,
|
|
1523
|
+
...(previousChildNodes === undefined ? {} : { previousNodes: previousChildNodes }),
|
|
1524
|
+
},
|
|
821
1525
|
);
|
|
822
1526
|
fiber.child = childResult.fiber;
|
|
823
1527
|
if (previousChildNodes !== undefined) {
|
|
824
1528
|
reportExtraHydrationNodes(options, `${path}.c`, previousChildNodes, childResult.consumed);
|
|
825
1529
|
}
|
|
826
|
-
parent.child ??= fiber;
|
|
827
|
-
return { fiber, consumed: existing === undefined ? 0 : 1 };
|
|
1530
|
+
parent.child ??= fiber;
|
|
1531
|
+
return { fiber, consumed: existing === undefined ? 0 : 1 };
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
function isFunctionComponentType(value: unknown): value is (
|
|
1535
|
+
props: Record<string, unknown>,
|
|
1536
|
+
) => ReactCompatNode {
|
|
1537
|
+
return (
|
|
1538
|
+
typeof value === "function" &&
|
|
1539
|
+
typeof (value as { prototype?: { render?: unknown } }).prototype?.render !==
|
|
1540
|
+
"function"
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
function commitHostChildren(
|
|
1545
|
+
fiber: Fiber | undefined,
|
|
1546
|
+
parent: ParentNode,
|
|
1547
|
+
eventRoot: Element,
|
|
1548
|
+
path: string,
|
|
1549
|
+
options: RenderOptions = {},
|
|
1550
|
+
): Node[] {
|
|
1551
|
+
const nodes: Node[] = [];
|
|
1552
|
+
let cursor = fiber;
|
|
1553
|
+
let index = 0;
|
|
1554
|
+
|
|
1555
|
+
while (cursor !== undefined) {
|
|
1556
|
+
for (const node of commitHostFiber(cursor, parent, eventRoot, joinCommitPath(path, String(index)), options)) {
|
|
1557
|
+
nodes.push(node);
|
|
1558
|
+
}
|
|
1559
|
+
cursor = cursor.sibling;
|
|
1560
|
+
index += 1;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
return nodes;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
function commitHostDirtyChildren(
|
|
1567
|
+
fiber: Fiber | undefined,
|
|
1568
|
+
parent: ParentNode,
|
|
1569
|
+
eventRoot: Element,
|
|
1570
|
+
path: string,
|
|
1571
|
+
options: RenderOptions = {},
|
|
1572
|
+
): void {
|
|
1573
|
+
let cursor = fiber;
|
|
1574
|
+
let index = 0;
|
|
1575
|
+
|
|
1576
|
+
while (cursor !== undefined) {
|
|
1577
|
+
if (hasHostCommitWork(cursor)) {
|
|
1578
|
+
commitHostDirtyFiber(cursor, parent, eventRoot, joinCommitPath(path, String(index)), options);
|
|
1579
|
+
}
|
|
1580
|
+
cursor = cursor.sibling;
|
|
1581
|
+
index += 1;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
function commitHostDirtyFiber(
|
|
1586
|
+
fiber: Fiber,
|
|
1587
|
+
parent: ParentNode,
|
|
1588
|
+
eventRoot: Element,
|
|
1589
|
+
path: string,
|
|
1590
|
+
options: RenderOptions = {},
|
|
1591
|
+
): void {
|
|
1592
|
+
if (fiber.tag === "host-text") {
|
|
1593
|
+
commitHostFiber(fiber, parent, eventRoot, path, options);
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
if (fiber.tag === "host-component") {
|
|
1598
|
+
const element = fiber.stateNode;
|
|
1599
|
+
|
|
1600
|
+
if (!isHostElement(element)) {
|
|
1601
|
+
finishCommittedFiber(fiber);
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
const props = fiber.pendingProps as Record<string, unknown>;
|
|
1606
|
+
const propsAreUnchanged =
|
|
1607
|
+
fiber.hydrateExisting !== true &&
|
|
1608
|
+
hostPropsEqual(fiber.memoizedProps, props);
|
|
1609
|
+
const propsAreChildrenOnly =
|
|
1610
|
+
fiber.hydrateExisting !== true &&
|
|
1611
|
+
hostPropsAreChildrenOnly(fiber.memoizedProps) &&
|
|
1612
|
+
hostPropsAreChildrenOnly(props);
|
|
1613
|
+
const textOnlyRowUpdate =
|
|
1614
|
+
fiber.hydrateExisting !== true &&
|
|
1615
|
+
isRowTextOnlyUpdate(fiber.memoizedProps, props);
|
|
1616
|
+
|
|
1617
|
+
if (!propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
1618
|
+
applyProps(element, props, path, {
|
|
1619
|
+
...options,
|
|
1620
|
+
eventRoot,
|
|
1621
|
+
preserveHydrationAttributes: fiber.hydrateExisting,
|
|
1622
|
+
});
|
|
1623
|
+
applyRef(props.ref, element);
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
const directTextChild =
|
|
1627
|
+
fiber.child === undefined && fiber.hydrateExisting !== true
|
|
1628
|
+
? getDirectHostTextChild(props.children)
|
|
1629
|
+
: undefined;
|
|
1630
|
+
|
|
1631
|
+
if (directTextChild !== undefined) {
|
|
1632
|
+
syncDirectHostTextChild(element, directTextChild);
|
|
1633
|
+
} else if (fiber.subtreeFlags !== NoFlags) {
|
|
1634
|
+
commitHostDirtyChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
if (!propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
1638
|
+
applyPostChildFormProps(element, props);
|
|
1639
|
+
}
|
|
1640
|
+
fiber.memoizedProps = props;
|
|
1641
|
+
finishCommittedFiber(fiber);
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
if (fiber.tag === "portal") {
|
|
1646
|
+
const container = fiber.stateNode;
|
|
1647
|
+
|
|
1648
|
+
if (container instanceof Element) {
|
|
1649
|
+
setLogicalEventParent(container, parent);
|
|
1650
|
+
commitHostDirtyChildren(fiber.child, container, container, `${path}.portal`, options);
|
|
1651
|
+
}
|
|
1652
|
+
fiber.memoizedProps = fiber.pendingProps;
|
|
1653
|
+
finishCommittedFiber(fiber);
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
if (fiber.subtreeFlags !== NoFlags) {
|
|
1658
|
+
commitHostDirtyChildren(fiber.child, parent, eventRoot, path, options);
|
|
1659
|
+
}
|
|
1660
|
+
fiber.memoizedProps = fiber.pendingProps;
|
|
1661
|
+
finishCommittedFiber(fiber);
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
function hasHostCommitWork(fiber: Fiber): boolean {
|
|
1665
|
+
return (
|
|
1666
|
+
fiber.flags !== NoFlags ||
|
|
1667
|
+
fiber.subtreeFlags !== NoFlags ||
|
|
1668
|
+
fiber.hostChildListChanged ||
|
|
1669
|
+
fiber.childListChanged ||
|
|
1670
|
+
fiber.subtreeChildListChanged ||
|
|
1671
|
+
fiber.hydrateExisting
|
|
1672
|
+
);
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
function commitHostKeyedChildListMutation(
|
|
1676
|
+
fiber: Fiber | undefined,
|
|
1677
|
+
parent: ParentNode,
|
|
1678
|
+
eventRoot: Element,
|
|
1679
|
+
path: string,
|
|
1680
|
+
options: RenderOptions = {},
|
|
1681
|
+
): boolean {
|
|
1682
|
+
let cursor = fiber;
|
|
1683
|
+
let index = 0;
|
|
1684
|
+
let committed = false;
|
|
1685
|
+
|
|
1686
|
+
while (cursor !== undefined) {
|
|
1687
|
+
if (!hasHostCommitWork(cursor)) {
|
|
1688
|
+
cursor = cursor.sibling;
|
|
1689
|
+
index += 1;
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
const childPath = joinCommitPath(path, String(index));
|
|
1694
|
+
const didCommit = commitHostKeyedChildListMutationFiber(
|
|
1695
|
+
cursor,
|
|
1696
|
+
parent,
|
|
1697
|
+
eventRoot,
|
|
1698
|
+
childPath,
|
|
1699
|
+
options,
|
|
1700
|
+
);
|
|
1701
|
+
|
|
1702
|
+
if (!didCommit) {
|
|
1703
|
+
return false;
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
committed = true;
|
|
1707
|
+
cursor = cursor.sibling;
|
|
1708
|
+
index += 1;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
return committed;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
function commitHostKeyedChildListMutationFiber(
|
|
1715
|
+
fiber: Fiber,
|
|
1716
|
+
parent: ParentNode,
|
|
1717
|
+
eventRoot: Element,
|
|
1718
|
+
path: string,
|
|
1719
|
+
options: RenderOptions = {},
|
|
1720
|
+
): boolean {
|
|
1721
|
+
if (fiber.childListChanged) {
|
|
1722
|
+
const mutationParent =
|
|
1723
|
+
fiber.tag === "host-component" && isHostElement(fiber.stateNode)
|
|
1724
|
+
? fiber.stateNode
|
|
1725
|
+
: parent;
|
|
1726
|
+
|
|
1727
|
+
if (fiber.tag === "host-component" && !isHostElement(fiber.stateNode)) {
|
|
1728
|
+
return false;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
if (commitHostAppendSuffix(fiber, mutationParent, eventRoot, path, options)) {
|
|
1732
|
+
finishHostPassthroughFiber(fiber);
|
|
1733
|
+
return true;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
if (commitHostSingleRemoval(fiber, mutationParent)) {
|
|
1737
|
+
finishHostPassthroughFiber(fiber);
|
|
1738
|
+
return true;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
return false;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
if (fiber.subtreeChildListChanged) {
|
|
1745
|
+
if (fiber.tag === "host-component") {
|
|
1746
|
+
const element = fiber.stateNode;
|
|
1747
|
+
|
|
1748
|
+
if (!isHostElement(element)) {
|
|
1749
|
+
return false;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
if (!commitHostKeyedChildListMutation(fiber.child, element, eventRoot, `${path}.c`, options)) {
|
|
1753
|
+
return false;
|
|
1754
|
+
}
|
|
1755
|
+
finishHostPassthroughFiber(fiber);
|
|
1756
|
+
return true;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
if (!commitHostKeyedChildListMutation(fiber.child, parent, eventRoot, path, options)) {
|
|
1760
|
+
return false;
|
|
1761
|
+
}
|
|
1762
|
+
finishHostPassthroughFiber(fiber);
|
|
1763
|
+
return true;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
commitHostDirtyFiber(fiber, parent, eventRoot, path, options);
|
|
1767
|
+
return true;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
function commitHostAppendSuffix(
|
|
1771
|
+
fiber: Fiber,
|
|
1772
|
+
parent: ParentNode,
|
|
1773
|
+
eventRoot: Element,
|
|
1774
|
+
path: string,
|
|
1775
|
+
options: RenderOptions,
|
|
1776
|
+
): boolean {
|
|
1777
|
+
const append = getPlacementAppendSuffix(fiber.child) ?? getAppendSuffix(fiber.alternate?.child, fiber.child);
|
|
1778
|
+
|
|
1779
|
+
if (append === undefined) {
|
|
1780
|
+
return false;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
let cursor: Fiber | undefined = append.fiber;
|
|
1784
|
+
let index = append.index;
|
|
1785
|
+
|
|
1786
|
+
while (cursor !== undefined) {
|
|
1787
|
+
for (const node of commitHostFiber(cursor, parent, eventRoot, joinCommitPath(path, String(index)), options)) {
|
|
1788
|
+
parent.appendChild(node);
|
|
1789
|
+
}
|
|
1790
|
+
cursor = cursor.sibling;
|
|
1791
|
+
index += 1;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
return true;
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
function getPlacementAppendSuffix(next: Fiber | undefined): { fiber: Fiber; index: number } | undefined {
|
|
1798
|
+
let nextCursor = next;
|
|
1799
|
+
let index = 0;
|
|
1800
|
+
|
|
1801
|
+
while (nextCursor !== undefined) {
|
|
1802
|
+
if ((nextCursor.flags & Placement) !== NoFlags) {
|
|
1803
|
+
if (index === 0) {
|
|
1804
|
+
return undefined;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
let appendCursor: Fiber | undefined = nextCursor;
|
|
1808
|
+
|
|
1809
|
+
while (appendCursor !== undefined) {
|
|
1810
|
+
if ((appendCursor.flags & Placement) === NoFlags) {
|
|
1811
|
+
return undefined;
|
|
1812
|
+
}
|
|
1813
|
+
appendCursor = appendCursor.sibling;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
return { fiber: nextCursor, index };
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
if (hasHostCommitWork(nextCursor)) {
|
|
1820
|
+
return undefined;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
nextCursor = nextCursor.sibling;
|
|
1824
|
+
index += 1;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
return undefined;
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
function commitHostSingleRemoval(fiber: Fiber, parent: ParentNode): boolean {
|
|
1831
|
+
const removed = getSingleRemovedFiber(fiber.alternate?.child, fiber.child);
|
|
1832
|
+
|
|
1833
|
+
if (removed === undefined) {
|
|
1834
|
+
return false;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
let removedAny = false;
|
|
1838
|
+
|
|
1839
|
+
for (const node of collectCommittedHostNodes(removed)) {
|
|
1840
|
+
if (node.parentNode !== parent) {
|
|
1841
|
+
return false;
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
parent.removeChild(node);
|
|
1845
|
+
removedAny = true;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
return removedAny;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
function getAppendSuffix(
|
|
1852
|
+
current: Fiber | undefined,
|
|
1853
|
+
next: Fiber | undefined,
|
|
1854
|
+
): { fiber: Fiber; index: number } | undefined {
|
|
1855
|
+
let currentCursor = current;
|
|
1856
|
+
let nextCursor = next;
|
|
1857
|
+
let index = 0;
|
|
1858
|
+
|
|
1859
|
+
while (currentCursor !== undefined && nextCursor !== undefined) {
|
|
1860
|
+
if (!isSameFiberSlot(currentCursor, nextCursor) || hasHostCommitWork(nextCursor)) {
|
|
1861
|
+
return undefined;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
currentCursor = currentCursor.sibling;
|
|
1865
|
+
nextCursor = nextCursor.sibling;
|
|
1866
|
+
index += 1;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
if (currentCursor !== undefined || nextCursor === undefined) {
|
|
1870
|
+
return undefined;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
return { fiber: nextCursor, index };
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
function getSingleRemovedFiber(
|
|
1877
|
+
current: Fiber | undefined,
|
|
1878
|
+
next: Fiber | undefined,
|
|
1879
|
+
): Fiber | undefined {
|
|
1880
|
+
let currentCursor = current;
|
|
1881
|
+
let nextCursor = next;
|
|
1882
|
+
|
|
1883
|
+
while (currentCursor !== undefined && nextCursor !== undefined) {
|
|
1884
|
+
if (!isSameFiberSlot(currentCursor, nextCursor)) {
|
|
1885
|
+
break;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
if (hasHostCommitWork(nextCursor)) {
|
|
1889
|
+
return undefined;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
currentCursor = currentCursor.sibling;
|
|
1893
|
+
nextCursor = nextCursor.sibling;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
if (currentCursor === undefined) {
|
|
1897
|
+
return undefined;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
const removed = currentCursor;
|
|
1901
|
+
currentCursor = currentCursor.sibling;
|
|
1902
|
+
|
|
1903
|
+
while (currentCursor !== undefined && nextCursor !== undefined) {
|
|
1904
|
+
if (!isSameFiberSlot(currentCursor, nextCursor) || hasHostCommitWork(nextCursor)) {
|
|
1905
|
+
return undefined;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
currentCursor = currentCursor.sibling;
|
|
1909
|
+
nextCursor = nextCursor.sibling;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
return currentCursor === undefined && nextCursor === undefined ? removed : undefined;
|
|
828
1913
|
}
|
|
829
1914
|
|
|
830
|
-
function
|
|
831
|
-
props: Record<string, unknown>,
|
|
832
|
-
) => ReactCompatNode {
|
|
1915
|
+
function isSameFiberSlot(current: Fiber, next: Fiber): boolean {
|
|
833
1916
|
return (
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1917
|
+
(next === current || next.alternate === current) &&
|
|
1918
|
+
current.tag === next.tag &&
|
|
1919
|
+
current.type === next.type &&
|
|
1920
|
+
current.key === next.key
|
|
837
1921
|
);
|
|
838
1922
|
}
|
|
839
1923
|
|
|
840
|
-
function
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
1924
|
+
function collectCommittedHostNodes(fiber: Fiber): Node[] {
|
|
1925
|
+
if (
|
|
1926
|
+
(fiber.tag === "host-component" || fiber.tag === "host-text") &&
|
|
1927
|
+
fiber.stateNode instanceof Node
|
|
1928
|
+
) {
|
|
1929
|
+
return [fiber.stateNode];
|
|
1930
|
+
}
|
|
1931
|
+
|
|
847
1932
|
const nodes: Node[] = [];
|
|
848
|
-
let
|
|
849
|
-
let index = 0;
|
|
1933
|
+
let child = fiber.child;
|
|
850
1934
|
|
|
851
|
-
while (
|
|
852
|
-
nodes.push(...
|
|
853
|
-
|
|
854
|
-
index += 1;
|
|
1935
|
+
while (child !== undefined) {
|
|
1936
|
+
nodes.push(...collectCommittedHostNodes(child));
|
|
1937
|
+
child = child.sibling;
|
|
855
1938
|
}
|
|
856
1939
|
|
|
857
1940
|
return nodes;
|
|
858
1941
|
}
|
|
859
1942
|
|
|
1943
|
+
function finishHostPassthroughFiber(fiber: Fiber): void {
|
|
1944
|
+
fiber.memoizedProps = fiber.pendingProps;
|
|
1945
|
+
finishCommittedFiber(fiber);
|
|
1946
|
+
}
|
|
1947
|
+
|
|
860
1948
|
function commitHostFiber(
|
|
861
1949
|
fiber: Fiber,
|
|
862
1950
|
parent: ParentNode,
|
|
@@ -868,92 +1956,171 @@ function commitHostFiber(
|
|
|
868
1956
|
const text = fiber.stateNode;
|
|
869
1957
|
|
|
870
1958
|
if (!(text instanceof Text)) {
|
|
1959
|
+
finishCommittedFiber(fiber);
|
|
871
1960
|
return [];
|
|
872
1961
|
}
|
|
873
1962
|
|
|
874
|
-
|
|
1963
|
+
const nextText = String(fiber.pendingProps);
|
|
1964
|
+
if (text.data !== nextText) {
|
|
1965
|
+
text.data = nextText;
|
|
1966
|
+
}
|
|
875
1967
|
fiber.memoizedProps = fiber.pendingProps;
|
|
1968
|
+
finishCommittedFiber(fiber);
|
|
876
1969
|
return [text];
|
|
877
1970
|
}
|
|
878
1971
|
|
|
879
1972
|
if (fiber.tag === "host-component") {
|
|
880
1973
|
const element = fiber.stateNode;
|
|
881
1974
|
|
|
882
|
-
if (!(element
|
|
1975
|
+
if (!isHostElement(element)) {
|
|
1976
|
+
finishCommittedFiber(fiber);
|
|
883
1977
|
return [];
|
|
884
1978
|
}
|
|
885
1979
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1980
|
+
if (
|
|
1981
|
+
fiber.hydrateExisting !== true &&
|
|
1982
|
+
fiber.flags === NoFlags &&
|
|
1983
|
+
fiber.subtreeFlags === NoFlags &&
|
|
1984
|
+
fiber.hostChildListChanged !== true
|
|
1985
|
+
) {
|
|
1986
|
+
fiber.memoizedProps = fiber.pendingProps;
|
|
1987
|
+
return [element];
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
const props = fiber.pendingProps as Record<string, unknown>;
|
|
1991
|
+
const propsAreUnchanged =
|
|
1992
|
+
fiber.hydrateExisting !== true &&
|
|
1993
|
+
hostPropsEqual(fiber.memoizedProps, props);
|
|
1994
|
+
const propsAreChildrenOnly =
|
|
1995
|
+
fiber.hydrateExisting !== true &&
|
|
1996
|
+
hostPropsAreChildrenOnly(fiber.memoizedProps) &&
|
|
1997
|
+
hostPropsAreChildrenOnly(props);
|
|
1998
|
+
const textOnlyRowUpdate =
|
|
1999
|
+
fiber.hydrateExisting !== true &&
|
|
2000
|
+
isRowTextOnlyUpdate(fiber.memoizedProps, props);
|
|
2001
|
+
|
|
2002
|
+
if (!propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
2003
|
+
applyProps(element, props, path, {
|
|
2004
|
+
...options,
|
|
2005
|
+
eventRoot,
|
|
2006
|
+
preserveHydrationAttributes: fiber.hydrateExisting,
|
|
2007
|
+
});
|
|
2008
|
+
applyRef(props.ref, element);
|
|
2009
|
+
}
|
|
2010
|
+
const directTextChild =
|
|
2011
|
+
fiber.child === undefined && fiber.hydrateExisting !== true
|
|
2012
|
+
? getDirectHostTextChild(props.children)
|
|
2013
|
+
: undefined;
|
|
2014
|
+
|
|
2015
|
+
if (directTextChild !== undefined) {
|
|
2016
|
+
syncDirectHostTextChild(element, directTextChild);
|
|
2017
|
+
} else if (
|
|
2018
|
+
fiber.hostChildListChanged ||
|
|
2019
|
+
fiber.childListChanged ||
|
|
2020
|
+
fiber.hydrateExisting === true ||
|
|
2021
|
+
(fiber.subtreeFlags & Placement) !== NoFlags
|
|
2022
|
+
) {
|
|
2023
|
+
const childNodes = commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
2024
|
+
if (
|
|
2025
|
+
!(childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
2026
|
+
!shouldPreserveContentEditableChildren(element, props, childNodes)
|
|
2027
|
+
) {
|
|
2028
|
+
syncChildNodes(element, childNodes);
|
|
2029
|
+
}
|
|
2030
|
+
} else if (fiber.subtreeFlags !== NoFlags) {
|
|
2031
|
+
commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
if (!propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
2035
|
+
applyPostChildFormProps(element, props);
|
|
2036
|
+
}
|
|
2037
|
+
fiber.memoizedProps = props;
|
|
2038
|
+
finishCommittedFiber(fiber);
|
|
896
2039
|
return [element];
|
|
897
2040
|
}
|
|
898
2041
|
|
|
899
2042
|
if (fiber.tag === "fragment") {
|
|
900
2043
|
fiber.memoizedProps = fiber.pendingProps;
|
|
901
|
-
|
|
2044
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.f`, options);
|
|
2045
|
+
finishCommittedFiber(fiber);
|
|
2046
|
+
return nodes;
|
|
902
2047
|
}
|
|
903
2048
|
|
|
904
2049
|
if (fiber.tag === "profiler") {
|
|
905
2050
|
fiber.memoizedProps = fiber.pendingProps;
|
|
906
|
-
|
|
2051
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.profiler`, options);
|
|
2052
|
+
finishCommittedFiber(fiber);
|
|
2053
|
+
return nodes;
|
|
907
2054
|
}
|
|
908
2055
|
|
|
909
2056
|
if (fiber.tag === "strict-mode") {
|
|
910
2057
|
fiber.memoizedProps = fiber.pendingProps;
|
|
911
|
-
|
|
2058
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.strict`, options);
|
|
2059
|
+
finishCommittedFiber(fiber);
|
|
2060
|
+
return nodes;
|
|
912
2061
|
}
|
|
913
2062
|
|
|
914
2063
|
if (fiber.tag === "suspense") {
|
|
915
2064
|
fiber.memoizedProps = fiber.pendingProps;
|
|
916
|
-
|
|
2065
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.s`, options);
|
|
2066
|
+
finishCommittedFiber(fiber);
|
|
2067
|
+
return nodes;
|
|
917
2068
|
}
|
|
918
2069
|
|
|
919
2070
|
if (fiber.tag === "suspense-list") {
|
|
920
2071
|
fiber.memoizedProps = fiber.pendingProps;
|
|
921
|
-
|
|
2072
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.sl`, options);
|
|
2073
|
+
finishCommittedFiber(fiber);
|
|
2074
|
+
return nodes;
|
|
922
2075
|
}
|
|
923
2076
|
|
|
924
2077
|
if (fiber.tag === "context-provider" || fiber.tag === "context-consumer") {
|
|
925
2078
|
fiber.memoizedProps = fiber.pendingProps;
|
|
926
|
-
|
|
2079
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.ctx`, options);
|
|
2080
|
+
finishCommittedFiber(fiber);
|
|
2081
|
+
return nodes;
|
|
927
2082
|
}
|
|
928
2083
|
|
|
929
2084
|
if (fiber.tag === "function-component") {
|
|
930
2085
|
fiber.memoizedProps = fiber.pendingProps;
|
|
931
|
-
|
|
2086
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.fc`, options);
|
|
2087
|
+
finishCommittedFiber(fiber);
|
|
2088
|
+
return nodes;
|
|
932
2089
|
}
|
|
933
2090
|
|
|
934
2091
|
if (fiber.tag === "forward-ref") {
|
|
935
2092
|
fiber.memoizedProps = fiber.pendingProps;
|
|
936
|
-
|
|
2093
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.fr`, options);
|
|
2094
|
+
finishCommittedFiber(fiber);
|
|
2095
|
+
return nodes;
|
|
937
2096
|
}
|
|
938
2097
|
|
|
939
2098
|
if (fiber.tag === "memo") {
|
|
940
2099
|
fiber.memoizedProps = fiber.pendingProps;
|
|
941
|
-
|
|
2100
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.memo`, options);
|
|
2101
|
+
finishCommittedFiber(fiber);
|
|
2102
|
+
return nodes;
|
|
942
2103
|
}
|
|
943
2104
|
|
|
944
2105
|
if (fiber.tag === "lazy") {
|
|
945
2106
|
fiber.memoizedProps = fiber.pendingProps;
|
|
946
|
-
|
|
2107
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.lazy`, options);
|
|
2108
|
+
finishCommittedFiber(fiber);
|
|
2109
|
+
return nodes;
|
|
947
2110
|
}
|
|
948
2111
|
|
|
949
2112
|
if (fiber.tag === "error-boundary") {
|
|
950
2113
|
fiber.memoizedProps = fiber.pendingProps;
|
|
951
|
-
|
|
2114
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.eb`, options);
|
|
2115
|
+
finishCommittedFiber(fiber);
|
|
2116
|
+
return nodes;
|
|
952
2117
|
}
|
|
953
2118
|
|
|
954
2119
|
if (fiber.tag === "class-component") {
|
|
955
2120
|
fiber.memoizedProps = fiber.pendingProps;
|
|
956
|
-
|
|
2121
|
+
const nodes = commitHostChildren(fiber.child, parent, eventRoot, `${path}.class`, options);
|
|
2122
|
+
finishCommittedFiber(fiber);
|
|
2123
|
+
return nodes;
|
|
957
2124
|
}
|
|
958
2125
|
|
|
959
2126
|
if (fiber.tag === "portal") {
|
|
@@ -964,21 +2131,281 @@ function commitHostFiber(
|
|
|
964
2131
|
}
|
|
965
2132
|
|
|
966
2133
|
setLogicalEventParent(container, parent);
|
|
2134
|
+
committedPortalContainers.add(container);
|
|
2135
|
+
const portalEventRoot =
|
|
2136
|
+
eventRoot !== container && eventRoot.contains(container) ? eventRoot : container;
|
|
967
2137
|
const childNodes = commitHostChildren(
|
|
968
2138
|
fiber.child,
|
|
969
2139
|
container,
|
|
970
|
-
|
|
2140
|
+
portalEventRoot,
|
|
971
2141
|
`${path}.portal`,
|
|
972
2142
|
options,
|
|
973
2143
|
);
|
|
974
|
-
|
|
2144
|
+
const previousNodes = Array.isArray(fiber.alternate?.memoizedState)
|
|
2145
|
+
? fiber.alternate.memoizedState.filter((node): node is Node => node instanceof Node)
|
|
2146
|
+
: [];
|
|
2147
|
+
syncOwnedChildNodes(container, previousNodes, childNodes);
|
|
2148
|
+
fiber.memoizedState = childNodes;
|
|
975
2149
|
fiber.memoizedProps = fiber.pendingProps;
|
|
2150
|
+
finishCommittedFiber(fiber);
|
|
976
2151
|
return [];
|
|
977
2152
|
}
|
|
978
2153
|
|
|
2154
|
+
finishCommittedFiber(fiber);
|
|
979
2155
|
return [];
|
|
980
2156
|
}
|
|
981
2157
|
|
|
2158
|
+
function finishCommittedFiber(fiber: Fiber): void {
|
|
2159
|
+
fiber.flags = NoFlags;
|
|
2160
|
+
fiber.subtreeFlags = NoFlags;
|
|
2161
|
+
fiber.childListChanged = false;
|
|
2162
|
+
fiber.subtreeChildListChanged = false;
|
|
2163
|
+
fiber.hostChildListChanged = false;
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
function hasChildListMutation(fiber: Fiber): boolean {
|
|
2167
|
+
return fiber.childListChanged || fiber.subtreeChildListChanged;
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
function hostPropsEqual(previous: unknown, next: Record<string, unknown>): boolean {
|
|
2171
|
+
if (previous === next) {
|
|
2172
|
+
return true;
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
if (typeof previous !== "object" || previous === null) {
|
|
2176
|
+
return false;
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
const previousProps = previous as Record<string, unknown>;
|
|
2180
|
+
let previousCount = 0;
|
|
2181
|
+
let nextCount = 0;
|
|
2182
|
+
|
|
2183
|
+
for (const key in previousProps) {
|
|
2184
|
+
if (!hasOwnProperty.call(previousProps, key)) {
|
|
2185
|
+
continue;
|
|
2186
|
+
}
|
|
2187
|
+
previousCount += 1;
|
|
2188
|
+
if (!hasOwnProperty.call(next, key)) {
|
|
2189
|
+
return false;
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
if (!Object.is(previousProps[key], next[key])) {
|
|
2193
|
+
return false;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
for (const key in next) {
|
|
2198
|
+
if (hasOwnProperty.call(next, key)) {
|
|
2199
|
+
nextCount += 1;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
return previousCount === nextCount;
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
function hostOwnPropsEqual(previous: unknown, next: Record<string, unknown>): boolean {
|
|
2207
|
+
if (previous === next) {
|
|
2208
|
+
return true;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
if (typeof previous !== "object" || previous === null) {
|
|
2212
|
+
return false;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
const previousProps = previous as Record<string, unknown>;
|
|
2216
|
+
const previousMeta = getHostOwnPropsMeta(previousProps);
|
|
2217
|
+
const nextMeta = getHostOwnPropsMeta(next);
|
|
2218
|
+
|
|
2219
|
+
if (previousMeta !== undefined && nextMeta !== undefined) {
|
|
2220
|
+
return previousMeta === nextMeta;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
let previousCount = 0;
|
|
2224
|
+
let nextCount = 0;
|
|
2225
|
+
|
|
2226
|
+
for (const key in previousProps) {
|
|
2227
|
+
if (!hasOwnProperty.call(previousProps, key) || key === "children") {
|
|
2228
|
+
continue;
|
|
2229
|
+
}
|
|
2230
|
+
previousCount += 1;
|
|
2231
|
+
if (!hasOwnProperty.call(next, key)) {
|
|
2232
|
+
return false;
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
if (!Object.is(previousProps[key], next[key])) {
|
|
2236
|
+
return false;
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
for (const key in next) {
|
|
2241
|
+
if (hasOwnProperty.call(next, key) && key !== "children") {
|
|
2242
|
+
nextCount += 1;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
return previousCount === nextCount;
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
function getHostOwnPropsMeta(props: Record<string, unknown>): number | undefined {
|
|
2250
|
+
return (props as { [HOST_OWN_PROPS_META]?: number })[HOST_OWN_PROPS_META];
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
function hostDirectTextChildChanged(previous: unknown, next: Record<string, unknown>): boolean {
|
|
2254
|
+
const previousText = getDirectHostTextChild(hostFiberChildrenProp(previous));
|
|
2255
|
+
const nextText = getDirectHostTextChild(next.children);
|
|
2256
|
+
|
|
2257
|
+
return (previousText !== undefined || nextText !== undefined) && previousText !== nextText;
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
function hostChildListChanged(previous: unknown, next: Record<string, unknown>): boolean {
|
|
2261
|
+
const previousChildren = hostFiberChildrenProp(previous);
|
|
2262
|
+
const nextChildren = next.children;
|
|
2263
|
+
|
|
2264
|
+
if (Object.is(previousChildren, nextChildren)) {
|
|
2265
|
+
return false;
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
if (
|
|
2269
|
+
getDirectHostTextChild(previousChildren) !== undefined ||
|
|
2270
|
+
getDirectHostTextChild(nextChildren) !== undefined
|
|
2271
|
+
) {
|
|
2272
|
+
return false;
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
if (sameSingleHostChild(previousChildren, nextChildren)) {
|
|
2276
|
+
return false;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
if (sameHostChildList(previousChildren, nextChildren)) {
|
|
2280
|
+
return false;
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
return true;
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
function sameSingleHostChild(previous: unknown, next: unknown): boolean {
|
|
2287
|
+
return (
|
|
2288
|
+
isReactCompatElement(previous) &&
|
|
2289
|
+
isReactCompatElement(next) &&
|
|
2290
|
+
previous.key === next.key &&
|
|
2291
|
+
previous.type === next.type
|
|
2292
|
+
);
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
function sameHostChildList(previous: unknown, next: unknown): boolean {
|
|
2296
|
+
if (!Array.isArray(previous) || !Array.isArray(next) || previous.length !== next.length) {
|
|
2297
|
+
return false;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
for (let index = 0; index < previous.length; index += 1) {
|
|
2301
|
+
const previousChild = previous[index];
|
|
2302
|
+
const nextChild = next[index];
|
|
2303
|
+
|
|
2304
|
+
if (Object.is(previousChild, nextChild)) {
|
|
2305
|
+
continue;
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
if (!sameSingleHostChild(previousChild, nextChild)) {
|
|
2309
|
+
return false;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
return (
|
|
2314
|
+
previous.length > 0 ||
|
|
2315
|
+
(Array.isArray(previous) && Array.isArray(next))
|
|
2316
|
+
);
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
function hostPropsAreChildrenOnly(props: unknown): boolean {
|
|
2320
|
+
if (typeof props !== "object" || props === null) {
|
|
2321
|
+
return false;
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
for (const key in props) {
|
|
2325
|
+
if (
|
|
2326
|
+
Object.prototype.hasOwnProperty.call(props, key) &&
|
|
2327
|
+
key !== "children"
|
|
2328
|
+
) {
|
|
2329
|
+
return false;
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
return true;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
function isRowTextOnlyUpdate(previous: unknown, next: Record<string, unknown>): boolean {
|
|
2337
|
+
if (typeof previous !== "object" || previous === null) {
|
|
2338
|
+
return false;
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
const previousProps = previous as Record<string, unknown>;
|
|
2342
|
+
const previousMeta = getHostOwnPropsMeta(previousProps);
|
|
2343
|
+
const nextMeta = getHostOwnPropsMeta(next);
|
|
2344
|
+
|
|
2345
|
+
if (previousMeta === undefined || previousMeta !== nextMeta) {
|
|
2346
|
+
return false;
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
const previousText = getDirectHostTextChild(previousProps.children);
|
|
2350
|
+
const nextText = getDirectHostTextChild(next.children);
|
|
2351
|
+
|
|
2352
|
+
return previousText !== undefined && nextText !== undefined && previousText !== nextText;
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
function hostFiberChildrenProp(props: unknown): unknown {
|
|
2356
|
+
return typeof props === "object" && props !== null
|
|
2357
|
+
? (props as { children?: unknown }).children
|
|
2358
|
+
: undefined;
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
function getDirectHostTextChild(children: unknown): string | undefined {
|
|
2362
|
+
return typeof children === "string" || typeof children === "number"
|
|
2363
|
+
? String(children)
|
|
2364
|
+
: undefined;
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
function shouldUseDirectHostTextChild(): boolean {
|
|
2368
|
+
const globalProcess = (globalThis as { process?: { env?: Record<string, string | undefined> } })
|
|
2369
|
+
.process;
|
|
2370
|
+
return globalProcess?.env?.NODE_ENV === "production";
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
function syncDirectHostTextChild(element: Element, text: string): void {
|
|
2374
|
+
const firstChild = element.firstChild;
|
|
2375
|
+
|
|
2376
|
+
if (firstChild instanceof Text && firstChild.nextSibling === null) {
|
|
2377
|
+
if (firstChild.data !== text) {
|
|
2378
|
+
firstChild.data = text;
|
|
2379
|
+
}
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
element.textContent = text;
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
function shouldPreserveContentEditableChildren(
|
|
2387
|
+
element: Element,
|
|
2388
|
+
props: Record<string, unknown>,
|
|
2389
|
+
childNodes: readonly Node[],
|
|
2390
|
+
): boolean {
|
|
2391
|
+
void childNodes;
|
|
2392
|
+
|
|
2393
|
+
if (
|
|
2394
|
+
!element.hasAttribute("contenteditable") ||
|
|
2395
|
+
element.getAttribute("contenteditable") === "false"
|
|
2396
|
+
) {
|
|
2397
|
+
return false;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
const children = props.children;
|
|
2401
|
+
return (
|
|
2402
|
+
children === undefined ||
|
|
2403
|
+
children === null ||
|
|
2404
|
+
children === false ||
|
|
2405
|
+
(Array.isArray(children) && children.length === 0)
|
|
2406
|
+
);
|
|
2407
|
+
}
|
|
2408
|
+
|
|
982
2409
|
function createSuspenseFiber(
|
|
983
2410
|
current: Fiber | undefined,
|
|
984
2411
|
element: ReactCompatElement,
|
|
@@ -1078,25 +2505,8 @@ function createStrictModeFiber(
|
|
|
1078
2505
|
? createWorkInProgress(current, element.props)
|
|
1079
2506
|
: createFiber("strict-mode", element.props, key);
|
|
1080
2507
|
fiber.type = element.type;
|
|
1081
|
-
const snapshot = takeRuntimeSnapshot(runtime);
|
|
1082
|
-
|
|
1083
|
-
try {
|
|
1084
|
-
createHostFiber(
|
|
1085
|
-
fiber,
|
|
1086
|
-
undefined,
|
|
1087
|
-
element.props.children as ReactCompatNode,
|
|
1088
|
-
undefined,
|
|
1089
|
-
runtime,
|
|
1090
|
-
`${path}.strict.preview`,
|
|
1091
|
-
options.previousNodes === undefined
|
|
1092
|
-
? options
|
|
1093
|
-
: { ...options, previousNodes: [] },
|
|
1094
|
-
);
|
|
1095
|
-
} finally {
|
|
1096
|
-
restoreRuntimeSnapshot(runtime, snapshot);
|
|
1097
|
-
}
|
|
1098
2508
|
|
|
1099
|
-
const childResult =
|
|
2509
|
+
const { result: childResult, memoValues, memoValuesByHook } = renderWithStrictModeMemoCapture(
|
|
1100
2510
|
runtime,
|
|
1101
2511
|
() =>
|
|
1102
2512
|
reconcileHostChild(
|
|
@@ -1109,6 +2519,29 @@ function createStrictModeFiber(
|
|
|
1109
2519
|
),
|
|
1110
2520
|
);
|
|
1111
2521
|
fiber.child = childResult.fiber;
|
|
2522
|
+
|
|
2523
|
+
const snapshot = takeRuntimeSnapshot(runtime);
|
|
2524
|
+
try {
|
|
2525
|
+
renderStrictModeReplay(
|
|
2526
|
+
runtime,
|
|
2527
|
+
memoValues,
|
|
2528
|
+
memoValuesByHook,
|
|
2529
|
+
() =>
|
|
2530
|
+
reconcileHostChild(
|
|
2531
|
+
fiber,
|
|
2532
|
+
childResult.fiber,
|
|
2533
|
+
element.props.children as ReactCompatNode,
|
|
2534
|
+
runtime,
|
|
2535
|
+
`${path}.strict`,
|
|
2536
|
+
options.previousNodes === undefined
|
|
2537
|
+
? options
|
|
2538
|
+
: { ...options, previousNodes: [] },
|
|
2539
|
+
),
|
|
2540
|
+
);
|
|
2541
|
+
} finally {
|
|
2542
|
+
restoreRuntimeSnapshot(runtime, snapshot);
|
|
2543
|
+
}
|
|
2544
|
+
|
|
1112
2545
|
return { fiber, consumed: childResult.consumed };
|
|
1113
2546
|
}
|
|
1114
2547
|
|
|
@@ -1220,6 +2653,7 @@ function reconcileSuspenseListForwards(
|
|
|
1220
2653
|
|
|
1221
2654
|
fiber.return = parent;
|
|
1222
2655
|
fiber.sibling = undefined;
|
|
2656
|
+
bubbleHostChild(parent, fiber);
|
|
1223
2657
|
|
|
1224
2658
|
if (first === undefined) {
|
|
1225
2659
|
first = fiber;
|
|
@@ -1272,6 +2706,7 @@ function reconcileSuspenseListBackwards(
|
|
|
1272
2706
|
|
|
1273
2707
|
fiber.return = parent;
|
|
1274
2708
|
fiber.sibling = undefined;
|
|
2709
|
+
bubbleHostChild(parent, fiber);
|
|
1275
2710
|
fibers.unshift(fiber);
|
|
1276
2711
|
|
|
1277
2712
|
if (isSuspendedSuspenseFiber(fiber)) {
|
|
@@ -1309,6 +2744,32 @@ function hasSuspendedChild(fiber: Fiber | undefined): boolean {
|
|
|
1309
2744
|
return false;
|
|
1310
2745
|
}
|
|
1311
2746
|
|
|
2747
|
+
function hasPendingAsyncChild(fiber: Fiber | undefined): boolean {
|
|
2748
|
+
let cursor = fiber;
|
|
2749
|
+
|
|
2750
|
+
while (cursor !== undefined) {
|
|
2751
|
+
if (isPendingLazyFiber(cursor) || isSuspendedSuspenseFiber(cursor)) {
|
|
2752
|
+
return true;
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
if (cursor.child !== undefined && hasPendingAsyncChild(cursor.child)) {
|
|
2756
|
+
return true;
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
cursor = cursor.sibling;
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
return false;
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
function isPendingLazyFiber(fiber: Fiber): boolean {
|
|
2766
|
+
if (fiber.tag !== "lazy" || !isLazyType(fiber.type)) {
|
|
2767
|
+
return false;
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
return fiber.type.status !== "resolved" || fiber.child === undefined;
|
|
2771
|
+
}
|
|
2772
|
+
|
|
1312
2773
|
function isSuspendedSuspenseFiber(fiber: Fiber): boolean {
|
|
1313
2774
|
return (
|
|
1314
2775
|
fiber.tag === "suspense" &&
|
|
@@ -1437,7 +2898,7 @@ function createPortalFiber(
|
|
|
1437
2898
|
portal.children,
|
|
1438
2899
|
runtime,
|
|
1439
2900
|
`${path}.portal`,
|
|
1440
|
-
options,
|
|
2901
|
+
{ ...options, documentRef: portal.container.ownerDocument },
|
|
1441
2902
|
);
|
|
1442
2903
|
fiber.child = childResult.fiber;
|
|
1443
2904
|
fiber.return = parent;
|
|
@@ -1452,6 +2913,10 @@ function normalizeChildren(node: ReactCompatNode): ReactCompatNode[] {
|
|
|
1452
2913
|
return Array.isArray(node) ? node : [node];
|
|
1453
2914
|
}
|
|
1454
2915
|
|
|
2916
|
+
function getDocumentRef(options: FiberHydrationOptions): Document {
|
|
2917
|
+
return options.documentRef ?? document;
|
|
2918
|
+
}
|
|
2919
|
+
|
|
1455
2920
|
function collectExistingKeyedFibers(
|
|
1456
2921
|
firstChild: Fiber | undefined,
|
|
1457
2922
|
): Map<string, Fiber> {
|
|
@@ -1473,11 +2938,65 @@ function getNodeKey(node: ReactCompatNode): string | undefined {
|
|
|
1473
2938
|
return isReactCompatElement(node) && node.key !== null ? node.key : undefined;
|
|
1474
2939
|
}
|
|
1475
2940
|
|
|
2941
|
+
function hasKeyedChild(children: readonly ReactCompatNode[]): boolean {
|
|
2942
|
+
for (const child of children) {
|
|
2943
|
+
if (getNodeKey(child) !== undefined) {
|
|
2944
|
+
return true;
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
return false;
|
|
2949
|
+
}
|
|
2950
|
+
|
|
1476
2951
|
function getNodePathSegment(node: ReactCompatNode, index: number): string {
|
|
1477
2952
|
const key = getNodeKey(node);
|
|
1478
2953
|
return key === undefined ? String(index) : `k:${key}`;
|
|
1479
2954
|
}
|
|
1480
2955
|
|
|
2956
|
+
function getReconcileChildPath(
|
|
2957
|
+
path: string,
|
|
2958
|
+
node: ReactCompatNode,
|
|
2959
|
+
index: number,
|
|
2960
|
+
options: FiberHydrationOptions,
|
|
2961
|
+
): string {
|
|
2962
|
+
if (!shouldTrackReconcilePath(node, options)) {
|
|
2963
|
+
return "";
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
return joinPath(path, getNodePathSegment(node, index));
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
function shouldTrackReconcilePath(
|
|
2970
|
+
node: ReactCompatNode,
|
|
2971
|
+
options: FiberHydrationOptions,
|
|
2972
|
+
): boolean {
|
|
2973
|
+
if (
|
|
2974
|
+
options.previousNodes !== undefined ||
|
|
2975
|
+
options.hydration?.onRecoverableError !== undefined ||
|
|
2976
|
+
options.resumeId !== undefined
|
|
2977
|
+
) {
|
|
2978
|
+
return true;
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
return !isHostElementWithDirectTextChild(node);
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
function isHostElementWithDirectTextChild(node: ReactCompatNode): boolean {
|
|
2985
|
+
return (
|
|
2986
|
+
isReactCompatElement(node) &&
|
|
2987
|
+
typeof node.type === "string" &&
|
|
2988
|
+
getDirectHostTextChild(node.props.children) !== undefined
|
|
2989
|
+
);
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
function getRootCommitPath(options: RenderOptions): string {
|
|
2993
|
+
return options.hydration?.onRecoverableError === undefined ? SKIP_COMMIT_PATH : "0";
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
function joinCommitPath(path: string, segment: string): string {
|
|
2997
|
+
return path === SKIP_COMMIT_PATH ? "" : joinPath(path, segment);
|
|
2998
|
+
}
|
|
2999
|
+
|
|
1481
3000
|
function getComponentName(component: Function): string {
|
|
1482
3001
|
return component.name === "" ? "Anonymous" : component.name;
|
|
1483
3002
|
}
|
|
@@ -1552,6 +3071,21 @@ function hasDirtyInstance(runtime: RootRuntime, keys: readonly string[]): boolea
|
|
|
1552
3071
|
);
|
|
1553
3072
|
}
|
|
1554
3073
|
|
|
3074
|
+
function hasUnflushedMountEffectInstance(runtime: RootRuntime, keys: readonly string[]): boolean {
|
|
3075
|
+
return keys.some((key) => {
|
|
3076
|
+
const instance = runtime.instances.get(key) as
|
|
3077
|
+
| { hooks?: readonly ({ kind?: string; mounted?: boolean; disposed?: boolean } | undefined)[] }
|
|
3078
|
+
| undefined;
|
|
3079
|
+
|
|
3080
|
+
return instance?.hooks?.some(
|
|
3081
|
+
(slot) =>
|
|
3082
|
+
slot?.kind === "effect" &&
|
|
3083
|
+
slot.disposed !== true &&
|
|
3084
|
+
slot.mounted !== true,
|
|
3085
|
+
) === true;
|
|
3086
|
+
});
|
|
3087
|
+
}
|
|
3088
|
+
|
|
1555
3089
|
function applyRef(ref: unknown, node: unknown): void {
|
|
1556
3090
|
if (typeof ref === "function") {
|
|
1557
3091
|
ref(node);
|