@wsxjs/wsx-core 0.0.23 → 0.0.24

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.
@@ -73,6 +73,7 @@ var _RenderContext = class _RenderContext {
73
73
  * @param fn The function to execute (usually the render method).
74
74
  */
75
75
  static runInContext(component, fn) {
76
+ resetCounterForNewRenderCycle(component);
76
77
  const prev = _RenderContext.current;
77
78
  _RenderContext.current = component;
78
79
  try {
@@ -98,21 +99,20 @@ _RenderContext.current = null;
98
99
  var RenderContext = _RenderContext;
99
100
 
100
101
  // src/utils/cache-key.ts
101
- var POSITION_ID_KEY = "__wsxPositionId";
102
102
  var INDEX_KEY = "__wsxIndex";
103
103
  var componentElementCounters = /* @__PURE__ */ new WeakMap();
104
104
  var componentIdCache = /* @__PURE__ */ new WeakMap();
105
105
  function generateCacheKey(tag, props, componentId, component) {
106
- const positionId = props?.[POSITION_ID_KEY];
107
106
  const userKey = props?.key;
108
107
  const index = props?.[INDEX_KEY];
108
+ const positionId = props?.__wsxPositionId;
109
109
  if (userKey !== void 0 && userKey !== null) {
110
110
  return `${componentId}:${tag}:key-${String(userKey)}`;
111
111
  }
112
112
  if (index !== void 0 && index !== null) {
113
113
  return `${componentId}:${tag}:idx-${String(index)}`;
114
114
  }
115
- if (positionId !== void 0 && positionId !== null && positionId !== "no-id") {
115
+ if (positionId !== void 0 && positionId !== null) {
116
116
  return `${componentId}:${tag}:${String(positionId)}`;
117
117
  }
118
118
  if (component) {
@@ -123,6 +123,9 @@ function generateCacheKey(tag, props, componentId, component) {
123
123
  }
124
124
  return `${componentId}:${tag}:fallback-${Date.now()}-${Math.random()}`;
125
125
  }
126
+ function resetCounterForNewRenderCycle(component) {
127
+ componentElementCounters.set(component, 0);
128
+ }
126
129
  function getComponentId() {
127
130
  const component = RenderContext.getCurrentComponent();
128
131
  if (component) {
@@ -540,7 +543,9 @@ function applySingleProp(element, key, value, tag, isSVG) {
540
543
  }
541
544
  if (key.startsWith("on") && typeof value === "function") {
542
545
  const eventName = key.slice(2).toLowerCase();
546
+ const listenerKey = `__wsxListener_${eventName}`;
543
547
  element.addEventListener(eventName, value);
548
+ element[listenerKey] = value;
544
549
  return;
545
550
  }
546
551
  if (typeof value === "boolean") {
@@ -635,7 +640,7 @@ function findElementNode(oldChild, parent) {
635
640
  function findTextNode(parent, domIndex) {
636
641
  while (domIndex.value < parent.childNodes.length) {
637
642
  const node = parent.childNodes[domIndex.value];
638
- if (node.nodeType === Node.TEXT_NODE) {
643
+ if (node.nodeType === Node.TEXT_NODE && node.parentNode === parent) {
639
644
  const textNode = node;
640
645
  domIndex.value++;
641
646
  return textNode;
@@ -649,13 +654,23 @@ function updateOrCreateTextNode(parent, oldNode, newText) {
649
654
  if (oldNode.textContent !== newText) {
650
655
  oldNode.textContent = newText;
651
656
  }
657
+ return oldNode;
652
658
  } else {
659
+ if (!oldNode) {
660
+ for (let i = 0; i < parent.childNodes.length; i++) {
661
+ const node = parent.childNodes[i];
662
+ if (node.nodeType === Node.TEXT_NODE && node.parentNode === parent && node.textContent === newText) {
663
+ return node;
664
+ }
665
+ }
666
+ }
653
667
  const newTextNode = document.createTextNode(newText);
654
668
  if (oldNode && !shouldPreserveElement(oldNode)) {
655
669
  parent.replaceChild(newTextNode, oldNode);
656
670
  } else {
657
671
  parent.insertBefore(newTextNode, oldNode || null);
658
672
  }
673
+ return newTextNode;
659
674
  }
660
675
  }
661
676
  function removeNodeIfNotPreserved(parent, node) {
@@ -664,29 +679,66 @@ function removeNodeIfNotPreserved(parent, node) {
664
679
  }
665
680
  }
666
681
  function replaceOrInsertElement(parent, newChild, oldNode) {
682
+ const targetNextSibling = oldNode && shouldPreserveElement(oldNode) ? oldNode : oldNode?.nextSibling || null;
683
+ replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling);
684
+ }
685
+ function replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling) {
667
686
  if (newChild.parentNode && newChild.parentNode !== parent) {
668
687
  newChild.parentNode.removeChild(newChild);
669
688
  }
670
- if (oldNode && !shouldPreserveElement(oldNode)) {
689
+ const isInCorrectPosition = newChild.parentNode === parent && newChild.nextSibling === targetNextSibling;
690
+ if (isInCorrectPosition) {
691
+ return;
692
+ }
693
+ if (newChild.parentNode === parent) {
694
+ parent.insertBefore(newChild, targetNextSibling);
695
+ return;
696
+ }
697
+ if (oldNode && oldNode.parentNode === parent && !shouldPreserveElement(oldNode)) {
671
698
  if (oldNode !== newChild) {
672
699
  parent.replaceChild(newChild, oldNode);
673
700
  }
674
- } else if (newChild.parentNode !== parent) {
675
- parent.insertBefore(newChild, oldNode || null);
701
+ } else {
702
+ if (newChild.parentNode === parent) {
703
+ return;
704
+ }
705
+ const newChildCacheKey = getElementCacheKey(newChild);
706
+ if (!newChildCacheKey) {
707
+ const newChildContent = newChild.textContent || "";
708
+ const newChildTag = newChild.tagName.toLowerCase();
709
+ for (let i = 0; i < parent.childNodes.length; i++) {
710
+ const existingNode = parent.childNodes[i];
711
+ if (existingNode instanceof HTMLElement || existingNode instanceof SVGElement) {
712
+ const existingCacheKey = getElementCacheKey(existingNode);
713
+ if (!existingCacheKey && existingNode.tagName.toLowerCase() === newChildTag && existingNode.textContent === newChildContent && existingNode !== newChild) {
714
+ return;
715
+ }
716
+ }
717
+ }
718
+ }
719
+ parent.insertBefore(newChild, targetNextSibling);
676
720
  }
677
721
  }
678
- function appendNewChild(parent, child) {
722
+ function appendNewChild(parent, child, processedNodes) {
679
723
  if (child === null || child === void 0 || child === false) {
680
724
  return;
681
725
  }
682
726
  if (typeof child === "string" || typeof child === "number") {
683
- parent.appendChild(document.createTextNode(String(child)));
727
+ const newTextNode = document.createTextNode(String(child));
728
+ parent.appendChild(newTextNode);
729
+ if (processedNodes) {
730
+ processedNodes.add(newTextNode);
731
+ }
684
732
  } else if (child instanceof HTMLElement || child instanceof SVGElement) {
733
+ if (child.parentNode === parent) {
734
+ return;
735
+ }
685
736
  if (child.parentNode && child.parentNode !== parent) {
686
737
  child.parentNode.removeChild(child);
687
738
  }
688
- if (child.parentNode !== parent) {
689
- parent.appendChild(child);
739
+ parent.appendChild(child);
740
+ if (processedNodes) {
741
+ processedNodes.add(child);
690
742
  }
691
743
  } else if (child instanceof DocumentFragment) {
692
744
  parent.appendChild(child);
@@ -708,10 +760,22 @@ function buildNewChildrenMaps(flatNew) {
708
760
  }
709
761
  return { elementSet, cacheKeyMap };
710
762
  }
711
- function shouldRemoveNode(node, elementSet, cacheKeyMap) {
763
+ function shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes) {
712
764
  if (shouldPreserveElement(node)) {
713
765
  return false;
714
766
  }
767
+ if (node.nodeType === Node.TEXT_NODE && processedNodes && processedNodes.has(node)) {
768
+ return false;
769
+ }
770
+ if (node.nodeType === Node.TEXT_NODE && processedNodes) {
771
+ let parent = node.parentNode;
772
+ while (parent) {
773
+ if (processedNodes.has(parent) && parent.parentNode) {
774
+ return false;
775
+ }
776
+ parent = parent.parentNode;
777
+ }
778
+ }
715
779
  if (node instanceof HTMLElement || node instanceof SVGElement || node instanceof DocumentFragment) {
716
780
  if (elementSet.has(node)) {
717
781
  return false;
@@ -740,24 +804,39 @@ function deduplicateCacheKeys(parent, cacheKeyMap) {
740
804
  if (child !== newChild) {
741
805
  parent.replaceChild(newChild, child);
742
806
  }
807
+ } else if (cacheKey && cacheKeyMap.has(cacheKey) && processedCacheKeys.has(cacheKey)) {
808
+ const newChild = cacheKeyMap.get(cacheKey);
809
+ if (child !== newChild) {
810
+ parent.removeChild(child);
811
+ }
743
812
  }
744
813
  }
745
814
  }
746
815
  }
747
- function collectNodesToRemove(parent, elementSet, cacheKeyMap) {
816
+ function collectNodesToRemove(parent, elementSet, cacheKeyMap, processedNodes) {
748
817
  const nodesToRemove = [];
749
818
  for (let i = 0; i < parent.childNodes.length; i++) {
750
819
  const node = parent.childNodes[i];
751
- if (shouldRemoveNode(node, elementSet, cacheKeyMap)) {
820
+ if (shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes)) {
752
821
  nodesToRemove.push(node);
753
822
  }
754
823
  }
755
824
  return nodesToRemove;
756
825
  }
757
- function removeNodes(parent, nodes) {
826
+ function removeNodes(parent, nodes, cacheManager) {
758
827
  for (let i = nodes.length - 1; i >= 0; i--) {
759
828
  const node = nodes[i];
760
829
  if (node.parentNode === parent) {
830
+ if (cacheManager && (node instanceof HTMLElement || node instanceof SVGElement)) {
831
+ const metadata = cacheManager.getMetadata(node);
832
+ const refCallback = metadata?.ref;
833
+ if (typeof refCallback === "function") {
834
+ try {
835
+ refCallback(null);
836
+ } catch {
837
+ }
838
+ }
839
+ }
761
840
  parent.removeChild(node);
762
841
  }
763
842
  }
@@ -777,6 +856,12 @@ function flattenChildrenSafe(children) {
777
856
  function removeProp(element, key, oldValue, tag) {
778
857
  const isSVG = shouldUseSVGNamespace(tag);
779
858
  if (key === "ref") {
859
+ if (typeof oldValue === "function") {
860
+ try {
861
+ oldValue(null);
862
+ } catch {
863
+ }
864
+ }
780
865
  return;
781
866
  }
782
867
  if (key === "className" || key === "class") {
@@ -792,6 +877,13 @@ function removeProp(element, key, oldValue, tag) {
792
877
  return;
793
878
  }
794
879
  if (key.startsWith("on") && typeof oldValue === "function") {
880
+ const eventName = key.slice(2).toLowerCase();
881
+ const listenerKey = `__wsxListener_${eventName}`;
882
+ const savedListener = element[listenerKey];
883
+ if (savedListener) {
884
+ element.removeEventListener(eventName, savedListener);
885
+ delete element[listenerKey];
886
+ }
795
887
  return;
796
888
  }
797
889
  if (key === "value") {
@@ -835,7 +927,13 @@ function applySingleProp2(element, key, value, tag, isSVG) {
835
927
  }
836
928
  if (key.startsWith("on") && typeof value === "function") {
837
929
  const eventName = key.slice(2).toLowerCase();
930
+ const listenerKey = `__wsxListener_${eventName}`;
931
+ const oldListener = element[listenerKey];
932
+ if (oldListener) {
933
+ element.removeEventListener(eventName, oldListener);
934
+ }
838
935
  element.addEventListener(eventName, value);
936
+ element[listenerKey] = value;
839
937
  return;
840
938
  }
841
939
  if (typeof value === "boolean") {
@@ -890,12 +988,13 @@ function updateProps(element, oldProps, newProps, tag) {
890
988
  applySingleProp2(element, key, newValue, tag, isSVG);
891
989
  }
892
990
  }
893
- function updateChildren(element, oldChildren, newChildren, cacheManager) {
991
+ function updateChildren(element, oldChildren, newChildren, _cacheManager) {
894
992
  const flatOld = flattenChildrenSafe(oldChildren);
895
993
  const flatNew = flattenChildrenSafe(newChildren);
896
994
  const preservedElements = collectPreservedElements(element);
897
995
  const minLength = Math.min(flatOld.length, flatNew.length);
898
996
  const domIndex = { value: 0 };
997
+ const processedNodes = /* @__PURE__ */ new Set();
899
998
  for (let i = 0; i < minLength; i++) {
900
999
  const oldChild = flatOld[i];
901
1000
  const newChild = flatNew[i];
@@ -911,9 +1010,10 @@ function updateChildren(element, oldChildren, newChildren, cacheManager) {
911
1010
  } else if (typeof oldChild === "string" || typeof oldChild === "number") {
912
1011
  oldNode = findTextNode(element, domIndex);
913
1012
  if (!oldNode && element.childNodes.length > 0) {
1013
+ const oldText = String(oldChild);
914
1014
  for (let j = domIndex.value; j < element.childNodes.length; j++) {
915
1015
  const node = element.childNodes[j];
916
- if (node.nodeType === Node.TEXT_NODE) {
1016
+ if (node.nodeType === Node.TEXT_NODE && node.parentNode === element && node.textContent === oldText) {
917
1017
  oldNode = node;
918
1018
  domIndex.value = j + 1;
919
1019
  break;
@@ -927,7 +1027,14 @@ function updateChildren(element, oldChildren, newChildren, cacheManager) {
927
1027
  const newText = String(newChild);
928
1028
  const needsUpdate = oldText !== newText || oldNode && oldNode.nodeType === Node.TEXT_NODE && oldNode.textContent !== newText;
929
1029
  if (needsUpdate) {
930
- updateOrCreateTextNode(element, oldNode, newText);
1030
+ const updatedNode = updateOrCreateTextNode(element, oldNode, newText);
1031
+ if (updatedNode && !processedNodes.has(updatedNode)) {
1032
+ processedNodes.add(updatedNode);
1033
+ }
1034
+ } else {
1035
+ if (oldNode && oldNode.parentNode === element) {
1036
+ processedNodes.add(oldNode);
1037
+ }
931
1038
  }
932
1039
  } else {
933
1040
  removeNodeIfNotPreserved(element, oldNode);
@@ -941,30 +1048,48 @@ function updateChildren(element, oldChildren, newChildren, cacheManager) {
941
1048
  if (oldNode && shouldPreserveElement(oldNode)) {
942
1049
  continue;
943
1050
  }
944
- if (newChild === oldChild && (newChild instanceof HTMLElement || newChild instanceof SVGElement)) {
945
- if (cacheManager) {
946
- const childMetadata = cacheManager.getMetadata(newChild);
947
- if (childMetadata) {
948
- if (oldNode === newChild && newChild.parentNode === element) {
949
- continue;
1051
+ if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
1052
+ let targetNextSibling = null;
1053
+ let foundPreviousElement = false;
1054
+ for (let j = i - 1; j >= 0; j--) {
1055
+ const prevChild = flatNew[j];
1056
+ if (prevChild instanceof HTMLElement || prevChild instanceof SVGElement) {
1057
+ if (prevChild.parentNode === element) {
1058
+ targetNextSibling = prevChild.nextSibling;
1059
+ foundPreviousElement = true;
1060
+ break;
950
1061
  }
951
1062
  }
952
- } else {
953
- if (oldNode === newChild && newChild.parentNode === element) {
954
- continue;
955
- }
956
1063
  }
957
- }
958
- if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
959
- if (newChild.parentNode === element && oldNode === newChild) {
1064
+ if (!foundPreviousElement) {
1065
+ const firstChild = Array.from(element.childNodes).find(
1066
+ (node) => !shouldPreserveElement(node) && !processedNodes.has(node)
1067
+ );
1068
+ targetNextSibling = firstChild || null;
1069
+ }
1070
+ const isInCorrectPosition = newChild.parentNode === element && newChild.nextSibling === targetNextSibling;
1071
+ if (newChild === oldChild && isInCorrectPosition) {
1072
+ if (oldNode) processedNodes.add(oldNode);
1073
+ processedNodes.add(newChild);
960
1074
  continue;
961
1075
  }
962
- replaceOrInsertElement(element, newChild, oldNode);
1076
+ const referenceNode = oldNode && oldNode.parentNode === element ? oldNode : null;
1077
+ replaceOrInsertElementAtPosition(
1078
+ element,
1079
+ newChild,
1080
+ referenceNode,
1081
+ targetNextSibling
1082
+ );
1083
+ if (oldNode && oldNode !== newChild) {
1084
+ processedNodes.delete(oldNode);
1085
+ }
1086
+ processedNodes.add(newChild);
963
1087
  } else {
964
1088
  removeNodeIfNotPreserved(element, oldNode);
965
1089
  if (typeof newChild === "string" || typeof newChild === "number") {
966
1090
  const newTextNode = document.createTextNode(String(newChild));
967
1091
  element.insertBefore(newTextNode, oldNode?.nextSibling || null);
1092
+ processedNodes.add(newTextNode);
968
1093
  } else if (newChild instanceof DocumentFragment) {
969
1094
  element.insertBefore(newChild, oldNode?.nextSibling || null);
970
1095
  }
@@ -972,12 +1097,12 @@ function updateChildren(element, oldChildren, newChildren, cacheManager) {
972
1097
  }
973
1098
  }
974
1099
  for (let i = minLength; i < flatNew.length; i++) {
975
- appendNewChild(element, flatNew[i]);
1100
+ appendNewChild(element, flatNew[i], processedNodes);
976
1101
  }
977
1102
  const { elementSet, cacheKeyMap } = buildNewChildrenMaps(flatNew);
978
1103
  deduplicateCacheKeys(element, cacheKeyMap);
979
- const nodesToRemove = collectNodesToRemove(element, elementSet, cacheKeyMap);
980
- removeNodes(element, nodesToRemove);
1104
+ const nodesToRemove = collectNodesToRemove(element, elementSet, cacheKeyMap, processedNodes);
1105
+ removeNodes(element, nodesToRemove, _cacheManager);
981
1106
  reinsertPreservedElements(element, preservedElements);
982
1107
  }
983
1108
  function updateElement(element, newProps, newChildren, tag, cacheManager) {