tutuca 0.9.2 → 0.9.4

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) Mariano Guerra
3
+ Copyright (c) 2025 Mariano Guerra
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -2389,7 +2389,7 @@ class App {
2389
2389
  this.render();
2390
2390
  }
2391
2391
  });
2392
- injectCss("tutuca-app", this.comps.compileStyles());
2392
+ injectCss("tutuca-app", this.comps.compileStyles(), opts?.head ?? document.head);
2393
2393
  if (opts?.noCache) {
2394
2394
  this.renderer.setNullCache();
2395
2395
  this.comps.setNullComputedCache();
@@ -2434,15 +2434,15 @@ class App {
2434
2434
  this._evictCacheId = null;
2435
2435
  }
2436
2436
  }
2437
- function injectCss(nodeId, style) {
2437
+ function injectCss(nodeId, style, styleTarget = document.head) {
2438
2438
  const styleNode = document.createElement("style");
2439
- const currentNodeWithId = document.head.querySelector(`#${nodeId}`);
2439
+ const currentNodeWithId = styleTarget.querySelector(`#${nodeId}`);
2440
2440
  if (currentNodeWithId) {
2441
- document.head.removeChild(currentNodeWithId);
2441
+ styleTarget.removeChild(currentNodeWithId);
2442
2442
  }
2443
2443
  styleNode.id = nodeId;
2444
2444
  styleNode.innerHTML = style;
2445
- document.head.appendChild(styleNode);
2445
+ styleTarget.appendChild(styleNode);
2446
2446
  }
2447
2447
  function getClosestDropTarget(target, rootNode, count) {
2448
2448
  let node = target;
@@ -7445,16 +7445,10 @@ function* klistEntries(seq) {
7445
7445
  }
7446
7446
  }
7447
7447
  seqInfoByClass.set(KList, ["data-sk", klistEntries]);
7448
- // deps/vdom.js
7448
+ // src/vdom.js
7449
7449
  function isHtmlAttribute(propName) {
7450
7450
  return propName[4] === "-" && (propName[0] === "d" || propName[0] === "a");
7451
7451
  }
7452
- function getDomProp(node, propName) {
7453
- return node[propName];
7454
- }
7455
- function setDomProp(node, propName, value) {
7456
- node[propName] = value;
7457
- }
7458
7452
  function applyProperties(node, props, previous) {
7459
7453
  for (const propName in props) {
7460
7454
  const propValue = props[propName];
@@ -7462,37 +7456,42 @@ function applyProperties(node, props, previous) {
7462
7456
  removeProperty(node, propName, previous);
7463
7457
  } else if (isHtmlAttribute(propName)) {
7464
7458
  node.setAttribute(propName, propValue);
7459
+ } else if (propName === "dangerouslySetInnerHTML") {
7460
+ node.innerHTML = propValue.__html ?? "";
7461
+ } else if (typeof propValue === "object" && propValue !== null) {
7462
+ patchObject(node, previous, propName, propValue);
7463
+ } else if (propName === "className") {
7464
+ node.setAttribute("class", propValue);
7465
7465
  } else {
7466
- if (typeof propValue === "object" && propValue !== null) {
7467
- patchObject(node, previous, propName, propValue);
7468
- } else {
7469
- setDomProp(node, propName, propValue);
7470
- }
7466
+ node[propName] = propValue;
7471
7467
  }
7472
7468
  }
7473
7469
  }
7474
7470
  function removeProperty(node, propName, previous) {
7475
7471
  const previousValue = previous[propName];
7476
- if (isHtmlAttribute(propName)) {
7472
+ if (propName === "dangerouslySetInnerHTML") {
7473
+ node.innerHTML = "";
7474
+ } else if (isHtmlAttribute(propName)) {
7477
7475
  node.removeAttribute(propName);
7478
7476
  } else if (typeof previousValue === "string") {
7479
- setDomProp(node, propName, "");
7477
+ if (propName !== "className")
7478
+ node[propName] = "";
7480
7479
  const attrName = propName === "className" ? "class" : propName === "htmlFor" ? "for" : propName;
7481
7480
  node.removeAttribute(attrName);
7482
7481
  } else {
7483
- setDomProp(node, propName, null);
7482
+ node[propName] = null;
7484
7483
  }
7485
7484
  }
7486
7485
  function patchObject(node, previous, propName, propValue) {
7487
7486
  const previousValue = previous?.[propName];
7488
7487
  if (previousValue && typeof previousValue === "object" && Object.getPrototypeOf(previousValue) !== Object.getPrototypeOf(propValue)) {
7489
- setDomProp(node, propName, propValue);
7488
+ node[propName] = propValue;
7490
7489
  return;
7491
7490
  }
7492
- let current = getDomProp(node, propName);
7491
+ let current = node[propName];
7493
7492
  if (typeof current !== "object" || current === null) {
7494
- setDomProp(node, propName, {});
7495
- current = getDomProp(node, propName);
7493
+ node[propName] = {};
7494
+ current = node[propName];
7496
7495
  }
7497
7496
  const target = current;
7498
7497
  for (const k in propValue) {
@@ -7500,28 +7499,6 @@ function patchObject(node, previous, propName, propValue) {
7500
7499
  }
7501
7500
  }
7502
7501
 
7503
- class Warning {
7504
- type;
7505
- message;
7506
- constructor(type, message) {
7507
- this.type = type;
7508
- this.message = message;
7509
- }
7510
- }
7511
-
7512
- class DuplicatedKeysWarning extends Warning {
7513
- duplicatedKeys;
7514
- parentTag;
7515
- parentIndex;
7516
- constructor(duplicatedKeys, parentTag, parentIndex) {
7517
- const keys = [...duplicatedKeys].join(", ");
7518
- super("DuplicatedKeys", `Duplicate keys found: [${keys}] in ${parentTag || "fragment"} at index ${parentIndex}. Nodes with duplicated keys are matched positionally.`);
7519
- this.duplicatedKeys = duplicatedKeys;
7520
- this.parentTag = parentTag;
7521
- this.parentIndex = parentIndex;
7522
- }
7523
- }
7524
-
7525
7502
  class VBase {
7526
7503
  isEqualTo(other) {
7527
7504
  return this === other;
@@ -7530,12 +7507,8 @@ class VBase {
7530
7507
  return null;
7531
7508
  }
7532
7509
  }
7533
- function getKey(node) {
7534
- return node instanceof VNode2 ? node.key : undefined;
7535
- }
7536
- function effectiveKey(node, duplicatedKeys) {
7537
- const key = getKey(node);
7538
- return key && duplicatedKeys?.has(key) ? undefined : key;
7510
+ function getKey(child) {
7511
+ return child instanceof VNode2 ? child.key : undefined;
7539
7512
  }
7540
7513
  function isIterable(obj) {
7541
7514
  return obj != null && typeof obj !== "string" && typeof obj[Symbol.iterator] === "function";
@@ -7560,7 +7533,6 @@ function addChild(normalizedChildren, child) {
7560
7533
  }
7561
7534
 
7562
7535
  class VText extends VBase {
7563
- text;
7564
7536
  constructor(text) {
7565
7537
  super();
7566
7538
  this.text = String(text);
@@ -7577,7 +7549,6 @@ class VText extends VBase {
7577
7549
  }
7578
7550
 
7579
7551
  class VComment extends VBase {
7580
- text;
7581
7552
  constructor(text) {
7582
7553
  super();
7583
7554
  this.text = text;
@@ -7594,7 +7565,6 @@ class VComment extends VBase {
7594
7565
  }
7595
7566
 
7596
7567
  class VFragment extends VBase {
7597
- childs;
7598
7568
  constructor(childs) {
7599
7569
  super();
7600
7570
  const normalized = [];
@@ -7628,12 +7598,6 @@ class VFragment extends VBase {
7628
7598
  }
7629
7599
 
7630
7600
  class VNode2 extends VBase {
7631
- tag;
7632
- attrs;
7633
- childs;
7634
- key;
7635
- namespace;
7636
- attrCount;
7637
7601
  constructor(tag, attrs, childs, key, namespace) {
7638
7602
  super();
7639
7603
  this.tag = tag;
@@ -7641,13 +7605,12 @@ class VNode2 extends VBase {
7641
7605
  this.childs = childs ?? [];
7642
7606
  this.key = key != null ? String(key) : undefined;
7643
7607
  this.namespace = typeof namespace === "string" ? namespace : null;
7644
- this.attrCount = Object.keys(this.attrs).length;
7645
7608
  }
7646
7609
  get nodeType() {
7647
7610
  return 1;
7648
7611
  }
7649
7612
  isEqualTo(other) {
7650
- if (!(other instanceof VNode2) || this.tag !== other.tag || this.key !== other.key || this.namespace !== other.namespace || this.attrCount !== other.attrCount || this.childs.length !== other.childs.length) {
7613
+ if (!(other instanceof VNode2) || this.tag !== other.tag || this.key !== other.key || this.namespace !== other.namespace || this.childs.length !== other.childs.length) {
7651
7614
  return false;
7652
7615
  }
7653
7616
  for (const key in this.attrs) {
@@ -7655,6 +7618,11 @@ class VNode2 extends VBase {
7655
7618
  return false;
7656
7619
  }
7657
7620
  }
7621
+ for (const key in other.attrs) {
7622
+ if (!Object.hasOwn(this.attrs, key)) {
7623
+ return false;
7624
+ }
7625
+ }
7658
7626
  for (let i = 0;i < this.childs.length; i++) {
7659
7627
  if (!this.childs[i].isEqualTo(other.childs[i])) {
7660
7628
  return false;
@@ -7709,155 +7677,6 @@ function diffProps(a, b) {
7709
7677
  }
7710
7678
  return diff;
7711
7679
  }
7712
- function reorder(oldChildren, newChildren) {
7713
- const rawNew = keyIndex(newChildren);
7714
- if (rawNew.free.length === newChildren.length) {
7715
- return {
7716
- children: newChildren,
7717
- moves: null,
7718
- duplicatedKeys: rawNew.duplicatedKeys
7719
- };
7720
- }
7721
- const rawOld = keyIndex(oldChildren);
7722
- const duplicatedKeys = rawNew.duplicatedKeys || rawOld.duplicatedKeys ? new Set([...rawNew.duplicatedKeys || [], ...rawOld.duplicatedKeys || []]) : null;
7723
- if (rawOld.free.length === oldChildren.length) {
7724
- return {
7725
- children: newChildren,
7726
- moves: null,
7727
- duplicatedKeys
7728
- };
7729
- }
7730
- let newKeys;
7731
- let newFree;
7732
- let oldKeys;
7733
- if (duplicatedKeys) {
7734
- const updatedNew = keyIndex(newChildren, duplicatedKeys);
7735
- newKeys = updatedNew.keys;
7736
- newFree = updatedNew.free;
7737
- oldKeys = keyIndex(oldChildren, duplicatedKeys).keys;
7738
- } else {
7739
- newKeys = rawNew.keys;
7740
- newFree = rawNew.free;
7741
- oldKeys = rawOld.keys;
7742
- }
7743
- const reordered = [];
7744
- let freeIndex = 0;
7745
- const freeCount = newFree.length;
7746
- let deletedItems = 0;
7747
- for (let i = 0;i < oldChildren.length; i++) {
7748
- const oldItem = oldChildren[i];
7749
- const oldKey = effectiveKey(oldItem, duplicatedKeys);
7750
- if (oldKey) {
7751
- if (Object.hasOwn(newKeys, oldKey)) {
7752
- const itemIndex = newKeys[oldKey];
7753
- reordered.push(newChildren[itemIndex]);
7754
- } else {
7755
- deletedItems++;
7756
- reordered.push(null);
7757
- }
7758
- } else {
7759
- if (freeIndex < freeCount) {
7760
- const itemIndex = newFree[freeIndex++];
7761
- reordered.push(newChildren[itemIndex]);
7762
- } else {
7763
- deletedItems++;
7764
- reordered.push(null);
7765
- }
7766
- }
7767
- }
7768
- const lastFreeIndex = freeIndex >= newFree.length ? newChildren.length : newFree[freeIndex];
7769
- for (let j = 0;j < newChildren.length; j++) {
7770
- const newItem = newChildren[j];
7771
- const newKey = effectiveKey(newItem, duplicatedKeys);
7772
- if (newKey) {
7773
- if (!Object.hasOwn(oldKeys, newKey)) {
7774
- reordered.push(newItem);
7775
- }
7776
- } else if (j >= lastFreeIndex) {
7777
- reordered.push(newItem);
7778
- }
7779
- }
7780
- const moves = computeMoves(reordered, newChildren, newKeys, duplicatedKeys, deletedItems);
7781
- return { children: reordered, moves, duplicatedKeys };
7782
- }
7783
- function computeMoves(reordered, newChildren, newKeys, duplicatedKeys, deletedItems) {
7784
- const simulate = reordered.slice();
7785
- let simulateIndex = 0;
7786
- const removes = [];
7787
- const inserts = [];
7788
- const wantedKeys = new Array(newChildren.length);
7789
- for (let i = 0;i < newChildren.length; i++) {
7790
- wantedKeys[i] = effectiveKey(newChildren[i], duplicatedKeys);
7791
- }
7792
- for (let k = 0;k < newChildren.length; ) {
7793
- const wantedKey = wantedKeys[k];
7794
- let simulateItem = simulate[simulateIndex];
7795
- let simulateKey = effectiveKey(simulateItem, duplicatedKeys);
7796
- while (simulateItem === null && simulate.length) {
7797
- simulate.splice(simulateIndex, 1);
7798
- removes.push({ from: simulateIndex, key: null });
7799
- simulateItem = simulate[simulateIndex];
7800
- simulateKey = effectiveKey(simulateItem, duplicatedKeys);
7801
- }
7802
- if (simulateItem && simulateKey === wantedKey) {
7803
- simulateIndex++;
7804
- k++;
7805
- continue;
7806
- }
7807
- if (wantedKey) {
7808
- if (simulateKey && newKeys[simulateKey] !== k + 1) {
7809
- simulate.splice(simulateIndex, 1);
7810
- removes.push({ from: simulateIndex, key: simulateKey });
7811
- simulateItem = simulate[simulateIndex];
7812
- simulateKey = effectiveKey(simulateItem, duplicatedKeys);
7813
- if (simulateItem && simulateKey === wantedKey) {
7814
- simulateIndex++;
7815
- k++;
7816
- continue;
7817
- }
7818
- }
7819
- inserts.push({ key: wantedKey, to: k });
7820
- k++;
7821
- continue;
7822
- }
7823
- if (simulateKey) {
7824
- simulate.splice(simulateIndex, 1);
7825
- removes.push({ from: simulateIndex, key: simulateKey });
7826
- continue;
7827
- }
7828
- k++;
7829
- }
7830
- while (simulateIndex < simulate.length) {
7831
- const simulateItem = simulate[simulateIndex];
7832
- simulate.splice(simulateIndex, 1);
7833
- removes.push({
7834
- from: simulateIndex,
7835
- key: effectiveKey(simulateItem, duplicatedKeys)
7836
- });
7837
- }
7838
- if (removes.length === deletedItems && !inserts.length) {
7839
- return null;
7840
- }
7841
- return { removes, inserts };
7842
- }
7843
- function keyIndex(children, excludeKeys) {
7844
- const keys = {};
7845
- const free = [];
7846
- let duplicatedKeys = null;
7847
- for (let i = 0;i < children.length; i++) {
7848
- const key = getKey(children[i]);
7849
- if (key && !excludeKeys?.has(key)) {
7850
- if (key in keys) {
7851
- duplicatedKeys ??= new Set;
7852
- duplicatedKeys.add(key);
7853
- }
7854
- keys[key] = i;
7855
- } else {
7856
- free.push(i);
7857
- }
7858
- }
7859
- return { keys, free, duplicatedKeys };
7860
- }
7861
7680
  function replaceNode(domNode, vnode, options) {
7862
7681
  const parentNode = domNode.parentNode;
7863
7682
  const newNode = vnode.toDom(options);
@@ -7878,7 +7697,9 @@ function morphNode(domNode, source, target, opts) {
7878
7697
  if (propsDiff) {
7879
7698
  applyProperties(domNode, propsDiff, source.attrs);
7880
7699
  }
7881
- morphChildren(domNode, source.childs, target.childs, source.tag, opts);
7700
+ if (!target.attrs.dangerouslySetInnerHTML) {
7701
+ morphChildren(domNode, source.childs, target.childs, source.tag, opts);
7702
+ }
7882
7703
  return domNode;
7883
7704
  }
7884
7705
  if (source instanceof VFragment && target instanceof VFragment) {
@@ -7887,7 +7708,7 @@ function morphNode(domNode, source, target, opts) {
7887
7708
  }
7888
7709
  return replaceNode(domNode, target, opts);
7889
7710
  }
7890
- function morphChildren(parentDom, oldChilds, newChilds, parentTag, opts) {
7711
+ function morphChildren(parentDom, oldChilds, newChilds, _parentTag, opts) {
7891
7712
  if (oldChilds.length === 0) {
7892
7713
  for (const child of newChilds) {
7893
7714
  const node = child.toDom(opts);
@@ -7902,58 +7723,50 @@ function morphChildren(parentDom, oldChilds, newChilds, parentTag, opts) {
7902
7723
  }
7903
7724
  return;
7904
7725
  }
7905
- const orderedSet = reorder(oldChilds, newChilds);
7906
- const reorderedChilds = orderedSet.children;
7907
- if (orderedSet.duplicatedKeys && opts.onWarning) {
7908
- opts.onWarning(new DuplicatedKeysWarning(orderedSet.duplicatedKeys, parentTag, 0));
7909
- }
7910
- const domChildren = Array.from(parentDom.childNodes);
7911
- const oldLen = oldChilds.length;
7912
- const reorderedLen = reorderedChilds.length;
7913
- const len = Math.max(oldLen, reorderedLen);
7914
- const toRemove = [];
7915
- for (let i = 0;i < len; i++) {
7916
- const leftNode = oldChilds[i];
7917
- const rightNode = reorderedChilds[i];
7918
- if (!leftNode && rightNode) {
7919
- const newNode = rightNode.toDom(opts);
7920
- if (newNode)
7921
- parentDom.appendChild(newNode);
7922
- } else if (leftNode && rightNode) {
7923
- const domChild = domChildren[i];
7924
- if (domChild) {
7925
- morphNode(domChild, leftNode, rightNode, opts);
7726
+ const domNodes = Array.from(parentDom.childNodes);
7727
+ const oldKeyMap = {};
7728
+ for (let i = 0;i < oldChilds.length; i++) {
7729
+ const key = getKey(oldChilds[i]);
7730
+ if (key != null)
7731
+ oldKeyMap[key] = i;
7732
+ }
7733
+ const used = new Uint8Array(oldChilds.length);
7734
+ let unkeyedCursor = 0;
7735
+ for (let j = 0;j < newChilds.length; j++) {
7736
+ const newChild = newChilds[j];
7737
+ const newKey = getKey(newChild);
7738
+ let oldIdx = -1;
7739
+ if (newKey != null) {
7740
+ if (newKey in oldKeyMap && !used[oldKeyMap[newKey]]) {
7741
+ oldIdx = oldKeyMap[newKey];
7926
7742
  }
7927
- } else if (leftNode && !rightNode) {
7928
- if (!orderedSet.moves && domChildren[i]) {
7929
- toRemove.push(domChildren[i]);
7743
+ } else {
7744
+ while (unkeyedCursor < oldChilds.length) {
7745
+ if (!used[unkeyedCursor] && getKey(oldChilds[unkeyedCursor]) == null) {
7746
+ oldIdx = unkeyedCursor++;
7747
+ break;
7748
+ }
7749
+ unkeyedCursor++;
7930
7750
  }
7931
7751
  }
7932
- }
7933
- for (const node of toRemove) {
7934
- if (node.parentNode === parentDom) {
7935
- parentDom.removeChild(node);
7752
+ if (oldIdx >= 0) {
7753
+ used[oldIdx] = 1;
7754
+ const dom = domNodes[oldIdx];
7755
+ const newDom = morphNode(dom, oldChilds[oldIdx], newChild, opts);
7756
+ const ref = parentDom.childNodes[j] ?? null;
7757
+ if (newDom !== ref)
7758
+ parentDom.insertBefore(newDom, ref);
7759
+ } else {
7760
+ const dom = newChild.toDom(opts);
7761
+ if (dom) {
7762
+ const ref = parentDom.childNodes[j] ?? null;
7763
+ parentDom.insertBefore(dom, ref);
7764
+ }
7936
7765
  }
7937
7766
  }
7938
- if (orderedSet.moves) {
7939
- applyMoves(parentDom, orderedSet.moves);
7940
- }
7941
- }
7942
- function applyMoves(domNode, moves) {
7943
- const childNodes = domNode.childNodes;
7944
- const keyMap = {};
7945
- for (const remove2 of moves.removes) {
7946
- const node = childNodes[remove2.from];
7947
- if (remove2.key)
7948
- keyMap[remove2.key] = node;
7949
- domNode.removeChild(node);
7950
- }
7951
- let length = childNodes.length;
7952
- for (let j = 0;j < moves.inserts.length; j++) {
7953
- const insert = moves.inserts[j];
7954
- const node = keyMap[insert.key];
7955
- if (node) {
7956
- domNode.insertBefore(node, insert.to >= length++ ? null : childNodes[insert.to]);
7767
+ for (let i = oldChilds.length - 1;i >= 0; i--) {
7768
+ if (!used[i] && domNodes[i].parentNode === parentDom) {
7769
+ parentDom.removeChild(domNodes[i]);
7957
7770
  }
7958
7771
  }
7959
7772
  }
@@ -8284,6 +8097,7 @@ export {
8284
8097
  isKeyed,
8285
8098
  isIndexed,
8286
8099
  isMap as isIMap,
8100
+ injectCss,
8287
8101
  html,
8288
8102
  fieldsByClass,
8289
8103
  css,