@wsxjs/wsx-core 0.0.21 → 0.0.23

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/index.js CHANGED
@@ -182,6 +182,10 @@ var CACHE_KEY_PROP = "__wsxCacheKey";
182
182
  function markElement(element, cacheKey) {
183
183
  element[CACHE_KEY_PROP] = cacheKey;
184
184
  }
185
+ function getElementCacheKey(element) {
186
+ const key = element[CACHE_KEY_PROP];
187
+ return key !== void 0 ? String(key) : null;
188
+ }
185
189
  function isCreatedByH(element) {
186
190
  if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
187
191
  return false;
@@ -629,6 +633,185 @@ function createElementWithPropsAndChildren(tag, props, children) {
629
633
  return element;
630
634
  }
631
635
 
636
+ // src/utils/update-children-helpers.ts
637
+ function collectPreservedElements(element) {
638
+ const preserved = [];
639
+ for (let i = 0; i < element.childNodes.length; i++) {
640
+ const child = element.childNodes[i];
641
+ if (shouldPreserveElement(child)) {
642
+ preserved.push(child);
643
+ }
644
+ }
645
+ return preserved;
646
+ }
647
+ function findDOMNodeByReference(oldChild, parent) {
648
+ if (oldChild.parentNode === parent && !shouldPreserveElement(oldChild)) {
649
+ return oldChild;
650
+ }
651
+ return null;
652
+ }
653
+ function findDOMNodeByCacheKey(cacheKey, parent) {
654
+ for (let i = 0; i < parent.childNodes.length; i++) {
655
+ const child = parent.childNodes[i];
656
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
657
+ if (shouldPreserveElement(child)) continue;
658
+ if (getElementCacheKey(child) === cacheKey) {
659
+ return child;
660
+ }
661
+ }
662
+ }
663
+ return null;
664
+ }
665
+ function findElementNode(oldChild, parent) {
666
+ const byRef = findDOMNodeByReference(oldChild, parent);
667
+ if (byRef) return byRef;
668
+ const cacheKey = getElementCacheKey(oldChild);
669
+ if (cacheKey) {
670
+ return findDOMNodeByCacheKey(cacheKey, parent);
671
+ }
672
+ return null;
673
+ }
674
+ function findTextNode(parent, domIndex) {
675
+ while (domIndex.value < parent.childNodes.length) {
676
+ const node = parent.childNodes[domIndex.value];
677
+ if (node.nodeType === Node.TEXT_NODE) {
678
+ const textNode = node;
679
+ domIndex.value++;
680
+ return textNode;
681
+ }
682
+ domIndex.value++;
683
+ }
684
+ return null;
685
+ }
686
+ function updateOrCreateTextNode(parent, oldNode, newText) {
687
+ if (oldNode && oldNode.nodeType === Node.TEXT_NODE) {
688
+ if (oldNode.textContent !== newText) {
689
+ oldNode.textContent = newText;
690
+ }
691
+ } else {
692
+ const newTextNode = document.createTextNode(newText);
693
+ if (oldNode && !shouldPreserveElement(oldNode)) {
694
+ parent.replaceChild(newTextNode, oldNode);
695
+ } else {
696
+ parent.insertBefore(newTextNode, oldNode || null);
697
+ }
698
+ }
699
+ }
700
+ function removeNodeIfNotPreserved(parent, node) {
701
+ if (node && !shouldPreserveElement(node) && node.parentNode === parent) {
702
+ parent.removeChild(node);
703
+ }
704
+ }
705
+ function replaceOrInsertElement(parent, newChild, oldNode) {
706
+ if (newChild.parentNode && newChild.parentNode !== parent) {
707
+ newChild.parentNode.removeChild(newChild);
708
+ }
709
+ if (oldNode && !shouldPreserveElement(oldNode)) {
710
+ if (oldNode !== newChild) {
711
+ parent.replaceChild(newChild, oldNode);
712
+ }
713
+ } else if (newChild.parentNode !== parent) {
714
+ parent.insertBefore(newChild, oldNode || null);
715
+ }
716
+ }
717
+ function appendNewChild(parent, child) {
718
+ if (child === null || child === void 0 || child === false) {
719
+ return;
720
+ }
721
+ if (typeof child === "string" || typeof child === "number") {
722
+ parent.appendChild(document.createTextNode(String(child)));
723
+ } else if (child instanceof HTMLElement || child instanceof SVGElement) {
724
+ if (child.parentNode && child.parentNode !== parent) {
725
+ child.parentNode.removeChild(child);
726
+ }
727
+ if (child.parentNode !== parent) {
728
+ parent.appendChild(child);
729
+ }
730
+ } else if (child instanceof DocumentFragment) {
731
+ parent.appendChild(child);
732
+ }
733
+ }
734
+ function buildNewChildrenMaps(flatNew) {
735
+ const elementSet = /* @__PURE__ */ new Set();
736
+ const cacheKeyMap = /* @__PURE__ */ new Map();
737
+ for (const child of flatNew) {
738
+ if (child instanceof HTMLElement || child instanceof SVGElement || child instanceof DocumentFragment) {
739
+ elementSet.add(child);
740
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
741
+ const cacheKey = getElementCacheKey(child);
742
+ if (cacheKey) {
743
+ cacheKeyMap.set(cacheKey, child);
744
+ }
745
+ }
746
+ }
747
+ }
748
+ return { elementSet, cacheKeyMap };
749
+ }
750
+ function shouldRemoveNode(node, elementSet, cacheKeyMap) {
751
+ if (shouldPreserveElement(node)) {
752
+ return false;
753
+ }
754
+ if (node instanceof HTMLElement || node instanceof SVGElement || node instanceof DocumentFragment) {
755
+ if (elementSet.has(node)) {
756
+ return false;
757
+ }
758
+ if (node instanceof HTMLElement || node instanceof SVGElement) {
759
+ const cacheKey = getElementCacheKey(node);
760
+ if (cacheKey && cacheKeyMap.has(cacheKey)) {
761
+ return false;
762
+ }
763
+ }
764
+ }
765
+ return true;
766
+ }
767
+ function deduplicateCacheKeys(parent, cacheKeyMap) {
768
+ const processedCacheKeys = /* @__PURE__ */ new Set();
769
+ for (let i = parent.childNodes.length - 1; i >= 0; i--) {
770
+ const child = parent.childNodes[i];
771
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
772
+ if (shouldPreserveElement(child)) {
773
+ continue;
774
+ }
775
+ const cacheKey = getElementCacheKey(child);
776
+ if (cacheKey && cacheKeyMap.has(cacheKey) && !processedCacheKeys.has(cacheKey)) {
777
+ processedCacheKeys.add(cacheKey);
778
+ const newChild = cacheKeyMap.get(cacheKey);
779
+ if (child !== newChild) {
780
+ parent.replaceChild(newChild, child);
781
+ }
782
+ }
783
+ }
784
+ }
785
+ }
786
+ function collectNodesToRemove(parent, elementSet, cacheKeyMap) {
787
+ const nodesToRemove = [];
788
+ for (let i = 0; i < parent.childNodes.length; i++) {
789
+ const node = parent.childNodes[i];
790
+ if (shouldRemoveNode(node, elementSet, cacheKeyMap)) {
791
+ nodesToRemove.push(node);
792
+ }
793
+ }
794
+ return nodesToRemove;
795
+ }
796
+ function removeNodes(parent, nodes) {
797
+ for (let i = nodes.length - 1; i >= 0; i--) {
798
+ const node = nodes[i];
799
+ if (node.parentNode === parent) {
800
+ parent.removeChild(node);
801
+ }
802
+ }
803
+ }
804
+ function reinsertPreservedElements(parent, preservedElements) {
805
+ for (const element of preservedElements) {
806
+ if (element.parentNode !== parent) {
807
+ parent.appendChild(element);
808
+ }
809
+ }
810
+ }
811
+ function flattenChildrenSafe(children) {
812
+ return flattenChildren(children);
813
+ }
814
+
632
815
  // src/utils/element-update.ts
633
816
  function removeProp(element, key, oldValue, tag) {
634
817
  const isSVG = shouldUseSVGNamespace(tag);
@@ -729,123 +912,123 @@ function updateProps(element, oldProps, newProps, tag) {
729
912
  if (oldValue === newValue) {
730
913
  continue;
731
914
  }
915
+ if (oldValue === void 0) {
916
+ applySingleProp2(element, key, newValue, tag, isSVG);
917
+ continue;
918
+ }
732
919
  if (typeof oldValue === "object" && oldValue !== null && typeof newValue === "object" && newValue !== null) {
733
- if (JSON.stringify(oldValue) === JSON.stringify(newValue)) {
734
- continue;
920
+ try {
921
+ const oldJson = JSON.stringify(oldValue);
922
+ const newJson = JSON.stringify(newValue);
923
+ if (oldJson === newJson) {
924
+ continue;
925
+ }
926
+ } catch {
735
927
  }
736
928
  }
737
929
  applySingleProp2(element, key, newValue, tag, isSVG);
738
930
  }
739
931
  }
740
- function updateChildren(element, oldChildren, newChildren) {
741
- const flatOld = flattenChildren(oldChildren);
742
- const flatNew = flattenChildren(newChildren);
932
+ function updateChildren(element, oldChildren, newChildren, cacheManager) {
933
+ const flatOld = flattenChildrenSafe(oldChildren);
934
+ const flatNew = flattenChildrenSafe(newChildren);
935
+ const preservedElements = collectPreservedElements(element);
743
936
  const minLength = Math.min(flatOld.length, flatNew.length);
937
+ const domIndex = { value: 0 };
744
938
  for (let i = 0; i < minLength; i++) {
745
939
  const oldChild = flatOld[i];
746
940
  const newChild = flatNew[i];
941
+ let oldNode = null;
942
+ if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
943
+ oldNode = findElementNode(oldChild, element);
944
+ if (oldNode && oldNode.parentNode === element) {
945
+ const nodeIndex = Array.from(element.childNodes).indexOf(oldNode);
946
+ if (nodeIndex !== -1 && nodeIndex >= domIndex.value) {
947
+ domIndex.value = nodeIndex + 1;
948
+ }
949
+ }
950
+ } else if (typeof oldChild === "string" || typeof oldChild === "number") {
951
+ oldNode = findTextNode(element, domIndex);
952
+ if (!oldNode && element.childNodes.length > 0) {
953
+ for (let j = domIndex.value; j < element.childNodes.length; j++) {
954
+ const node = element.childNodes[j];
955
+ if (node.nodeType === Node.TEXT_NODE) {
956
+ oldNode = node;
957
+ domIndex.value = j + 1;
958
+ break;
959
+ }
960
+ }
961
+ }
962
+ }
747
963
  if (typeof oldChild === "string" || typeof oldChild === "number") {
748
964
  if (typeof newChild === "string" || typeof newChild === "number") {
749
- const textNode = element.childNodes[i];
750
- if (textNode && textNode.nodeType === Node.TEXT_NODE) {
751
- textNode.textContent = String(newChild);
752
- } else {
753
- const newTextNode = document.createTextNode(String(newChild));
754
- if (textNode) {
755
- element.replaceChild(newTextNode, textNode);
756
- } else {
757
- element.appendChild(newTextNode);
758
- }
965
+ const oldText = String(oldChild);
966
+ const newText = String(newChild);
967
+ const needsUpdate = oldText !== newText || oldNode && oldNode.nodeType === Node.TEXT_NODE && oldNode.textContent !== newText;
968
+ if (needsUpdate) {
969
+ updateOrCreateTextNode(element, oldNode, newText);
759
970
  }
760
971
  } else {
761
- const textNode = element.childNodes[i];
762
- if (textNode) {
763
- if (!shouldPreserveElement(textNode)) {
764
- element.removeChild(textNode);
765
- }
766
- }
767
- if (typeof newChild === "string" || typeof newChild === "number") {
768
- element.appendChild(document.createTextNode(String(newChild)));
769
- } else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
770
- element.appendChild(newChild);
972
+ removeNodeIfNotPreserved(element, oldNode);
973
+ if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
974
+ replaceOrInsertElement(element, newChild, oldNode);
771
975
  } else if (newChild instanceof DocumentFragment) {
772
- element.appendChild(newChild);
976
+ element.insertBefore(newChild, oldNode || null);
773
977
  }
774
978
  }
775
979
  } else if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
776
- if (newChild === oldChild) {
980
+ if (oldNode && shouldPreserveElement(oldNode)) {
777
981
  continue;
778
- } else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
779
- const oldNode = element.childNodes[i];
780
- if (oldNode) {
781
- if (!shouldPreserveElement(oldNode)) {
782
- if (oldNode !== newChild) {
783
- element.replaceChild(newChild, oldNode);
784
- }
785
- } else {
786
- if (newChild.parentNode !== element) {
787
- element.appendChild(newChild);
982
+ }
983
+ if (newChild === oldChild && (newChild instanceof HTMLElement || newChild instanceof SVGElement)) {
984
+ if (cacheManager) {
985
+ const childMetadata = cacheManager.getMetadata(newChild);
986
+ if (childMetadata) {
987
+ if (oldNode === newChild && newChild.parentNode === element) {
988
+ continue;
788
989
  }
789
990
  }
790
991
  } else {
791
- if (newChild.parentNode !== element) {
792
- element.appendChild(newChild);
992
+ if (oldNode === newChild && newChild.parentNode === element) {
993
+ continue;
793
994
  }
794
995
  }
795
- } else {
796
- const oldNode = element.childNodes[i];
797
- if (oldNode) {
798
- if (!shouldPreserveElement(oldNode)) {
799
- element.removeChild(oldNode);
800
- }
996
+ }
997
+ if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
998
+ if (newChild.parentNode === element && oldNode === newChild) {
999
+ continue;
801
1000
  }
1001
+ replaceOrInsertElement(element, newChild, oldNode);
1002
+ } else {
1003
+ removeNodeIfNotPreserved(element, oldNode);
802
1004
  if (typeof newChild === "string" || typeof newChild === "number") {
803
- element.appendChild(document.createTextNode(String(newChild)));
1005
+ const newTextNode = document.createTextNode(String(newChild));
1006
+ element.insertBefore(newTextNode, oldNode?.nextSibling || null);
804
1007
  } else if (newChild instanceof DocumentFragment) {
805
- element.appendChild(newChild);
1008
+ element.insertBefore(newChild, oldNode?.nextSibling || null);
806
1009
  }
807
1010
  }
808
1011
  }
809
1012
  }
810
1013
  for (let i = minLength; i < flatNew.length; i++) {
811
- const newChild = flatNew[i];
812
- if (newChild === null || newChild === void 0 || newChild === false) {
813
- continue;
814
- }
815
- if (typeof newChild === "string" || typeof newChild === "number") {
816
- element.appendChild(document.createTextNode(String(newChild)));
817
- } else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
818
- if (newChild.parentNode !== element) {
819
- element.appendChild(newChild);
820
- }
821
- } else if (newChild instanceof DocumentFragment) {
822
- element.appendChild(newChild);
823
- }
824
- }
825
- const nodesToRemove = [];
826
- for (let i = flatNew.length; i < element.childNodes.length; i++) {
827
- const child = element.childNodes[i];
828
- if (!shouldPreserveElement(child)) {
829
- nodesToRemove.push(child);
830
- }
831
- }
832
- for (let i = nodesToRemove.length - 1; i >= 0; i--) {
833
- const node = nodesToRemove[i];
834
- if (node.parentNode === element) {
835
- element.removeChild(node);
836
- }
1014
+ appendNewChild(element, flatNew[i]);
837
1015
  }
1016
+ const { elementSet, cacheKeyMap } = buildNewChildrenMaps(flatNew);
1017
+ deduplicateCacheKeys(element, cacheKeyMap);
1018
+ const nodesToRemove = collectNodesToRemove(element, elementSet, cacheKeyMap);
1019
+ removeNodes(element, nodesToRemove);
1020
+ reinsertPreservedElements(element, preservedElements);
838
1021
  }
839
1022
  function updateElement(element, newProps, newChildren, tag, cacheManager) {
840
1023
  const oldMetadata = cacheManager.getMetadata(element);
841
1024
  const oldProps = oldMetadata?.props || null;
842
1025
  const oldChildren = oldMetadata?.children || [];
843
- updateProps(element, oldProps, newProps, tag);
844
- updateChildren(element, oldChildren, newChildren);
845
1026
  cacheManager.setMetadata(element, {
846
1027
  props: newProps || {},
847
1028
  children: newChildren
848
1029
  });
1030
+ updateProps(element, oldProps, newProps, tag);
1031
+ updateChildren(element, oldChildren, newChildren, cacheManager);
849
1032
  }
850
1033
 
851
1034
  // src/jsx-factory.ts
@@ -859,7 +1042,36 @@ function h(tag, props = {}, ...children) {
859
1042
  if (context && cacheManager) {
860
1043
  return tryUseCacheOrCreate(tag, props, children, context, cacheManager);
861
1044
  }
862
- return createElementWithPropsAndChildren(tag, props, children);
1045
+ try {
1046
+ const nodeEnv = typeof globalThis.process !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
1047
+ globalThis.process.env?.NODE_ENV;
1048
+ if (nodeEnv === "development") {
1049
+ if (!context) {
1050
+ logger3.debug(
1051
+ `h() called without render context. Tag: "${tag}", ComponentId: "${getComponentId()}"`,
1052
+ {
1053
+ tag,
1054
+ props: props ? Object.keys(props) : [],
1055
+ hasCacheManager: !!cacheManager
1056
+ }
1057
+ );
1058
+ } else if (!cacheManager) {
1059
+ logger3.debug(
1060
+ `h() called with context but no cache manager. Tag: "${tag}", Component: "${context.constructor.name}"`,
1061
+ {
1062
+ tag,
1063
+ component: context.constructor.name
1064
+ }
1065
+ );
1066
+ }
1067
+ }
1068
+ } catch {
1069
+ }
1070
+ const element = createElementWithPropsAndChildren(tag, props, children);
1071
+ const componentId = getComponentId();
1072
+ const cacheKey = generateCacheKey(tag, props, componentId, context || void 0);
1073
+ markElement(element, cacheKey);
1074
+ return element;
863
1075
  }
864
1076
  function tryUseCacheOrCreate(tag, props, children, context, cacheManager) {
865
1077
  try {
@@ -869,6 +1081,14 @@ function tryUseCacheOrCreate(tag, props, children, context, cacheManager) {
869
1081
  if (cachedElement) {
870
1082
  const element2 = cachedElement;
871
1083
  updateElement(element2, props, children, tag, cacheManager);
1084
+ const isCustomElement = tag.includes("-") && customElements.get(tag);
1085
+ if (isCustomElement && element2.isConnected) {
1086
+ const parent = element2.parentNode;
1087
+ if (parent) {
1088
+ parent.removeChild(element2);
1089
+ parent.appendChild(element2);
1090
+ }
1091
+ }
872
1092
  return element2;
873
1093
  }
874
1094
  const element = createElementWithPropsAndChildren(tag, props, children);
@@ -892,7 +1112,12 @@ function handleCacheError(error, tag, props, children) {
892
1112
  }
893
1113
  } catch {
894
1114
  }
895
- return createElementWithPropsAndChildren(tag, props, children);
1115
+ const element = createElementWithPropsAndChildren(tag, props, children);
1116
+ const context = RenderContext.getCurrentComponent();
1117
+ const componentId = getComponentId();
1118
+ const cacheKey = generateCacheKey(tag, props, componentId, context || void 0);
1119
+ markElement(element, cacheKey);
1120
+ return element;
896
1121
  }
897
1122
  function Fragment(_props, children) {
898
1123
  const fragment = document.createDocumentFragment();
@@ -1703,7 +1928,7 @@ var WebComponent = class extends BaseComponent {
1703
1928
  const styleName = this.config.styleName || this.constructor.name;
1704
1929
  StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
1705
1930
  }
1706
- const content = this.render();
1931
+ const content = RenderContext.runInContext(this, () => this.render());
1707
1932
  this.shadowRoot.appendChild(content);
1708
1933
  }
1709
1934
  this.initializeEventListeners();
@@ -1783,9 +2008,18 @@ var WebComponent = class extends BaseComponent {
1783
2008
  }
1784
2009
  requestAnimationFrame(() => {
1785
2010
  this.shadowRoot.appendChild(content);
1786
- const oldChildren = Array.from(this.shadowRoot.children).filter(
1787
- (child) => child !== content
1788
- );
2011
+ const oldChildren = Array.from(this.shadowRoot.children).filter((child) => {
2012
+ if (child === content) {
2013
+ return false;
2014
+ }
2015
+ if (child instanceof HTMLStyleElement) {
2016
+ return false;
2017
+ }
2018
+ if (shouldPreserveElement(child)) {
2019
+ return false;
2020
+ }
2021
+ return true;
2022
+ });
1789
2023
  oldChildren.forEach((child) => child.remove());
1790
2024
  requestAnimationFrame(() => {
1791
2025
  this.restoreFocusState(focusState);
@@ -1965,6 +2199,9 @@ var LightComponent = class extends BaseComponent {
1965
2199
  if (child instanceof HTMLElement && jsxChildren.includes(child)) {
1966
2200
  return false;
1967
2201
  }
2202
+ if (shouldPreserveElement(child)) {
2203
+ return false;
2204
+ }
1968
2205
  return true;
1969
2206
  });
1970
2207
  oldChildren.forEach((child) => child.remove());
package/dist/index.mjs CHANGED
@@ -2,8 +2,9 @@ import {
2
2
  Fragment,
3
3
  RenderContext,
4
4
  createLogger,
5
- h
6
- } from "./chunk-AR3DIDLV.mjs";
5
+ h,
6
+ shouldPreserveElement
7
+ } from "./chunk-ESZYREJK.mjs";
7
8
 
8
9
  // src/styles/style-manager.ts
9
10
  var StyleManager = class {
@@ -796,7 +797,7 @@ var WebComponent = class extends BaseComponent {
796
797
  const styleName = this.config.styleName || this.constructor.name;
797
798
  StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
798
799
  }
799
- const content = this.render();
800
+ const content = RenderContext.runInContext(this, () => this.render());
800
801
  this.shadowRoot.appendChild(content);
801
802
  }
802
803
  this.initializeEventListeners();
@@ -876,9 +877,18 @@ var WebComponent = class extends BaseComponent {
876
877
  }
877
878
  requestAnimationFrame(() => {
878
879
  this.shadowRoot.appendChild(content);
879
- const oldChildren = Array.from(this.shadowRoot.children).filter(
880
- (child) => child !== content
881
- );
880
+ const oldChildren = Array.from(this.shadowRoot.children).filter((child) => {
881
+ if (child === content) {
882
+ return false;
883
+ }
884
+ if (child instanceof HTMLStyleElement) {
885
+ return false;
886
+ }
887
+ if (shouldPreserveElement(child)) {
888
+ return false;
889
+ }
890
+ return true;
891
+ });
882
892
  oldChildren.forEach((child) => child.remove());
883
893
  requestAnimationFrame(() => {
884
894
  this.restoreFocusState(focusState);
@@ -1058,6 +1068,9 @@ var LightComponent = class extends BaseComponent {
1058
1068
  if (child instanceof HTMLElement && jsxChildren.includes(child)) {
1059
1069
  return false;
1060
1070
  }
1071
+ if (shouldPreserveElement(child)) {
1072
+ return false;
1073
+ }
1061
1074
  return true;
1062
1075
  });
1063
1076
  oldChildren.forEach((child) => child.remove());