@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/jsx.js CHANGED
@@ -25,6 +25,174 @@ __export(jsx_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(jsx_exports);
27
27
 
28
+ // src/utils/dom-utils.ts
29
+ function parseHTMLToNodes(html) {
30
+ if (!html) return [];
31
+ const temp = document.createElement("div");
32
+ temp.innerHTML = html;
33
+ return Array.from(temp.childNodes).map((node) => {
34
+ if (node instanceof HTMLElement || node instanceof SVGElement) {
35
+ return node;
36
+ } else {
37
+ return node.textContent || "";
38
+ }
39
+ });
40
+ }
41
+ function isHTMLString(str) {
42
+ const trimmed = str.trim();
43
+ if (!trimmed) return false;
44
+ const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
45
+ const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
46
+ if (looksLikeMath) return false;
47
+ return htmlTagPattern.test(trimmed);
48
+ }
49
+ function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
50
+ if (depth > 10) {
51
+ console.warn(
52
+ "[WSX] flattenChildren: Maximum depth exceeded, treating remaining children as text"
53
+ );
54
+ return children.filter(
55
+ (child) => typeof child === "string" || typeof child === "number"
56
+ );
57
+ }
58
+ const result = [];
59
+ for (const child of children) {
60
+ if (child === null || child === void 0 || child === false) {
61
+ continue;
62
+ } else if (Array.isArray(child)) {
63
+ result.push(...flattenChildren(child, skipHTMLDetection, depth + 1));
64
+ } else if (typeof child === "string") {
65
+ if (skipHTMLDetection) {
66
+ result.push(child);
67
+ } else if (isHTMLString(child)) {
68
+ try {
69
+ const nodes = parseHTMLToNodes(child);
70
+ if (nodes.length > 0) {
71
+ for (const node of nodes) {
72
+ if (typeof node === "string") {
73
+ result.push(node);
74
+ } else {
75
+ result.push(node);
76
+ }
77
+ }
78
+ } else {
79
+ result.push(child);
80
+ }
81
+ } catch (error) {
82
+ console.warn("[WSX] Failed to parse HTML string, treating as text:", error);
83
+ result.push(child);
84
+ }
85
+ } else {
86
+ result.push(child);
87
+ }
88
+ } else {
89
+ result.push(child);
90
+ }
91
+ }
92
+ return result;
93
+ }
94
+
95
+ // src/render-context.ts
96
+ var _RenderContext = class _RenderContext {
97
+ /**
98
+ * Executes a function within the context of a component.
99
+ * @param component The component instance currently rendering.
100
+ * @param fn The function to execute (usually the render method).
101
+ */
102
+ static runInContext(component, fn) {
103
+ const prev = _RenderContext.current;
104
+ _RenderContext.current = component;
105
+ try {
106
+ return fn();
107
+ } finally {
108
+ _RenderContext.current = prev;
109
+ }
110
+ }
111
+ /**
112
+ * Gets the currently rendering component.
113
+ */
114
+ static getCurrentComponent() {
115
+ return _RenderContext.current;
116
+ }
117
+ /**
118
+ * Gets the current component's DOM cache.
119
+ */
120
+ static getDOMCache() {
121
+ return _RenderContext.current?.getDomCache();
122
+ }
123
+ };
124
+ _RenderContext.current = null;
125
+ var RenderContext = _RenderContext;
126
+
127
+ // src/utils/cache-key.ts
128
+ var POSITION_ID_KEY = "__wsxPositionId";
129
+ var INDEX_KEY = "__wsxIndex";
130
+ var componentElementCounters = /* @__PURE__ */ new WeakMap();
131
+ var componentIdCache = /* @__PURE__ */ new WeakMap();
132
+ function generateCacheKey(tag, props, componentId, component) {
133
+ const positionId = props?.[POSITION_ID_KEY];
134
+ const userKey = props?.key;
135
+ const index = props?.[INDEX_KEY];
136
+ if (userKey !== void 0 && userKey !== null) {
137
+ return `${componentId}:${tag}:key-${String(userKey)}`;
138
+ }
139
+ if (index !== void 0 && index !== null) {
140
+ return `${componentId}:${tag}:idx-${String(index)}`;
141
+ }
142
+ if (positionId !== void 0 && positionId !== null && positionId !== "no-id") {
143
+ return `${componentId}:${tag}:${String(positionId)}`;
144
+ }
145
+ if (component) {
146
+ let counter = componentElementCounters.get(component) || 0;
147
+ counter++;
148
+ componentElementCounters.set(component, counter);
149
+ return `${componentId}:${tag}:auto-${counter}`;
150
+ }
151
+ return `${componentId}:${tag}:fallback-${Date.now()}-${Math.random()}`;
152
+ }
153
+ function getComponentId() {
154
+ const component = RenderContext.getCurrentComponent();
155
+ if (component) {
156
+ let cachedId = componentIdCache.get(component);
157
+ if (cachedId) {
158
+ return cachedId;
159
+ }
160
+ const instanceId = component._instanceId || "default";
161
+ cachedId = `${component.constructor.name}:${instanceId}`;
162
+ componentIdCache.set(component, cachedId);
163
+ return cachedId;
164
+ }
165
+ return "unknown";
166
+ }
167
+
168
+ // src/utils/element-marking.ts
169
+ var CACHE_KEY_PROP = "__wsxCacheKey";
170
+ function markElement(element, cacheKey) {
171
+ element[CACHE_KEY_PROP] = cacheKey;
172
+ }
173
+ function getElementCacheKey(element) {
174
+ const key = element[CACHE_KEY_PROP];
175
+ return key !== void 0 ? String(key) : null;
176
+ }
177
+ function isCreatedByH(element) {
178
+ if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
179
+ return false;
180
+ }
181
+ return element[CACHE_KEY_PROP] !== void 0;
182
+ }
183
+ function shouldPreserveElement(element) {
184
+ if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
185
+ return true;
186
+ }
187
+ if (!isCreatedByH(element)) {
188
+ return true;
189
+ }
190
+ if (element.hasAttribute("data-wsx-preserve")) {
191
+ return true;
192
+ }
193
+ return false;
194
+ }
195
+
28
196
  // src/utils/svg-utils.ts
29
197
  var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
30
198
  var SVG_ONLY_ELEMENTS = /* @__PURE__ */ new Set([
@@ -137,21 +305,63 @@ function getSVGAttributeName(attributeName) {
137
305
  return SVG_ATTRIBUTE_MAP.get(attributeName) || attributeName;
138
306
  }
139
307
 
140
- // src/utils/dom-utils.ts
141
- function parseHTMLToNodes(html) {
142
- if (!html) return [];
143
- const temp = document.createElement("div");
144
- temp.innerHTML = html;
145
- return Array.from(temp.childNodes).map((node) => {
146
- if (node instanceof HTMLElement || node instanceof SVGElement) {
147
- return node;
148
- } else {
149
- return node.textContent || "";
308
+ // src/utils/logger.ts
309
+ var WSXLogger = class {
310
+ constructor(prefix = "[WSX]", enabled = true, level = "info") {
311
+ this.prefix = prefix;
312
+ this.enabled = enabled;
313
+ this.level = level;
314
+ }
315
+ shouldLog(level) {
316
+ if (!this.enabled) return false;
317
+ const levels = ["debug", "info", "warn", "error"];
318
+ const currentLevelIndex = levels.indexOf(this.level);
319
+ const messageLevelIndex = levels.indexOf(level);
320
+ return messageLevelIndex >= currentLevelIndex;
321
+ }
322
+ debug(message, ...args) {
323
+ if (this.shouldLog("debug")) {
324
+ console.debug(`${this.prefix} ${message}`, ...args);
150
325
  }
151
- });
326
+ }
327
+ info(message, ...args) {
328
+ if (this.shouldLog("info")) {
329
+ console.info(`${this.prefix} ${message}`, ...args);
330
+ }
331
+ }
332
+ warn(message, ...args) {
333
+ if (this.shouldLog("warn")) {
334
+ console.warn(`${this.prefix} ${message}`, ...args);
335
+ }
336
+ }
337
+ error(message, ...args) {
338
+ if (this.shouldLog("error")) {
339
+ console.error(`${this.prefix} ${message}`, ...args);
340
+ }
341
+ }
342
+ };
343
+ var logger = new WSXLogger();
344
+ function createLogger(componentName) {
345
+ return new WSXLogger(`[WSX:${componentName}]`);
152
346
  }
153
347
 
154
- // src/jsx-factory.ts
348
+ // src/utils/props-utils.ts
349
+ var logger2 = createLogger("Props Utilities");
350
+ function isFrameworkInternalProp(key) {
351
+ if (key === "key") {
352
+ return true;
353
+ }
354
+ if (key === "__wsxPositionId" || key === "__wsxIndex") {
355
+ return true;
356
+ }
357
+ if (key === "__testId") {
358
+ return true;
359
+ }
360
+ if (key === "ref") {
361
+ return true;
362
+ }
363
+ return false;
364
+ }
155
365
  function isStandardHTMLAttribute(key) {
156
366
  const standardAttributes = /* @__PURE__ */ new Set([
157
367
  // 全局属性
@@ -234,7 +444,8 @@ function isStandardHTMLAttribute(key) {
234
444
  function isSpecialProperty(key, value) {
235
445
  return key === "ref" || key === "className" || key === "class" || key === "style" || key.startsWith("on") && typeof value === "function" || typeof value === "boolean" || key === "value";
236
446
  }
237
- function setSmartProperty(element, key, value, isSVG = false) {
447
+ function setSmartProperty(element, key, value, tag) {
448
+ const isSVG = shouldUseSVGNamespace(tag);
238
449
  if (isSpecialProperty(key, value)) {
239
450
  return;
240
451
  }
@@ -244,13 +455,13 @@ function setSmartProperty(element, key, value, isSVG = false) {
244
455
  try {
245
456
  const serialized = JSON.stringify(value);
246
457
  if (serialized.length > 1024 * 1024) {
247
- console.warn(
458
+ logger2.warn(
248
459
  `[WSX] Attribute "${key}" value too large, consider using a non-standard property name instead`
249
460
  );
250
461
  }
251
462
  element.setAttribute(attributeName, serialized);
252
463
  } catch (error) {
253
- console.warn(`[WSX] Cannot serialize attribute "${key}":`, error);
464
+ logger2.warn(`Cannot serialize attribute "${key}":`, error);
254
465
  }
255
466
  } else {
256
467
  element.setAttribute(attributeName, String(value));
@@ -264,7 +475,7 @@ function setSmartProperty(element, key, value, isSVG = false) {
264
475
  const serialized = JSON.stringify(value);
265
476
  element.setAttribute(attributeName, serialized);
266
477
  } catch (error) {
267
- console.warn(`[WSX] Cannot serialize SVG attribute "${key}":`, error);
478
+ logger2.warn(`Cannot serialize SVG attribute "${key}":`, error);
268
479
  }
269
480
  } else {
270
481
  element.setAttribute(attributeName, String(value));
@@ -288,7 +499,7 @@ function setSmartProperty(element, key, value, isSVG = false) {
288
499
  const serialized = JSON.stringify(value);
289
500
  element.setAttribute(attributeName, serialized);
290
501
  } catch (error) {
291
- console.warn(`[WSX] Cannot serialize readonly property "${key}":`, error);
502
+ logger2.warn(`Cannot serialize readonly property "${key}":`, error);
292
503
  }
293
504
  } else {
294
505
  element.setAttribute(attributeName, String(value));
@@ -303,7 +514,7 @@ function setSmartProperty(element, key, value, isSVG = false) {
303
514
  const serialized = JSON.stringify(value);
304
515
  element.setAttribute(attributeName, serialized);
305
516
  } catch (error) {
306
- console.warn(
517
+ logger2.warn(
307
518
  `[WSX] Cannot serialize property "${key}" for attribute:`,
308
519
  error
309
520
  );
@@ -319,59 +530,76 @@ function setSmartProperty(element, key, value, isSVG = false) {
319
530
  try {
320
531
  const serialized = JSON.stringify(value);
321
532
  if (serialized.length > 1024 * 1024) {
322
- console.warn(
533
+ logger2.warn(
323
534
  `[WSX] Property "${key}" value too large for attribute, consider using a JavaScript property instead`
324
535
  );
325
536
  }
326
537
  element.setAttribute(attributeName, serialized);
327
538
  } catch (error) {
328
- console.warn(`[WSX] Cannot serialize property "${key}" for attribute:`, error);
539
+ logger2.warn(`Cannot serialize property "${key}" for attribute:`, error);
329
540
  }
330
541
  } else {
331
542
  element.setAttribute(attributeName, String(value));
332
543
  }
333
544
  }
334
545
  }
335
- function h(tag, props = {}, ...children) {
336
- if (typeof tag === "function") {
337
- return tag(props, children);
546
+
547
+ // src/utils/element-creation.ts
548
+ function applySingleProp(element, key, value, tag, isSVG) {
549
+ if (value === null || value === void 0 || value === false) {
550
+ return;
338
551
  }
339
- const element = createElement(tag);
340
- if (props) {
341
- const isSVG = shouldUseSVGNamespace(tag);
342
- Object.entries(props).forEach(([key, value]) => {
343
- if (value === null || value === void 0 || value === false) {
344
- return;
345
- }
346
- if (key === "ref" && typeof value === "function") {
347
- value(element);
348
- } else if (key === "className" || key === "class") {
349
- if (isSVG) {
350
- element.setAttribute("class", value);
351
- } else {
352
- element.className = value;
353
- }
354
- } else if (key === "style" && typeof value === "string") {
355
- element.setAttribute("style", value);
356
- } else if (key.startsWith("on") && typeof value === "function") {
357
- const eventName = key.slice(2).toLowerCase();
358
- element.addEventListener(eventName, value);
359
- } else if (typeof value === "boolean") {
360
- if (value) {
361
- element.setAttribute(key, "");
362
- }
363
- } else if (key === "value") {
364
- if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
365
- element.value = String(value);
366
- } else {
367
- const attributeName = isSVG ? getSVGAttributeName(key) : key;
368
- element.setAttribute(attributeName, String(value));
369
- }
370
- } else {
371
- setSmartProperty(element, key, value, isSVG);
372
- }
373
- });
552
+ if (key === "ref" && typeof value === "function") {
553
+ value(element);
554
+ return;
555
+ }
556
+ if (key === "className" || key === "class") {
557
+ if (isSVG) {
558
+ element.setAttribute("class", value);
559
+ } else {
560
+ element.className = value;
561
+ }
562
+ return;
374
563
  }
564
+ if (key === "style" && typeof value === "string") {
565
+ element.setAttribute("style", value);
566
+ return;
567
+ }
568
+ if (key.startsWith("on") && typeof value === "function") {
569
+ const eventName = key.slice(2).toLowerCase();
570
+ element.addEventListener(eventName, value);
571
+ return;
572
+ }
573
+ if (typeof value === "boolean") {
574
+ if (value) {
575
+ element.setAttribute(key, "");
576
+ }
577
+ return;
578
+ }
579
+ if (key === "value") {
580
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
581
+ element.value = String(value);
582
+ } else {
583
+ const attributeName = isSVG ? getSVGAttributeName(key) : key;
584
+ element.setAttribute(attributeName, String(value));
585
+ }
586
+ return;
587
+ }
588
+ if (isFrameworkInternalProp(key)) {
589
+ return;
590
+ }
591
+ setSmartProperty(element, key, value, tag);
592
+ }
593
+ function applyPropsToElement(element, props, tag) {
594
+ if (!props) {
595
+ return;
596
+ }
597
+ const isSVG = shouldUseSVGNamespace(tag);
598
+ Object.entries(props).forEach(([key, value]) => {
599
+ applySingleProp(element, key, value, tag, isSVG);
600
+ });
601
+ }
602
+ function appendChildrenToElement(element, children) {
375
603
  const flatChildren = flattenChildren(children);
376
604
  flatChildren.forEach((child) => {
377
605
  if (child === null || child === void 0 || child === false) {
@@ -385,60 +613,458 @@ function h(tag, props = {}, ...children) {
385
613
  element.appendChild(child);
386
614
  }
387
615
  });
616
+ }
617
+ function createElementWithPropsAndChildren(tag, props, children) {
618
+ const element = createElement(tag);
619
+ applyPropsToElement(element, props, tag);
620
+ appendChildrenToElement(element, children);
388
621
  return element;
389
622
  }
390
- function isHTMLString(str) {
391
- const trimmed = str.trim();
392
- if (!trimmed) return false;
393
- const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
394
- const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
395
- if (looksLikeMath) return false;
396
- return htmlTagPattern.test(trimmed);
623
+
624
+ // src/utils/element-update.ts
625
+ function removeProp(element, key, oldValue, tag) {
626
+ const isSVG = shouldUseSVGNamespace(tag);
627
+ if (key === "ref") {
628
+ return;
629
+ }
630
+ if (key === "className" || key === "class") {
631
+ if (isSVG) {
632
+ element.removeAttribute("class");
633
+ } else {
634
+ element.className = "";
635
+ }
636
+ return;
637
+ }
638
+ if (key === "style") {
639
+ element.removeAttribute("style");
640
+ return;
641
+ }
642
+ if (key.startsWith("on") && typeof oldValue === "function") {
643
+ return;
644
+ }
645
+ if (key === "value") {
646
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
647
+ element.value = "";
648
+ } else {
649
+ const attributeName2 = isSVG ? getSVGAttributeName(key) : key;
650
+ element.removeAttribute(attributeName2);
651
+ }
652
+ return;
653
+ }
654
+ if (isFrameworkInternalProp(key)) {
655
+ return;
656
+ }
657
+ const attributeName = isSVG ? getSVGAttributeName(key) : key;
658
+ element.removeAttribute(attributeName);
659
+ try {
660
+ delete element[key];
661
+ } catch {
662
+ }
397
663
  }
398
- function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
399
- if (depth > 10) {
400
- console.warn(
401
- "[WSX] flattenChildren: Maximum depth exceeded, treating remaining children as text"
402
- );
403
- return children.filter(
404
- (child) => typeof child === "string" || typeof child === "number"
405
- );
664
+ function applySingleProp2(element, key, value, tag, isSVG) {
665
+ if (value === null || value === void 0 || value === false) {
666
+ return;
406
667
  }
407
- const result = [];
408
- for (const child of children) {
409
- if (child === null || child === void 0 || child === false) {
668
+ if (key === "ref" && typeof value === "function") {
669
+ value(element);
670
+ return;
671
+ }
672
+ if (key === "className" || key === "class") {
673
+ if (isSVG) {
674
+ element.setAttribute("class", value);
675
+ } else {
676
+ element.className = value;
677
+ }
678
+ return;
679
+ }
680
+ if (key === "style" && typeof value === "string") {
681
+ element.setAttribute("style", value);
682
+ return;
683
+ }
684
+ if (key.startsWith("on") && typeof value === "function") {
685
+ const eventName = key.slice(2).toLowerCase();
686
+ element.addEventListener(eventName, value);
687
+ return;
688
+ }
689
+ if (typeof value === "boolean") {
690
+ if (value) {
691
+ element.setAttribute(key, "");
692
+ }
693
+ return;
694
+ }
695
+ if (key === "value") {
696
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
697
+ element.value = String(value);
698
+ } else {
699
+ const attributeName = isSVG ? getSVGAttributeName(key) : key;
700
+ element.setAttribute(attributeName, String(value));
701
+ }
702
+ return;
703
+ }
704
+ if (isFrameworkInternalProp(key)) {
705
+ return;
706
+ }
707
+ setSmartProperty(element, key, value, tag);
708
+ }
709
+ function updateProps(element, oldProps, newProps, tag) {
710
+ const isSVG = shouldUseSVGNamespace(tag);
711
+ const old = oldProps || {};
712
+ const new_ = newProps || {};
713
+ for (const key in old) {
714
+ if (!(key in new_)) {
715
+ removeProp(element, key, old[key], tag);
716
+ }
717
+ }
718
+ for (const key in new_) {
719
+ const oldValue = old[key];
720
+ const newValue = new_[key];
721
+ if (oldValue === newValue) {
410
722
  continue;
411
- } else if (Array.isArray(child)) {
412
- result.push(...flattenChildren(child, skipHTMLDetection, depth + 1));
413
- } else if (typeof child === "string") {
414
- if (skipHTMLDetection) {
415
- result.push(child);
416
- } else if (isHTMLString(child)) {
417
- try {
418
- const nodes = parseHTMLToNodes(child);
419
- if (nodes.length > 0) {
420
- for (const node of nodes) {
421
- if (typeof node === "string") {
422
- result.push(node);
723
+ }
724
+ if (oldValue === void 0) {
725
+ applySingleProp2(element, key, newValue, tag, isSVG);
726
+ continue;
727
+ }
728
+ if (typeof oldValue === "object" && oldValue !== null && typeof newValue === "object" && newValue !== null) {
729
+ try {
730
+ const oldJson = JSON.stringify(oldValue);
731
+ const newJson = JSON.stringify(newValue);
732
+ if (oldJson === newJson) {
733
+ continue;
734
+ }
735
+ } catch {
736
+ }
737
+ }
738
+ applySingleProp2(element, key, newValue, tag, isSVG);
739
+ }
740
+ }
741
+ function updateChildren(element, oldChildren, newChildren) {
742
+ const flatOld = flattenChildren(oldChildren);
743
+ const flatNew = flattenChildren(newChildren);
744
+ const minLength = Math.min(flatOld.length, flatNew.length);
745
+ let domIndex = 0;
746
+ for (let i = 0; i < minLength; i++) {
747
+ const oldChild = flatOld[i];
748
+ const newChild = flatNew[i];
749
+ let oldNode = null;
750
+ if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
751
+ if (oldChild.parentNode === element) {
752
+ if (!shouldPreserveElement(oldChild)) {
753
+ oldNode = oldChild;
754
+ }
755
+ } else {
756
+ const oldCacheKey = getElementCacheKey(oldChild);
757
+ if (oldCacheKey) {
758
+ for (let j = 0; j < element.childNodes.length; j++) {
759
+ const domChild = element.childNodes[j];
760
+ if (domChild instanceof HTMLElement || domChild instanceof SVGElement) {
761
+ if (shouldPreserveElement(domChild)) {
762
+ continue;
763
+ }
764
+ const domCacheKey = getElementCacheKey(domChild);
765
+ if (domCacheKey === oldCacheKey) {
766
+ oldNode = domChild;
767
+ break;
768
+ }
769
+ }
770
+ }
771
+ }
772
+ }
773
+ } else if (typeof oldChild === "string" || typeof oldChild === "number") {
774
+ while (domIndex < element.childNodes.length) {
775
+ const node = element.childNodes[domIndex];
776
+ if (node.nodeType === Node.TEXT_NODE) {
777
+ oldNode = node;
778
+ domIndex++;
779
+ break;
780
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
781
+ domIndex++;
782
+ } else {
783
+ domIndex++;
784
+ }
785
+ }
786
+ }
787
+ if (typeof oldChild === "string" || typeof oldChild === "number") {
788
+ if (typeof newChild === "string" || typeof newChild === "number") {
789
+ const oldText = String(oldChild);
790
+ const newText = String(newChild);
791
+ const needsUpdate = oldText !== newText || oldNode && oldNode.nodeType === Node.TEXT_NODE && oldNode.textContent !== newText;
792
+ if (!needsUpdate) {
793
+ continue;
794
+ }
795
+ if (oldNode && oldNode.nodeType === Node.TEXT_NODE) {
796
+ oldNode.textContent = newText;
797
+ } else {
798
+ const newTextNode = document.createTextNode(newText);
799
+ if (oldNode && !shouldPreserveElement(oldNode)) {
800
+ element.replaceChild(newTextNode, oldNode);
801
+ } else {
802
+ element.insertBefore(newTextNode, oldNode || null);
803
+ }
804
+ }
805
+ } else {
806
+ if (oldNode && !shouldPreserveElement(oldNode)) {
807
+ element.removeChild(oldNode);
808
+ }
809
+ if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
810
+ if (newChild.parentNode !== element) {
811
+ element.insertBefore(newChild, oldNode || null);
812
+ }
813
+ } else if (newChild instanceof DocumentFragment) {
814
+ element.insertBefore(newChild, oldNode || null);
815
+ }
816
+ }
817
+ } else if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
818
+ if (oldNode && shouldPreserveElement(oldNode)) {
819
+ continue;
820
+ }
821
+ if (newChild === oldChild) {
822
+ continue;
823
+ } else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
824
+ const oldCacheKey = oldNode && (oldNode instanceof HTMLElement || oldNode instanceof SVGElement) ? getElementCacheKey(oldNode) : null;
825
+ const newCacheKey = getElementCacheKey(newChild);
826
+ const hasSameCacheKey = oldCacheKey && newCacheKey && oldCacheKey === newCacheKey;
827
+ if (oldNode) {
828
+ if (!shouldPreserveElement(oldNode)) {
829
+ if (oldNode !== newChild) {
830
+ if (newChild.parentNode === element) {
831
+ if (hasSameCacheKey) {
832
+ if (newChild !== oldNode) {
833
+ element.replaceChild(newChild, oldNode);
834
+ }
835
+ } else {
836
+ element.removeChild(newChild);
837
+ element.replaceChild(newChild, oldNode);
838
+ }
839
+ } else if (newChild.parentNode) {
840
+ newChild.parentNode.removeChild(newChild);
841
+ element.replaceChild(newChild, oldNode);
423
842
  } else {
424
- result.push(node);
843
+ element.replaceChild(newChild, oldNode);
425
844
  }
426
845
  }
427
846
  } else {
428
- result.push(child);
847
+ if (newChild.parentNode !== element) {
848
+ if (newChild.parentNode) {
849
+ newChild.parentNode.removeChild(newChild);
850
+ }
851
+ element.insertBefore(newChild, oldNode.nextSibling);
852
+ }
853
+ }
854
+ } else {
855
+ if (newChild.parentNode !== element) {
856
+ if (newChild.parentNode) {
857
+ newChild.parentNode.removeChild(newChild);
858
+ }
859
+ element.appendChild(newChild);
429
860
  }
430
- } catch (error) {
431
- console.warn("[WSX] Failed to parse HTML string, treating as text:", error);
432
- result.push(child);
433
861
  }
434
862
  } else {
435
- result.push(child);
863
+ if (oldNode && !shouldPreserveElement(oldNode)) {
864
+ element.removeChild(oldNode);
865
+ }
866
+ if (typeof newChild === "string" || typeof newChild === "number") {
867
+ const newTextNode = document.createTextNode(String(newChild));
868
+ element.insertBefore(newTextNode, oldNode?.nextSibling || null);
869
+ } else if (newChild instanceof DocumentFragment) {
870
+ element.insertBefore(newChild, oldNode?.nextSibling || null);
871
+ }
436
872
  }
437
- } else {
438
- result.push(child);
439
873
  }
440
874
  }
441
- return result;
875
+ for (let i = minLength; i < flatNew.length; i++) {
876
+ const newChild = flatNew[i];
877
+ if (newChild === null || newChild === void 0 || newChild === false) {
878
+ continue;
879
+ }
880
+ if (typeof newChild === "string" || typeof newChild === "number") {
881
+ element.appendChild(document.createTextNode(String(newChild)));
882
+ } else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
883
+ if (newChild.parentNode === element) {
884
+ const currentIndex = Array.from(element.childNodes).indexOf(newChild);
885
+ const expectedIndex = element.childNodes.length - 1;
886
+ if (currentIndex !== expectedIndex) {
887
+ element.removeChild(newChild);
888
+ element.appendChild(newChild);
889
+ }
890
+ continue;
891
+ } else if (newChild.parentNode) {
892
+ newChild.parentNode.removeChild(newChild);
893
+ }
894
+ element.appendChild(newChild);
895
+ } else if (newChild instanceof DocumentFragment) {
896
+ element.appendChild(newChild);
897
+ }
898
+ }
899
+ const nodesToRemove = [];
900
+ const newChildSet = /* @__PURE__ */ new Set();
901
+ const newChildCacheKeyMap = /* @__PURE__ */ new Map();
902
+ for (const child of flatNew) {
903
+ if (child instanceof HTMLElement || child instanceof SVGElement || child instanceof DocumentFragment) {
904
+ newChildSet.add(child);
905
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
906
+ const cacheKey = getElementCacheKey(child);
907
+ if (cacheKey) {
908
+ newChildCacheKeyMap.set(cacheKey, child);
909
+ }
910
+ }
911
+ }
912
+ }
913
+ const processedCacheKeys = /* @__PURE__ */ new Set();
914
+ const newChildToIndexMap = /* @__PURE__ */ new Map();
915
+ for (let i = 0; i < flatNew.length; i++) {
916
+ const child = flatNew[i];
917
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
918
+ newChildToIndexMap.set(child, i);
919
+ }
920
+ }
921
+ for (let i = element.childNodes.length - 1; i >= 0; i--) {
922
+ const child = element.childNodes[i];
923
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
924
+ if (shouldPreserveElement(child)) {
925
+ continue;
926
+ }
927
+ const cacheKey = getElementCacheKey(child);
928
+ if (cacheKey && newChildCacheKeyMap.has(cacheKey) && !processedCacheKeys.has(cacheKey)) {
929
+ processedCacheKeys.add(cacheKey);
930
+ const newChild = newChildCacheKeyMap.get(cacheKey);
931
+ if (child !== newChild) {
932
+ if (newChild.parentNode === element) {
933
+ element.replaceChild(newChild, child);
934
+ } else {
935
+ element.replaceChild(newChild, child);
936
+ }
937
+ } else {
938
+ }
939
+ }
940
+ }
941
+ }
942
+ for (let i = 0; i < element.childNodes.length; i++) {
943
+ const child = element.childNodes[i];
944
+ if (shouldPreserveElement(child)) {
945
+ continue;
946
+ }
947
+ if (child instanceof HTMLElement || child instanceof SVGElement) {
948
+ if (newChildSet.has(child)) {
949
+ continue;
950
+ }
951
+ const cacheKey = getElementCacheKey(child);
952
+ if (cacheKey && newChildCacheKeyMap.has(cacheKey)) {
953
+ continue;
954
+ }
955
+ } else if (child instanceof DocumentFragment) {
956
+ if (newChildSet.has(child)) {
957
+ continue;
958
+ }
959
+ }
960
+ nodesToRemove.push(child);
961
+ }
962
+ for (let i = nodesToRemove.length - 1; i >= 0; i--) {
963
+ const node = nodesToRemove[i];
964
+ if (node.parentNode === element) {
965
+ element.removeChild(node);
966
+ }
967
+ }
968
+ }
969
+ function updateElement(element, newProps, newChildren, tag, cacheManager) {
970
+ const oldMetadata = cacheManager.getMetadata(element);
971
+ const oldProps = oldMetadata?.props || null;
972
+ const oldChildren = oldMetadata?.children || [];
973
+ cacheManager.setMetadata(element, {
974
+ props: newProps || {},
975
+ children: newChildren
976
+ });
977
+ updateProps(element, oldProps, newProps, tag);
978
+ updateChildren(element, oldChildren, newChildren);
979
+ }
980
+
981
+ // src/jsx-factory.ts
982
+ var logger3 = createLogger("JSX Factory");
983
+ function h(tag, props = {}, ...children) {
984
+ if (typeof tag === "function") {
985
+ return tag(props, children);
986
+ }
987
+ const context = RenderContext.getCurrentComponent();
988
+ const cacheManager = context ? RenderContext.getDOMCache() : null;
989
+ if (context && cacheManager) {
990
+ return tryUseCacheOrCreate(tag, props, children, context, cacheManager);
991
+ }
992
+ try {
993
+ const nodeEnv = typeof globalThis.process !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
994
+ globalThis.process.env?.NODE_ENV;
995
+ if (nodeEnv === "development") {
996
+ if (!context) {
997
+ logger3.debug(
998
+ `h() called without render context. Tag: "${tag}", ComponentId: "${getComponentId()}"`,
999
+ {
1000
+ tag,
1001
+ props: props ? Object.keys(props) : [],
1002
+ hasCacheManager: !!cacheManager
1003
+ }
1004
+ );
1005
+ } else if (!cacheManager) {
1006
+ logger3.debug(
1007
+ `h() called with context but no cache manager. Tag: "${tag}", Component: "${context.constructor.name}"`,
1008
+ {
1009
+ tag,
1010
+ component: context.constructor.name
1011
+ }
1012
+ );
1013
+ }
1014
+ }
1015
+ } catch {
1016
+ }
1017
+ const element = createElementWithPropsAndChildren(tag, props, children);
1018
+ const componentId = getComponentId();
1019
+ const cacheKey = generateCacheKey(tag, props, componentId, context || void 0);
1020
+ markElement(element, cacheKey);
1021
+ return element;
1022
+ }
1023
+ function tryUseCacheOrCreate(tag, props, children, context, cacheManager) {
1024
+ try {
1025
+ const componentId = getComponentId();
1026
+ const cacheKey = generateCacheKey(tag, props, componentId, context);
1027
+ const cachedElement = cacheManager.get(cacheKey);
1028
+ if (cachedElement) {
1029
+ const element2 = cachedElement;
1030
+ updateElement(element2, props, children, tag, cacheManager);
1031
+ const isCustomElement = tag.includes("-") && customElements.get(tag);
1032
+ if (isCustomElement && element2.isConnected) {
1033
+ const parent = element2.parentNode;
1034
+ if (parent) {
1035
+ parent.removeChild(element2);
1036
+ parent.appendChild(element2);
1037
+ }
1038
+ }
1039
+ return element2;
1040
+ }
1041
+ const element = createElementWithPropsAndChildren(tag, props, children);
1042
+ cacheManager.set(cacheKey, element);
1043
+ markElement(element, cacheKey);
1044
+ cacheManager.setMetadata(element, {
1045
+ props: props || {},
1046
+ children
1047
+ });
1048
+ return element;
1049
+ } catch (error) {
1050
+ return handleCacheError(error, tag, props, children);
1051
+ }
1052
+ }
1053
+ function handleCacheError(error, tag, props, children) {
1054
+ try {
1055
+ const nodeEnv = typeof globalThis.process !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
1056
+ globalThis.process.env?.NODE_ENV;
1057
+ if (nodeEnv === "development") {
1058
+ logger3.warn("[WSX DOM Cache] Cache error, falling back to create new element:", error);
1059
+ }
1060
+ } catch {
1061
+ }
1062
+ const element = createElementWithPropsAndChildren(tag, props, children);
1063
+ const context = RenderContext.getCurrentComponent();
1064
+ const componentId = getComponentId();
1065
+ const cacheKey = generateCacheKey(tag, props, componentId, context || void 0);
1066
+ markElement(element, cacheKey);
1067
+ return element;
442
1068
  }
443
1069
  function Fragment(_props, children) {
444
1070
  const fragment = document.createDocumentFragment();