@fictjs/runtime 0.0.10 → 0.0.12
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/index.cjs +315 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.dev.js +330 -48
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +315 -50
- package/dist/index.js.map +1 -1
- package/dist/slim.cjs +293 -40
- package/dist/slim.cjs.map +1 -1
- package/dist/slim.js +293 -40
- package/dist/slim.js.map +1 -1
- package/package.json +1 -1
- package/src/binding.ts +3 -1
- package/src/dev.d.ts +5 -0
- package/src/dom.ts +20 -2
- package/src/effect.ts +16 -5
- package/src/error-boundary.ts +11 -2
- package/src/lifecycle.ts +4 -1
- package/src/list-helpers.ts +243 -22
- package/src/props.ts +2 -4
- package/src/reconcile.ts +4 -0
- package/src/signal.ts +154 -25
- package/src/suspense.ts +24 -7
- package/src/transition.ts +4 -1
package/dist/index.d.cts
CHANGED
|
@@ -1431,6 +1431,10 @@ declare const UnitlessStyles: Set<string>;
|
|
|
1431
1431
|
* @param a - The old array of nodes (currently in DOM)
|
|
1432
1432
|
* @param b - The new array of nodes (target state)
|
|
1433
1433
|
*
|
|
1434
|
+
* **Note:** This function may mutate the input array `a` during the swap
|
|
1435
|
+
* optimization (step 5a). If you need to preserve the original array,
|
|
1436
|
+
* pass a shallow copy: `reconcileArrays(parent, [...oldNodes], newNodes)`.
|
|
1437
|
+
*
|
|
1434
1438
|
* @example
|
|
1435
1439
|
* ```ts
|
|
1436
1440
|
* const oldNodes = [node1, node2, node3]
|
package/dist/index.d.ts
CHANGED
|
@@ -1431,6 +1431,10 @@ declare const UnitlessStyles: Set<string>;
|
|
|
1431
1431
|
* @param a - The old array of nodes (currently in DOM)
|
|
1432
1432
|
* @param b - The new array of nodes (target state)
|
|
1433
1433
|
*
|
|
1434
|
+
* **Note:** This function may mutate the input array `a` during the swap
|
|
1435
|
+
* optimization (step 5a). If you need to preserve the original array,
|
|
1436
|
+
* pass a shallow copy: `reconcileArrays(parent, [...oldNodes], newNodes)`.
|
|
1437
|
+
*
|
|
1434
1438
|
* @example
|
|
1435
1439
|
* ```ts
|
|
1436
1440
|
* const oldNodes = [node1, node2, node3]
|
package/dist/index.dev.js
CHANGED
|
@@ -301,7 +301,7 @@ function handleError(err, info, startRoot) {
|
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
|
-
|
|
304
|
+
return false;
|
|
305
305
|
}
|
|
306
306
|
function handleSuspend(token, startRoot) {
|
|
307
307
|
let root = startRoot ?? currentRoot;
|
|
@@ -348,6 +348,11 @@ var isInTransition = false;
|
|
|
348
348
|
var enqueueMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : (fn) => {
|
|
349
349
|
Promise.resolve().then(fn);
|
|
350
350
|
};
|
|
351
|
+
var inCleanup = false;
|
|
352
|
+
var SIGNAL_MARKER = Symbol.for("fict:signal");
|
|
353
|
+
var COMPUTED_MARKER = Symbol.for("fict:computed");
|
|
354
|
+
var EFFECT_MARKER = Symbol.for("fict:effect");
|
|
355
|
+
var EFFECT_SCOPE_MARKER = Symbol.for("fict:effectScope");
|
|
351
356
|
function link(dep, sub, version) {
|
|
352
357
|
const prevDep = sub.depsTail;
|
|
353
358
|
if (prevDep !== void 0 && prevDep.dep === dep) return;
|
|
@@ -471,13 +476,21 @@ function checkDirty(firstLink, sub) {
|
|
|
471
476
|
dirty = true;
|
|
472
477
|
}
|
|
473
478
|
} else if ((depFlags & MutablePending) === MutablePending) {
|
|
474
|
-
if (
|
|
475
|
-
|
|
479
|
+
if (!dep.deps) {
|
|
480
|
+
const nextDep = link2.nextDep;
|
|
481
|
+
if (nextDep !== void 0) {
|
|
482
|
+
link2 = nextDep;
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
} else {
|
|
486
|
+
if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
|
|
487
|
+
stack = { value: link2, prev: stack };
|
|
488
|
+
}
|
|
489
|
+
link2 = dep.deps;
|
|
490
|
+
sub = dep;
|
|
491
|
+
++checkDepth;
|
|
492
|
+
continue;
|
|
476
493
|
}
|
|
477
|
-
link2 = dep.deps;
|
|
478
|
-
sub = dep;
|
|
479
|
-
++checkDepth;
|
|
480
|
-
continue;
|
|
481
494
|
}
|
|
482
495
|
if (!dirty) {
|
|
483
496
|
const nextDep = link2.nextDep;
|
|
@@ -555,8 +568,12 @@ function disposeNode(node) {
|
|
|
555
568
|
node.depsTail = void 0;
|
|
556
569
|
node.flags = 0;
|
|
557
570
|
purgeDeps(node);
|
|
558
|
-
|
|
559
|
-
|
|
571
|
+
let sub = node.subs;
|
|
572
|
+
while (sub !== void 0) {
|
|
573
|
+
const next = sub.nextSub;
|
|
574
|
+
unlink(sub);
|
|
575
|
+
sub = next;
|
|
576
|
+
}
|
|
560
577
|
}
|
|
561
578
|
function updateSignal(s) {
|
|
562
579
|
s.flags = Mutable;
|
|
@@ -593,7 +610,15 @@ function updateComputed(c) {
|
|
|
593
610
|
}
|
|
594
611
|
function runEffect(e) {
|
|
595
612
|
const flags = e.flags;
|
|
596
|
-
if (flags & Dirty
|
|
613
|
+
if (flags & Dirty) {
|
|
614
|
+
if (e.runCleanup) {
|
|
615
|
+
inCleanup = true;
|
|
616
|
+
try {
|
|
617
|
+
e.runCleanup();
|
|
618
|
+
} finally {
|
|
619
|
+
inCleanup = false;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
597
622
|
++cycle;
|
|
598
623
|
effectRunDevtools(e);
|
|
599
624
|
e.depsTail = void 0;
|
|
@@ -610,6 +635,35 @@ function runEffect(e) {
|
|
|
610
635
|
e.flags = Watching;
|
|
611
636
|
throw err;
|
|
612
637
|
}
|
|
638
|
+
} else if (flags & Pending && e.deps) {
|
|
639
|
+
if (e.runCleanup) {
|
|
640
|
+
inCleanup = true;
|
|
641
|
+
try {
|
|
642
|
+
e.runCleanup();
|
|
643
|
+
} finally {
|
|
644
|
+
inCleanup = false;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
if (checkDirty(e.deps, e)) {
|
|
648
|
+
++cycle;
|
|
649
|
+
effectRunDevtools(e);
|
|
650
|
+
e.depsTail = void 0;
|
|
651
|
+
e.flags = WatchingRunning;
|
|
652
|
+
const prevSub = activeSub;
|
|
653
|
+
activeSub = e;
|
|
654
|
+
try {
|
|
655
|
+
e.fn();
|
|
656
|
+
activeSub = prevSub;
|
|
657
|
+
e.flags = Watching;
|
|
658
|
+
purgeDeps(e);
|
|
659
|
+
} catch (err) {
|
|
660
|
+
activeSub = prevSub;
|
|
661
|
+
e.flags = Watching;
|
|
662
|
+
throw err;
|
|
663
|
+
}
|
|
664
|
+
} else {
|
|
665
|
+
e.flags = Watching;
|
|
666
|
+
}
|
|
613
667
|
} else {
|
|
614
668
|
e.flags = Watching;
|
|
615
669
|
}
|
|
@@ -688,7 +742,9 @@ function signal(initialValue) {
|
|
|
688
742
|
__id: void 0
|
|
689
743
|
};
|
|
690
744
|
registerSignalDevtools(initialValue, s);
|
|
691
|
-
|
|
745
|
+
const accessor = signalOper.bind(s);
|
|
746
|
+
accessor[SIGNAL_MARKER] = true;
|
|
747
|
+
return accessor;
|
|
692
748
|
}
|
|
693
749
|
function signalOper(value) {
|
|
694
750
|
if (arguments.length > 0) {
|
|
@@ -705,7 +761,7 @@ function signalOper(value) {
|
|
|
705
761
|
return;
|
|
706
762
|
}
|
|
707
763
|
const flags = this.flags;
|
|
708
|
-
if (flags & Dirty) {
|
|
764
|
+
if (flags & Dirty && !inCleanup) {
|
|
709
765
|
if (updateSignal(this)) {
|
|
710
766
|
const subs = this.subs;
|
|
711
767
|
if (subs !== void 0) shallowPropagate(subs);
|
|
@@ -733,6 +789,7 @@ function computed(getter) {
|
|
|
733
789
|
getter
|
|
734
790
|
};
|
|
735
791
|
const bound = computedOper.bind(c);
|
|
792
|
+
bound[COMPUTED_MARKER] = true;
|
|
736
793
|
return bound;
|
|
737
794
|
}
|
|
738
795
|
function computedOper() {
|
|
@@ -785,7 +842,35 @@ function effect(fn) {
|
|
|
785
842
|
activeSub = prevSub;
|
|
786
843
|
e.flags &= ~Running;
|
|
787
844
|
}
|
|
788
|
-
|
|
845
|
+
const disposer = effectOper.bind(e);
|
|
846
|
+
disposer[EFFECT_MARKER] = true;
|
|
847
|
+
return disposer;
|
|
848
|
+
}
|
|
849
|
+
function effectWithCleanup(fn, cleanupRunner) {
|
|
850
|
+
const e = {
|
|
851
|
+
fn,
|
|
852
|
+
subs: void 0,
|
|
853
|
+
subsTail: void 0,
|
|
854
|
+
deps: void 0,
|
|
855
|
+
depsTail: void 0,
|
|
856
|
+
flags: WatchingRunning,
|
|
857
|
+
runCleanup: cleanupRunner,
|
|
858
|
+
__id: void 0
|
|
859
|
+
};
|
|
860
|
+
registerEffectDevtools(e);
|
|
861
|
+
const prevSub = activeSub;
|
|
862
|
+
if (prevSub !== void 0) link(e, prevSub, 0);
|
|
863
|
+
activeSub = e;
|
|
864
|
+
try {
|
|
865
|
+
effectRunDevtools(e);
|
|
866
|
+
fn();
|
|
867
|
+
} finally {
|
|
868
|
+
activeSub = prevSub;
|
|
869
|
+
e.flags &= ~Running;
|
|
870
|
+
}
|
|
871
|
+
const disposer = effectOper.bind(e);
|
|
872
|
+
disposer[EFFECT_MARKER] = true;
|
|
873
|
+
return disposer;
|
|
789
874
|
}
|
|
790
875
|
function effectOper() {
|
|
791
876
|
disposeNode(this);
|
|
@@ -800,7 +885,9 @@ function effectScope(fn) {
|
|
|
800
885
|
} finally {
|
|
801
886
|
activeSub = prevSub;
|
|
802
887
|
}
|
|
803
|
-
|
|
888
|
+
const disposer = effectScopeOper.bind(e);
|
|
889
|
+
disposer[EFFECT_SCOPE_MARKER] = true;
|
|
890
|
+
return disposer;
|
|
804
891
|
}
|
|
805
892
|
function effectScopeOper() {
|
|
806
893
|
disposeNode(this);
|
|
@@ -1034,8 +1121,11 @@ var $memo = createMemo;
|
|
|
1034
1121
|
function createEffect(fn) {
|
|
1035
1122
|
let cleanups = [];
|
|
1036
1123
|
const rootForError = getCurrentRoot();
|
|
1037
|
-
const
|
|
1124
|
+
const doCleanup = () => {
|
|
1038
1125
|
runCleanupList(cleanups);
|
|
1126
|
+
cleanups = [];
|
|
1127
|
+
};
|
|
1128
|
+
const run = () => {
|
|
1039
1129
|
const bucket = [];
|
|
1040
1130
|
withEffectCleanups(bucket, () => {
|
|
1041
1131
|
try {
|
|
@@ -1052,7 +1142,7 @@ function createEffect(fn) {
|
|
|
1052
1142
|
});
|
|
1053
1143
|
cleanups = bucket;
|
|
1054
1144
|
};
|
|
1055
|
-
const disposeEffect =
|
|
1145
|
+
const disposeEffect = effectWithCleanup(run, doCleanup);
|
|
1056
1146
|
const teardown = () => {
|
|
1057
1147
|
runCleanupList(cleanups);
|
|
1058
1148
|
disposeEffect();
|
|
@@ -1064,11 +1154,13 @@ var $effect = createEffect;
|
|
|
1064
1154
|
function createRenderEffect(fn) {
|
|
1065
1155
|
let cleanup;
|
|
1066
1156
|
const rootForError = getCurrentRoot();
|
|
1067
|
-
const
|
|
1157
|
+
const doCleanup = () => {
|
|
1068
1158
|
if (cleanup) {
|
|
1069
1159
|
cleanup();
|
|
1070
1160
|
cleanup = void 0;
|
|
1071
1161
|
}
|
|
1162
|
+
};
|
|
1163
|
+
const run = () => {
|
|
1072
1164
|
try {
|
|
1073
1165
|
const maybeCleanup = fn();
|
|
1074
1166
|
if (typeof maybeCleanup === "function") {
|
|
@@ -1082,7 +1174,7 @@ function createRenderEffect(fn) {
|
|
|
1082
1174
|
throw err;
|
|
1083
1175
|
}
|
|
1084
1176
|
};
|
|
1085
|
-
const disposeEffect =
|
|
1177
|
+
const disposeEffect = effectWithCleanup(run, doCleanup);
|
|
1086
1178
|
const teardown = () => {
|
|
1087
1179
|
if (cleanup) {
|
|
1088
1180
|
cleanup();
|
|
@@ -1614,9 +1706,6 @@ function mergeProps(...sources) {
|
|
|
1614
1706
|
};
|
|
1615
1707
|
return new Proxy({}, {
|
|
1616
1708
|
get(_, prop) {
|
|
1617
|
-
if (typeof prop === "symbol") {
|
|
1618
|
-
return void 0;
|
|
1619
|
-
}
|
|
1620
1709
|
for (let i = validSources.length - 1; i >= 0; i--) {
|
|
1621
1710
|
const src = validSources[i];
|
|
1622
1711
|
const raw = resolveSource(src);
|
|
@@ -1689,8 +1778,8 @@ function startTransition(fn) {
|
|
|
1689
1778
|
function useTransition() {
|
|
1690
1779
|
const pending = signal(false);
|
|
1691
1780
|
const start = (fn) => {
|
|
1692
|
-
pending(true);
|
|
1693
1781
|
startTransition(() => {
|
|
1782
|
+
pending(true);
|
|
1694
1783
|
try {
|
|
1695
1784
|
fn();
|
|
1696
1785
|
} finally {
|
|
@@ -1725,6 +1814,7 @@ function untrack2(fn) {
|
|
|
1725
1814
|
// src/dom.ts
|
|
1726
1815
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
1727
1816
|
var MATHML_NS = "http://www.w3.org/1998/Math/MathML";
|
|
1817
|
+
var isDev = true ? true : typeof process === "undefined" || process.env?.NODE_ENV !== "production";
|
|
1728
1818
|
function render(view, container) {
|
|
1729
1819
|
const root = createRootContext();
|
|
1730
1820
|
const prev = pushRoot(root);
|
|
@@ -1935,18 +2025,28 @@ function applyRef(el, value) {
|
|
|
1935
2025
|
if (typeof value === "function") {
|
|
1936
2026
|
const refFn = value;
|
|
1937
2027
|
refFn(el);
|
|
1938
|
-
|
|
2028
|
+
const root = getCurrentRoot();
|
|
2029
|
+
if (root) {
|
|
1939
2030
|
registerRootCleanup(() => {
|
|
1940
2031
|
refFn(null);
|
|
1941
2032
|
});
|
|
2033
|
+
} else if (isDev) {
|
|
2034
|
+
console.warn(
|
|
2035
|
+
"[fict] Ref applied outside of a root context. The ref cleanup (setting to null) will not run automatically. Consider using createRoot() or ensure the element is created within a component."
|
|
2036
|
+
);
|
|
1942
2037
|
}
|
|
1943
2038
|
} else if (value && typeof value === "object" && "current" in value) {
|
|
1944
2039
|
const refObj = value;
|
|
1945
2040
|
refObj.current = el;
|
|
1946
|
-
|
|
2041
|
+
const root = getCurrentRoot();
|
|
2042
|
+
if (root) {
|
|
1947
2043
|
registerRootCleanup(() => {
|
|
1948
2044
|
refObj.current = null;
|
|
1949
2045
|
});
|
|
2046
|
+
} else if (isDev) {
|
|
2047
|
+
console.warn(
|
|
2048
|
+
"[fict] Ref applied outside of a root context. The ref cleanup (setting to null) will not run automatically. Consider using createRoot() or ensure the element is created within a component."
|
|
2049
|
+
);
|
|
1950
2050
|
}
|
|
1951
2051
|
}
|
|
1952
2052
|
}
|
|
@@ -2360,6 +2460,7 @@ function reconcileArrays(parentNode, a, b) {
|
|
|
2360
2460
|
}
|
|
2361
2461
|
|
|
2362
2462
|
// src/list-helpers.ts
|
|
2463
|
+
var isDev2 = true ? true : typeof process === "undefined" || process.env?.NODE_ENV !== "production";
|
|
2363
2464
|
function moveNodesBefore(parent, nodes, anchor) {
|
|
2364
2465
|
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
2365
2466
|
const node = nodes[i];
|
|
@@ -2421,6 +2522,7 @@ function removeBlockRange(block) {
|
|
|
2421
2522
|
cursor = next;
|
|
2422
2523
|
}
|
|
2423
2524
|
}
|
|
2525
|
+
var MAX_SAFE_VERSION = 9007199254740991;
|
|
2424
2526
|
function createVersionedSignalAccessor(initialValue) {
|
|
2425
2527
|
let current = initialValue;
|
|
2426
2528
|
let version = 0;
|
|
@@ -2431,7 +2533,7 @@ function createVersionedSignalAccessor(initialValue) {
|
|
|
2431
2533
|
return current;
|
|
2432
2534
|
}
|
|
2433
2535
|
current = value;
|
|
2434
|
-
version
|
|
2536
|
+
version = version >= MAX_SAFE_VERSION ? 1 : version + 1;
|
|
2435
2537
|
track2(version);
|
|
2436
2538
|
}
|
|
2437
2539
|
return accessor;
|
|
@@ -2487,15 +2589,21 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
|
|
|
2487
2589
|
});
|
|
2488
2590
|
const root = createRootContext(hostRoot);
|
|
2489
2591
|
const prevRoot = pushRoot(root);
|
|
2490
|
-
const prevSub = setActiveSub(void 0);
|
|
2491
2592
|
let nodes = [];
|
|
2593
|
+
let scopeDispose;
|
|
2594
|
+
const prevSub = setActiveSub(void 0);
|
|
2492
2595
|
try {
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2596
|
+
scopeDispose = effectScope(() => {
|
|
2597
|
+
const rendered = render2(itemSig, indexSig, key);
|
|
2598
|
+
if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
|
|
2599
|
+
nodes = toNodeArray(rendered);
|
|
2600
|
+
} else {
|
|
2601
|
+
const element = createElement(rendered);
|
|
2602
|
+
nodes = toNodeArray(element);
|
|
2603
|
+
}
|
|
2604
|
+
});
|
|
2605
|
+
if (scopeDispose) {
|
|
2606
|
+
root.cleanups.push(scopeDispose);
|
|
2499
2607
|
}
|
|
2500
2608
|
} finally {
|
|
2501
2609
|
setActiveSub(prevSub);
|
|
@@ -2522,6 +2630,86 @@ function isNodeBetweenMarkers(node, startMarker, endMarker) {
|
|
|
2522
2630
|
}
|
|
2523
2631
|
return false;
|
|
2524
2632
|
}
|
|
2633
|
+
function reorderBySwap(parent, first, second) {
|
|
2634
|
+
if (first === second) return false;
|
|
2635
|
+
const firstNodes = first.nodes;
|
|
2636
|
+
const secondNodes = second.nodes;
|
|
2637
|
+
if (firstNodes.length === 0 || secondNodes.length === 0) return false;
|
|
2638
|
+
const lastFirst = firstNodes[firstNodes.length - 1];
|
|
2639
|
+
const lastSecond = secondNodes[secondNodes.length - 1];
|
|
2640
|
+
const afterFirst = lastFirst.nextSibling;
|
|
2641
|
+
const afterSecond = lastSecond.nextSibling;
|
|
2642
|
+
moveNodesBefore(parent, firstNodes, afterSecond);
|
|
2643
|
+
moveNodesBefore(parent, secondNodes, afterFirst);
|
|
2644
|
+
return true;
|
|
2645
|
+
}
|
|
2646
|
+
function getLISIndices(sequence) {
|
|
2647
|
+
const predecessors = new Array(sequence.length);
|
|
2648
|
+
const result = [];
|
|
2649
|
+
for (let i = 0; i < sequence.length; i++) {
|
|
2650
|
+
const value = sequence[i];
|
|
2651
|
+
if (value < 0) {
|
|
2652
|
+
predecessors[i] = -1;
|
|
2653
|
+
continue;
|
|
2654
|
+
}
|
|
2655
|
+
let low = 0;
|
|
2656
|
+
let high = result.length;
|
|
2657
|
+
while (low < high) {
|
|
2658
|
+
const mid = low + high >> 1;
|
|
2659
|
+
if (sequence[result[mid]] < value) {
|
|
2660
|
+
low = mid + 1;
|
|
2661
|
+
} else {
|
|
2662
|
+
high = mid;
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
predecessors[i] = low > 0 ? result[low - 1] : -1;
|
|
2666
|
+
if (low === result.length) {
|
|
2667
|
+
result.push(i);
|
|
2668
|
+
} else {
|
|
2669
|
+
result[low] = i;
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
const lis = new Array(result.length);
|
|
2673
|
+
let k = result.length > 0 ? result[result.length - 1] : -1;
|
|
2674
|
+
for (let i = result.length - 1; i >= 0; i--) {
|
|
2675
|
+
lis[i] = k;
|
|
2676
|
+
k = predecessors[k];
|
|
2677
|
+
}
|
|
2678
|
+
return lis;
|
|
2679
|
+
}
|
|
2680
|
+
function reorderByLIS(parent, endMarker, prev, next) {
|
|
2681
|
+
const positions = /* @__PURE__ */ new Map();
|
|
2682
|
+
for (let i = 0; i < prev.length; i++) {
|
|
2683
|
+
positions.set(prev[i], i);
|
|
2684
|
+
}
|
|
2685
|
+
const sequence = new Array(next.length);
|
|
2686
|
+
for (let i = 0; i < next.length; i++) {
|
|
2687
|
+
const position = positions.get(next[i]);
|
|
2688
|
+
if (position === void 0) return false;
|
|
2689
|
+
sequence[i] = position;
|
|
2690
|
+
}
|
|
2691
|
+
const lisIndices = getLISIndices(sequence);
|
|
2692
|
+
if (lisIndices.length === sequence.length) return true;
|
|
2693
|
+
const inLIS = new Array(sequence.length).fill(false);
|
|
2694
|
+
for (let i = 0; i < lisIndices.length; i++) {
|
|
2695
|
+
inLIS[lisIndices[i]] = true;
|
|
2696
|
+
}
|
|
2697
|
+
let anchor = endMarker;
|
|
2698
|
+
let moved = false;
|
|
2699
|
+
for (let i = next.length - 1; i >= 0; i--) {
|
|
2700
|
+
const block = next[i];
|
|
2701
|
+
const nodes = block.nodes;
|
|
2702
|
+
if (nodes.length === 0) continue;
|
|
2703
|
+
if (inLIS[i]) {
|
|
2704
|
+
anchor = nodes[0];
|
|
2705
|
+
continue;
|
|
2706
|
+
}
|
|
2707
|
+
moveNodesBefore(parent, nodes, anchor);
|
|
2708
|
+
anchor = nodes[0];
|
|
2709
|
+
moved = true;
|
|
2710
|
+
}
|
|
2711
|
+
return moved;
|
|
2712
|
+
}
|
|
2525
2713
|
function createKeyedList(getItems, keyFn, renderItem, needsIndex) {
|
|
2526
2714
|
const resolvedNeedsIndex = arguments.length >= 4 ? !!needsIndex : renderItem.length > 1;
|
|
2527
2715
|
return createFineGrainedKeyedList(getItems, keyFn, renderItem, resolvedNeedsIndex);
|
|
@@ -2554,10 +2742,6 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
|
|
|
2554
2742
|
const prevOrderedBlocks = container.orderedBlocks;
|
|
2555
2743
|
const nextOrderedBlocks = container.nextOrderedBlocks;
|
|
2556
2744
|
const orderedIndexByKey = container.orderedIndexByKey;
|
|
2557
|
-
newBlocks.clear();
|
|
2558
|
-
nextOrderedBlocks.length = 0;
|
|
2559
|
-
orderedIndexByKey.clear();
|
|
2560
|
-
const createdBlocks = [];
|
|
2561
2745
|
const newItems = getItems();
|
|
2562
2746
|
if (newItems.length === 0) {
|
|
2563
2747
|
if (oldBlocks.size > 0) {
|
|
@@ -2580,8 +2764,44 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
|
|
|
2580
2764
|
return;
|
|
2581
2765
|
}
|
|
2582
2766
|
const prevCount = prevOrderedBlocks.length;
|
|
2767
|
+
if (prevCount > 0 && newItems.length === prevCount && orderedIndexByKey.size === prevCount) {
|
|
2768
|
+
let stableOrder = true;
|
|
2769
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2770
|
+
for (let i = 0; i < prevCount; i++) {
|
|
2771
|
+
const item = newItems[i];
|
|
2772
|
+
const key = keyFn(item, i);
|
|
2773
|
+
if (seen.has(key) || prevOrderedBlocks[i].key !== key) {
|
|
2774
|
+
stableOrder = false;
|
|
2775
|
+
break;
|
|
2776
|
+
}
|
|
2777
|
+
seen.add(key);
|
|
2778
|
+
}
|
|
2779
|
+
if (stableOrder) {
|
|
2780
|
+
for (let i = 0; i < prevCount; i++) {
|
|
2781
|
+
const item = newItems[i];
|
|
2782
|
+
const block = prevOrderedBlocks[i];
|
|
2783
|
+
if (block.rawItem !== item) {
|
|
2784
|
+
block.rawItem = item;
|
|
2785
|
+
block.item(item);
|
|
2786
|
+
}
|
|
2787
|
+
if (needsIndex && block.rawIndex !== i) {
|
|
2788
|
+
block.rawIndex = i;
|
|
2789
|
+
block.index(i);
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
newBlocks.clear();
|
|
2796
|
+
nextOrderedBlocks.length = 0;
|
|
2797
|
+
orderedIndexByKey.clear();
|
|
2798
|
+
const createdBlocks = [];
|
|
2583
2799
|
let appendCandidate = prevCount > 0 && newItems.length >= prevCount;
|
|
2584
2800
|
const appendedBlocks = [];
|
|
2801
|
+
let mismatchCount = 0;
|
|
2802
|
+
let mismatchFirst = -1;
|
|
2803
|
+
let mismatchSecond = -1;
|
|
2804
|
+
let hasDuplicateKey = false;
|
|
2585
2805
|
newItems.forEach((item, index) => {
|
|
2586
2806
|
const key = keyFn(item, index);
|
|
2587
2807
|
let block = oldBlocks.get(key);
|
|
@@ -2602,6 +2822,11 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
|
|
|
2602
2822
|
} else {
|
|
2603
2823
|
const existingBlock = newBlocks.get(key);
|
|
2604
2824
|
if (existingBlock) {
|
|
2825
|
+
if (isDev2) {
|
|
2826
|
+
console.warn(
|
|
2827
|
+
`[fict] Duplicate key "${String(key)}" detected in list rendering. Each item should have a unique key. The previous item with this key will be replaced.`
|
|
2828
|
+
);
|
|
2829
|
+
}
|
|
2605
2830
|
destroyRoot(existingBlock.root);
|
|
2606
2831
|
removeNodes(existingBlock.nodes);
|
|
2607
2832
|
}
|
|
@@ -2613,6 +2838,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
|
|
|
2613
2838
|
const position = orderedIndexByKey.get(key);
|
|
2614
2839
|
if (position !== void 0) {
|
|
2615
2840
|
appendCandidate = false;
|
|
2841
|
+
hasDuplicateKey = true;
|
|
2616
2842
|
const prior = nextOrderedBlocks[position];
|
|
2617
2843
|
if (prior && prior !== resolvedBlock) {
|
|
2618
2844
|
destroyRoot(prior.root);
|
|
@@ -2629,8 +2855,17 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
|
|
|
2629
2855
|
appendCandidate = false;
|
|
2630
2856
|
}
|
|
2631
2857
|
}
|
|
2632
|
-
|
|
2858
|
+
const nextIndex = nextOrderedBlocks.length;
|
|
2859
|
+
orderedIndexByKey.set(key, nextIndex);
|
|
2633
2860
|
nextOrderedBlocks.push(resolvedBlock);
|
|
2861
|
+
if (mismatchCount < 3 && (nextIndex >= prevCount || prevOrderedBlocks[nextIndex] !== resolvedBlock)) {
|
|
2862
|
+
if (mismatchCount === 0) {
|
|
2863
|
+
mismatchFirst = nextIndex;
|
|
2864
|
+
} else if (mismatchCount === 1) {
|
|
2865
|
+
mismatchSecond = nextIndex;
|
|
2866
|
+
}
|
|
2867
|
+
mismatchCount++;
|
|
2868
|
+
}
|
|
2634
2869
|
}
|
|
2635
2870
|
if (appendCandidate && index >= prevCount) {
|
|
2636
2871
|
appendedBlocks.push(resolvedBlock);
|
|
@@ -2671,7 +2906,26 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
|
|
|
2671
2906
|
}
|
|
2672
2907
|
oldBlocks.clear();
|
|
2673
2908
|
}
|
|
2674
|
-
|
|
2909
|
+
const canReorderInPlace = createdBlocks.length === 0 && oldBlocks.size === 0 && nextOrderedBlocks.length === prevOrderedBlocks.length;
|
|
2910
|
+
let skipReconcile = false;
|
|
2911
|
+
let updateNodeBuffer = true;
|
|
2912
|
+
if (canReorderInPlace && nextOrderedBlocks.length > 0 && !hasDuplicateKey) {
|
|
2913
|
+
if (mismatchCount === 0) {
|
|
2914
|
+
skipReconcile = true;
|
|
2915
|
+
updateNodeBuffer = false;
|
|
2916
|
+
} else if (mismatchCount === 2 && prevOrderedBlocks[mismatchFirst] === nextOrderedBlocks[mismatchSecond] && prevOrderedBlocks[mismatchSecond] === nextOrderedBlocks[mismatchFirst]) {
|
|
2917
|
+
if (reorderBySwap(
|
|
2918
|
+
parent,
|
|
2919
|
+
prevOrderedBlocks[mismatchFirst],
|
|
2920
|
+
prevOrderedBlocks[mismatchSecond]
|
|
2921
|
+
)) {
|
|
2922
|
+
skipReconcile = true;
|
|
2923
|
+
}
|
|
2924
|
+
} else if (reorderByLIS(parent, container.endMarker, prevOrderedBlocks, nextOrderedBlocks)) {
|
|
2925
|
+
skipReconcile = true;
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
if (!skipReconcile && (newBlocks.size > 0 || container.currentNodes.length > 0)) {
|
|
2675
2929
|
const prevNodes = container.currentNodes;
|
|
2676
2930
|
const nextNodes = container.nextNodes;
|
|
2677
2931
|
nextNodes.length = 0;
|
|
@@ -2686,6 +2940,20 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
|
|
|
2686
2940
|
reconcileArrays(parent, prevNodes, nextNodes);
|
|
2687
2941
|
container.currentNodes = nextNodes;
|
|
2688
2942
|
container.nextNodes = prevNodes;
|
|
2943
|
+
} else if (skipReconcile && updateNodeBuffer) {
|
|
2944
|
+
const prevNodes = container.currentNodes;
|
|
2945
|
+
const nextNodes = container.nextNodes;
|
|
2946
|
+
nextNodes.length = 0;
|
|
2947
|
+
nextNodes.push(container.startMarker);
|
|
2948
|
+
for (let i = 0; i < nextOrderedBlocks.length; i++) {
|
|
2949
|
+
const nodes = nextOrderedBlocks[i].nodes;
|
|
2950
|
+
for (let j = 0; j < nodes.length; j++) {
|
|
2951
|
+
nextNodes.push(nodes[j]);
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
nextNodes.push(container.endMarker);
|
|
2955
|
+
container.currentNodes = nextNodes;
|
|
2956
|
+
container.nextNodes = prevNodes;
|
|
2689
2957
|
}
|
|
2690
2958
|
container.blocks = newBlocks;
|
|
2691
2959
|
container.nextBlocks = oldBlocks;
|
|
@@ -3332,7 +3600,9 @@ function bindEvent(el, eventName, handler, options2) {
|
|
|
3332
3600
|
const fn = resolveHandler();
|
|
3333
3601
|
callEventHandler(fn, args[0], el);
|
|
3334
3602
|
} catch (err) {
|
|
3335
|
-
handleError(err, { source: "event", eventName }, rootRef)
|
|
3603
|
+
if (!handleError(err, { source: "event", eventName }, rootRef)) {
|
|
3604
|
+
throw err;
|
|
3605
|
+
}
|
|
3336
3606
|
}
|
|
3337
3607
|
};
|
|
3338
3608
|
return () => {
|
|
@@ -4119,10 +4389,12 @@ function ErrorBoundary(props) {
|
|
|
4119
4389
|
renderingFallback = true;
|
|
4120
4390
|
try {
|
|
4121
4391
|
renderValue(toView(err));
|
|
4122
|
-
} finally {
|
|
4123
4392
|
renderingFallback = false;
|
|
4393
|
+
props.onError?.(err);
|
|
4394
|
+
} catch (fallbackErr) {
|
|
4395
|
+
props.onError?.(err);
|
|
4396
|
+
throw fallbackErr;
|
|
4124
4397
|
}
|
|
4125
|
-
props.onError?.(err);
|
|
4126
4398
|
return;
|
|
4127
4399
|
}
|
|
4128
4400
|
popRoot(prev);
|
|
@@ -4217,7 +4489,9 @@ function Suspense(props) {
|
|
|
4217
4489
|
popRoot(prev);
|
|
4218
4490
|
flushOnMount(root);
|
|
4219
4491
|
destroyRoot(root);
|
|
4220
|
-
handleError(err, { source: "render" })
|
|
4492
|
+
if (!handleError(err, { source: "render" }, hostRoot)) {
|
|
4493
|
+
throw err;
|
|
4494
|
+
}
|
|
4221
4495
|
return;
|
|
4222
4496
|
}
|
|
4223
4497
|
popRoot(prev);
|
|
@@ -4247,18 +4521,26 @@ function Suspense(props) {
|
|
|
4247
4521
|
if (thenable) {
|
|
4248
4522
|
thenable.then(
|
|
4249
4523
|
() => {
|
|
4250
|
-
if (epoch !== tokenEpoch)
|
|
4251
|
-
|
|
4252
|
-
|
|
4524
|
+
if (epoch !== tokenEpoch) {
|
|
4525
|
+
return;
|
|
4526
|
+
}
|
|
4527
|
+
const newPending = Math.max(0, pending() - 1);
|
|
4528
|
+
pending(newPending);
|
|
4529
|
+
if (newPending === 0) {
|
|
4253
4530
|
switchView(props.children ?? null);
|
|
4254
4531
|
onResolveMaybe();
|
|
4255
4532
|
}
|
|
4256
4533
|
},
|
|
4257
4534
|
(err) => {
|
|
4258
|
-
if (epoch !== tokenEpoch)
|
|
4259
|
-
|
|
4535
|
+
if (epoch !== tokenEpoch) {
|
|
4536
|
+
return;
|
|
4537
|
+
}
|
|
4538
|
+
const newPending = Math.max(0, pending() - 1);
|
|
4539
|
+
pending(newPending);
|
|
4260
4540
|
props.onReject?.(err);
|
|
4261
|
-
handleError(err, { source: "render" }, hostRoot)
|
|
4541
|
+
if (!handleError(err, { source: "render" }, hostRoot)) {
|
|
4542
|
+
throw err;
|
|
4543
|
+
}
|
|
4262
4544
|
}
|
|
4263
4545
|
);
|
|
4264
4546
|
return true;
|