@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/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.qSync = null;
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: async (action, isAsync) => {
32
- if (!action || typeof action !== "function" && typeof action !== "object") return;
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
- const generator = action;
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
- _vode.stats.liveEffectCount++;
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
- _vode.qSync = mergeState(_vode.qSync || {}, _vode.qAsync, false);
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 (isAsync) {
85
+ if (isAnimated) {
79
86
  _vode.stats.asyncRenderPatchCount++;
80
87
  _vode.qAsync = mergeState(_vode.qAsync || {}, action, false);
81
- await _vode.renderAsync();
88
+ _vode.renderAsync();
82
89
  } else {
83
90
  _vode.stats.syncRenderPatchCount++;
84
- _vode.qSync = mergeState(_vode.qSync || {}, action, false);
91
+ mergeState(_vode.state, action, true);
85
92
  _vode.renderSync();
86
93
  }
87
94
  }
88
95
  }
89
96
  });
90
- function renderDom(isAsync) {
91
- const sw = Date.now();
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 (!isAsync) {
99
- _vode.stats.lastSyncRenderTime = Date.now() - sw;
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 = false;
102
- if (_vode.qSync) _vode.renderSync();
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 || !_vode.qSync) return;
113
- _vode.isRendering = true;
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 = Date.now();
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 = Date.now() - sw;
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
- _vode.isRendering = true;
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.isRendering = false;
156
- if (_vode.qSync) _vode.renderSync();
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, componentOrProps) {
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 componentOrProps !== "function") throw new Error("second argument to memo() must be a function that returns a vode or props object");
237
- componentOrProps.__memo = compare;
238
- return componentOrProps;
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, nextSibling);
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, nextSibling);
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 newKids = children(newVode);
406
- if (newKids) {
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 < newKids.length; i++) {
410
- const child2 = newKids[i];
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) xmlns = properties.xmlns;
429
- if (newvode[1]?.__memo) {
430
- const prev = newvode[1];
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 newKids = children(newVode);
443
- const oldKids = children(oldVode);
444
- if (newKids) {
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 < newKids.length; i++) {
448
- const child2 = newKids[i];
449
- const oldChild = oldKids && oldKids[i];
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 + childOffset] = attached;
455
+ newVode[i + newStart] = attached;
452
456
  if (attached) indexP++;
453
457
  }
454
458
  }
455
- if (oldKids) {
456
- const newKidsCount = newKids ? newKids.length : 0;
457
- for (let i = oldKids.length - 1; i >= newKidsCount; i--) {
458
- render(state, oldNode, i, i, oldKids[i], void 0, xmlns);
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
- const result = present(state);
530
- if (typeof result === "function" && result?.__memo) {
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
- return newRender;
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
  }