@wsxjs/wsx-core 0.0.20 → 0.0.21
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-AR3DIDLV.mjs +906 -0
- package/dist/index.js +671 -111
- package/dist/index.mjs +137 -17
- package/dist/jsx-runtime.js +541 -99
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/jsx.js +541 -99
- 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 +73 -451
- package/src/light-component.ts +3 -2
- 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 +377 -0
- package/src/utils/props-utils.ts +307 -0
- package/src/web-component.ts +2 -1
- package/dist/chunk-7FXISNME.mjs +0 -462
- package/dist/tsconfig.tsbuildinfo +0 -1
package/dist/jsx.js
CHANGED
|
@@ -25,6 +25,170 @@ __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 isCreatedByH(element) {
|
|
174
|
+
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
return element[CACHE_KEY_PROP] !== void 0;
|
|
178
|
+
}
|
|
179
|
+
function shouldPreserveElement(element) {
|
|
180
|
+
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
if (!isCreatedByH(element)) {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
if (element.hasAttribute("data-wsx-preserve")) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
28
192
|
// src/utils/svg-utils.ts
|
|
29
193
|
var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
30
194
|
var SVG_ONLY_ELEMENTS = /* @__PURE__ */ new Set([
|
|
@@ -137,21 +301,63 @@ function getSVGAttributeName(attributeName) {
|
|
|
137
301
|
return SVG_ATTRIBUTE_MAP.get(attributeName) || attributeName;
|
|
138
302
|
}
|
|
139
303
|
|
|
140
|
-
// src/utils/
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
304
|
+
// src/utils/logger.ts
|
|
305
|
+
var WSXLogger = class {
|
|
306
|
+
constructor(prefix = "[WSX]", enabled = true, level = "info") {
|
|
307
|
+
this.prefix = prefix;
|
|
308
|
+
this.enabled = enabled;
|
|
309
|
+
this.level = level;
|
|
310
|
+
}
|
|
311
|
+
shouldLog(level) {
|
|
312
|
+
if (!this.enabled) return false;
|
|
313
|
+
const levels = ["debug", "info", "warn", "error"];
|
|
314
|
+
const currentLevelIndex = levels.indexOf(this.level);
|
|
315
|
+
const messageLevelIndex = levels.indexOf(level);
|
|
316
|
+
return messageLevelIndex >= currentLevelIndex;
|
|
317
|
+
}
|
|
318
|
+
debug(message, ...args) {
|
|
319
|
+
if (this.shouldLog("debug")) {
|
|
320
|
+
console.debug(`${this.prefix} ${message}`, ...args);
|
|
150
321
|
}
|
|
151
|
-
}
|
|
322
|
+
}
|
|
323
|
+
info(message, ...args) {
|
|
324
|
+
if (this.shouldLog("info")) {
|
|
325
|
+
console.info(`${this.prefix} ${message}`, ...args);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
warn(message, ...args) {
|
|
329
|
+
if (this.shouldLog("warn")) {
|
|
330
|
+
console.warn(`${this.prefix} ${message}`, ...args);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
error(message, ...args) {
|
|
334
|
+
if (this.shouldLog("error")) {
|
|
335
|
+
console.error(`${this.prefix} ${message}`, ...args);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
var logger = new WSXLogger();
|
|
340
|
+
function createLogger(componentName) {
|
|
341
|
+
return new WSXLogger(`[WSX:${componentName}]`);
|
|
152
342
|
}
|
|
153
343
|
|
|
154
|
-
// src/
|
|
344
|
+
// src/utils/props-utils.ts
|
|
345
|
+
var logger2 = createLogger("Props Utilities");
|
|
346
|
+
function isFrameworkInternalProp(key) {
|
|
347
|
+
if (key === "key") {
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
if (key === "__wsxPositionId" || key === "__wsxIndex") {
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
if (key === "__testId") {
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
if (key === "ref") {
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
155
361
|
function isStandardHTMLAttribute(key) {
|
|
156
362
|
const standardAttributes = /* @__PURE__ */ new Set([
|
|
157
363
|
// 全局属性
|
|
@@ -234,7 +440,8 @@ function isStandardHTMLAttribute(key) {
|
|
|
234
440
|
function isSpecialProperty(key, value) {
|
|
235
441
|
return key === "ref" || key === "className" || key === "class" || key === "style" || key.startsWith("on") && typeof value === "function" || typeof value === "boolean" || key === "value";
|
|
236
442
|
}
|
|
237
|
-
function setSmartProperty(element, key, value,
|
|
443
|
+
function setSmartProperty(element, key, value, tag) {
|
|
444
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
238
445
|
if (isSpecialProperty(key, value)) {
|
|
239
446
|
return;
|
|
240
447
|
}
|
|
@@ -244,13 +451,13 @@ function setSmartProperty(element, key, value, isSVG = false) {
|
|
|
244
451
|
try {
|
|
245
452
|
const serialized = JSON.stringify(value);
|
|
246
453
|
if (serialized.length > 1024 * 1024) {
|
|
247
|
-
|
|
454
|
+
logger2.warn(
|
|
248
455
|
`[WSX] Attribute "${key}" value too large, consider using a non-standard property name instead`
|
|
249
456
|
);
|
|
250
457
|
}
|
|
251
458
|
element.setAttribute(attributeName, serialized);
|
|
252
459
|
} catch (error) {
|
|
253
|
-
|
|
460
|
+
logger2.warn(`Cannot serialize attribute "${key}":`, error);
|
|
254
461
|
}
|
|
255
462
|
} else {
|
|
256
463
|
element.setAttribute(attributeName, String(value));
|
|
@@ -264,7 +471,7 @@ function setSmartProperty(element, key, value, isSVG = false) {
|
|
|
264
471
|
const serialized = JSON.stringify(value);
|
|
265
472
|
element.setAttribute(attributeName, serialized);
|
|
266
473
|
} catch (error) {
|
|
267
|
-
|
|
474
|
+
logger2.warn(`Cannot serialize SVG attribute "${key}":`, error);
|
|
268
475
|
}
|
|
269
476
|
} else {
|
|
270
477
|
element.setAttribute(attributeName, String(value));
|
|
@@ -288,7 +495,7 @@ function setSmartProperty(element, key, value, isSVG = false) {
|
|
|
288
495
|
const serialized = JSON.stringify(value);
|
|
289
496
|
element.setAttribute(attributeName, serialized);
|
|
290
497
|
} catch (error) {
|
|
291
|
-
|
|
498
|
+
logger2.warn(`Cannot serialize readonly property "${key}":`, error);
|
|
292
499
|
}
|
|
293
500
|
} else {
|
|
294
501
|
element.setAttribute(attributeName, String(value));
|
|
@@ -303,7 +510,7 @@ function setSmartProperty(element, key, value, isSVG = false) {
|
|
|
303
510
|
const serialized = JSON.stringify(value);
|
|
304
511
|
element.setAttribute(attributeName, serialized);
|
|
305
512
|
} catch (error) {
|
|
306
|
-
|
|
513
|
+
logger2.warn(
|
|
307
514
|
`[WSX] Cannot serialize property "${key}" for attribute:`,
|
|
308
515
|
error
|
|
309
516
|
);
|
|
@@ -319,59 +526,76 @@ function setSmartProperty(element, key, value, isSVG = false) {
|
|
|
319
526
|
try {
|
|
320
527
|
const serialized = JSON.stringify(value);
|
|
321
528
|
if (serialized.length > 1024 * 1024) {
|
|
322
|
-
|
|
529
|
+
logger2.warn(
|
|
323
530
|
`[WSX] Property "${key}" value too large for attribute, consider using a JavaScript property instead`
|
|
324
531
|
);
|
|
325
532
|
}
|
|
326
533
|
element.setAttribute(attributeName, serialized);
|
|
327
534
|
} catch (error) {
|
|
328
|
-
|
|
535
|
+
logger2.warn(`Cannot serialize property "${key}" for attribute:`, error);
|
|
329
536
|
}
|
|
330
537
|
} else {
|
|
331
538
|
element.setAttribute(attributeName, String(value));
|
|
332
539
|
}
|
|
333
540
|
}
|
|
334
541
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
542
|
+
|
|
543
|
+
// src/utils/element-creation.ts
|
|
544
|
+
function applySingleProp(element, key, value, tag, isSVG) {
|
|
545
|
+
if (value === null || value === void 0 || value === false) {
|
|
546
|
+
return;
|
|
338
547
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
});
|
|
548
|
+
if (key === "ref" && typeof value === "function") {
|
|
549
|
+
value(element);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
if (key === "className" || key === "class") {
|
|
553
|
+
if (isSVG) {
|
|
554
|
+
element.setAttribute("class", value);
|
|
555
|
+
} else {
|
|
556
|
+
element.className = value;
|
|
557
|
+
}
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (key === "style" && typeof value === "string") {
|
|
561
|
+
element.setAttribute("style", value);
|
|
562
|
+
return;
|
|
374
563
|
}
|
|
564
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
565
|
+
const eventName = key.slice(2).toLowerCase();
|
|
566
|
+
element.addEventListener(eventName, value);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
if (typeof value === "boolean") {
|
|
570
|
+
if (value) {
|
|
571
|
+
element.setAttribute(key, "");
|
|
572
|
+
}
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (key === "value") {
|
|
576
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
577
|
+
element.value = String(value);
|
|
578
|
+
} else {
|
|
579
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
580
|
+
element.setAttribute(attributeName, String(value));
|
|
581
|
+
}
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
if (isFrameworkInternalProp(key)) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
setSmartProperty(element, key, value, tag);
|
|
588
|
+
}
|
|
589
|
+
function applyPropsToElement(element, props, tag) {
|
|
590
|
+
if (!props) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
594
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
595
|
+
applySingleProp(element, key, value, tag, isSVG);
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
function appendChildrenToElement(element, children) {
|
|
375
599
|
const flatChildren = flattenChildren(children);
|
|
376
600
|
flatChildren.forEach((child) => {
|
|
377
601
|
if (child === null || child === void 0 || child === false) {
|
|
@@ -385,60 +609,278 @@ function h(tag, props = {}, ...children) {
|
|
|
385
609
|
element.appendChild(child);
|
|
386
610
|
}
|
|
387
611
|
});
|
|
612
|
+
}
|
|
613
|
+
function createElementWithPropsAndChildren(tag, props, children) {
|
|
614
|
+
const element = createElement(tag);
|
|
615
|
+
applyPropsToElement(element, props, tag);
|
|
616
|
+
appendChildrenToElement(element, children);
|
|
388
617
|
return element;
|
|
389
618
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
619
|
+
|
|
620
|
+
// src/utils/element-update.ts
|
|
621
|
+
function removeProp(element, key, oldValue, tag) {
|
|
622
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
623
|
+
if (key === "ref") {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
if (key === "className" || key === "class") {
|
|
627
|
+
if (isSVG) {
|
|
628
|
+
element.removeAttribute("class");
|
|
629
|
+
} else {
|
|
630
|
+
element.className = "";
|
|
631
|
+
}
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
if (key === "style") {
|
|
635
|
+
element.removeAttribute("style");
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
if (key.startsWith("on") && typeof oldValue === "function") {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (key === "value") {
|
|
642
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
643
|
+
element.value = "";
|
|
644
|
+
} else {
|
|
645
|
+
const attributeName2 = isSVG ? getSVGAttributeName(key) : key;
|
|
646
|
+
element.removeAttribute(attributeName2);
|
|
647
|
+
}
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (isFrameworkInternalProp(key)) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
654
|
+
element.removeAttribute(attributeName);
|
|
655
|
+
try {
|
|
656
|
+
delete element[key];
|
|
657
|
+
} catch {
|
|
658
|
+
}
|
|
397
659
|
}
|
|
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
|
-
);
|
|
660
|
+
function applySingleProp2(element, key, value, tag, isSVG) {
|
|
661
|
+
if (value === null || value === void 0 || value === false) {
|
|
662
|
+
return;
|
|
406
663
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
664
|
+
if (key === "ref" && typeof value === "function") {
|
|
665
|
+
value(element);
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
if (key === "className" || key === "class") {
|
|
669
|
+
if (isSVG) {
|
|
670
|
+
element.setAttribute("class", value);
|
|
671
|
+
} else {
|
|
672
|
+
element.className = value;
|
|
673
|
+
}
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (key === "style" && typeof value === "string") {
|
|
677
|
+
element.setAttribute("style", value);
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
681
|
+
const eventName = key.slice(2).toLowerCase();
|
|
682
|
+
element.addEventListener(eventName, value);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
if (typeof value === "boolean") {
|
|
686
|
+
if (value) {
|
|
687
|
+
element.setAttribute(key, "");
|
|
688
|
+
}
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (key === "value") {
|
|
692
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
693
|
+
element.value = String(value);
|
|
694
|
+
} else {
|
|
695
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
696
|
+
element.setAttribute(attributeName, String(value));
|
|
697
|
+
}
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (isFrameworkInternalProp(key)) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
setSmartProperty(element, key, value, tag);
|
|
704
|
+
}
|
|
705
|
+
function updateProps(element, oldProps, newProps, tag) {
|
|
706
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
707
|
+
const old = oldProps || {};
|
|
708
|
+
const new_ = newProps || {};
|
|
709
|
+
for (const key in old) {
|
|
710
|
+
if (!(key in new_)) {
|
|
711
|
+
removeProp(element, key, old[key], tag);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
for (const key in new_) {
|
|
715
|
+
const oldValue = old[key];
|
|
716
|
+
const newValue = new_[key];
|
|
717
|
+
if (oldValue === newValue) {
|
|
410
718
|
continue;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
719
|
+
}
|
|
720
|
+
if (typeof oldValue === "object" && oldValue !== null && typeof newValue === "object" && newValue !== null) {
|
|
721
|
+
if (JSON.stringify(oldValue) === JSON.stringify(newValue)) {
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
applySingleProp2(element, key, newValue, tag, isSVG);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
function updateChildren(element, oldChildren, newChildren) {
|
|
729
|
+
const flatOld = flattenChildren(oldChildren);
|
|
730
|
+
const flatNew = flattenChildren(newChildren);
|
|
731
|
+
const minLength = Math.min(flatOld.length, flatNew.length);
|
|
732
|
+
for (let i = 0; i < minLength; i++) {
|
|
733
|
+
const oldChild = flatOld[i];
|
|
734
|
+
const newChild = flatNew[i];
|
|
735
|
+
if (typeof oldChild === "string" || typeof oldChild === "number") {
|
|
736
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
737
|
+
const textNode = element.childNodes[i];
|
|
738
|
+
if (textNode && textNode.nodeType === Node.TEXT_NODE) {
|
|
739
|
+
textNode.textContent = String(newChild);
|
|
740
|
+
} else {
|
|
741
|
+
const newTextNode = document.createTextNode(String(newChild));
|
|
742
|
+
if (textNode) {
|
|
743
|
+
element.replaceChild(newTextNode, textNode);
|
|
744
|
+
} else {
|
|
745
|
+
element.appendChild(newTextNode);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
} else {
|
|
749
|
+
const textNode = element.childNodes[i];
|
|
750
|
+
if (textNode) {
|
|
751
|
+
if (!shouldPreserveElement(textNode)) {
|
|
752
|
+
element.removeChild(textNode);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
756
|
+
element.appendChild(document.createTextNode(String(newChild)));
|
|
757
|
+
} else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
758
|
+
element.appendChild(newChild);
|
|
759
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
760
|
+
element.appendChild(newChild);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
} else if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
|
|
764
|
+
if (newChild === oldChild) {
|
|
765
|
+
continue;
|
|
766
|
+
} else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
767
|
+
const oldNode = element.childNodes[i];
|
|
768
|
+
if (oldNode) {
|
|
769
|
+
if (!shouldPreserveElement(oldNode)) {
|
|
770
|
+
if (oldNode !== newChild) {
|
|
771
|
+
element.replaceChild(newChild, oldNode);
|
|
426
772
|
}
|
|
427
773
|
} else {
|
|
428
|
-
|
|
774
|
+
if (newChild.parentNode !== element) {
|
|
775
|
+
element.appendChild(newChild);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
} else {
|
|
779
|
+
if (newChild.parentNode !== element) {
|
|
780
|
+
element.appendChild(newChild);
|
|
429
781
|
}
|
|
430
|
-
} catch (error) {
|
|
431
|
-
console.warn("[WSX] Failed to parse HTML string, treating as text:", error);
|
|
432
|
-
result.push(child);
|
|
433
782
|
}
|
|
434
783
|
} else {
|
|
435
|
-
|
|
784
|
+
const oldNode = element.childNodes[i];
|
|
785
|
+
if (oldNode) {
|
|
786
|
+
if (!shouldPreserveElement(oldNode)) {
|
|
787
|
+
element.removeChild(oldNode);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
791
|
+
element.appendChild(document.createTextNode(String(newChild)));
|
|
792
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
793
|
+
element.appendChild(newChild);
|
|
794
|
+
}
|
|
436
795
|
}
|
|
437
|
-
} else {
|
|
438
|
-
result.push(child);
|
|
439
796
|
}
|
|
440
797
|
}
|
|
441
|
-
|
|
798
|
+
for (let i = minLength; i < flatNew.length; i++) {
|
|
799
|
+
const newChild = flatNew[i];
|
|
800
|
+
if (newChild === null || newChild === void 0 || newChild === false) {
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
804
|
+
element.appendChild(document.createTextNode(String(newChild)));
|
|
805
|
+
} else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
806
|
+
if (newChild.parentNode !== element) {
|
|
807
|
+
element.appendChild(newChild);
|
|
808
|
+
}
|
|
809
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
810
|
+
element.appendChild(newChild);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
const nodesToRemove = [];
|
|
814
|
+
for (let i = flatNew.length; i < element.childNodes.length; i++) {
|
|
815
|
+
const child = element.childNodes[i];
|
|
816
|
+
if (!shouldPreserveElement(child)) {
|
|
817
|
+
nodesToRemove.push(child);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
for (let i = nodesToRemove.length - 1; i >= 0; i--) {
|
|
821
|
+
const node = nodesToRemove[i];
|
|
822
|
+
if (node.parentNode === element) {
|
|
823
|
+
element.removeChild(node);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
function updateElement(element, newProps, newChildren, tag, cacheManager) {
|
|
828
|
+
const oldMetadata = cacheManager.getMetadata(element);
|
|
829
|
+
const oldProps = oldMetadata?.props || null;
|
|
830
|
+
const oldChildren = oldMetadata?.children || [];
|
|
831
|
+
updateProps(element, oldProps, newProps, tag);
|
|
832
|
+
updateChildren(element, oldChildren, newChildren);
|
|
833
|
+
cacheManager.setMetadata(element, {
|
|
834
|
+
props: newProps || {},
|
|
835
|
+
children: newChildren
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/jsx-factory.ts
|
|
840
|
+
var logger3 = createLogger("JSX Factory");
|
|
841
|
+
function h(tag, props = {}, ...children) {
|
|
842
|
+
if (typeof tag === "function") {
|
|
843
|
+
return tag(props, children);
|
|
844
|
+
}
|
|
845
|
+
const context = RenderContext.getCurrentComponent();
|
|
846
|
+
const cacheManager = context ? RenderContext.getDOMCache() : null;
|
|
847
|
+
if (context && cacheManager) {
|
|
848
|
+
return tryUseCacheOrCreate(tag, props, children, context, cacheManager);
|
|
849
|
+
}
|
|
850
|
+
return createElementWithPropsAndChildren(tag, props, children);
|
|
851
|
+
}
|
|
852
|
+
function tryUseCacheOrCreate(tag, props, children, context, cacheManager) {
|
|
853
|
+
try {
|
|
854
|
+
const componentId = getComponentId();
|
|
855
|
+
const cacheKey = generateCacheKey(tag, props, componentId, context);
|
|
856
|
+
const cachedElement = cacheManager.get(cacheKey);
|
|
857
|
+
if (cachedElement) {
|
|
858
|
+
const element2 = cachedElement;
|
|
859
|
+
updateElement(element2, props, children, tag, cacheManager);
|
|
860
|
+
return element2;
|
|
861
|
+
}
|
|
862
|
+
const element = createElementWithPropsAndChildren(tag, props, children);
|
|
863
|
+
cacheManager.set(cacheKey, element);
|
|
864
|
+
markElement(element, cacheKey);
|
|
865
|
+
cacheManager.setMetadata(element, {
|
|
866
|
+
props: props || {},
|
|
867
|
+
children
|
|
868
|
+
});
|
|
869
|
+
return element;
|
|
870
|
+
} catch (error) {
|
|
871
|
+
return handleCacheError(error, tag, props, children);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
function handleCacheError(error, tag, props, children) {
|
|
875
|
+
try {
|
|
876
|
+
const nodeEnv = typeof globalThis.process !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
877
|
+
globalThis.process.env?.NODE_ENV;
|
|
878
|
+
if (nodeEnv === "development") {
|
|
879
|
+
logger3.warn("[WSX DOM Cache] Cache error, falling back to create new element:", error);
|
|
880
|
+
}
|
|
881
|
+
} catch {
|
|
882
|
+
}
|
|
883
|
+
return createElementWithPropsAndChildren(tag, props, children);
|
|
442
884
|
}
|
|
443
885
|
function Fragment(_props, children) {
|
|
444
886
|
const fragment = document.createDocumentFragment();
|