@ryupold/vode 1.8.7 → 1.8.10
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 +34 -56
- package/dist/vode.cjs.min.js +2 -2
- package/dist/vode.d.ts +10 -10
- package/dist/vode.es5.min.js +7 -7
- package/dist/vode.js +97 -113
- package/dist/vode.min.js +1 -1
- package/dist/vode.min.mjs +1 -1
- package/dist/vode.mjs +97 -113
- package/dist/vode.tests.mjs +5303 -0
- package/package.json +5 -5
- package/src/state-context.ts +6 -4
- package/src/vode.ts +114 -126
- package/test/helper.ts +304 -113
- package/test/index.ts +10 -47
- package/test/mocks.ts +199 -43
- package/test/run-tests.ts +61 -0
- package/test/tests-app.ts +154 -38
- package/test/tests-catch.ts +160 -0
- package/test/tests-children.ts +31 -31
- package/test/tests-createPatch.ts +12 -12
- package/test/tests-createState.ts +11 -11
- package/test/tests-defuse.ts +35 -14
- package/test/tests-examples.ts +991 -0
- package/test/tests-hydrate.ts +59 -25
- package/test/tests-memo.ts +106 -64
- package/test/tests-mergeClass.ts +31 -31
- package/test/tests-mergeProps.ts +19 -19
- package/test/tests-mergeStyle.ts +28 -14
- package/test/tests-mount-unmount.ts +177 -154
- package/test/tests-patch-advanced.ts +86 -0
- package/test/tests-patch-merge.ts +66 -0
- package/test/tests-props.ts +15 -15
- package/test/tests-state-context.ts +56 -25
- package/test/tests-tag.ts +14 -14
- package/test/tests-vode.ts +6 -6
package/dist/vode.mjs
CHANGED
|
@@ -17,53 +17,62 @@ function app(container, state, dom, ...initialPatches) {
|
|
|
17
17
|
const _vode = {};
|
|
18
18
|
_vode.syncRenderer = globals.requestAnimationFrame;
|
|
19
19
|
_vode.asyncRenderer = globals.startViewTransition;
|
|
20
|
-
_vode.
|
|
20
|
+
_vode.isRendering = 0;
|
|
21
21
|
_vode.qAsync = null;
|
|
22
22
|
_vode.stats = { lastSyncRenderTime: 0, lastAsyncRenderTime: 0, syncRenderCount: 0, asyncRenderCount: 0, liveEffectCount: 0, patchCount: 0, syncRenderPatchCount: 0, asyncRenderPatchCount: 0 };
|
|
23
23
|
const patchableState = state;
|
|
24
24
|
if ("patch" in state && typeof state.patch === "function" && Array.isArray(state.patch.initialPatches)) {
|
|
25
25
|
initialPatches = [...state.patch.initialPatches, ...initialPatches];
|
|
26
26
|
}
|
|
27
|
+
async function promisePatch(action, isAnimated) {
|
|
28
|
+
_vode.stats.liveEffectCount++;
|
|
29
|
+
try {
|
|
30
|
+
const resolvedPatch = await action;
|
|
31
|
+
patchableState.patch(resolvedPatch, isAnimated);
|
|
32
|
+
} finally {
|
|
33
|
+
_vode.stats.liveEffectCount--;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function generatorPatch(action, isAnimated) {
|
|
37
|
+
const generator = action;
|
|
38
|
+
_vode.stats.liveEffectCount++;
|
|
39
|
+
try {
|
|
40
|
+
let v = await generator.next();
|
|
41
|
+
while (v.done === false) {
|
|
42
|
+
_vode.stats.liveEffectCount++;
|
|
43
|
+
try {
|
|
44
|
+
patchableState.patch(v.value, isAnimated);
|
|
45
|
+
v = await generator.next();
|
|
46
|
+
} finally {
|
|
47
|
+
_vode.stats.liveEffectCount--;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
patchableState.patch(v.value, isAnimated);
|
|
51
|
+
} finally {
|
|
52
|
+
_vode.stats.liveEffectCount--;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
27
55
|
Object.defineProperty(state, "patch", {
|
|
28
56
|
enumerable: false,
|
|
29
57
|
configurable: true,
|
|
30
58
|
writable: false,
|
|
31
|
-
value:
|
|
32
|
-
|
|
59
|
+
value: (action, isAnimated) => {
|
|
60
|
+
while (typeof action === "function") {
|
|
61
|
+
action = action(_vode.state);
|
|
62
|
+
}
|
|
63
|
+
if (!action || typeof action !== "object") return;
|
|
33
64
|
_vode.stats.patchCount++;
|
|
34
65
|
if (action?.next) {
|
|
35
|
-
|
|
36
|
-
_vode.stats.liveEffectCount++;
|
|
37
|
-
try {
|
|
38
|
-
let v = await generator.next();
|
|
39
|
-
while (v.done === false) {
|
|
40
|
-
_vode.stats.liveEffectCount++;
|
|
41
|
-
try {
|
|
42
|
-
patchableState.patch(v.value, isAsync);
|
|
43
|
-
v = await generator.next();
|
|
44
|
-
} finally {
|
|
45
|
-
_vode.stats.liveEffectCount--;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
patchableState.patch(v.value, isAsync);
|
|
49
|
-
} finally {
|
|
50
|
-
_vode.stats.liveEffectCount--;
|
|
51
|
-
}
|
|
66
|
+
generatorPatch(action, isAnimated);
|
|
52
67
|
} else if (action.then) {
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const resolvedPatch = await action;
|
|
56
|
-
patchableState.patch(resolvedPatch, isAsync);
|
|
57
|
-
} finally {
|
|
58
|
-
_vode.stats.liveEffectCount--;
|
|
59
|
-
}
|
|
68
|
+
promisePatch(action, isAnimated);
|
|
60
69
|
} else if (Array.isArray(action)) {
|
|
61
70
|
if (action.length > 0) {
|
|
62
71
|
for (const p of action) {
|
|
63
72
|
patchableState.patch(p, !document.hidden && !!_vode.asyncRenderer);
|
|
64
73
|
}
|
|
65
74
|
} else {
|
|
66
|
-
|
|
75
|
+
mergeState(_vode.state, _vode.qAsync, true);
|
|
67
76
|
_vode.qAsync = null;
|
|
68
77
|
try {
|
|
69
78
|
globals.currentViewTransition?.skipTransition();
|
|
@@ -72,34 +81,34 @@ function app(container, state, dom, ...initialPatches) {
|
|
|
72
81
|
_vode.stats.syncRenderPatchCount++;
|
|
73
82
|
_vode.renderSync();
|
|
74
83
|
}
|
|
75
|
-
} else if (typeof action === "function") {
|
|
76
|
-
patchableState.patch(action(_vode.state), isAsync);
|
|
77
84
|
} else {
|
|
78
|
-
if (
|
|
85
|
+
if (isAnimated) {
|
|
79
86
|
_vode.stats.asyncRenderPatchCount++;
|
|
80
87
|
_vode.qAsync = mergeState(_vode.qAsync || {}, action, false);
|
|
81
|
-
|
|
88
|
+
_vode.renderAsync();
|
|
82
89
|
} else {
|
|
83
90
|
_vode.stats.syncRenderPatchCount++;
|
|
84
|
-
|
|
91
|
+
mergeState(_vode.state, action, true);
|
|
85
92
|
_vode.renderSync();
|
|
86
93
|
}
|
|
87
94
|
}
|
|
88
95
|
}
|
|
89
96
|
});
|
|
90
|
-
function renderDom(
|
|
91
|
-
const sw =
|
|
97
|
+
function renderDom(isAnimated) {
|
|
98
|
+
const sw = performance.now();
|
|
92
99
|
const vom = dom(_vode.state);
|
|
93
100
|
_vode.vode = render(_vode.state, container.parentElement, 0, 0, _vode.vode, vom);
|
|
94
101
|
if (container.tagName.toUpperCase() !== vom[0].toUpperCase()) {
|
|
95
102
|
container = _vode.vode.node;
|
|
96
103
|
container._vode = _vode;
|
|
97
104
|
}
|
|
98
|
-
if (!
|
|
99
|
-
_vode.stats.lastSyncRenderTime =
|
|
105
|
+
if (!isAnimated) {
|
|
106
|
+
_vode.stats.lastSyncRenderTime = performance.now() - sw;
|
|
107
|
+
const changesSinceRender = _vode.isRendering !== _vode.stats.syncRenderPatchCount;
|
|
100
108
|
_vode.stats.syncRenderCount++;
|
|
101
|
-
_vode.isRendering =
|
|
102
|
-
if (
|
|
109
|
+
_vode.isRendering = 0;
|
|
110
|
+
if (changesSinceRender)
|
|
111
|
+
_vode.renderSync();
|
|
103
112
|
}
|
|
104
113
|
}
|
|
105
114
|
const sr = renderDom.bind(null, false);
|
|
@@ -109,10 +118,8 @@ function app(container, state, dom, ...initialPatches) {
|
|
|
109
118
|
configurable: true,
|
|
110
119
|
writable: false,
|
|
111
120
|
value: () => {
|
|
112
|
-
if (_vode.isRendering
|
|
113
|
-
_vode.isRendering =
|
|
114
|
-
_vode.state = mergeState(_vode.state, _vode.qSync, true);
|
|
115
|
-
_vode.qSync = null;
|
|
121
|
+
if (_vode.isRendering) return;
|
|
122
|
+
_vode.isRendering = _vode.stats.syncRenderPatchCount;
|
|
116
123
|
_vode.syncRenderer(sr);
|
|
117
124
|
}
|
|
118
125
|
});
|
|
@@ -125,14 +132,14 @@ function app(container, state, dom, ...initialPatches) {
|
|
|
125
132
|
await globals.currentViewTransition?.updateCallbackDone;
|
|
126
133
|
if (_vode.isAnimating || !_vode.qAsync || document.hidden) return;
|
|
127
134
|
_vode.isAnimating = true;
|
|
128
|
-
const sw =
|
|
135
|
+
const sw = performance.now();
|
|
129
136
|
try {
|
|
130
137
|
_vode.state = mergeState(_vode.state, _vode.qAsync, true);
|
|
131
138
|
_vode.qAsync = null;
|
|
132
139
|
globals.currentViewTransition = _vode.asyncRenderer(ar);
|
|
133
140
|
await globals.currentViewTransition?.updateCallbackDone;
|
|
134
141
|
} finally {
|
|
135
|
-
_vode.stats.lastAsyncRenderTime =
|
|
142
|
+
_vode.stats.lastAsyncRenderTime = performance.now() - sw;
|
|
136
143
|
_vode.stats.asyncRenderCount++;
|
|
137
144
|
_vode.isAnimating = false;
|
|
138
145
|
}
|
|
@@ -143,7 +150,8 @@ function app(container, state, dom, ...initialPatches) {
|
|
|
143
150
|
const root = container;
|
|
144
151
|
root._vode = _vode;
|
|
145
152
|
const indexInParent = Array.from(container.parentElement.children).indexOf(container);
|
|
146
|
-
|
|
153
|
+
const patchCountBefore = _vode.stats.syncRenderPatchCount;
|
|
154
|
+
_vode.isRendering = _vode.stats.syncRenderPatchCount;
|
|
147
155
|
_vode.vode = render(
|
|
148
156
|
state,
|
|
149
157
|
container.parentElement,
|
|
@@ -152,8 +160,10 @@ function app(container, state, dom, ...initialPatches) {
|
|
|
152
160
|
hydrate(container, true),
|
|
153
161
|
dom(state)
|
|
154
162
|
);
|
|
155
|
-
_vode.
|
|
156
|
-
|
|
163
|
+
const continueRendering = _vode.stats.syncRenderPatchCount !== patchCountBefore;
|
|
164
|
+
_vode.isRendering = 0;
|
|
165
|
+
_vode.stats.syncRenderCount++;
|
|
166
|
+
if (continueRendering) _vode.renderSync();
|
|
157
167
|
for (const effect of initialPatches) {
|
|
158
168
|
patchableState.patch(effect);
|
|
159
169
|
}
|
|
@@ -231,11 +241,15 @@ function hydrate(element, prepareForRender) {
|
|
|
231
241
|
return void 0;
|
|
232
242
|
}
|
|
233
243
|
}
|
|
234
|
-
function memo(compare,
|
|
244
|
+
function memo(compare, component) {
|
|
235
245
|
if (!compare || !Array.isArray(compare)) throw new Error("first argument to memo() must be an array of values to compare");
|
|
236
|
-
if (typeof
|
|
237
|
-
|
|
238
|
-
|
|
246
|
+
if (typeof component !== "function") throw new Error("second argument to memo() must be a function that returns a child vode");
|
|
247
|
+
if (component.__memo) {
|
|
248
|
+
const comp = component;
|
|
249
|
+
component = (s) => comp(s);
|
|
250
|
+
}
|
|
251
|
+
component.__memo = compare;
|
|
252
|
+
return component;
|
|
239
253
|
}
|
|
240
254
|
function createState(state) {
|
|
241
255
|
if (!state || typeof state !== "object") throw new Error("createState() must be called with a state object");
|
|
@@ -360,7 +374,7 @@ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmln
|
|
|
360
374
|
for (let i = indexInParent; i < parent.childNodes.length; i++) {
|
|
361
375
|
const nextSibling = parent.childNodes[i];
|
|
362
376
|
if (nextSibling) {
|
|
363
|
-
nextSibling.before(text
|
|
377
|
+
nextSibling.before(text);
|
|
364
378
|
inserted = true;
|
|
365
379
|
break;
|
|
366
380
|
}
|
|
@@ -393,7 +407,7 @@ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmln
|
|
|
393
407
|
for (let i = indexInParent; i < parent.childNodes.length; i++) {
|
|
394
408
|
const nextSibling = parent.childNodes[i];
|
|
395
409
|
if (nextSibling) {
|
|
396
|
-
nextSibling.before(newNode
|
|
410
|
+
nextSibling.before(newNode);
|
|
397
411
|
inserted = true;
|
|
398
412
|
break;
|
|
399
413
|
}
|
|
@@ -402,12 +416,12 @@ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmln
|
|
|
402
416
|
parent.appendChild(newNode);
|
|
403
417
|
}
|
|
404
418
|
}
|
|
405
|
-
const
|
|
406
|
-
if (
|
|
419
|
+
const newStart = childrenStart(newVode);
|
|
420
|
+
if (newStart > 0) {
|
|
407
421
|
const childOffset = !!properties ? 2 : 1;
|
|
408
422
|
let indexP = 0;
|
|
409
|
-
for (let i = 0; i <
|
|
410
|
-
const child2 =
|
|
423
|
+
for (let i = 0; i < newVode.length - newStart; i++) {
|
|
424
|
+
const child2 = newVode[i + newStart];
|
|
411
425
|
const attached = render(state, newNode, i, indexP, void 0, child2, xmlns ?? null);
|
|
412
426
|
newVode[i + childOffset] = attached;
|
|
413
427
|
if (attached) indexP++;
|
|
@@ -421,41 +435,31 @@ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmln
|
|
|
421
435
|
}
|
|
422
436
|
if (!oldIsText && isNode && oldVode[0] === newVode[0]) {
|
|
423
437
|
newVode.node = oldNode;
|
|
424
|
-
const newvode = newVode;
|
|
425
|
-
const oldvode = oldVode;
|
|
426
438
|
const properties = props(newVode);
|
|
427
439
|
const oldProps = props(oldVode);
|
|
428
|
-
if (properties?.xmlns !== void 0)
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
newvode[1] = remember(state, newvode[1], oldvode[1]);
|
|
432
|
-
if (prev !== newvode[1]) {
|
|
433
|
-
patchProperties(state, oldNode, oldProps, properties, xmlns);
|
|
434
|
-
}
|
|
435
|
-
} else {
|
|
436
|
-
patchProperties(state, oldNode, oldProps, properties, xmlns);
|
|
437
|
-
}
|
|
440
|
+
if (properties?.xmlns !== void 0)
|
|
441
|
+
xmlns = properties.xmlns;
|
|
442
|
+
patchProperties(state, oldNode, oldProps, properties, xmlns);
|
|
438
443
|
if (!!properties?.catch && oldProps?.catch !== properties.catch) {
|
|
439
444
|
newVode.node["catch"] = null;
|
|
440
445
|
newVode.node.removeAttribute("catch");
|
|
441
446
|
}
|
|
442
|
-
const
|
|
443
|
-
const
|
|
444
|
-
if (
|
|
445
|
-
const childOffset = !!properties ? 2 : 1;
|
|
447
|
+
const newStart = childrenStart(newVode);
|
|
448
|
+
const oldStart = childrenStart(oldVode);
|
|
449
|
+
if (newStart > 0) {
|
|
446
450
|
let indexP = 0;
|
|
447
|
-
for (let i = 0; i <
|
|
448
|
-
const child2 =
|
|
449
|
-
const oldChild =
|
|
451
|
+
for (let i = 0; i < newVode.length - newStart; i++) {
|
|
452
|
+
const child2 = newVode[i + newStart];
|
|
453
|
+
const oldChild = oldStart > 0 ? oldVode[i + oldStart] : void 0;
|
|
450
454
|
const attached = render(state, oldNode, i, indexP, oldChild, child2, xmlns);
|
|
451
|
-
newVode[i +
|
|
455
|
+
newVode[i + newStart] = attached;
|
|
452
456
|
if (attached) indexP++;
|
|
453
457
|
}
|
|
454
458
|
}
|
|
455
|
-
if (
|
|
456
|
-
const newKidsCount =
|
|
457
|
-
for (let i =
|
|
458
|
-
render(state, oldNode, i, i,
|
|
459
|
+
if (oldStart > 0) {
|
|
460
|
+
const newKidsCount = newStart > 0 ? newVode.length - newStart : 0;
|
|
461
|
+
for (let i = oldVode.length - 1 - oldStart; i >= newKidsCount; i--) {
|
|
462
|
+
render(state, oldNode, i, i, oldVode[i + oldStart], void 0, xmlns);
|
|
459
463
|
}
|
|
460
464
|
}
|
|
461
465
|
newVode._unmountCount = (properties?.onUnmount ? 1 : 0) + sumChildUnmountCounts(newVode);
|
|
@@ -512,6 +516,9 @@ function isTextVode(x) {
|
|
|
512
516
|
return typeof x === "string" || x?.nodeType === Node.TEXT_NODE;
|
|
513
517
|
}
|
|
514
518
|
function remember(state, present, past) {
|
|
519
|
+
while (typeof present === "function" && !present.__memo) {
|
|
520
|
+
present = present(state);
|
|
521
|
+
}
|
|
515
522
|
if (typeof present !== "function")
|
|
516
523
|
return present;
|
|
517
524
|
const presentMemo = present?.__memo;
|
|
@@ -526,37 +533,13 @@ function remember(state, present, past) {
|
|
|
526
533
|
}
|
|
527
534
|
if (same) return past;
|
|
528
535
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
const resultMemo = result.__memo;
|
|
532
|
-
if (Array.isArray(resultMemo) && Array.isArray(pastMemo) && resultMemo.length === pastMemo.length) {
|
|
533
|
-
let same = true;
|
|
534
|
-
for (let i = 0; i < resultMemo.length; i++) {
|
|
535
|
-
if (resultMemo[i] !== pastMemo[i]) {
|
|
536
|
-
same = false;
|
|
537
|
-
break;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
if (same) return past;
|
|
541
|
-
}
|
|
542
|
-
const innerRender = result(state);
|
|
543
|
-
if (typeof innerRender === "object") {
|
|
544
|
-
innerRender.__memo = resultMemo;
|
|
545
|
-
}
|
|
546
|
-
return innerRender;
|
|
547
|
-
}
|
|
548
|
-
const newRender = typeof result === "function" ? unwrap(result, state) : result;
|
|
549
|
-
if (typeof newRender === "object") {
|
|
550
|
-
newRender.__memo = result?.__memo || present?.__memo;
|
|
536
|
+
while (typeof present === "function") {
|
|
537
|
+
present = present(state);
|
|
551
538
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
function unwrap(c, s) {
|
|
555
|
-
if (typeof c === "function") {
|
|
556
|
-
return unwrap(c(s), s);
|
|
557
|
-
} else {
|
|
558
|
-
return c;
|
|
539
|
+
if (typeof present === "object") {
|
|
540
|
+
present.__memo = presentMemo;
|
|
559
541
|
}
|
|
542
|
+
return present;
|
|
560
543
|
}
|
|
561
544
|
function patchProperties(s, node, oldProps, newProps, xmlns) {
|
|
562
545
|
if (!newProps && !oldProps) return;
|
|
@@ -973,10 +956,11 @@ var ProxyStateContextImpl = class _ProxyStateContextImpl {
|
|
|
973
956
|
}
|
|
974
957
|
raw[keys[i]] = value;
|
|
975
958
|
} else if (keys.length === 1) {
|
|
976
|
-
if (typeof target[keys[0]] === "object" && typeof value === "object")
|
|
959
|
+
if (typeof target[keys[0]] === "object" && typeof value === "object" && value !== null) {
|
|
977
960
|
Object.assign(target[keys[0]], value);
|
|
978
|
-
else
|
|
961
|
+
} else {
|
|
979
962
|
target[keys[0]] = value;
|
|
963
|
+
}
|
|
980
964
|
} else {
|
|
981
965
|
Object.assign(target, value);
|
|
982
966
|
}
|