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