@reckona/mreact-compat 0.0.168 → 0.0.170
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dom-children.d.ts.map +1 -1
- package/dist/dom-children.js +53 -0
- package/dist/dom-children.js.map +1 -1
- package/dist/element.d.ts +19 -4
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +56 -19
- package/dist/element.js.map +1 -1
- package/dist/hooks.js +18 -11
- package/dist/hooks.js.map +1 -1
- package/dist/host-reconciler.d.ts.map +1 -1
- package/dist/host-reconciler.js +263 -31
- package/dist/host-reconciler.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/jsx-dev-runtime.d.ts +1 -1
- package/dist/jsx-dev-runtime.d.ts.map +1 -1
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/jsx-runtime.d.ts.map +1 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/package.json +3 -3
- package/src/dom-children.ts +78 -0
- package/src/element.ts +109 -23
- package/src/hooks.ts +24 -11
- package/src/host-reconciler.ts +430 -42
- package/src/index.ts +1 -0
- package/src/jsx-dev-runtime.ts +1 -1
- package/src/jsx-runtime.ts +3 -3
package/src/host-reconciler.ts
CHANGED
|
@@ -78,6 +78,9 @@ import {
|
|
|
78
78
|
interface MemoFiberState {
|
|
79
79
|
props: Record<string, unknown>;
|
|
80
80
|
instanceKeys: string[];
|
|
81
|
+
hasDirtyInstanceDependencies: boolean;
|
|
82
|
+
hasUnflushedEffectDependencies: boolean;
|
|
83
|
+
hasRetainedInstanceDependencies: boolean;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
interface FunctionFiberState {
|
|
@@ -346,6 +349,7 @@ function reconcileHostChild(
|
|
|
346
349
|
|
|
347
350
|
const childCount = children === undefined ? 1 : children.length;
|
|
348
351
|
const hasKeyedChildren = children !== undefined && hasKeyedChild(children);
|
|
352
|
+
const canReuseCurrentFibersInList = !hasKeyedChildren || currentFirstChild === undefined;
|
|
349
353
|
let existingByKey: Map<string, Fiber> | undefined;
|
|
350
354
|
let currentKeyed: Fiber | undefined = currentFirstChild;
|
|
351
355
|
let currentUnkeyed = currentFirstChild;
|
|
@@ -353,29 +357,57 @@ function reconcileHostChild(
|
|
|
353
357
|
let previous: Fiber | undefined;
|
|
354
358
|
let consumed = 0;
|
|
355
359
|
let skipRemainingKeyedLookup = false;
|
|
356
|
-
|
|
357
|
-
|
|
360
|
+
let sequentialCurrentCursor = currentFirstChild;
|
|
361
|
+
let childListOrderChanged = false;
|
|
362
|
+
let usedCurrentChildren =
|
|
363
|
+
currentFirstChild === undefined || hasKeyedChildren ? undefined : new Set<Fiber>();
|
|
364
|
+
const ensureUsedCurrentChildren = (): Set<Fiber> => {
|
|
365
|
+
if (usedCurrentChildren === undefined) {
|
|
366
|
+
usedCurrentChildren = new Set<Fiber>();
|
|
367
|
+
let cursor = currentFirstChild;
|
|
368
|
+
|
|
369
|
+
while (cursor !== undefined && cursor !== sequentialCurrentCursor) {
|
|
370
|
+
usedCurrentChildren.add(cursor);
|
|
371
|
+
cursor = cursor.sibling;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return usedCurrentChildren;
|
|
376
|
+
};
|
|
358
377
|
|
|
359
378
|
for (let index = 0; index < childCount; index += 1) {
|
|
360
379
|
const child = children === undefined ? node : children[index];
|
|
361
380
|
const key = getNodeKey(child);
|
|
362
381
|
let matchedCurrent: Fiber | undefined;
|
|
382
|
+
let canReuseMatchedCurrentFiber = canReuseCurrentFibersInList;
|
|
363
383
|
|
|
364
384
|
if (key === undefined) {
|
|
385
|
+
if (hasKeyedChildren && currentUnkeyed !== undefined) {
|
|
386
|
+
childListOrderChanged = true;
|
|
387
|
+
ensureUsedCurrentChildren();
|
|
388
|
+
}
|
|
365
389
|
matchedCurrent = currentUnkeyed;
|
|
366
390
|
} else if (skipRemainingKeyedLookup) {
|
|
391
|
+
childListOrderChanged = true;
|
|
367
392
|
matchedCurrent = undefined;
|
|
368
393
|
} else if (existingByKey !== undefined) {
|
|
394
|
+
childListOrderChanged = true;
|
|
369
395
|
matchedCurrent = existingByKey.get(key);
|
|
396
|
+
canReuseMatchedCurrentFiber = matchedCurrent === undefined;
|
|
370
397
|
} else if (currentKeyed?.key === key) {
|
|
371
398
|
matchedCurrent = currentKeyed;
|
|
399
|
+
canReuseMatchedCurrentFiber = true;
|
|
372
400
|
currentKeyed = currentKeyed.sibling;
|
|
401
|
+
sequentialCurrentCursor = currentKeyed;
|
|
373
402
|
} else if (
|
|
374
403
|
children !== undefined &&
|
|
375
404
|
currentKeyed?.sibling?.key === key &&
|
|
376
405
|
canSkipSingleDeletedKeyedFiber(children, index, currentKeyed.sibling)
|
|
377
406
|
) {
|
|
407
|
+
childListOrderChanged = true;
|
|
408
|
+
ensureUsedCurrentChildren();
|
|
378
409
|
matchedCurrent = currentKeyed.sibling;
|
|
410
|
+
canReuseMatchedCurrentFiber = false;
|
|
379
411
|
currentKeyed = currentKeyed.sibling.sibling;
|
|
380
412
|
} else {
|
|
381
413
|
if (
|
|
@@ -383,19 +415,30 @@ function reconcileHostChild(
|
|
|
383
415
|
hasKeyedChildren &&
|
|
384
416
|
canSkipRemainingKeyedLookup(currentKeyed, children, index)
|
|
385
417
|
) {
|
|
418
|
+
childListOrderChanged = true;
|
|
386
419
|
skipRemainingKeyedLookup = true;
|
|
387
420
|
currentKeyed = undefined;
|
|
388
421
|
} else if (hasKeyedChildren) {
|
|
422
|
+
childListOrderChanged = true;
|
|
423
|
+
ensureUsedCurrentChildren();
|
|
389
424
|
existingByKey = collectExistingKeyedFibers(currentKeyed);
|
|
390
425
|
matchedCurrent = existingByKey.get(key);
|
|
426
|
+
canReuseMatchedCurrentFiber = matchedCurrent === undefined;
|
|
391
427
|
}
|
|
392
428
|
}
|
|
393
429
|
|
|
430
|
+
const memoBailout = tryReuseDependencyFreeMemoBailout(
|
|
431
|
+
matchedCurrent,
|
|
432
|
+
child,
|
|
433
|
+
runtime,
|
|
434
|
+
options,
|
|
435
|
+
canReuseMatchedCurrentFiber,
|
|
436
|
+
);
|
|
394
437
|
const previousNodes =
|
|
395
|
-
options.previousNodes === undefined
|
|
438
|
+
memoBailout !== undefined || options.previousNodes === undefined
|
|
396
439
|
? undefined
|
|
397
440
|
: options.previousNodes.slice(consumed);
|
|
398
|
-
const result = createHostFiber(
|
|
441
|
+
const result = memoBailout ?? createHostFiber(
|
|
399
442
|
parent,
|
|
400
443
|
matchedCurrent,
|
|
401
444
|
child,
|
|
@@ -403,6 +446,7 @@ function reconcileHostChild(
|
|
|
403
446
|
runtime,
|
|
404
447
|
getReconcileChildPath(path, child, index, options),
|
|
405
448
|
previousNodes === undefined ? options : { ...options, previousNodes },
|
|
449
|
+
canReuseMatchedCurrentFiber,
|
|
406
450
|
);
|
|
407
451
|
const fiber = result.fiber;
|
|
408
452
|
|
|
@@ -449,8 +493,13 @@ function reconcileHostChild(
|
|
|
449
493
|
previous = fiber;
|
|
450
494
|
}
|
|
451
495
|
|
|
452
|
-
|
|
453
|
-
|
|
496
|
+
if (usedCurrentChildren === undefined && hasKeyedChildren && currentKeyed !== undefined) {
|
|
497
|
+
markOptimizedChildrenForDeletion(parent, currentKeyed);
|
|
498
|
+
} else {
|
|
499
|
+
markUnusedCurrentChildrenForDeletion(parent, currentFirstChild, usedCurrentChildren);
|
|
500
|
+
}
|
|
501
|
+
parent.childListChanged =
|
|
502
|
+
childListOrderChanged || childFiberListShapeChanged(currentFirstChild, first);
|
|
454
503
|
|
|
455
504
|
return { fiber: first, consumed };
|
|
456
505
|
}
|
|
@@ -465,6 +514,7 @@ function reconcileKeyedRowHostChildren(
|
|
|
465
514
|
children.length === 0 ||
|
|
466
515
|
currentFirstChild === undefined ||
|
|
467
516
|
options.previousNodes !== undefined ||
|
|
517
|
+
!isKeyedRowHostElementCandidate(children[0]) ||
|
|
468
518
|
!shouldUseDirectHostTextChild()
|
|
469
519
|
) {
|
|
470
520
|
return undefined;
|
|
@@ -681,6 +731,15 @@ function createKeyedRowHostElementScratch(): KeyedRowHostElement {
|
|
|
681
731
|
};
|
|
682
732
|
}
|
|
683
733
|
|
|
734
|
+
function isKeyedRowHostElementCandidate(node: ReactCompatNode): boolean {
|
|
735
|
+
return (
|
|
736
|
+
isReactCompatElement(node) &&
|
|
737
|
+
typeof node.type === "string" &&
|
|
738
|
+
node.key !== null &&
|
|
739
|
+
node.ref === null
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
|
|
684
743
|
function readKeyedRowHostElement(
|
|
685
744
|
node: ReactCompatNode,
|
|
686
745
|
row: KeyedRowHostElement,
|
|
@@ -958,8 +1017,18 @@ function createHostFiber(
|
|
|
958
1017
|
runtime: RootRuntime | undefined,
|
|
959
1018
|
path: string,
|
|
960
1019
|
options: FiberHydrationOptions = {},
|
|
1020
|
+
canReuseCurrentFiber = true,
|
|
961
1021
|
): FiberReconcileResult {
|
|
962
|
-
const result = createHostFiberImpl(
|
|
1022
|
+
const result = createHostFiberImpl(
|
|
1023
|
+
parent,
|
|
1024
|
+
current,
|
|
1025
|
+
node,
|
|
1026
|
+
key,
|
|
1027
|
+
runtime,
|
|
1028
|
+
path,
|
|
1029
|
+
options,
|
|
1030
|
+
canReuseCurrentFiber,
|
|
1031
|
+
);
|
|
963
1032
|
|
|
964
1033
|
if (result.fiber !== undefined) {
|
|
965
1034
|
if (canFinalizeNewHostFiber(result.fiber, current, node, options)) {
|
|
@@ -1000,6 +1069,7 @@ function createHostFiberImpl(
|
|
|
1000
1069
|
runtime: RootRuntime | undefined,
|
|
1001
1070
|
path: string,
|
|
1002
1071
|
options: FiberHydrationOptions = {},
|
|
1072
|
+
canReuseCurrentFiber = true,
|
|
1003
1073
|
): FiberReconcileResult {
|
|
1004
1074
|
if (node === null || node === undefined || typeof node === "boolean") {
|
|
1005
1075
|
return { fiber: undefined, consumed: 0 };
|
|
@@ -1052,6 +1122,18 @@ function createHostFiberImpl(
|
|
|
1052
1122
|
return { fiber, consumed: childResult.consumed };
|
|
1053
1123
|
}
|
|
1054
1124
|
|
|
1125
|
+
const memoBailout = tryReuseMemoBailout(
|
|
1126
|
+
current,
|
|
1127
|
+
node,
|
|
1128
|
+
runtime,
|
|
1129
|
+
path,
|
|
1130
|
+
options,
|
|
1131
|
+
canReuseCurrentFiber,
|
|
1132
|
+
);
|
|
1133
|
+
if (memoBailout !== undefined) {
|
|
1134
|
+
return memoBailout;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1055
1137
|
if (!isReactCompatElement(node)) {
|
|
1056
1138
|
if (isReactCompatPortal(node)) {
|
|
1057
1139
|
return createPortalFiber(parent, current, node, key, runtime, path, options);
|
|
@@ -1246,10 +1328,7 @@ function createHostFiberImpl(
|
|
|
1246
1328
|
forwardRefType,
|
|
1247
1329
|
);
|
|
1248
1330
|
fiber.memoizedState = getDevToolsHookState(runtime, path);
|
|
1249
|
-
const childOptions =
|
|
1250
|
-
options,
|
|
1251
|
-
getComponentName(forwardRefType.render),
|
|
1252
|
-
);
|
|
1331
|
+
const childOptions = getHydrationChildOptions(options, forwardRefType.render);
|
|
1253
1332
|
const childResult = reconcileHostChild(
|
|
1254
1333
|
fiber,
|
|
1255
1334
|
current?.tag === "forward-ref" ? current.child : undefined,
|
|
@@ -1269,28 +1348,47 @@ function createHostFiberImpl(
|
|
|
1269
1348
|
|
|
1270
1349
|
const memoType = node.type;
|
|
1271
1350
|
const memoPath = `${path}.memo`;
|
|
1351
|
+
const previousMemoFiber =
|
|
1352
|
+
current?.tag === "memo" && current.type === memoType ? current : undefined;
|
|
1272
1353
|
const previousMemoState =
|
|
1273
|
-
|
|
1274
|
-
? (
|
|
1354
|
+
previousMemoFiber !== undefined
|
|
1355
|
+
? (previousMemoFiber.memoizedState as MemoFiberState | undefined)
|
|
1275
1356
|
: undefined;
|
|
1276
|
-
const fiber =
|
|
1277
|
-
current?.tag === "memo" && current.type === memoType
|
|
1278
|
-
? createWorkInProgress(current, node.props)
|
|
1279
|
-
: createFiber("memo", node.props, key);
|
|
1280
|
-
fiber.type = memoType;
|
|
1281
1357
|
|
|
1282
1358
|
if (
|
|
1359
|
+
previousMemoFiber !== undefined &&
|
|
1283
1360
|
previousMemoState !== undefined &&
|
|
1284
|
-
!
|
|
1285
|
-
|
|
1361
|
+
!(
|
|
1362
|
+
memoStateNeedsDirtyInstanceCheck(previousMemoState) &&
|
|
1363
|
+
hasDirtyInstance(runtime, previousMemoState.instanceKeys, memoPath)
|
|
1364
|
+
) &&
|
|
1365
|
+
!(
|
|
1366
|
+
memoStateNeedsEffectCheck(previousMemoState) &&
|
|
1367
|
+
hasUnflushedMountEffectInstance(runtime, previousMemoState.instanceKeys)
|
|
1368
|
+
) &&
|
|
1286
1369
|
areMemoPropsEqual(memoType, previousMemoState.props, node.props)
|
|
1287
1370
|
) {
|
|
1288
|
-
|
|
1289
|
-
|
|
1371
|
+
const fiber = getMemoBailoutFiber(
|
|
1372
|
+
runtime,
|
|
1373
|
+
previousMemoFiber,
|
|
1374
|
+
node.props,
|
|
1375
|
+
previousMemoState,
|
|
1376
|
+
canReuseCurrentFiber,
|
|
1377
|
+
);
|
|
1378
|
+
fiber.child = getSkippedChild(previousMemoFiber);
|
|
1290
1379
|
fiber.memoizedState = previousMemoState;
|
|
1291
|
-
return {
|
|
1380
|
+
return {
|
|
1381
|
+
fiber,
|
|
1382
|
+
consumed: options.previousNodes?.length ?? 0,
|
|
1383
|
+
};
|
|
1292
1384
|
}
|
|
1293
1385
|
|
|
1386
|
+
const fiber =
|
|
1387
|
+
previousMemoFiber !== undefined
|
|
1388
|
+
? createWorkInProgress(previousMemoFiber, node.props)
|
|
1389
|
+
: createFiber("memo", node.props, key);
|
|
1390
|
+
fiber.type = memoType;
|
|
1391
|
+
|
|
1294
1392
|
const renderedElement: ReactCompatElement = {
|
|
1295
1393
|
...node,
|
|
1296
1394
|
type: memoType.type,
|
|
@@ -1310,9 +1408,19 @@ function createHostFiberImpl(
|
|
|
1310
1408
|
fiber.child.sibling = undefined;
|
|
1311
1409
|
bubbleHostChild(fiber, fiber.child);
|
|
1312
1410
|
}
|
|
1411
|
+
const instanceKeys = collectInstanceKeys(runtime, memoPath);
|
|
1412
|
+
const hasClassDescendant = hasClassComponentDescendant(fiber.child);
|
|
1313
1413
|
fiber.memoizedState = {
|
|
1314
|
-
props:
|
|
1315
|
-
instanceKeys
|
|
1414
|
+
props: node.props as Record<string, unknown>,
|
|
1415
|
+
instanceKeys,
|
|
1416
|
+
hasDirtyInstanceDependencies:
|
|
1417
|
+
hasDirtyInstanceDependencies(runtime, instanceKeys) || hasClassDescendant,
|
|
1418
|
+
hasUnflushedEffectDependencies: hasUnflushedEffectDependencies(
|
|
1419
|
+
runtime,
|
|
1420
|
+
instanceKeys,
|
|
1421
|
+
),
|
|
1422
|
+
hasRetainedInstanceDependencies:
|
|
1423
|
+
hasRetainedInstanceDependencies(runtime, instanceKeys) || hasClassDescendant,
|
|
1316
1424
|
};
|
|
1317
1425
|
return { fiber, consumed: childResult.consumed };
|
|
1318
1426
|
}
|
|
@@ -1422,10 +1530,7 @@ function createHostFiberImpl(
|
|
|
1422
1530
|
return { fiber, consumed: options.previousNodes?.length ?? 0 };
|
|
1423
1531
|
}
|
|
1424
1532
|
|
|
1425
|
-
const childOptions =
|
|
1426
|
-
options,
|
|
1427
|
-
getComponentName(classType),
|
|
1428
|
-
);
|
|
1533
|
+
const childOptions = getHydrationChildOptions(options, classType);
|
|
1429
1534
|
|
|
1430
1535
|
try {
|
|
1431
1536
|
const childResult = reconcileHostChild(
|
|
@@ -1507,10 +1612,7 @@ function createHostFiberImpl(
|
|
|
1507
1612
|
node.type,
|
|
1508
1613
|
);
|
|
1509
1614
|
fiber.memoizedState = getDevToolsHookState(runtime, path);
|
|
1510
|
-
const childOptions =
|
|
1511
|
-
options,
|
|
1512
|
-
getComponentName(node.type as Function),
|
|
1513
|
-
);
|
|
1615
|
+
const childOptions = getHydrationChildOptions(options, node.type as Function);
|
|
1514
1616
|
const childResult = reconcileHostChild(
|
|
1515
1617
|
fiber,
|
|
1516
1618
|
current?.tag === "function-component" ? current.child : undefined,
|
|
@@ -2082,6 +2184,26 @@ function collectCommittedHostNodes(fiber: Fiber): Node[] {
|
|
|
2082
2184
|
return nodes;
|
|
2083
2185
|
}
|
|
2084
2186
|
|
|
2187
|
+
function hasCommittedHostNode(fiber: Fiber): boolean {
|
|
2188
|
+
if (
|
|
2189
|
+
(fiber.tag === "host-component" || fiber.tag === "host-text") &&
|
|
2190
|
+
fiber.stateNode instanceof Node
|
|
2191
|
+
) {
|
|
2192
|
+
return true;
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
let child = fiber.child;
|
|
2196
|
+
|
|
2197
|
+
while (child !== undefined) {
|
|
2198
|
+
if (hasCommittedHostNode(child)) {
|
|
2199
|
+
return true;
|
|
2200
|
+
}
|
|
2201
|
+
child = child.sibling;
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
return false;
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2085
2207
|
function getSkippedChild(current: Fiber | undefined): Fiber | undefined {
|
|
2086
2208
|
const child = current?.child;
|
|
2087
2209
|
const alternateChild = current?.alternate?.child;
|
|
@@ -2089,8 +2211,8 @@ function getSkippedChild(current: Fiber | undefined): Fiber | undefined {
|
|
|
2089
2211
|
if (
|
|
2090
2212
|
child !== undefined &&
|
|
2091
2213
|
alternateChild !== undefined &&
|
|
2092
|
-
|
|
2093
|
-
|
|
2214
|
+
!hasCommittedHostNode(child) &&
|
|
2215
|
+
hasCommittedHostNode(alternateChild)
|
|
2094
2216
|
) {
|
|
2095
2217
|
return alternateChild;
|
|
2096
2218
|
}
|
|
@@ -2980,6 +3102,104 @@ function hasPendingAsyncChild(fiber: Fiber | undefined): boolean {
|
|
|
2980
3102
|
return false;
|
|
2981
3103
|
}
|
|
2982
3104
|
|
|
3105
|
+
function tryReuseMemoBailout(
|
|
3106
|
+
current: Fiber | undefined,
|
|
3107
|
+
node: ReactCompatNode,
|
|
3108
|
+
runtime: RootRuntime | undefined,
|
|
3109
|
+
path: string,
|
|
3110
|
+
options: FiberHydrationOptions,
|
|
3111
|
+
canReuseCurrentFiber = true,
|
|
3112
|
+
): FiberReconcileResult | undefined {
|
|
3113
|
+
if (
|
|
3114
|
+
current?.tag !== "memo" ||
|
|
3115
|
+
runtime === undefined ||
|
|
3116
|
+
!isReactCompatElement(node) ||
|
|
3117
|
+
node.type !== current.type ||
|
|
3118
|
+
!isMemoType(node.type)
|
|
3119
|
+
) {
|
|
3120
|
+
return undefined;
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
const previousMemoState = current.memoizedState as MemoFiberState | undefined;
|
|
3124
|
+
const memoPath = `${path}.memo`;
|
|
3125
|
+
|
|
3126
|
+
if (
|
|
3127
|
+
previousMemoState === undefined ||
|
|
3128
|
+
(
|
|
3129
|
+
memoStateNeedsDirtyInstanceCheck(previousMemoState) &&
|
|
3130
|
+
hasDirtyInstance(runtime, previousMemoState.instanceKeys, memoPath)
|
|
3131
|
+
) ||
|
|
3132
|
+
(
|
|
3133
|
+
memoStateNeedsEffectCheck(previousMemoState) &&
|
|
3134
|
+
hasUnflushedMountEffectInstance(runtime, previousMemoState.instanceKeys)
|
|
3135
|
+
) ||
|
|
3136
|
+
!areMemoPropsEqual(node.type, previousMemoState.props, node.props)
|
|
3137
|
+
) {
|
|
3138
|
+
return undefined;
|
|
3139
|
+
}
|
|
3140
|
+
|
|
3141
|
+
const fiber = getMemoBailoutFiber(
|
|
3142
|
+
runtime,
|
|
3143
|
+
current,
|
|
3144
|
+
node.props,
|
|
3145
|
+
previousMemoState,
|
|
3146
|
+
canReuseCurrentFiber,
|
|
3147
|
+
);
|
|
3148
|
+
fiber.type = node.type;
|
|
3149
|
+
fiber.child = getSkippedChild(current);
|
|
3150
|
+
fiber.memoizedState = previousMemoState;
|
|
3151
|
+
return {
|
|
3152
|
+
fiber,
|
|
3153
|
+
consumed: options.previousNodes?.length ?? 0,
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
function tryReuseDependencyFreeMemoBailout(
|
|
3158
|
+
current: Fiber | undefined,
|
|
3159
|
+
node: ReactCompatNode,
|
|
3160
|
+
runtime: RootRuntime | undefined,
|
|
3161
|
+
options: FiberHydrationOptions,
|
|
3162
|
+
canReuseCurrentFiber: boolean,
|
|
3163
|
+
): FiberReconcileResult | undefined {
|
|
3164
|
+
if (
|
|
3165
|
+
options.previousNodes !== undefined ||
|
|
3166
|
+
current?.tag !== "memo" ||
|
|
3167
|
+
runtime === undefined ||
|
|
3168
|
+
!isReactCompatElement(node) ||
|
|
3169
|
+
node.type !== current.type ||
|
|
3170
|
+
!isMemoType(node.type)
|
|
3171
|
+
) {
|
|
3172
|
+
return undefined;
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
const previousMemoState = current.memoizedState as MemoFiberState | undefined;
|
|
3176
|
+
|
|
3177
|
+
if (
|
|
3178
|
+
previousMemoState === undefined ||
|
|
3179
|
+
previousMemoState.hasDirtyInstanceDependencies !== false ||
|
|
3180
|
+
previousMemoState.hasUnflushedEffectDependencies !== false ||
|
|
3181
|
+
previousMemoState.hasRetainedInstanceDependencies !== false ||
|
|
3182
|
+
!areMemoPropsEqual(node.type, previousMemoState.props, node.props)
|
|
3183
|
+
) {
|
|
3184
|
+
return undefined;
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
const fiber = getMemoBailoutFiber(
|
|
3188
|
+
runtime,
|
|
3189
|
+
current,
|
|
3190
|
+
node.props,
|
|
3191
|
+
previousMemoState,
|
|
3192
|
+
canReuseCurrentFiber,
|
|
3193
|
+
);
|
|
3194
|
+
fiber.type = node.type;
|
|
3195
|
+
fiber.child = getSkippedChild(current);
|
|
3196
|
+
fiber.memoizedState = previousMemoState;
|
|
3197
|
+
return {
|
|
3198
|
+
fiber,
|
|
3199
|
+
consumed: 0,
|
|
3200
|
+
};
|
|
3201
|
+
}
|
|
3202
|
+
|
|
2983
3203
|
function isPendingLazyFiber(fiber: Fiber): boolean {
|
|
2984
3204
|
if (fiber.tag !== "lazy" || !isLazyType(fiber.type)) {
|
|
2985
3205
|
return false;
|
|
@@ -3223,6 +3443,15 @@ function joinCommitPath(path: string, segment: string): string {
|
|
|
3223
3443
|
return path === SKIP_COMMIT_PATH ? "" : joinPath(path, segment);
|
|
3224
3444
|
}
|
|
3225
3445
|
|
|
3446
|
+
function getHydrationChildOptions(
|
|
3447
|
+
options: FiberHydrationOptions,
|
|
3448
|
+
component: Function,
|
|
3449
|
+
): FiberHydrationOptions {
|
|
3450
|
+
return options.hydration === undefined
|
|
3451
|
+
? options
|
|
3452
|
+
: withHydrationComponentStack(options, getComponentName(component));
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3226
3455
|
function getComponentName(component: Function): string {
|
|
3227
3456
|
return component.name === "" ? "Anonymous" : component.name;
|
|
3228
3457
|
}
|
|
@@ -3288,6 +3517,152 @@ function markActiveInstanceKeys(runtime: RootRuntime, keys: readonly string[]):
|
|
|
3288
3517
|
}
|
|
3289
3518
|
}
|
|
3290
3519
|
|
|
3520
|
+
type RuntimeHookSlotLike = {
|
|
3521
|
+
kind?: string;
|
|
3522
|
+
};
|
|
3523
|
+
|
|
3524
|
+
type RuntimeEffectHookSlotLike = RuntimeHookSlotLike & {
|
|
3525
|
+
mounted?: boolean;
|
|
3526
|
+
disposed?: boolean;
|
|
3527
|
+
};
|
|
3528
|
+
|
|
3529
|
+
interface RuntimeInstanceLike {
|
|
3530
|
+
hooks?: readonly (RuntimeHookSlotLike | undefined)[];
|
|
3531
|
+
contextDependencies?: unknown;
|
|
3532
|
+
}
|
|
3533
|
+
|
|
3534
|
+
function memoStateNeedsDirtyInstanceCheck(state: MemoFiberState): boolean {
|
|
3535
|
+
return state.hasDirtyInstanceDependencies !== false;
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3538
|
+
function memoStateNeedsEffectCheck(state: MemoFiberState): boolean {
|
|
3539
|
+
return state.hasUnflushedEffectDependencies !== false;
|
|
3540
|
+
}
|
|
3541
|
+
|
|
3542
|
+
function memoStateNeedsActiveInstanceMark(state: MemoFiberState): boolean {
|
|
3543
|
+
return state.hasRetainedInstanceDependencies !== false;
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3546
|
+
function getMemoBailoutFiber(
|
|
3547
|
+
runtime: RootRuntime,
|
|
3548
|
+
current: Fiber,
|
|
3549
|
+
pendingProps: unknown,
|
|
3550
|
+
state: MemoFiberState,
|
|
3551
|
+
canReuseCurrentFiber: boolean,
|
|
3552
|
+
): Fiber {
|
|
3553
|
+
if (canReuseCurrentFiber && canReuseMemoBailoutFiber(current, state)) {
|
|
3554
|
+
current.pendingProps = pendingProps;
|
|
3555
|
+
current.flags = NoFlags;
|
|
3556
|
+
current.subtreeFlags = NoFlags;
|
|
3557
|
+
current.childListChanged = false;
|
|
3558
|
+
current.subtreeChildListChanged = false;
|
|
3559
|
+
current.hostChildListChanged = false;
|
|
3560
|
+
return current;
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3563
|
+
const fiber = createWorkInProgress(current, pendingProps);
|
|
3564
|
+
if (memoStateNeedsActiveInstanceMark(state)) {
|
|
3565
|
+
markActiveInstanceKeys(runtime, state.instanceKeys);
|
|
3566
|
+
}
|
|
3567
|
+
return fiber;
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
function canReuseMemoBailoutFiber(current: Fiber, state: MemoFiberState): boolean {
|
|
3571
|
+
return (
|
|
3572
|
+
state.hasRetainedInstanceDependencies === false &&
|
|
3573
|
+
current.hasRefSubtree !== true &&
|
|
3574
|
+
current.hydrateExisting !== true
|
|
3575
|
+
);
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
function hasDirtyInstanceDependencies(
|
|
3579
|
+
runtime: RootRuntime,
|
|
3580
|
+
keys: readonly string[],
|
|
3581
|
+
): boolean {
|
|
3582
|
+
for (const key of keys) {
|
|
3583
|
+
const instance = runtime.instances.get(key) as RuntimeInstanceLike | undefined;
|
|
3584
|
+
|
|
3585
|
+
if (instance === undefined || instance.contextDependencies !== undefined) {
|
|
3586
|
+
return true;
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
if (instance.hooks?.some(isDirtyCapableHookSlot) === true) {
|
|
3590
|
+
return true;
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
return false;
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3597
|
+
function hasRetainedInstanceDependencies(
|
|
3598
|
+
runtime: RootRuntime,
|
|
3599
|
+
keys: readonly string[],
|
|
3600
|
+
): boolean {
|
|
3601
|
+
for (const key of keys) {
|
|
3602
|
+
const instance = runtime.instances.get(key) as RuntimeInstanceLike | undefined;
|
|
3603
|
+
|
|
3604
|
+
if (instance === undefined || instance.contextDependencies !== undefined) {
|
|
3605
|
+
return true;
|
|
3606
|
+
}
|
|
3607
|
+
|
|
3608
|
+
if (instance.hooks !== undefined && instance.hooks.length > 0) {
|
|
3609
|
+
return true;
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
return false;
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
function isDirtyCapableHookSlot(slot: RuntimeHookSlotLike | undefined): boolean {
|
|
3617
|
+
if (slot === undefined) {
|
|
3618
|
+
return false;
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3621
|
+
return (
|
|
3622
|
+
slot.kind !== "ref" &&
|
|
3623
|
+
slot.kind !== "memo" &&
|
|
3624
|
+
slot.kind !== "debug" &&
|
|
3625
|
+
slot.kind !== "effect"
|
|
3626
|
+
);
|
|
3627
|
+
}
|
|
3628
|
+
|
|
3629
|
+
function hasUnflushedEffectDependencies(
|
|
3630
|
+
runtime: RootRuntime,
|
|
3631
|
+
keys: readonly string[],
|
|
3632
|
+
): boolean {
|
|
3633
|
+
for (const key of keys) {
|
|
3634
|
+
const instance = runtime.instances.get(key) as RuntimeInstanceLike | undefined;
|
|
3635
|
+
|
|
3636
|
+
if (instance === undefined) {
|
|
3637
|
+
return true;
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3640
|
+
if (instance.hooks?.some((slot) => slot?.kind === "effect") === true) {
|
|
3641
|
+
return true;
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3645
|
+
return false;
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
function hasClassComponentDescendant(fiber: Fiber | undefined): boolean {
|
|
3649
|
+
let cursor = fiber;
|
|
3650
|
+
|
|
3651
|
+
while (cursor !== undefined) {
|
|
3652
|
+
if (cursor.tag === "class-component") {
|
|
3653
|
+
return true;
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3656
|
+
if (hasClassComponentDescendant(cursor.child)) {
|
|
3657
|
+
return true;
|
|
3658
|
+
}
|
|
3659
|
+
|
|
3660
|
+
cursor = cursor.sibling;
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
return false;
|
|
3664
|
+
}
|
|
3665
|
+
|
|
3291
3666
|
function hasDirtyInstance(
|
|
3292
3667
|
runtime: RootRuntime | undefined,
|
|
3293
3668
|
keys: readonly string[],
|
|
@@ -3301,6 +3676,10 @@ function hasDirtyInstance(
|
|
|
3301
3676
|
return true;
|
|
3302
3677
|
}
|
|
3303
3678
|
|
|
3679
|
+
if (keys.length === 0) {
|
|
3680
|
+
return false;
|
|
3681
|
+
}
|
|
3682
|
+
|
|
3304
3683
|
if (keys.some(
|
|
3305
3684
|
(key) =>
|
|
3306
3685
|
(runtime.instances.get(key) as { dirty?: boolean } | undefined)?.dirty === true,
|
|
@@ -3312,12 +3691,21 @@ function hasDirtyInstance(
|
|
|
3312
3691
|
return false;
|
|
3313
3692
|
}
|
|
3314
3693
|
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3694
|
+
// Resolve dirty descendants through the prefix index instead of scanning
|
|
3695
|
+
// every runtime instance. The previous full-map scan made memo/function
|
|
3696
|
+
// bailout O(total instances) per node, i.e. O(n^2) for large keyed lists
|
|
3697
|
+
// (js-framework-benchmark update-every-10th / select). The index is the same
|
|
3698
|
+
// source of truth used by collectRuntimeInstanceKeys.
|
|
3699
|
+
const keysUnderPrefix = runtime.instanceKeysByPrefix.get(prefix);
|
|
3700
|
+
|
|
3701
|
+
if (keysUnderPrefix !== undefined) {
|
|
3702
|
+
for (const key of keysUnderPrefix) {
|
|
3703
|
+
if (
|
|
3704
|
+
(runtime.instances.get(key) as { dirty?: boolean } | undefined)?.dirty ===
|
|
3705
|
+
true
|
|
3706
|
+
) {
|
|
3707
|
+
return true;
|
|
3708
|
+
}
|
|
3321
3709
|
}
|
|
3322
3710
|
}
|
|
3323
3711
|
|
|
@@ -3327,7 +3715,7 @@ function hasDirtyInstance(
|
|
|
3327
3715
|
function hasUnflushedMountEffectInstance(runtime: RootRuntime, keys: readonly string[]): boolean {
|
|
3328
3716
|
return keys.some((key) => {
|
|
3329
3717
|
const instance = runtime.instances.get(key) as
|
|
3330
|
-
| { hooks?: readonly (
|
|
3718
|
+
| { hooks?: readonly (RuntimeEffectHookSlotLike | undefined)[] }
|
|
3331
3719
|
| undefined;
|
|
3332
3720
|
|
|
3333
3721
|
return instance?.hooks?.some(
|
package/src/index.ts
CHANGED
package/src/jsx-dev-runtime.ts
CHANGED
|
@@ -38,7 +38,7 @@ export namespace JSX {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/** Creates a JSX element with development metadata arguments. */
|
|
41
|
-
export function jsxDEV<P extends
|
|
41
|
+
export function jsxDEV<P extends object>(
|
|
42
42
|
type: ElementType<P>,
|
|
43
43
|
props: (P & { children?: ReactCompatNode; key?: unknown; ref?: unknown }) | null,
|
|
44
44
|
key: unknown,
|