@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/chunk-OXFZ575O.mjs +1091 -0
- package/dist/index.js +869 -113
- package/dist/index.mjs +155 -22
- package/dist/jsx-runtime.js +723 -97
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/jsx.js +723 -97
- package/dist/jsx.mjs +1 -1
- package/package.json +2 -2
- package/src/base-component.ts +15 -0
- package/src/dom-cache-manager.ts +135 -0
- package/src/jsx-factory.ts +133 -447
- package/src/light-component.ts +12 -4
- package/src/reactive-decorator.ts +9 -0
- package/src/render-context.ts +40 -0
- package/src/utils/cache-key.ts +114 -0
- package/src/utils/dom-utils.ts +119 -0
- package/src/utils/element-creation.ts +140 -0
- package/src/utils/element-marking.ts +80 -0
- package/src/utils/element-update.ts +633 -0
- package/src/utils/props-utils.ts +307 -0
- package/src/web-component.ts +24 -6
- package/dist/chunk-7FXISNME.mjs +0 -462
- package/dist/tsconfig.tsbuildinfo +0 -1
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/
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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/
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
|
399
|
-
if (
|
|
400
|
-
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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
|
-
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
|
|
843
|
+
element.replaceChild(newChild, oldNode);
|
|
425
844
|
}
|
|
426
845
|
}
|
|
427
846
|
} else {
|
|
428
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|