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