@ryupold/vode 1.8.6 → 1.8.8

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
@@ -20,7 +20,6 @@ function app(container, state, dom, ...initialPatches) {
20
20
  _vode.qSync = null;
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
- _vode.unmounts = [];
24
23
  const patchableState = state;
25
24
  if ("patch" in state && typeof state.patch === "function" && Array.isArray(state.patch.initialPatches)) {
26
25
  initialPatches = [...state.patch.initialPatches, ...initialPatches];
@@ -89,15 +88,15 @@ function app(container, state, dom, ...initialPatches) {
89
88
  }
90
89
  });
91
90
  function renderDom(isAsync) {
92
- const sw = Date.now();
91
+ const sw = performance.now();
93
92
  const vom = dom(_vode.state);
94
- _vode.vode = render(_vode.state, container.parentElement, 0, 0, _vode.vode, vom, null, _vode.unmounts, 0);
93
+ _vode.vode = render(_vode.state, container.parentElement, 0, 0, _vode.vode, vom);
95
94
  if (container.tagName.toUpperCase() !== vom[0].toUpperCase()) {
96
95
  container = _vode.vode.node;
97
96
  container._vode = _vode;
98
97
  }
99
98
  if (!isAsync) {
100
- _vode.stats.lastSyncRenderTime = Date.now() - sw;
99
+ _vode.stats.lastSyncRenderTime = performance.now() - sw;
101
100
  _vode.stats.syncRenderCount++;
102
101
  _vode.isRendering = false;
103
102
  if (_vode.qSync) _vode.renderSync();
@@ -126,14 +125,14 @@ function app(container, state, dom, ...initialPatches) {
126
125
  await globals.currentViewTransition?.updateCallbackDone;
127
126
  if (_vode.isAnimating || !_vode.qAsync || document.hidden) return;
128
127
  _vode.isAnimating = true;
129
- const sw = Date.now();
128
+ const sw = performance.now();
130
129
  try {
131
130
  _vode.state = mergeState(_vode.state, _vode.qAsync, true);
132
131
  _vode.qAsync = null;
133
132
  globals.currentViewTransition = _vode.asyncRenderer(ar);
134
133
  await globals.currentViewTransition?.updateCallbackDone;
135
134
  } finally {
136
- _vode.stats.lastAsyncRenderTime = Date.now() - sw;
135
+ _vode.stats.lastAsyncRenderTime = performance.now() - sw;
137
136
  _vode.stats.asyncRenderCount++;
138
137
  _vode.isAnimating = false;
139
138
  }
@@ -151,10 +150,7 @@ function app(container, state, dom, ...initialPatches) {
151
150
  indexInParent,
152
151
  indexInParent,
153
152
  hydrate(container, true),
154
- dom(state),
155
- null,
156
- _vode.unmounts,
157
- 0
153
+ dom(state)
158
154
  );
159
155
  _vode.isRendering = false;
160
156
  if (_vode.qSync) _vode.renderSync();
@@ -235,11 +231,15 @@ function hydrate(element, prepareForRender) {
235
231
  return void 0;
236
232
  }
237
233
  }
238
- function memo(compare, componentOrProps) {
234
+ function memo(compare, component) {
239
235
  if (!compare || !Array.isArray(compare)) throw new Error("first argument to memo() must be an array of values to compare");
240
- if (typeof componentOrProps !== "function") throw new Error("second argument to memo() must be a function that returns a vode or props object");
241
- componentOrProps.__memo = compare;
242
- return componentOrProps;
236
+ if (typeof component !== "function") throw new Error("second argument to memo() must be a function that returns a child vode");
237
+ if (component.__memo) {
238
+ const comp = component;
239
+ component = (s) => comp(s);
240
+ }
241
+ component.__memo = compare;
242
+ return component;
243
243
  }
244
244
  function createState(state) {
245
245
  if (!state || typeof state !== "object") throw new Error("createState() must be called with a state object");
@@ -324,7 +324,7 @@ function mergeState(target, source, allowDeletion) {
324
324
  }
325
325
  return target;
326
326
  }
327
- function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmlns, unmounts, unmountStart) {
327
+ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmlns) {
328
328
  try {
329
329
  newVode = remember(state, newVode, oldVode);
330
330
  const isNoVode = !newVode || typeof newVode === "number" || typeof newVode === "boolean";
@@ -334,17 +334,7 @@ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmln
334
334
  const oldIsText = oldVode?.nodeType === Node.TEXT_NODE;
335
335
  const oldNode = oldIsText ? oldVode : oldVode?.node;
336
336
  if (isNoVode) {
337
- if (!oldIsText && typeof oldVode?.unmountCount === "number") {
338
- const start = oldVode.unmountStart;
339
- const count = oldVode.unmountCount;
340
- for (let i = count - 1; i >= 0; i--) {
341
- const fn = unmounts[start + i];
342
- if (fn) {
343
- state.patch(fn(state, oldNode));
344
- unmounts[start + i] = null;
345
- }
346
- }
347
- }
337
+ unmountTree(state, oldVode);
348
338
  oldNode?.remove();
349
339
  return void 0;
350
340
  }
@@ -367,24 +357,14 @@ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmln
367
357
  if (isText && (!oldNode || !oldIsText)) {
368
358
  const text = document.createTextNode(newVode);
369
359
  if (oldNode) {
370
- if (!oldIsText && typeof oldVode?.unmountCount === "number") {
371
- const start = oldVode.unmountStart;
372
- const count = oldVode.unmountCount;
373
- for (let i = count - 1; i >= 0; i--) {
374
- const fn = unmounts[start + i];
375
- if (fn) {
376
- state.patch(fn(state, oldNode));
377
- unmounts[start + i] = null;
378
- }
379
- }
380
- }
360
+ unmountTree(state, oldVode);
381
361
  oldNode.replaceWith(text);
382
362
  } else {
383
363
  let inserted = false;
384
364
  for (let i = indexInParent; i < parent.childNodes.length; i++) {
385
365
  const nextSibling = parent.childNodes[i];
386
366
  if (nextSibling) {
387
- nextSibling.before(text, nextSibling);
367
+ nextSibling.before(text);
388
368
  inserted = true;
389
369
  break;
390
370
  }
@@ -410,26 +390,14 @@ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmln
410
390
  newVode.node.removeAttribute("catch");
411
391
  }
412
392
  if (oldNode) {
413
- if (!oldIsText && typeof oldVode?.unmountCount === "number") {
414
- const start = oldVode.unmountStart;
415
- const count = oldVode.unmountCount;
416
- for (let i = count - 1; i >= 0; i--) {
417
- const fn = unmounts[start + i];
418
- if (fn) {
419
- state.patch(fn(state, oldNode));
420
- unmounts[start + i] = null;
421
- }
422
- }
423
- }
424
- unmounts[unmountStart] = properties?.onUnmount ?? null;
393
+ unmountTree(state, oldVode);
425
394
  oldNode.replaceWith(newNode);
426
395
  } else {
427
- unmounts[unmountStart] = properties?.onUnmount ?? null;
428
396
  let inserted = false;
429
397
  for (let i = indexInParent; i < parent.childNodes.length; i++) {
430
398
  const nextSibling = parent.childNodes[i];
431
399
  if (nextSibling) {
432
- nextSibling.before(newNode, nextSibling);
400
+ nextSibling.before(newNode);
433
401
  inserted = true;
434
402
  break;
435
403
  }
@@ -438,78 +406,53 @@ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmln
438
406
  parent.appendChild(newNode);
439
407
  }
440
408
  }
441
- let totalChildUnmounts = 0;
442
- let childUnmountStart = unmountStart + 1;
443
- const newKids = children(newVode);
444
- if (newKids) {
409
+ const newStart = childrenStart(newVode);
410
+ if (newStart > 0) {
445
411
  const childOffset = !!properties ? 2 : 1;
446
412
  let indexP = 0;
447
- for (let i = 0; i < newKids.length; i++) {
448
- const child2 = newKids[i];
449
- const attached = render(state, newNode, i, indexP, void 0, child2, xmlns ?? null, unmounts, childUnmountStart);
413
+ for (let i = 0; i < newVode.length - newStart; i++) {
414
+ const child2 = newVode[i + newStart];
415
+ const attached = render(state, newNode, i, indexP, void 0, child2, xmlns ?? null);
450
416
  newVode[i + childOffset] = attached;
451
- if (attached) {
452
- indexP++;
453
- const childUnmounts = attached.unmountCount || 0;
454
- totalChildUnmounts += childUnmounts;
455
- childUnmountStart += childUnmounts;
456
- }
417
+ if (attached) indexP++;
457
418
  }
458
419
  }
459
- newNode.onMount && state.patch(newNode.onMount(newNode));
460
- newVode.unmountCount = 1 + totalChildUnmounts;
461
- newVode.unmountStart = unmountStart;
420
+ newVode._unmountCount = (properties?.onUnmount ? 1 : 0) + sumChildUnmountCounts(newVode);
421
+ if (typeof properties?.onMount === "function") {
422
+ state.patch(properties.onMount(state, newNode));
423
+ }
462
424
  return newVode;
463
425
  }
464
426
  if (!oldIsText && isNode && oldVode[0] === newVode[0]) {
465
427
  newVode.node = oldNode;
466
- const newvode = newVode;
467
- const oldvode = oldVode;
468
428
  const properties = props(newVode);
469
429
  const oldProps = props(oldVode);
470
- if (properties?.xmlns !== void 0) xmlns = properties.xmlns;
471
- if (newvode[1]?.__memo) {
472
- const prev = newvode[1];
473
- newvode[1] = remember(state, newvode[1], oldvode[1]);
474
- if (prev !== newvode[1]) {
475
- patchProperties(state, oldNode, oldProps, properties, xmlns);
476
- }
477
- } else {
478
- patchProperties(state, oldNode, oldProps, properties, xmlns);
479
- }
430
+ if (properties?.xmlns !== void 0)
431
+ xmlns = properties.xmlns;
432
+ patchProperties(state, oldNode, oldProps, properties, xmlns);
480
433
  if (!!properties?.catch && oldProps?.catch !== properties.catch) {
481
434
  newVode.node["catch"] = null;
482
435
  newVode.node.removeAttribute("catch");
483
436
  }
484
- unmounts[unmountStart] = properties?.onUnmount ?? null;
485
- let totalChildUnmounts = 0;
486
- let childUnmountStart = unmountStart + 1;
487
- const newKids = children(newVode);
488
- const oldKids = children(oldVode);
489
- if (newKids) {
490
- const childOffset = !!properties ? 2 : 1;
437
+ const newStart = childrenStart(newVode);
438
+ const oldStart = childrenStart(oldVode);
439
+ if (newStart > 0) {
491
440
  let indexP = 0;
492
- for (let i = 0; i < newKids.length; i++) {
493
- const child2 = newKids[i];
494
- const oldChild = oldKids && oldKids[i];
495
- const attached = render(state, oldNode, i, indexP, oldChild, child2, xmlns, unmounts, childUnmountStart);
496
- newVode[i + childOffset] = attached;
497
- if (attached) {
498
- indexP++;
499
- const childUnmounts = attached.unmountCount || 0;
500
- totalChildUnmounts += childUnmounts;
501
- childUnmountStart += childUnmounts;
502
- }
441
+ for (let i = 0; i < newVode.length - newStart; i++) {
442
+ const child2 = newVode[i + newStart];
443
+ const oldChild = oldStart > 0 ? oldVode[i + oldStart] : void 0;
444
+ const attached = render(state, oldNode, i, indexP, oldChild, child2, xmlns);
445
+ newVode[i + newStart] = attached;
446
+ if (attached) indexP++;
503
447
  }
504
448
  }
505
- if (oldKids) {
506
- const newKidsCount = newKids ? newKids.length : 0;
507
- for (let i = oldKids.length - 1; i >= newKidsCount; i--) {
508
- render(state, oldNode, i, i, oldKids[i], void 0, xmlns, unmounts, oldKids[i].unmountStart);
449
+ if (oldStart > 0) {
450
+ const newKidsCount = newStart > 0 ? newVode.length - newStart : 0;
451
+ for (let i = oldVode.length - 1 - oldStart; i >= newKidsCount; i--) {
452
+ render(state, oldNode, i, i, oldVode[i + oldStart], void 0, xmlns);
509
453
  }
510
454
  }
511
- newVode.unmountCount = 1 + totalChildUnmounts;
512
- newVode.unmountStart = unmountStart;
455
+ newVode._unmountCount = (properties?.onUnmount ? 1 : 0) + sumChildUnmountCounts(newVode);
513
456
  return newVode;
514
457
  }
515
458
  } catch (error) {
@@ -523,9 +466,7 @@ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmln
523
466
  indexInParent,
524
467
  hydrate(newVode?.node || oldVode?.node, true),
525
468
  handledVode,
526
- xmlns,
527
- unmounts,
528
- unmountStart
469
+ xmlns
529
470
  );
530
471
  } else {
531
472
  throw error;
@@ -533,6 +474,31 @@ function render(state, parent, childIndex, indexInParent, oldVode, newVode, xmln
533
474
  }
534
475
  return void 0;
535
476
  }
477
+ function unmountTree(state, v) {
478
+ if (!v || !Array.isArray(v)) return;
479
+ if ((v._unmountCount | 0) === 0) return;
480
+ const kids = children(v);
481
+ if (kids) {
482
+ for (let i = kids.length - 1; i >= 0; i--) {
483
+ unmountTree(state, kids[i]);
484
+ }
485
+ }
486
+ const p = props(v);
487
+ if (typeof p?.onUnmount === "function") {
488
+ state.patch(p.onUnmount(state, v.node));
489
+ }
490
+ }
491
+ function sumChildUnmountCounts(v) {
492
+ const kids = children(v);
493
+ if (!kids) return 0;
494
+ let n = 0;
495
+ for (const k of kids) {
496
+ if (k && Array.isArray(k)) {
497
+ n += k._unmountCount | 0;
498
+ }
499
+ }
500
+ return n;
501
+ }
536
502
  function isNaturalVode(x) {
537
503
  return Array.isArray(x) && x.length > 0 && typeof x[0] === "string";
538
504
  }
@@ -540,6 +506,9 @@ function isTextVode(x) {
540
506
  return typeof x === "string" || x?.nodeType === Node.TEXT_NODE;
541
507
  }
542
508
  function remember(state, present, past) {
509
+ while (typeof present === "function" && !present.__memo) {
510
+ present = present(state);
511
+ }
543
512
  if (typeof present !== "function")
544
513
  return present;
545
514
  const presentMemo = present?.__memo;
@@ -554,37 +523,13 @@ function remember(state, present, past) {
554
523
  }
555
524
  if (same) return past;
556
525
  }
557
- const result = present(state);
558
- if (typeof result === "function" && result?.__memo) {
559
- const resultMemo = result.__memo;
560
- if (Array.isArray(resultMemo) && Array.isArray(pastMemo) && resultMemo.length === pastMemo.length) {
561
- let same = true;
562
- for (let i = 0; i < resultMemo.length; i++) {
563
- if (resultMemo[i] !== pastMemo[i]) {
564
- same = false;
565
- break;
566
- }
567
- }
568
- if (same) return past;
569
- }
570
- const innerRender = result(state);
571
- if (typeof innerRender === "object") {
572
- innerRender.__memo = resultMemo;
573
- }
574
- return innerRender;
526
+ while (typeof present === "function") {
527
+ present = present(state);
575
528
  }
576
- const newRender = typeof result === "function" ? unwrap(result, state) : result;
577
- if (typeof newRender === "object") {
578
- newRender.__memo = result?.__memo || present?.__memo;
579
- }
580
- return newRender;
581
- }
582
- function unwrap(c, s) {
583
- if (typeof c === "function") {
584
- return unwrap(c(s), s);
585
- } else {
586
- return c;
529
+ if (typeof present === "object") {
530
+ present.__memo = presentMemo;
587
531
  }
532
+ return present;
588
533
  }
589
534
  function patchProperties(s, node, oldProps, newProps, xmlns) {
590
535
  if (!newProps && !oldProps) return;
@@ -1001,10 +946,11 @@ var ProxyStateContextImpl = class _ProxyStateContextImpl {
1001
946
  }
1002
947
  raw[keys[i]] = value;
1003
948
  } else if (keys.length === 1) {
1004
- if (typeof target[keys[0]] === "object" && typeof value === "object")
949
+ if (typeof target[keys[0]] === "object" && typeof value === "object" && value !== null) {
1005
950
  Object.assign(target[keys[0]], value);
1006
- else
951
+ } else {
1007
952
  target[keys[0]] = value;
953
+ }
1008
954
  } else {
1009
955
  Object.assign(target, value);
1010
956
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryupold/vode",
3
- "version": "1.8.6",
3
+ "version": "1.8.8",
4
4
  "description": "a minimalist web framework",
5
5
  "author": "Michael Scherbakow (ryupold)",
6
6
  "license": "MIT",
@@ -41,13 +41,13 @@ export interface SubContext<SubState> {
41
41
  }
42
42
 
43
43
  export type ProxyStateContext<S extends PatchableState, SubState> = StateContext<S, SubState> & {
44
- [K in keyof SubState]-?: SubState[K] extends object
44
+ [K in keyof SubState]-?: SubState[K] extends object | null
45
45
  ? ProxyStateContext<S, SubState[K]>
46
46
  : StateContext<S, SubState[K]>
47
47
  };
48
48
 
49
49
  export type ProxySubContext<SubState> = SubContext<SubState> & {
50
- [K in keyof SubState]-?: SubState[K] extends object
50
+ [K in keyof SubState]-?: SubState[K] extends object | null
51
51
  ? ProxySubContext<SubState[K]>
52
52
  : SubContext<SubState[K]>
53
53
  };
@@ -107,10 +107,12 @@ class ProxyStateContextImpl<S extends PatchableState, SubState>
107
107
  }
108
108
  raw[keys[i]] = value;
109
109
  } else if (keys.length === 1) {
110
- if (typeof (<any>target)[keys[0]] === "object" && typeof value === "object")
110
+ if (typeof (<any>target)[keys[0]] === "object" && typeof value === "object" && value !== null) {
111
111
  Object.assign((<any>target)[keys[0]], value);
112
- else
112
+ }
113
+ else {
113
114
  (<any>target)[keys[0]] = value;
115
+ }
114
116
  } else {
115
117
  Object.assign(target, value as DeepPartial<S>);
116
118
  }