@wsxjs/wsx-core 0.0.20 → 0.0.22

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
@@ -37,6 +37,174 @@ __export(index_exports, {
37
37
  });
38
38
  module.exports = __toCommonJS(index_exports);
39
39
 
40
+ // src/utils/dom-utils.ts
41
+ function parseHTMLToNodes(html) {
42
+ if (!html) return [];
43
+ const temp = document.createElement("div");
44
+ temp.innerHTML = html;
45
+ return Array.from(temp.childNodes).map((node) => {
46
+ if (node instanceof HTMLElement || node instanceof SVGElement) {
47
+ return node;
48
+ } else {
49
+ return node.textContent || "";
50
+ }
51
+ });
52
+ }
53
+ function isHTMLString(str) {
54
+ const trimmed = str.trim();
55
+ if (!trimmed) return false;
56
+ const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
57
+ const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
58
+ if (looksLikeMath) return false;
59
+ return htmlTagPattern.test(trimmed);
60
+ }
61
+ function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
62
+ if (depth > 10) {
63
+ console.warn(
64
+ "[WSX] flattenChildren: Maximum depth exceeded, treating remaining children as text"
65
+ );
66
+ return children.filter(
67
+ (child) => typeof child === "string" || typeof child === "number"
68
+ );
69
+ }
70
+ const result = [];
71
+ for (const child of children) {
72
+ if (child === null || child === void 0 || child === false) {
73
+ continue;
74
+ } else if (Array.isArray(child)) {
75
+ result.push(...flattenChildren(child, skipHTMLDetection, depth + 1));
76
+ } else if (typeof child === "string") {
77
+ if (skipHTMLDetection) {
78
+ result.push(child);
79
+ } else if (isHTMLString(child)) {
80
+ try {
81
+ const nodes = parseHTMLToNodes(child);
82
+ if (nodes.length > 0) {
83
+ for (const node of nodes) {
84
+ if (typeof node === "string") {
85
+ result.push(node);
86
+ } else {
87
+ result.push(node);
88
+ }
89
+ }
90
+ } else {
91
+ result.push(child);
92
+ }
93
+ } catch (error) {
94
+ console.warn("[WSX] Failed to parse HTML string, treating as text:", error);
95
+ result.push(child);
96
+ }
97
+ } else {
98
+ result.push(child);
99
+ }
100
+ } else {
101
+ result.push(child);
102
+ }
103
+ }
104
+ return result;
105
+ }
106
+
107
+ // src/render-context.ts
108
+ var _RenderContext = class _RenderContext {
109
+ /**
110
+ * Executes a function within the context of a component.
111
+ * @param component The component instance currently rendering.
112
+ * @param fn The function to execute (usually the render method).
113
+ */
114
+ static runInContext(component, fn) {
115
+ const prev = _RenderContext.current;
116
+ _RenderContext.current = component;
117
+ try {
118
+ return fn();
119
+ } finally {
120
+ _RenderContext.current = prev;
121
+ }
122
+ }
123
+ /**
124
+ * Gets the currently rendering component.
125
+ */
126
+ static getCurrentComponent() {
127
+ return _RenderContext.current;
128
+ }
129
+ /**
130
+ * Gets the current component's DOM cache.
131
+ */
132
+ static getDOMCache() {
133
+ return _RenderContext.current?.getDomCache();
134
+ }
135
+ };
136
+ _RenderContext.current = null;
137
+ var RenderContext = _RenderContext;
138
+
139
+ // src/utils/cache-key.ts
140
+ var POSITION_ID_KEY = "__wsxPositionId";
141
+ var INDEX_KEY = "__wsxIndex";
142
+ var componentElementCounters = /* @__PURE__ */ new WeakMap();
143
+ var componentIdCache = /* @__PURE__ */ new WeakMap();
144
+ function generateCacheKey(tag, props, componentId, component) {
145
+ const positionId = props?.[POSITION_ID_KEY];
146
+ const userKey = props?.key;
147
+ const index = props?.[INDEX_KEY];
148
+ if (userKey !== void 0 && userKey !== null) {
149
+ return `${componentId}:${tag}:key-${String(userKey)}`;
150
+ }
151
+ if (index !== void 0 && index !== null) {
152
+ return `${componentId}:${tag}:idx-${String(index)}`;
153
+ }
154
+ if (positionId !== void 0 && positionId !== null && positionId !== "no-id") {
155
+ return `${componentId}:${tag}:${String(positionId)}`;
156
+ }
157
+ if (component) {
158
+ let counter = componentElementCounters.get(component) || 0;
159
+ counter++;
160
+ componentElementCounters.set(component, counter);
161
+ return `${componentId}:${tag}:auto-${counter}`;
162
+ }
163
+ return `${componentId}:${tag}:fallback-${Date.now()}-${Math.random()}`;
164
+ }
165
+ function getComponentId() {
166
+ const component = RenderContext.getCurrentComponent();
167
+ if (component) {
168
+ let cachedId = componentIdCache.get(component);
169
+ if (cachedId) {
170
+ return cachedId;
171
+ }
172
+ const instanceId = component._instanceId || "default";
173
+ cachedId = `${component.constructor.name}:${instanceId}`;
174
+ componentIdCache.set(component, cachedId);
175
+ return cachedId;
176
+ }
177
+ return "unknown";
178
+ }
179
+
180
+ // src/utils/element-marking.ts
181
+ var CACHE_KEY_PROP = "__wsxCacheKey";
182
+ function markElement(element, cacheKey) {
183
+ element[CACHE_KEY_PROP] = cacheKey;
184
+ }
185
+ function getElementCacheKey(element) {
186
+ const key = element[CACHE_KEY_PROP];
187
+ return key !== void 0 ? String(key) : null;
188
+ }
189
+ function isCreatedByH(element) {
190
+ if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
191
+ return false;
192
+ }
193
+ return element[CACHE_KEY_PROP] !== void 0;
194
+ }
195
+ function shouldPreserveElement(element) {
196
+ if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
197
+ return true;
198
+ }
199
+ if (!isCreatedByH(element)) {
200
+ return true;
201
+ }
202
+ if (element.hasAttribute("data-wsx-preserve")) {
203
+ return true;
204
+ }
205
+ return false;
206
+ }
207
+
40
208
  // src/utils/svg-utils.ts
41
209
  var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
42
210
  var SVG_ONLY_ELEMENTS = /* @__PURE__ */ new Set([
@@ -149,21 +317,63 @@ function getSVGAttributeName(attributeName) {
149
317
  return SVG_ATTRIBUTE_MAP.get(attributeName) || attributeName;
150
318
  }
151
319
 
152
- // src/utils/dom-utils.ts
153
- function parseHTMLToNodes(html) {
154
- if (!html) return [];
155
- const temp = document.createElement("div");
156
- temp.innerHTML = html;
157
- return Array.from(temp.childNodes).map((node) => {
158
- if (node instanceof HTMLElement || node instanceof SVGElement) {
159
- return node;
160
- } else {
161
- return node.textContent || "";
320
+ // src/utils/logger.ts
321
+ var WSXLogger = class {
322
+ constructor(prefix = "[WSX]", enabled = true, level = "info") {
323
+ this.prefix = prefix;
324
+ this.enabled = enabled;
325
+ this.level = level;
326
+ }
327
+ shouldLog(level) {
328
+ if (!this.enabled) return false;
329
+ const levels = ["debug", "info", "warn", "error"];
330
+ const currentLevelIndex = levels.indexOf(this.level);
331
+ const messageLevelIndex = levels.indexOf(level);
332
+ return messageLevelIndex >= currentLevelIndex;
333
+ }
334
+ debug(message, ...args) {
335
+ if (this.shouldLog("debug")) {
336
+ console.debug(`${this.prefix} ${message}`, ...args);
162
337
  }
163
- });
338
+ }
339
+ info(message, ...args) {
340
+ if (this.shouldLog("info")) {
341
+ console.info(`${this.prefix} ${message}`, ...args);
342
+ }
343
+ }
344
+ warn(message, ...args) {
345
+ if (this.shouldLog("warn")) {
346
+ console.warn(`${this.prefix} ${message}`, ...args);
347
+ }
348
+ }
349
+ error(message, ...args) {
350
+ if (this.shouldLog("error")) {
351
+ console.error(`${this.prefix} ${message}`, ...args);
352
+ }
353
+ }
354
+ };
355
+ var logger = new WSXLogger();
356
+ function createLogger(componentName) {
357
+ return new WSXLogger(`[WSX:${componentName}]`);
164
358
  }
165
359
 
166
- // src/jsx-factory.ts
360
+ // src/utils/props-utils.ts
361
+ var logger2 = createLogger("Props Utilities");
362
+ function isFrameworkInternalProp(key) {
363
+ if (key === "key") {
364
+ return true;
365
+ }
366
+ if (key === "__wsxPositionId" || key === "__wsxIndex") {
367
+ return true;
368
+ }
369
+ if (key === "__testId") {
370
+ return true;
371
+ }
372
+ if (key === "ref") {
373
+ return true;
374
+ }
375
+ return false;
376
+ }
167
377
  function isStandardHTMLAttribute(key) {
168
378
  const standardAttributes = /* @__PURE__ */ new Set([
169
379
  // 全局属性
@@ -246,7 +456,8 @@ function isStandardHTMLAttribute(key) {
246
456
  function isSpecialProperty(key, value) {
247
457
  return key === "ref" || key === "className" || key === "class" || key === "style" || key.startsWith("on") && typeof value === "function" || typeof value === "boolean" || key === "value";
248
458
  }
249
- function setSmartProperty(element, key, value, isSVG = false) {
459
+ function setSmartProperty(element, key, value, tag) {
460
+ const isSVG = shouldUseSVGNamespace(tag);
250
461
  if (isSpecialProperty(key, value)) {
251
462
  return;
252
463
  }
@@ -256,13 +467,13 @@ function setSmartProperty(element, key, value, isSVG = false) {
256
467
  try {
257
468
  const serialized = JSON.stringify(value);
258
469
  if (serialized.length > 1024 * 1024) {
259
- console.warn(
470
+ logger2.warn(
260
471
  `[WSX] Attribute "${key}" value too large, consider using a non-standard property name instead`
261
472
  );
262
473
  }
263
474
  element.setAttribute(attributeName, serialized);
264
475
  } catch (error) {
265
- console.warn(`[WSX] Cannot serialize attribute "${key}":`, error);
476
+ logger2.warn(`Cannot serialize attribute "${key}":`, error);
266
477
  }
267
478
  } else {
268
479
  element.setAttribute(attributeName, String(value));
@@ -276,7 +487,7 @@ function setSmartProperty(element, key, value, isSVG = false) {
276
487
  const serialized = JSON.stringify(value);
277
488
  element.setAttribute(attributeName, serialized);
278
489
  } catch (error) {
279
- console.warn(`[WSX] Cannot serialize SVG attribute "${key}":`, error);
490
+ logger2.warn(`Cannot serialize SVG attribute "${key}":`, error);
280
491
  }
281
492
  } else {
282
493
  element.setAttribute(attributeName, String(value));
@@ -300,7 +511,7 @@ function setSmartProperty(element, key, value, isSVG = false) {
300
511
  const serialized = JSON.stringify(value);
301
512
  element.setAttribute(attributeName, serialized);
302
513
  } catch (error) {
303
- console.warn(`[WSX] Cannot serialize readonly property "${key}":`, error);
514
+ logger2.warn(`Cannot serialize readonly property "${key}":`, error);
304
515
  }
305
516
  } else {
306
517
  element.setAttribute(attributeName, String(value));
@@ -315,7 +526,7 @@ function setSmartProperty(element, key, value, isSVG = false) {
315
526
  const serialized = JSON.stringify(value);
316
527
  element.setAttribute(attributeName, serialized);
317
528
  } catch (error) {
318
- console.warn(
529
+ logger2.warn(
319
530
  `[WSX] Cannot serialize property "${key}" for attribute:`,
320
531
  error
321
532
  );
@@ -331,59 +542,76 @@ function setSmartProperty(element, key, value, isSVG = false) {
331
542
  try {
332
543
  const serialized = JSON.stringify(value);
333
544
  if (serialized.length > 1024 * 1024) {
334
- console.warn(
545
+ logger2.warn(
335
546
  `[WSX] Property "${key}" value too large for attribute, consider using a JavaScript property instead`
336
547
  );
337
548
  }
338
549
  element.setAttribute(attributeName, serialized);
339
550
  } catch (error) {
340
- console.warn(`[WSX] Cannot serialize property "${key}" for attribute:`, error);
551
+ logger2.warn(`Cannot serialize property "${key}" for attribute:`, error);
341
552
  }
342
553
  } else {
343
554
  element.setAttribute(attributeName, String(value));
344
555
  }
345
556
  }
346
557
  }
347
- function h(tag, props = {}, ...children) {
348
- if (typeof tag === "function") {
349
- return tag(props, children);
558
+
559
+ // src/utils/element-creation.ts
560
+ function applySingleProp(element, key, value, tag, isSVG) {
561
+ if (value === null || value === void 0 || value === false) {
562
+ return;
350
563
  }
351
- const element = createElement(tag);
352
- if (props) {
353
- const isSVG = shouldUseSVGNamespace(tag);
354
- Object.entries(props).forEach(([key, value]) => {
355
- if (value === null || value === void 0 || value === false) {
356
- return;
357
- }
358
- if (key === "ref" && typeof value === "function") {
359
- value(element);
360
- } else if (key === "className" || key === "class") {
361
- if (isSVG) {
362
- element.setAttribute("class", value);
363
- } else {
364
- element.className = value;
365
- }
366
- } else if (key === "style" && typeof value === "string") {
367
- element.setAttribute("style", value);
368
- } else if (key.startsWith("on") && typeof value === "function") {
369
- const eventName = key.slice(2).toLowerCase();
370
- element.addEventListener(eventName, value);
371
- } else if (typeof value === "boolean") {
372
- if (value) {
373
- element.setAttribute(key, "");
374
- }
375
- } else if (key === "value") {
376
- if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
377
- element.value = String(value);
378
- } else {
379
- const attributeName = isSVG ? getSVGAttributeName(key) : key;
380
- element.setAttribute(attributeName, String(value));
381
- }
382
- } else {
383
- setSmartProperty(element, key, value, isSVG);
384
- }
385
- });
564
+ if (key === "ref" && typeof value === "function") {
565
+ value(element);
566
+ return;
567
+ }
568
+ if (key === "className" || key === "class") {
569
+ if (isSVG) {
570
+ element.setAttribute("class", value);
571
+ } else {
572
+ element.className = value;
573
+ }
574
+ return;
386
575
  }
576
+ if (key === "style" && typeof value === "string") {
577
+ element.setAttribute("style", value);
578
+ return;
579
+ }
580
+ if (key.startsWith("on") && typeof value === "function") {
581
+ const eventName = key.slice(2).toLowerCase();
582
+ element.addEventListener(eventName, value);
583
+ return;
584
+ }
585
+ if (typeof value === "boolean") {
586
+ if (value) {
587
+ element.setAttribute(key, "");
588
+ }
589
+ return;
590
+ }
591
+ if (key === "value") {
592
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
593
+ element.value = String(value);
594
+ } else {
595
+ const attributeName = isSVG ? getSVGAttributeName(key) : key;
596
+ element.setAttribute(attributeName, String(value));
597
+ }
598
+ return;
599
+ }
600
+ if (isFrameworkInternalProp(key)) {
601
+ return;
602
+ }
603
+ setSmartProperty(element, key, value, tag);
604
+ }
605
+ function applyPropsToElement(element, props, tag) {
606
+ if (!props) {
607
+ return;
608
+ }
609
+ const isSVG = shouldUseSVGNamespace(tag);
610
+ Object.entries(props).forEach(([key, value]) => {
611
+ applySingleProp(element, key, value, tag, isSVG);
612
+ });
613
+ }
614
+ function appendChildrenToElement(element, children) {
387
615
  const flatChildren = flattenChildren(children);
388
616
  flatChildren.forEach((child) => {
389
617
  if (child === null || child === void 0 || child === false) {
@@ -397,60 +625,458 @@ function h(tag, props = {}, ...children) {
397
625
  element.appendChild(child);
398
626
  }
399
627
  });
628
+ }
629
+ function createElementWithPropsAndChildren(tag, props, children) {
630
+ const element = createElement(tag);
631
+ applyPropsToElement(element, props, tag);
632
+ appendChildrenToElement(element, children);
400
633
  return element;
401
634
  }
402
- function isHTMLString(str) {
403
- const trimmed = str.trim();
404
- if (!trimmed) return false;
405
- const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
406
- const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
407
- if (looksLikeMath) return false;
408
- return htmlTagPattern.test(trimmed);
635
+
636
+ // src/utils/element-update.ts
637
+ function removeProp(element, key, oldValue, tag) {
638
+ const isSVG = shouldUseSVGNamespace(tag);
639
+ if (key === "ref") {
640
+ return;
641
+ }
642
+ if (key === "className" || key === "class") {
643
+ if (isSVG) {
644
+ element.removeAttribute("class");
645
+ } else {
646
+ element.className = "";
647
+ }
648
+ return;
649
+ }
650
+ if (key === "style") {
651
+ element.removeAttribute("style");
652
+ return;
653
+ }
654
+ if (key.startsWith("on") && typeof oldValue === "function") {
655
+ return;
656
+ }
657
+ if (key === "value") {
658
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
659
+ element.value = "";
660
+ } else {
661
+ const attributeName2 = isSVG ? getSVGAttributeName(key) : key;
662
+ element.removeAttribute(attributeName2);
663
+ }
664
+ return;
665
+ }
666
+ if (isFrameworkInternalProp(key)) {
667
+ return;
668
+ }
669
+ const attributeName = isSVG ? getSVGAttributeName(key) : key;
670
+ element.removeAttribute(attributeName);
671
+ try {
672
+ delete element[key];
673
+ } catch {
674
+ }
409
675
  }
410
- function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
411
- if (depth > 10) {
412
- console.warn(
413
- "[WSX] flattenChildren: Maximum depth exceeded, treating remaining children as text"
414
- );
415
- return children.filter(
416
- (child) => typeof child === "string" || typeof child === "number"
417
- );
676
+ function applySingleProp2(element, key, value, tag, isSVG) {
677
+ if (value === null || value === void 0 || value === false) {
678
+ return;
418
679
  }
419
- const result = [];
420
- for (const child of children) {
421
- if (child === null || child === void 0 || child === false) {
680
+ if (key === "ref" && typeof value === "function") {
681
+ value(element);
682
+ return;
683
+ }
684
+ if (key === "className" || key === "class") {
685
+ if (isSVG) {
686
+ element.setAttribute("class", value);
687
+ } else {
688
+ element.className = value;
689
+ }
690
+ return;
691
+ }
692
+ if (key === "style" && typeof value === "string") {
693
+ element.setAttribute("style", value);
694
+ return;
695
+ }
696
+ if (key.startsWith("on") && typeof value === "function") {
697
+ const eventName = key.slice(2).toLowerCase();
698
+ element.addEventListener(eventName, value);
699
+ return;
700
+ }
701
+ if (typeof value === "boolean") {
702
+ if (value) {
703
+ element.setAttribute(key, "");
704
+ }
705
+ return;
706
+ }
707
+ if (key === "value") {
708
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
709
+ element.value = String(value);
710
+ } else {
711
+ const attributeName = isSVG ? getSVGAttributeName(key) : key;
712
+ element.setAttribute(attributeName, String(value));
713
+ }
714
+ return;
715
+ }
716
+ if (isFrameworkInternalProp(key)) {
717
+ return;
718
+ }
719
+ setSmartProperty(element, key, value, tag);
720
+ }
721
+ function updateProps(element, oldProps, newProps, tag) {
722
+ const isSVG = shouldUseSVGNamespace(tag);
723
+ const old = oldProps || {};
724
+ const new_ = newProps || {};
725
+ for (const key in old) {
726
+ if (!(key in new_)) {
727
+ removeProp(element, key, old[key], tag);
728
+ }
729
+ }
730
+ for (const key in new_) {
731
+ const oldValue = old[key];
732
+ const newValue = new_[key];
733
+ if (oldValue === newValue) {
422
734
  continue;
423
- } else if (Array.isArray(child)) {
424
- result.push(...flattenChildren(child, skipHTMLDetection, depth + 1));
425
- } else if (typeof child === "string") {
426
- if (skipHTMLDetection) {
427
- result.push(child);
428
- } else if (isHTMLString(child)) {
429
- try {
430
- const nodes = parseHTMLToNodes(child);
431
- if (nodes.length > 0) {
432
- for (const node of nodes) {
433
- if (typeof node === "string") {
434
- result.push(node);
735
+ }
736
+ if (oldValue === void 0) {
737
+ applySingleProp2(element, key, newValue, tag, isSVG);
738
+ continue;
739
+ }
740
+ if (typeof oldValue === "object" && oldValue !== null && typeof newValue === "object" && newValue !== null) {
741
+ try {
742
+ const oldJson = JSON.stringify(oldValue);
743
+ const newJson = JSON.stringify(newValue);
744
+ if (oldJson === newJson) {
745
+ continue;
746
+ }
747
+ } catch {
748
+ }
749
+ }
750
+ applySingleProp2(element, key, newValue, tag, isSVG);
751
+ }
752
+ }
753
+ function updateChildren(element, oldChildren, newChildren) {
754
+ const flatOld = flattenChildren(oldChildren);
755
+ const flatNew = flattenChildren(newChildren);
756
+ const minLength = Math.min(flatOld.length, flatNew.length);
757
+ let domIndex = 0;
758
+ for (let i = 0; i < minLength; i++) {
759
+ const oldChild = flatOld[i];
760
+ const newChild = flatNew[i];
761
+ let oldNode = null;
762
+ if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
763
+ if (oldChild.parentNode === element) {
764
+ if (!shouldPreserveElement(oldChild)) {
765
+ oldNode = oldChild;
766
+ }
767
+ } else {
768
+ const oldCacheKey = getElementCacheKey(oldChild);
769
+ if (oldCacheKey) {
770
+ for (let j = 0; j < element.childNodes.length; j++) {
771
+ const domChild = element.childNodes[j];
772
+ if (domChild instanceof HTMLElement || domChild instanceof SVGElement) {
773
+ if (shouldPreserveElement(domChild)) {
774
+ continue;
775
+ }
776
+ const domCacheKey = getElementCacheKey(domChild);
777
+ if (domCacheKey === oldCacheKey) {
778
+ oldNode = domChild;
779
+ break;
780
+ }
781
+ }
782
+ }
783
+ }
784
+ }
785
+ } else if (typeof oldChild === "string" || typeof oldChild === "number") {
786
+ while (domIndex < element.childNodes.length) {
787
+ const node = element.childNodes[domIndex];
788
+ if (node.nodeType === Node.TEXT_NODE) {
789
+ oldNode = node;
790
+ domIndex++;
791
+ break;
792
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
793
+ domIndex++;
794
+ } else {
795
+ domIndex++;
796
+ }
797
+ }
798
+ }
799
+ if (typeof oldChild === "string" || typeof oldChild === "number") {
800
+ if (typeof newChild === "string" || typeof newChild === "number") {
801
+ const oldText = String(oldChild);
802
+ const newText = String(newChild);
803
+ const needsUpdate = oldText !== newText || oldNode && oldNode.nodeType === Node.TEXT_NODE && oldNode.textContent !== newText;
804
+ if (!needsUpdate) {
805
+ continue;
806
+ }
807
+ if (oldNode && oldNode.nodeType === Node.TEXT_NODE) {
808
+ oldNode.textContent = newText;
809
+ } else {
810
+ const newTextNode = document.createTextNode(newText);
811
+ if (oldNode && !shouldPreserveElement(oldNode)) {
812
+ element.replaceChild(newTextNode, oldNode);
813
+ } else {
814
+ element.insertBefore(newTextNode, oldNode || null);
815
+ }
816
+ }
817
+ } else {
818
+ if (oldNode && !shouldPreserveElement(oldNode)) {
819
+ element.removeChild(oldNode);
820
+ }
821
+ if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
822
+ if (newChild.parentNode !== element) {
823
+ element.insertBefore(newChild, oldNode || null);
824
+ }
825
+ } else if (newChild instanceof DocumentFragment) {
826
+ element.insertBefore(newChild, oldNode || null);
827
+ }
828
+ }
829
+ } else if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
830
+ if (oldNode && shouldPreserveElement(oldNode)) {
831
+ continue;
832
+ }
833
+ if (newChild === oldChild) {
834
+ continue;
835
+ } else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
836
+ const oldCacheKey = oldNode && (oldNode instanceof HTMLElement || oldNode instanceof SVGElement) ? getElementCacheKey(oldNode) : null;
837
+ const newCacheKey = getElementCacheKey(newChild);
838
+ const hasSameCacheKey = oldCacheKey && newCacheKey && oldCacheKey === newCacheKey;
839
+ if (oldNode) {
840
+ if (!shouldPreserveElement(oldNode)) {
841
+ if (oldNode !== newChild) {
842
+ if (newChild.parentNode === element) {
843
+ if (hasSameCacheKey) {
844
+ if (newChild !== oldNode) {
845
+ element.replaceChild(newChild, oldNode);
846
+ }
847
+ } else {
848
+ element.removeChild(newChild);
849
+ element.replaceChild(newChild, oldNode);
850
+ }
851
+ } else if (newChild.parentNode) {
852
+ newChild.parentNode.removeChild(newChild);
853
+ element.replaceChild(newChild, oldNode);
435
854
  } else {
436
- result.push(node);
855
+ element.replaceChild(newChild, oldNode);
437
856
  }
438
857
  }
439
858
  } else {
440
- result.push(child);
859
+ if (newChild.parentNode !== element) {
860
+ if (newChild.parentNode) {
861
+ newChild.parentNode.removeChild(newChild);
862
+ }
863
+ element.insertBefore(newChild, oldNode.nextSibling);
864
+ }
865
+ }
866
+ } else {
867
+ if (newChild.parentNode !== element) {
868
+ if (newChild.parentNode) {
869
+ newChild.parentNode.removeChild(newChild);
870
+ }
871
+ element.appendChild(newChild);
441
872
  }
442
- } catch (error) {
443
- console.warn("[WSX] Failed to parse HTML string, treating as text:", error);
444
- result.push(child);
445
873
  }
446
874
  } else {
447
- result.push(child);
875
+ if (oldNode && !shouldPreserveElement(oldNode)) {
876
+ element.removeChild(oldNode);
877
+ }
878
+ if (typeof newChild === "string" || typeof newChild === "number") {
879
+ const newTextNode = document.createTextNode(String(newChild));
880
+ element.insertBefore(newTextNode, oldNode?.nextSibling || null);
881
+ } else if (newChild instanceof DocumentFragment) {
882
+ element.insertBefore(newChild, oldNode?.nextSibling || null);
883
+ }
448
884
  }
449
- } else {
450
- result.push(child);
451
885
  }
452
886
  }
453
- return result;
887
+ for (let i = minLength; i < flatNew.length; i++) {
888
+ const newChild = flatNew[i];
889
+ if (newChild === null || newChild === void 0 || newChild === false) {
890
+ continue;
891
+ }
892
+ if (typeof newChild === "string" || typeof newChild === "number") {
893
+ element.appendChild(document.createTextNode(String(newChild)));
894
+ } else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
895
+ if (newChild.parentNode === element) {
896
+ const currentIndex = Array.from(element.childNodes).indexOf(newChild);
897
+ const expectedIndex = element.childNodes.length - 1;
898
+ if (currentIndex !== expectedIndex) {
899
+ element.removeChild(newChild);
900
+ element.appendChild(newChild);
901
+ }
902
+ continue;
903
+ } else if (newChild.parentNode) {
904
+ newChild.parentNode.removeChild(newChild);
905
+ }
906
+ element.appendChild(newChild);
907
+ } else if (newChild instanceof DocumentFragment) {
908
+ element.appendChild(newChild);
909
+ }
910
+ }
911
+ const nodesToRemove = [];
912
+ const newChildSet = /* @__PURE__ */ new Set();
913
+ const newChildCacheKeyMap = /* @__PURE__ */ new Map();
914
+ for (const child of flatNew) {
915
+ if (child instanceof HTMLElement || child instanceof SVGElement || child instanceof DocumentFragment) {
916
+ newChildSet.add(child);
917
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
918
+ const cacheKey = getElementCacheKey(child);
919
+ if (cacheKey) {
920
+ newChildCacheKeyMap.set(cacheKey, child);
921
+ }
922
+ }
923
+ }
924
+ }
925
+ const processedCacheKeys = /* @__PURE__ */ new Set();
926
+ const newChildToIndexMap = /* @__PURE__ */ new Map();
927
+ for (let i = 0; i < flatNew.length; i++) {
928
+ const child = flatNew[i];
929
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
930
+ newChildToIndexMap.set(child, i);
931
+ }
932
+ }
933
+ for (let i = element.childNodes.length - 1; i >= 0; i--) {
934
+ const child = element.childNodes[i];
935
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
936
+ if (shouldPreserveElement(child)) {
937
+ continue;
938
+ }
939
+ const cacheKey = getElementCacheKey(child);
940
+ if (cacheKey && newChildCacheKeyMap.has(cacheKey) && !processedCacheKeys.has(cacheKey)) {
941
+ processedCacheKeys.add(cacheKey);
942
+ const newChild = newChildCacheKeyMap.get(cacheKey);
943
+ if (child !== newChild) {
944
+ if (newChild.parentNode === element) {
945
+ element.replaceChild(newChild, child);
946
+ } else {
947
+ element.replaceChild(newChild, child);
948
+ }
949
+ } else {
950
+ }
951
+ }
952
+ }
953
+ }
954
+ for (let i = 0; i < element.childNodes.length; i++) {
955
+ const child = element.childNodes[i];
956
+ if (shouldPreserveElement(child)) {
957
+ continue;
958
+ }
959
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
960
+ if (newChildSet.has(child)) {
961
+ continue;
962
+ }
963
+ const cacheKey = getElementCacheKey(child);
964
+ if (cacheKey && newChildCacheKeyMap.has(cacheKey)) {
965
+ continue;
966
+ }
967
+ } else if (child instanceof DocumentFragment) {
968
+ if (newChildSet.has(child)) {
969
+ continue;
970
+ }
971
+ }
972
+ nodesToRemove.push(child);
973
+ }
974
+ for (let i = nodesToRemove.length - 1; i >= 0; i--) {
975
+ const node = nodesToRemove[i];
976
+ if (node.parentNode === element) {
977
+ element.removeChild(node);
978
+ }
979
+ }
980
+ }
981
+ function updateElement(element, newProps, newChildren, tag, cacheManager) {
982
+ const oldMetadata = cacheManager.getMetadata(element);
983
+ const oldProps = oldMetadata?.props || null;
984
+ const oldChildren = oldMetadata?.children || [];
985
+ cacheManager.setMetadata(element, {
986
+ props: newProps || {},
987
+ children: newChildren
988
+ });
989
+ updateProps(element, oldProps, newProps, tag);
990
+ updateChildren(element, oldChildren, newChildren);
991
+ }
992
+
993
+ // src/jsx-factory.ts
994
+ var logger3 = createLogger("JSX Factory");
995
+ function h(tag, props = {}, ...children) {
996
+ if (typeof tag === "function") {
997
+ return tag(props, children);
998
+ }
999
+ const context = RenderContext.getCurrentComponent();
1000
+ const cacheManager = context ? RenderContext.getDOMCache() : null;
1001
+ if (context && cacheManager) {
1002
+ return tryUseCacheOrCreate(tag, props, children, context, cacheManager);
1003
+ }
1004
+ try {
1005
+ const nodeEnv = typeof globalThis.process !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
1006
+ globalThis.process.env?.NODE_ENV;
1007
+ if (nodeEnv === "development") {
1008
+ if (!context) {
1009
+ logger3.debug(
1010
+ `h() called without render context. Tag: "${tag}", ComponentId: "${getComponentId()}"`,
1011
+ {
1012
+ tag,
1013
+ props: props ? Object.keys(props) : [],
1014
+ hasCacheManager: !!cacheManager
1015
+ }
1016
+ );
1017
+ } else if (!cacheManager) {
1018
+ logger3.debug(
1019
+ `h() called with context but no cache manager. Tag: "${tag}", Component: "${context.constructor.name}"`,
1020
+ {
1021
+ tag,
1022
+ component: context.constructor.name
1023
+ }
1024
+ );
1025
+ }
1026
+ }
1027
+ } catch {
1028
+ }
1029
+ const element = createElementWithPropsAndChildren(tag, props, children);
1030
+ const componentId = getComponentId();
1031
+ const cacheKey = generateCacheKey(tag, props, componentId, context || void 0);
1032
+ markElement(element, cacheKey);
1033
+ return element;
1034
+ }
1035
+ function tryUseCacheOrCreate(tag, props, children, context, cacheManager) {
1036
+ try {
1037
+ const componentId = getComponentId();
1038
+ const cacheKey = generateCacheKey(tag, props, componentId, context);
1039
+ const cachedElement = cacheManager.get(cacheKey);
1040
+ if (cachedElement) {
1041
+ const element2 = cachedElement;
1042
+ updateElement(element2, props, children, tag, cacheManager);
1043
+ const isCustomElement = tag.includes("-") && customElements.get(tag);
1044
+ if (isCustomElement && element2.isConnected) {
1045
+ const parent = element2.parentNode;
1046
+ if (parent) {
1047
+ parent.removeChild(element2);
1048
+ parent.appendChild(element2);
1049
+ }
1050
+ }
1051
+ return element2;
1052
+ }
1053
+ const element = createElementWithPropsAndChildren(tag, props, children);
1054
+ cacheManager.set(cacheKey, element);
1055
+ markElement(element, cacheKey);
1056
+ cacheManager.setMetadata(element, {
1057
+ props: props || {},
1058
+ children
1059
+ });
1060
+ return element;
1061
+ } catch (error) {
1062
+ return handleCacheError(error, tag, props, children);
1063
+ }
1064
+ }
1065
+ function handleCacheError(error, tag, props, children) {
1066
+ try {
1067
+ const nodeEnv = typeof globalThis.process !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
1068
+ globalThis.process.env?.NODE_ENV;
1069
+ if (nodeEnv === "development") {
1070
+ logger3.warn("[WSX DOM Cache] Cache error, falling back to create new element:", error);
1071
+ }
1072
+ } catch {
1073
+ }
1074
+ const element = createElementWithPropsAndChildren(tag, props, children);
1075
+ const context = RenderContext.getCurrentComponent();
1076
+ const componentId = getComponentId();
1077
+ const cacheKey = generateCacheKey(tag, props, componentId, context || void 0);
1078
+ markElement(element, cacheKey);
1079
+ return element;
454
1080
  }
455
1081
  function Fragment(_props, children) {
456
1082
  const fragment = document.createDocumentFragment();
@@ -512,7 +1138,7 @@ StyleManager.styleSheets = /* @__PURE__ */ new Map();
512
1138
 
513
1139
  // src/utils/reactive.ts
514
1140
  var import_wsx_logger = require("@wsxjs/wsx-logger");
515
- var logger = (0, import_wsx_logger.createLogger)("ReactiveSystem");
1141
+ var logger4 = (0, import_wsx_logger.createLogger)("ReactiveSystem");
516
1142
  var UpdateScheduler = class {
517
1143
  constructor() {
518
1144
  this.pendingCallbacks = /* @__PURE__ */ new Set();
@@ -541,7 +1167,7 @@ var UpdateScheduler = class {
541
1167
  try {
542
1168
  callback();
543
1169
  } catch (error) {
544
- logger.error("[WSX Reactive] Error in callback:", error);
1170
+ logger4.error("[WSX Reactive] Error in callback:", error);
545
1171
  }
546
1172
  });
547
1173
  }
@@ -683,7 +1309,7 @@ var ReactiveDebug = {
683
1309
  */
684
1310
  log(message, ...args) {
685
1311
  if (this.isEnabled()) {
686
- logger.info(`[WSX Reactive] ${message}`, ...args);
1312
+ logger4.info(`[WSX Reactive] ${message}`, ...args);
687
1313
  }
688
1314
  }
689
1315
  };
@@ -731,6 +1357,108 @@ function reactiveWithDebug(obj, onChange, debugName) {
731
1357
  });
732
1358
  }
733
1359
 
1360
+ // src/dom-cache-manager.ts
1361
+ var logger5 = createLogger("DOMCacheManager");
1362
+ var DOMCacheManager = class {
1363
+ constructor() {
1364
+ // Map<CacheKey, DOMElement>
1365
+ this.cache = /* @__PURE__ */ new Map();
1366
+ // Map<DOMElement, Metadata>
1367
+ // Stores metadata (props, children) for cached elements to support diffing
1368
+ this.metadata = /* @__PURE__ */ new WeakMap();
1369
+ // Track key-parent relationships to detect duplicate keys in all environments
1370
+ // Map<CacheKey, ParentInfo>
1371
+ this.keyParentMap = /* @__PURE__ */ new Map();
1372
+ // Flag to enable duplicate key warnings (enabled by default, critical for correctness)
1373
+ this.warnDuplicateKeys = true;
1374
+ }
1375
+ /**
1376
+ * Retrieves an element from the cache.
1377
+ * @param key The unique cache key.
1378
+ */
1379
+ get(key) {
1380
+ return this.cache.get(key);
1381
+ }
1382
+ /**
1383
+ * Stores an element in the cache.
1384
+ * @param key The unique cache key.
1385
+ * @param element The DOM element to cache.
1386
+ */
1387
+ set(key, element) {
1388
+ if (this.warnDuplicateKeys) {
1389
+ this.checkDuplicateKey(key, element);
1390
+ }
1391
+ this.cache.set(key, element);
1392
+ }
1393
+ /**
1394
+ * Checks if a cache key is being reused in a different parent container.
1395
+ * Runs in all environments to help developers catch key conflicts early.
1396
+ * This is critical for correctness and helps prevent subtle bugs.
1397
+ */
1398
+ checkDuplicateKey(key, element) {
1399
+ const existing = this.keyParentMap.get(key);
1400
+ const currentParent = element.parentElement;
1401
+ if (existing && currentParent) {
1402
+ const currentParentInfo = this.getParentInfo(currentParent);
1403
+ const existingParentInfo = `${existing.parentTag}${existing.parentClass ? "." + existing.parentClass : ""}`;
1404
+ if (currentParentInfo !== existingParentInfo) {
1405
+ logger5.warn(
1406
+ `Duplicate key "${key}" detected in different parent containers!
1407
+ Previous parent: ${existingParentInfo}
1408
+ Current parent: ${currentParentInfo}
1409
+
1410
+ This may cause elements to appear in wrong containers or be moved unexpectedly.
1411
+
1412
+ Solution: Use unique key prefixes for different locations:
1413
+ Example: <wsx-link key="nav-0"> vs <wsx-link key="overflow-0">
1414
+
1415
+ See https://wsxjs.dev/docs/guide/DOM_CACHE_GUIDE for best practices.`
1416
+ );
1417
+ }
1418
+ }
1419
+ if (currentParent) {
1420
+ this.keyParentMap.set(key, {
1421
+ parentTag: currentParent.tagName.toLowerCase(),
1422
+ parentClass: currentParent.className,
1423
+ element
1424
+ });
1425
+ }
1426
+ }
1427
+ /**
1428
+ * Gets a formatted parent container description.
1429
+ */
1430
+ getParentInfo(parent) {
1431
+ const tag = parent.tagName.toLowerCase();
1432
+ const className = parent.className;
1433
+ return `${tag}${className ? "." + className.split(" ")[0] : ""}`;
1434
+ }
1435
+ /**
1436
+ * Checks if a key exists in the cache.
1437
+ */
1438
+ has(key) {
1439
+ return this.cache.has(key);
1440
+ }
1441
+ /**
1442
+ * Clears the cache.
1443
+ * Should be called when component is disconnected or cache is invalidated.
1444
+ */
1445
+ clear() {
1446
+ this.cache.clear();
1447
+ }
1448
+ /**
1449
+ * Stores metadata for an element (e.g. previous props).
1450
+ */
1451
+ setMetadata(element, meta) {
1452
+ this.metadata.set(element, meta);
1453
+ }
1454
+ /**
1455
+ * Retrieves metadata for an element.
1456
+ */
1457
+ getMetadata(element) {
1458
+ return this.metadata.get(element);
1459
+ }
1460
+ };
1461
+
734
1462
  // src/base-component.ts
735
1463
  var BaseComponent = class extends HTMLElement {
736
1464
  constructor(config = {}) {
@@ -738,6 +1466,11 @@ var BaseComponent = class extends HTMLElement {
738
1466
  this.connected = false;
739
1467
  this._isDebugEnabled = false;
740
1468
  this._reactiveStates = /* @__PURE__ */ new Map();
1469
+ /**
1470
+ * DOM Cache Manager for fine-grained updates (RFC 0037)
1471
+ * @internal
1472
+ */
1473
+ this._domCache = new DOMCacheManager();
741
1474
  /**
742
1475
  * 当前捕获的焦点状态(用于在 render 时使用捕获的值)
743
1476
  * @internal - 由 rerender() 方法管理
@@ -802,6 +1535,13 @@ var BaseComponent = class extends HTMLElement {
802
1535
  static get observedAttributes() {
803
1536
  return [];
804
1537
  }
1538
+ /**
1539
+ * Gets the DOMCacheManager instance.
1540
+ * @internal
1541
+ */
1542
+ getDomCache() {
1543
+ return this._domCache;
1544
+ }
805
1545
  /**
806
1546
  * Web Component生命周期:属性变化
807
1547
  */
@@ -1109,7 +1849,7 @@ var BaseComponent = class extends HTMLElement {
1109
1849
 
1110
1850
  // src/web-component.ts
1111
1851
  var import_wsx_logger2 = require("@wsxjs/wsx-logger");
1112
- var logger2 = (0, import_wsx_logger2.createLogger)("WebComponent");
1852
+ var logger6 = (0, import_wsx_logger2.createLogger)("WebComponent");
1113
1853
  var WebComponent = class extends BaseComponent {
1114
1854
  // Initialized by BaseComponent constructor
1115
1855
  constructor(config = {}) {
@@ -1147,7 +1887,7 @@ var WebComponent = class extends BaseComponent {
1147
1887
  const styleName = this.config.styleName || this.constructor.name;
1148
1888
  StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
1149
1889
  }
1150
- const content = this.render();
1890
+ const content = RenderContext.runInContext(this, () => this.render());
1151
1891
  this.shadowRoot.appendChild(content);
1152
1892
  }
1153
1893
  this.initializeEventListeners();
@@ -1158,7 +1898,7 @@ var WebComponent = class extends BaseComponent {
1158
1898
  });
1159
1899
  }
1160
1900
  } catch (error) {
1161
- logger2.error(`Error in connectedCallback:`, error);
1901
+ logger6.error(`Error in connectedCallback:`, error);
1162
1902
  this.renderError(error);
1163
1903
  }
1164
1904
  }
@@ -1211,7 +1951,7 @@ var WebComponent = class extends BaseComponent {
1211
1951
  StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
1212
1952
  }
1213
1953
  }
1214
- const content = this.render();
1954
+ const content = RenderContext.runInContext(this, () => this.render());
1215
1955
  if (focusState && focusState.key && focusState.value !== void 0) {
1216
1956
  const target = content.querySelector(
1217
1957
  `[data-wsx-key="${focusState.key}"]`
@@ -1227,9 +1967,18 @@ var WebComponent = class extends BaseComponent {
1227
1967
  }
1228
1968
  requestAnimationFrame(() => {
1229
1969
  this.shadowRoot.appendChild(content);
1230
- const oldChildren = Array.from(this.shadowRoot.children).filter(
1231
- (child) => child !== content
1232
- );
1970
+ const oldChildren = Array.from(this.shadowRoot.children).filter((child) => {
1971
+ if (child === content) {
1972
+ return false;
1973
+ }
1974
+ if (child instanceof HTMLStyleElement) {
1975
+ return false;
1976
+ }
1977
+ if (shouldPreserveElement(child)) {
1978
+ return false;
1979
+ }
1980
+ return true;
1981
+ });
1233
1982
  oldChildren.forEach((child) => child.remove());
1234
1983
  requestAnimationFrame(() => {
1235
1984
  this.restoreFocusState(focusState);
@@ -1239,7 +1988,7 @@ var WebComponent = class extends BaseComponent {
1239
1988
  });
1240
1989
  });
1241
1990
  } catch (error) {
1242
- logger2.error("Error in _rerender:", error);
1991
+ logger6.error("Error in _rerender:", error);
1243
1992
  this.renderError(error);
1244
1993
  this._isRendering = false;
1245
1994
  }
@@ -1267,7 +2016,7 @@ var WebComponent = class extends BaseComponent {
1267
2016
 
1268
2017
  // src/light-component.ts
1269
2018
  var import_wsx_logger3 = require("@wsxjs/wsx-logger");
1270
- var logger3 = (0, import_wsx_logger3.createLogger)("LightComponent");
2019
+ var logger7 = (0, import_wsx_logger3.createLogger)("LightComponent");
1271
2020
  var LightComponent = class extends BaseComponent {
1272
2021
  // Initialized by BaseComponent constructor
1273
2022
  constructor(config = {}) {
@@ -1309,7 +2058,7 @@ var LightComponent = class extends BaseComponent {
1309
2058
  (child) => child !== styleElement
1310
2059
  );
1311
2060
  childrenToRemove.forEach((child) => child.remove());
1312
- const content = this.render();
2061
+ const content = RenderContext.runInContext(this, () => this.render());
1313
2062
  this.appendChild(content);
1314
2063
  if (styleElement && styleElement !== this.firstChild) {
1315
2064
  this.insertBefore(styleElement, this.firstChild);
@@ -1323,7 +2072,7 @@ var LightComponent = class extends BaseComponent {
1323
2072
  });
1324
2073
  }
1325
2074
  } catch (error) {
1326
- logger3.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
2075
+ logger7.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
1327
2076
  this.renderError(error);
1328
2077
  }
1329
2078
  }
@@ -1371,7 +2120,7 @@ var LightComponent = class extends BaseComponent {
1371
2120
  this._pendingFocusState = focusState;
1372
2121
  const jsxChildren = this.getJSXChildren();
1373
2122
  try {
1374
- const content = this.render();
2123
+ const content = RenderContext.runInContext(this, () => this.render());
1375
2124
  if (focusState && focusState.key && focusState.value !== void 0) {
1376
2125
  const target = content.querySelector(
1377
2126
  `[data-wsx-key="${focusState.key}"]`
@@ -1409,6 +2158,9 @@ var LightComponent = class extends BaseComponent {
1409
2158
  if (child instanceof HTMLElement && jsxChildren.includes(child)) {
1410
2159
  return false;
1411
2160
  }
2161
+ if (shouldPreserveElement(child)) {
2162
+ return false;
2163
+ }
1412
2164
  return true;
1413
2165
  });
1414
2166
  oldChildren.forEach((child) => child.remove());
@@ -1428,7 +2180,7 @@ var LightComponent = class extends BaseComponent {
1428
2180
  });
1429
2181
  });
1430
2182
  } catch (error) {
1431
- logger3.error(`[${this.constructor.name}] Error in _rerender:`, error);
2183
+ logger7.error(`[${this.constructor.name}] Error in _rerender:`, error);
1432
2184
  this.renderError(error);
1433
2185
  this._isRendering = false;
1434
2186
  }
@@ -1550,6 +2302,10 @@ To fix this, please:
1550
2302
  See: https://github.com/wsxjs/wsxjs#setup for more details.`;
1551
2303
  }
1552
2304
  function state(targetOrContext, propertyKey) {
2305
+ const globalProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
2306
+ if (globalProcess?.env?.NODE_ENV === "test") {
2307
+ return;
2308
+ }
1553
2309
  let propertyName = "unknown";
1554
2310
  const propertyKeyIsObject = typeof propertyKey === "object" && propertyKey !== null;
1555
2311
  const targetIsObject = typeof targetOrContext === "object" && targetOrContext !== null;