@wsxjs/wsx-core 0.0.27 → 0.0.30
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-2ER76KOQ.mjs +1422 -0
- package/dist/chunk-34PNC5CJ.mjs +1307 -0
- package/dist/chunk-FAPFH5ON.mjs +1372 -0
- package/dist/chunk-HWJ7GZD6.mjs +1327 -0
- package/dist/chunk-U74WFVRE.mjs +1308 -0
- package/dist/chunk-UTWWJJ4C.mjs +1360 -0
- package/dist/chunk-ZY36MEHX.mjs +1306 -0
- package/dist/index.js +259 -138
- package/dist/index.mjs +49 -32
- package/dist/jsx-runtime.js +163 -107
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/jsx.js +163 -107
- package/dist/jsx.mjs +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/base-component.ts +14 -10
- package/src/index.ts +2 -0
- package/src/jsx-factory.ts +3 -1
- package/src/light-component.ts +61 -31
- package/src/utils/dom-utils.ts +32 -1
- package/src/utils/element-creation.ts +3 -1
- package/src/utils/element-marking.ts +9 -5
- package/src/utils/element-update.ts +178 -127
- package/src/utils/update-children-helpers.ts +133 -66
- package/src/web-component.ts +6 -5
|
@@ -0,0 +1,1306 @@
|
|
|
1
|
+
// src/utils/dom-utils.ts
|
|
2
|
+
function parseHTMLToNodes(html) {
|
|
3
|
+
if (!html) return [];
|
|
4
|
+
const temp = document.createElement("div");
|
|
5
|
+
temp.innerHTML = html;
|
|
6
|
+
return Array.from(temp.childNodes).map((node) => {
|
|
7
|
+
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
8
|
+
return node;
|
|
9
|
+
} else {
|
|
10
|
+
return node.textContent || "";
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function isHTMLString(str) {
|
|
15
|
+
const trimmed = str.trim();
|
|
16
|
+
if (!trimmed) return false;
|
|
17
|
+
const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
|
|
18
|
+
const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
|
|
19
|
+
if (looksLikeMath) return false;
|
|
20
|
+
return htmlTagPattern.test(trimmed);
|
|
21
|
+
}
|
|
22
|
+
function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
|
|
23
|
+
if (depth > 10) {
|
|
24
|
+
console.warn(
|
|
25
|
+
"[WSX] flattenChildren: Maximum depth exceeded, treating remaining children as text"
|
|
26
|
+
);
|
|
27
|
+
return children.filter(
|
|
28
|
+
(child) => typeof child === "string" || typeof child === "number"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const result = [];
|
|
32
|
+
for (const child of children) {
|
|
33
|
+
if (child === null || child === void 0 || child === false) {
|
|
34
|
+
continue;
|
|
35
|
+
} else if (Array.isArray(child)) {
|
|
36
|
+
result.push(...flattenChildren(child, skipHTMLDetection, depth + 1));
|
|
37
|
+
} else if (typeof child === "string") {
|
|
38
|
+
if (skipHTMLDetection) {
|
|
39
|
+
result.push(child);
|
|
40
|
+
} else if (isHTMLString(child)) {
|
|
41
|
+
try {
|
|
42
|
+
const nodes = parseHTMLToNodes(child);
|
|
43
|
+
if (nodes.length > 0) {
|
|
44
|
+
for (const node of nodes) {
|
|
45
|
+
if (typeof node === "string") {
|
|
46
|
+
result.push(node);
|
|
47
|
+
} else {
|
|
48
|
+
result.push(node);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
result.push(child);
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.warn("[WSX] Failed to parse HTML string, treating as text:", error);
|
|
56
|
+
result.push(child);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
result.push(child);
|
|
60
|
+
}
|
|
61
|
+
} else if (child instanceof DocumentFragment) {
|
|
62
|
+
const fragmentChildren = Array.from(child.childNodes);
|
|
63
|
+
for (const fragChild of fragmentChildren) {
|
|
64
|
+
if (fragChild instanceof HTMLElement || fragChild instanceof SVGElement) {
|
|
65
|
+
result.push(fragChild);
|
|
66
|
+
} else if (fragChild.nodeType === Node.TEXT_NODE) {
|
|
67
|
+
result.push(fragChild.textContent || "");
|
|
68
|
+
} else if (fragChild instanceof DocumentFragment) {
|
|
69
|
+
result.push(...flattenChildren([fragChild], skipHTMLDetection, depth + 1));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
result.push(child);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/render-context.ts
|
|
80
|
+
var _RenderContext = class _RenderContext {
|
|
81
|
+
/**
|
|
82
|
+
* Executes a function within the context of a component.
|
|
83
|
+
* @param component The component instance currently rendering.
|
|
84
|
+
* @param fn The function to execute (usually the render method).
|
|
85
|
+
*/
|
|
86
|
+
static runInContext(component, fn) {
|
|
87
|
+
resetCounterForNewRenderCycle(component);
|
|
88
|
+
const prev = _RenderContext.current;
|
|
89
|
+
_RenderContext.current = component;
|
|
90
|
+
try {
|
|
91
|
+
return fn();
|
|
92
|
+
} finally {
|
|
93
|
+
_RenderContext.current = prev;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Gets the currently rendering component.
|
|
98
|
+
*/
|
|
99
|
+
static getCurrentComponent() {
|
|
100
|
+
return _RenderContext.current;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Gets the current component's DOM cache.
|
|
104
|
+
*/
|
|
105
|
+
static getDOMCache() {
|
|
106
|
+
return _RenderContext.current?.getDomCache();
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
_RenderContext.current = null;
|
|
110
|
+
var RenderContext = _RenderContext;
|
|
111
|
+
|
|
112
|
+
// src/utils/cache-key.ts
|
|
113
|
+
var INDEX_KEY = "__wsxIndex";
|
|
114
|
+
var componentElementCounters = /* @__PURE__ */ new WeakMap();
|
|
115
|
+
var componentIdCache = /* @__PURE__ */ new WeakMap();
|
|
116
|
+
function generateCacheKey(tag, props, componentId, component) {
|
|
117
|
+
const userKey = props?.key;
|
|
118
|
+
const index = props?.[INDEX_KEY];
|
|
119
|
+
const positionId = props?.__wsxPositionId;
|
|
120
|
+
if (userKey !== void 0 && userKey !== null) {
|
|
121
|
+
return `${componentId}:${tag}:key-${String(userKey)}`;
|
|
122
|
+
}
|
|
123
|
+
if (index !== void 0 && index !== null) {
|
|
124
|
+
return `${componentId}:${tag}:idx-${String(index)}`;
|
|
125
|
+
}
|
|
126
|
+
if (positionId !== void 0 && positionId !== null) {
|
|
127
|
+
return `${componentId}:${tag}:${String(positionId)}`;
|
|
128
|
+
}
|
|
129
|
+
if (component) {
|
|
130
|
+
let counter = componentElementCounters.get(component) || 0;
|
|
131
|
+
counter++;
|
|
132
|
+
componentElementCounters.set(component, counter);
|
|
133
|
+
return `${componentId}:${tag}:auto-${counter}`;
|
|
134
|
+
}
|
|
135
|
+
return `${componentId}:${tag}:fallback-${Date.now()}-${Math.random()}`;
|
|
136
|
+
}
|
|
137
|
+
function resetCounterForNewRenderCycle(component) {
|
|
138
|
+
componentElementCounters.set(component, 0);
|
|
139
|
+
}
|
|
140
|
+
function getComponentId() {
|
|
141
|
+
const component = RenderContext.getCurrentComponent();
|
|
142
|
+
if (component) {
|
|
143
|
+
let cachedId = componentIdCache.get(component);
|
|
144
|
+
if (cachedId) {
|
|
145
|
+
return cachedId;
|
|
146
|
+
}
|
|
147
|
+
const instanceId = component._instanceId || "default";
|
|
148
|
+
cachedId = `${component.constructor.name}:${instanceId}`;
|
|
149
|
+
componentIdCache.set(component, cachedId);
|
|
150
|
+
return cachedId;
|
|
151
|
+
}
|
|
152
|
+
return "unknown";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/utils/element-marking.ts
|
|
156
|
+
var CACHE_KEY_PROP = "__wsxCacheKey";
|
|
157
|
+
function markElement(element, cacheKey) {
|
|
158
|
+
element[CACHE_KEY_PROP] = cacheKey;
|
|
159
|
+
}
|
|
160
|
+
function getElementCacheKey(element) {
|
|
161
|
+
const key = element[CACHE_KEY_PROP];
|
|
162
|
+
return key !== void 0 ? String(key) : null;
|
|
163
|
+
}
|
|
164
|
+
function isCreatedByH(element) {
|
|
165
|
+
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return element[CACHE_KEY_PROP] !== void 0;
|
|
169
|
+
}
|
|
170
|
+
function shouldPreserveElement(element) {
|
|
171
|
+
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
if (!isCreatedByH(element)) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
if (element.hasAttribute("data-wsx-preserve")) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/utils/svg-utils.ts
|
|
184
|
+
var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
185
|
+
var SVG_ONLY_ELEMENTS = /* @__PURE__ */ new Set([
|
|
186
|
+
// 结构元素 (Structural elements)
|
|
187
|
+
"svg",
|
|
188
|
+
"defs",
|
|
189
|
+
"g",
|
|
190
|
+
"symbol",
|
|
191
|
+
"use",
|
|
192
|
+
// 图形元素 (Graphics elements)
|
|
193
|
+
"circle",
|
|
194
|
+
"ellipse",
|
|
195
|
+
"line",
|
|
196
|
+
"path",
|
|
197
|
+
"polygon",
|
|
198
|
+
"polyline",
|
|
199
|
+
"rect",
|
|
200
|
+
// 文本元素 (Text elements)
|
|
201
|
+
"textPath",
|
|
202
|
+
"tspan",
|
|
203
|
+
// 渐变和模式 (Gradients and patterns)
|
|
204
|
+
"linearGradient",
|
|
205
|
+
"radialGradient",
|
|
206
|
+
"stop",
|
|
207
|
+
"pattern",
|
|
208
|
+
// 滤镜 (Filter elements)
|
|
209
|
+
"filter",
|
|
210
|
+
"feBlend",
|
|
211
|
+
"feColorMatrix",
|
|
212
|
+
"feComponentTransfer",
|
|
213
|
+
"feComposite",
|
|
214
|
+
"feConvolveMatrix",
|
|
215
|
+
"feDiffuseLighting",
|
|
216
|
+
"feDisplacementMap",
|
|
217
|
+
"feDistantLight",
|
|
218
|
+
"feDropShadow",
|
|
219
|
+
"feFlood",
|
|
220
|
+
"feFuncA",
|
|
221
|
+
"feFuncB",
|
|
222
|
+
"feFuncG",
|
|
223
|
+
"feFuncR",
|
|
224
|
+
"feGaussianBlur",
|
|
225
|
+
"feImage",
|
|
226
|
+
"feMerge",
|
|
227
|
+
"feMergeNode",
|
|
228
|
+
"feMorphology",
|
|
229
|
+
"feOffset",
|
|
230
|
+
"fePointLight",
|
|
231
|
+
"feSpecularLighting",
|
|
232
|
+
"feSpotLight",
|
|
233
|
+
"feTile",
|
|
234
|
+
"feTurbulence",
|
|
235
|
+
// 动画元素 (Animation elements)
|
|
236
|
+
"animate",
|
|
237
|
+
"animateMotion",
|
|
238
|
+
"animateTransform",
|
|
239
|
+
"set",
|
|
240
|
+
// 其他元素 (Other elements)
|
|
241
|
+
"clipPath",
|
|
242
|
+
"foreignObject",
|
|
243
|
+
"marker",
|
|
244
|
+
"mask",
|
|
245
|
+
"metadata",
|
|
246
|
+
"switch",
|
|
247
|
+
"desc"
|
|
248
|
+
]);
|
|
249
|
+
var DUAL_ELEMENTS = /* @__PURE__ */ new Set(["image", "style", "title", "text"]);
|
|
250
|
+
var FORCE_HTML_ELEMENTS = /* @__PURE__ */ new Set(["a"]);
|
|
251
|
+
var SVG_ELEMENTS = /* @__PURE__ */ new Set([
|
|
252
|
+
...SVG_ONLY_ELEMENTS,
|
|
253
|
+
...DUAL_ELEMENTS,
|
|
254
|
+
...FORCE_HTML_ELEMENTS
|
|
255
|
+
]);
|
|
256
|
+
var svgContext = false;
|
|
257
|
+
function isSVGOnlyElement(tagName) {
|
|
258
|
+
return SVG_ONLY_ELEMENTS.has(tagName);
|
|
259
|
+
}
|
|
260
|
+
function isDualElement(tagName) {
|
|
261
|
+
return DUAL_ELEMENTS.has(tagName);
|
|
262
|
+
}
|
|
263
|
+
function isForceHTMLElement(tagName) {
|
|
264
|
+
return FORCE_HTML_ELEMENTS.has(tagName);
|
|
265
|
+
}
|
|
266
|
+
function setSVGContext(inSVG) {
|
|
267
|
+
svgContext = inSVG;
|
|
268
|
+
}
|
|
269
|
+
function createElement(tagName) {
|
|
270
|
+
if (isForceHTMLElement(tagName)) {
|
|
271
|
+
return document.createElement(tagName);
|
|
272
|
+
}
|
|
273
|
+
if (isSVGOnlyElement(tagName)) {
|
|
274
|
+
setSVGContext(true);
|
|
275
|
+
return document.createElementNS(SVG_NAMESPACE, tagName);
|
|
276
|
+
}
|
|
277
|
+
if (isDualElement(tagName)) {
|
|
278
|
+
if (svgContext) {
|
|
279
|
+
return document.createElementNS(SVG_NAMESPACE, tagName);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return document.createElement(tagName);
|
|
283
|
+
}
|
|
284
|
+
function shouldUseSVGNamespace(tagName) {
|
|
285
|
+
return isSVGOnlyElement(tagName) || isDualElement(tagName) && svgContext;
|
|
286
|
+
}
|
|
287
|
+
var SVG_ATTRIBUTE_MAP = /* @__PURE__ */ new Map([
|
|
288
|
+
["className", "class"],
|
|
289
|
+
["htmlFor", "for"]
|
|
290
|
+
]);
|
|
291
|
+
function getSVGAttributeName(attributeName) {
|
|
292
|
+
return SVG_ATTRIBUTE_MAP.get(attributeName) || attributeName;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// src/utils/logger.ts
|
|
296
|
+
var WSXLogger = class {
|
|
297
|
+
constructor(prefix = "[WSX]", enabled = true, level = "info") {
|
|
298
|
+
this.prefix = prefix;
|
|
299
|
+
this.enabled = enabled;
|
|
300
|
+
this.level = level;
|
|
301
|
+
}
|
|
302
|
+
shouldLog(level) {
|
|
303
|
+
if (!this.enabled) return false;
|
|
304
|
+
const levels = ["debug", "info", "warn", "error"];
|
|
305
|
+
const currentLevelIndex = levels.indexOf(this.level);
|
|
306
|
+
const messageLevelIndex = levels.indexOf(level);
|
|
307
|
+
return messageLevelIndex >= currentLevelIndex;
|
|
308
|
+
}
|
|
309
|
+
debug(message, ...args) {
|
|
310
|
+
if (this.shouldLog("debug")) {
|
|
311
|
+
console.debug(`${this.prefix} ${message}`, ...args);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
info(message, ...args) {
|
|
315
|
+
if (this.shouldLog("info")) {
|
|
316
|
+
console.info(`${this.prefix} ${message}`, ...args);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
warn(message, ...args) {
|
|
320
|
+
if (this.shouldLog("warn")) {
|
|
321
|
+
console.warn(`${this.prefix} ${message}`, ...args);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
error(message, ...args) {
|
|
325
|
+
if (this.shouldLog("error")) {
|
|
326
|
+
console.error(`${this.prefix} ${message}`, ...args);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
var logger = new WSXLogger();
|
|
331
|
+
function createLogger(componentName) {
|
|
332
|
+
return new WSXLogger(`[WSX:${componentName}]`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// src/utils/props-utils.ts
|
|
336
|
+
var logger2 = createLogger("Props Utilities");
|
|
337
|
+
function isFrameworkInternalProp(key) {
|
|
338
|
+
if (key === "key") {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
if (key === "__wsxPositionId" || key === "__wsxIndex") {
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
if (key === "__testId") {
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
if (key === "ref") {
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
function isStandardHTMLAttribute(key) {
|
|
353
|
+
const standardAttributes = /* @__PURE__ */ new Set([
|
|
354
|
+
// 全局属性
|
|
355
|
+
"id",
|
|
356
|
+
"class",
|
|
357
|
+
"className",
|
|
358
|
+
"style",
|
|
359
|
+
"title",
|
|
360
|
+
"lang",
|
|
361
|
+
"dir",
|
|
362
|
+
"hidden",
|
|
363
|
+
"tabindex",
|
|
364
|
+
"accesskey",
|
|
365
|
+
"contenteditable",
|
|
366
|
+
"draggable",
|
|
367
|
+
"spellcheck",
|
|
368
|
+
"translate",
|
|
369
|
+
"autocapitalize",
|
|
370
|
+
"autocorrect",
|
|
371
|
+
// 表单属性
|
|
372
|
+
"name",
|
|
373
|
+
"value",
|
|
374
|
+
"type",
|
|
375
|
+
"placeholder",
|
|
376
|
+
"required",
|
|
377
|
+
"disabled",
|
|
378
|
+
"readonly",
|
|
379
|
+
"checked",
|
|
380
|
+
"selected",
|
|
381
|
+
"multiple",
|
|
382
|
+
"min",
|
|
383
|
+
"max",
|
|
384
|
+
"step",
|
|
385
|
+
"autocomplete",
|
|
386
|
+
"autofocus",
|
|
387
|
+
"form",
|
|
388
|
+
"formaction",
|
|
389
|
+
"formenctype",
|
|
390
|
+
"formmethod",
|
|
391
|
+
"formnovalidate",
|
|
392
|
+
"formtarget",
|
|
393
|
+
// 链接属性
|
|
394
|
+
"href",
|
|
395
|
+
"target",
|
|
396
|
+
"rel",
|
|
397
|
+
"download",
|
|
398
|
+
"hreflang",
|
|
399
|
+
"ping",
|
|
400
|
+
// 媒体属性
|
|
401
|
+
"src",
|
|
402
|
+
"alt",
|
|
403
|
+
"width",
|
|
404
|
+
"height",
|
|
405
|
+
"poster",
|
|
406
|
+
"preload",
|
|
407
|
+
"controls",
|
|
408
|
+
"autoplay",
|
|
409
|
+
"loop",
|
|
410
|
+
"muted",
|
|
411
|
+
"playsinline",
|
|
412
|
+
"crossorigin",
|
|
413
|
+
// ARIA 属性(部分常见)
|
|
414
|
+
"role"
|
|
415
|
+
]);
|
|
416
|
+
const lowerKey = key.toLowerCase();
|
|
417
|
+
if (standardAttributes.has(lowerKey)) {
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
if (lowerKey.startsWith("data-")) {
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
if (lowerKey.startsWith("aria-")) {
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
if (key.startsWith("xml:") || key.startsWith("xlink:")) {
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
function isSpecialProperty(key, value) {
|
|
432
|
+
return key === "ref" || key === "className" || key === "class" || key === "style" || key.startsWith("on") && typeof value === "function" || typeof value === "boolean" || key === "value";
|
|
433
|
+
}
|
|
434
|
+
function setSmartProperty(element, key, value, tag) {
|
|
435
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
436
|
+
if (isSpecialProperty(key, value)) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if (isStandardHTMLAttribute(key)) {
|
|
440
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
441
|
+
if (typeof value === "object" && value !== null) {
|
|
442
|
+
try {
|
|
443
|
+
const serialized = JSON.stringify(value);
|
|
444
|
+
if (serialized.length > 1024 * 1024) {
|
|
445
|
+
logger2.warn(
|
|
446
|
+
`[WSX] Attribute "${key}" value too large, consider using a non-standard property name instead`
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
element.setAttribute(attributeName, serialized);
|
|
450
|
+
} catch (error) {
|
|
451
|
+
logger2.warn(`Cannot serialize attribute "${key}":`, error);
|
|
452
|
+
}
|
|
453
|
+
} else {
|
|
454
|
+
element.setAttribute(attributeName, String(value));
|
|
455
|
+
}
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (element instanceof SVGElement) {
|
|
459
|
+
const attributeName = getSVGAttributeName(key);
|
|
460
|
+
if (typeof value === "object" && value !== null) {
|
|
461
|
+
try {
|
|
462
|
+
const serialized = JSON.stringify(value);
|
|
463
|
+
element.setAttribute(attributeName, serialized);
|
|
464
|
+
} catch (error) {
|
|
465
|
+
logger2.warn(`Cannot serialize SVG attribute "${key}":`, error);
|
|
466
|
+
}
|
|
467
|
+
} else {
|
|
468
|
+
element.setAttribute(attributeName, String(value));
|
|
469
|
+
}
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const hasProperty = key in element || Object.prototype.hasOwnProperty.call(element, key);
|
|
473
|
+
if (hasProperty) {
|
|
474
|
+
let isReadOnly = false;
|
|
475
|
+
try {
|
|
476
|
+
const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), key);
|
|
477
|
+
if (descriptor) {
|
|
478
|
+
isReadOnly = descriptor.get !== void 0 && descriptor.set === void 0 || descriptor.writable === false && descriptor.set === void 0;
|
|
479
|
+
}
|
|
480
|
+
} catch {
|
|
481
|
+
}
|
|
482
|
+
if (isReadOnly) {
|
|
483
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
484
|
+
if (typeof value === "object" && value !== null) {
|
|
485
|
+
try {
|
|
486
|
+
const serialized = JSON.stringify(value);
|
|
487
|
+
element.setAttribute(attributeName, serialized);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
logger2.warn(`Cannot serialize readonly property "${key}":`, error);
|
|
490
|
+
}
|
|
491
|
+
} else {
|
|
492
|
+
element.setAttribute(attributeName, String(value));
|
|
493
|
+
}
|
|
494
|
+
} else {
|
|
495
|
+
try {
|
|
496
|
+
element[key] = value;
|
|
497
|
+
} catch {
|
|
498
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
499
|
+
if (typeof value === "object" && value !== null) {
|
|
500
|
+
try {
|
|
501
|
+
const serialized = JSON.stringify(value);
|
|
502
|
+
element.setAttribute(attributeName, serialized);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
logger2.warn(
|
|
505
|
+
`[WSX] Cannot serialize property "${key}" for attribute:`,
|
|
506
|
+
error
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
} else {
|
|
510
|
+
element.setAttribute(attributeName, String(value));
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
} else {
|
|
515
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
516
|
+
if (typeof value === "object" && value !== null) {
|
|
517
|
+
try {
|
|
518
|
+
const serialized = JSON.stringify(value);
|
|
519
|
+
if (serialized.length > 1024 * 1024) {
|
|
520
|
+
logger2.warn(
|
|
521
|
+
`[WSX] Property "${key}" value too large for attribute, consider using a JavaScript property instead`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
element.setAttribute(attributeName, serialized);
|
|
525
|
+
} catch (error) {
|
|
526
|
+
logger2.warn(`Cannot serialize property "${key}" for attribute:`, error);
|
|
527
|
+
}
|
|
528
|
+
} else {
|
|
529
|
+
element.setAttribute(attributeName, String(value));
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// src/utils/element-creation.ts
|
|
535
|
+
function applySingleProp(element, key, value, tag, isSVG) {
|
|
536
|
+
if (value === null || value === void 0) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (key === "ref" && typeof value === "function") {
|
|
540
|
+
value(element);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (key === "className" || key === "class") {
|
|
544
|
+
if (isSVG) {
|
|
545
|
+
element.setAttribute("class", value);
|
|
546
|
+
} else {
|
|
547
|
+
element.className = value;
|
|
548
|
+
}
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if (key === "style" && typeof value === "string") {
|
|
552
|
+
element.setAttribute("style", value);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
556
|
+
const eventName = key.slice(2).toLowerCase();
|
|
557
|
+
const listenerKey = `__wsxListener_${eventName}`;
|
|
558
|
+
element.addEventListener(eventName, value);
|
|
559
|
+
element[listenerKey] = value;
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (typeof value === "boolean") {
|
|
563
|
+
if (value) {
|
|
564
|
+
element.setAttribute(key, "");
|
|
565
|
+
if (element instanceof HTMLInputElement) {
|
|
566
|
+
if (key === "checked") {
|
|
567
|
+
element.checked = true;
|
|
568
|
+
} else if (key === "disabled") {
|
|
569
|
+
element.disabled = true;
|
|
570
|
+
} else if (key === "readonly") {
|
|
571
|
+
element.readOnly = true;
|
|
572
|
+
}
|
|
573
|
+
} else if (element instanceof HTMLOptionElement && key === "selected") {
|
|
574
|
+
element.selected = true;
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
578
|
+
element.removeAttribute(attributeName);
|
|
579
|
+
if (element instanceof HTMLInputElement) {
|
|
580
|
+
if (key === "checked") {
|
|
581
|
+
element.checked = false;
|
|
582
|
+
} else if (key === "disabled") {
|
|
583
|
+
element.disabled = false;
|
|
584
|
+
} else if (key === "readonly") {
|
|
585
|
+
element.readOnly = false;
|
|
586
|
+
}
|
|
587
|
+
} else if (element instanceof HTMLOptionElement && key === "selected") {
|
|
588
|
+
element.selected = false;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (key === "value") {
|
|
594
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
595
|
+
element.value = String(value);
|
|
596
|
+
} else {
|
|
597
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
598
|
+
element.setAttribute(attributeName, String(value));
|
|
599
|
+
}
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
if (isFrameworkInternalProp(key)) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
setSmartProperty(element, key, value, tag);
|
|
606
|
+
}
|
|
607
|
+
function applyPropsToElement(element, props, tag) {
|
|
608
|
+
if (!props) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
612
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
613
|
+
applySingleProp(element, key, value, tag, isSVG);
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
function appendChildrenToElement(element, children) {
|
|
617
|
+
const flatChildren = flattenChildren(children);
|
|
618
|
+
flatChildren.forEach((child) => {
|
|
619
|
+
if (child === null || child === void 0 || child === false) {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
623
|
+
const textNode = document.createTextNode(String(child));
|
|
624
|
+
textNode.__wsxManaged = true;
|
|
625
|
+
element.appendChild(textNode);
|
|
626
|
+
} else if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
627
|
+
element.appendChild(child);
|
|
628
|
+
} else if (child instanceof DocumentFragment) {
|
|
629
|
+
element.appendChild(child);
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
function createElementWithPropsAndChildren(tag, props, children) {
|
|
634
|
+
const element = createElement(tag);
|
|
635
|
+
applyPropsToElement(element, props, tag);
|
|
636
|
+
appendChildrenToElement(element, children);
|
|
637
|
+
return element;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// src/utils/update-children-helpers.ts
|
|
641
|
+
function collectPreservedElements(element) {
|
|
642
|
+
const preserved = [];
|
|
643
|
+
for (let i = 0; i < element.childNodes.length; i++) {
|
|
644
|
+
const child = element.childNodes[i];
|
|
645
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (shouldPreserveElement(child)) {
|
|
649
|
+
preserved.push(child);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return preserved;
|
|
653
|
+
}
|
|
654
|
+
function findDOMNodeByReference(oldChild, parent) {
|
|
655
|
+
if (oldChild.parentNode === parent && !shouldPreserveElement(oldChild)) {
|
|
656
|
+
return oldChild;
|
|
657
|
+
}
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
function findDOMNodeByCacheKey(cacheKey, parent) {
|
|
661
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
662
|
+
const child = parent.childNodes[i];
|
|
663
|
+
if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
664
|
+
if (shouldPreserveElement(child)) continue;
|
|
665
|
+
if (getElementCacheKey(child) === cacheKey) {
|
|
666
|
+
return child;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
function findElementNode(oldChild, parent) {
|
|
673
|
+
const byRef = findDOMNodeByReference(oldChild, parent);
|
|
674
|
+
if (byRef) return byRef;
|
|
675
|
+
const cacheKey = getElementCacheKey(oldChild);
|
|
676
|
+
if (cacheKey) {
|
|
677
|
+
return findDOMNodeByCacheKey(cacheKey, parent);
|
|
678
|
+
}
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
function findTextNode(parent, domIndex, processedNodes) {
|
|
682
|
+
for (let i = domIndex.value; i < parent.childNodes.length; i++) {
|
|
683
|
+
const node = parent.childNodes[i];
|
|
684
|
+
if (node.nodeType === Node.TEXT_NODE && node.__wsxManaged === true && !processedNodes.has(node)) {
|
|
685
|
+
return node;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
function updateOrCreateTextNode(parent, oldNode, newText, insertBeforeNode) {
|
|
691
|
+
if (shouldPreserveElement(parent)) {
|
|
692
|
+
if (oldNode && oldNode.nodeType === Node.TEXT_NODE) {
|
|
693
|
+
return oldNode;
|
|
694
|
+
}
|
|
695
|
+
return document.createTextNode(newText);
|
|
696
|
+
}
|
|
697
|
+
if (oldNode && oldNode.nodeType === Node.TEXT_NODE) {
|
|
698
|
+
if (oldNode.textContent !== newText) {
|
|
699
|
+
oldNode.textContent = newText;
|
|
700
|
+
}
|
|
701
|
+
if (insertBeforeNode !== void 0) {
|
|
702
|
+
if (oldNode !== insertBeforeNode && oldNode.nextSibling !== insertBeforeNode) {
|
|
703
|
+
parent.insertBefore(oldNode, insertBeforeNode);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return oldNode;
|
|
707
|
+
} else {
|
|
708
|
+
const newTextNode = document.createTextNode(newText);
|
|
709
|
+
newTextNode.__wsxManaged = true;
|
|
710
|
+
if (oldNode && !shouldPreserveElement(oldNode)) {
|
|
711
|
+
parent.replaceChild(newTextNode, oldNode);
|
|
712
|
+
} else {
|
|
713
|
+
parent.insertBefore(newTextNode, insertBeforeNode ?? null);
|
|
714
|
+
}
|
|
715
|
+
return newTextNode;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
function removeNodeIfNotPreserved(parent, node) {
|
|
719
|
+
if (node && !shouldPreserveElement(node) && node.parentNode === parent) {
|
|
720
|
+
parent.removeChild(node);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
function appendNewChild(parent, child, processedNodes) {
|
|
724
|
+
if (child === null || child === void 0 || child === false) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
728
|
+
const newTextNode = document.createTextNode(String(child));
|
|
729
|
+
newTextNode.__wsxManaged = true;
|
|
730
|
+
parent.appendChild(newTextNode);
|
|
731
|
+
if (processedNodes) {
|
|
732
|
+
processedNodes.add(newTextNode);
|
|
733
|
+
}
|
|
734
|
+
} else if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
735
|
+
if (child.parentNode === parent) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (child.parentNode && child.parentNode !== parent) {
|
|
739
|
+
child.parentNode.removeChild(child);
|
|
740
|
+
}
|
|
741
|
+
parent.appendChild(child);
|
|
742
|
+
if (processedNodes) {
|
|
743
|
+
processedNodes.add(child);
|
|
744
|
+
}
|
|
745
|
+
} else if (child instanceof DocumentFragment) {
|
|
746
|
+
if (processedNodes) {
|
|
747
|
+
for (let i = 0; i < child.childNodes.length; i++) {
|
|
748
|
+
processedNodes.add(child.childNodes[i]);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
parent.appendChild(child);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
function buildNewChildrenMaps(flatNew) {
|
|
755
|
+
const elementSet = /* @__PURE__ */ new Set();
|
|
756
|
+
const cacheKeyMap = /* @__PURE__ */ new Map();
|
|
757
|
+
for (const child of flatNew) {
|
|
758
|
+
if (child instanceof HTMLElement || child instanceof SVGElement || child instanceof DocumentFragment) {
|
|
759
|
+
elementSet.add(child);
|
|
760
|
+
if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
761
|
+
const cacheKey = getElementCacheKey(child);
|
|
762
|
+
if (cacheKey) {
|
|
763
|
+
cacheKeyMap.set(cacheKey, child);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return { elementSet, cacheKeyMap };
|
|
769
|
+
}
|
|
770
|
+
function shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes) {
|
|
771
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
772
|
+
if (node.__wsxManaged === true) {
|
|
773
|
+
if (processedNodes && processedNodes.has(node)) {
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
const parent = node.parentNode;
|
|
779
|
+
if (parent && (parent instanceof HTMLElement || parent instanceof SVGElement)) {
|
|
780
|
+
if (shouldPreserveElement(parent)) {
|
|
781
|
+
return false;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return true;
|
|
785
|
+
} else {
|
|
786
|
+
if (shouldPreserveElement(node)) {
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
const isProcessed = processedNodes && processedNodes.has(node);
|
|
791
|
+
if (shouldPreserveElement(node)) {
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
794
|
+
if (node.nodeType === Node.TEXT_NODE && isProcessed) {
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
if (node instanceof HTMLElement || node instanceof SVGElement || node instanceof DocumentFragment) {
|
|
798
|
+
if (elementSet.has(node)) {
|
|
799
|
+
return false;
|
|
800
|
+
}
|
|
801
|
+
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
802
|
+
const cacheKey = getElementCacheKey(node);
|
|
803
|
+
if (cacheKey && cacheKeyMap.has(cacheKey)) {
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return true;
|
|
809
|
+
}
|
|
810
|
+
function deduplicateCacheKeys(parent, cacheKeyMap) {
|
|
811
|
+
const processedCacheKeys = /* @__PURE__ */ new Set();
|
|
812
|
+
for (let i = parent.childNodes.length - 1; i >= 0; i--) {
|
|
813
|
+
const child = parent.childNodes[i];
|
|
814
|
+
if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
815
|
+
if (shouldPreserveElement(child)) {
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
const cacheKey = getElementCacheKey(child);
|
|
819
|
+
if (cacheKey && cacheKeyMap.has(cacheKey) && !processedCacheKeys.has(cacheKey)) {
|
|
820
|
+
processedCacheKeys.add(cacheKey);
|
|
821
|
+
const newChild = cacheKeyMap.get(cacheKey);
|
|
822
|
+
if (child !== newChild) {
|
|
823
|
+
parent.replaceChild(newChild, child);
|
|
824
|
+
}
|
|
825
|
+
} else if (cacheKey && cacheKeyMap.has(cacheKey) && processedCacheKeys.has(cacheKey)) {
|
|
826
|
+
const newChild = cacheKeyMap.get(cacheKey);
|
|
827
|
+
if (child !== newChild) {
|
|
828
|
+
parent.removeChild(child);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
function collectNodesToRemove(parent, elementSet, cacheKeyMap, processedNodes) {
|
|
835
|
+
const nodesToRemove = [];
|
|
836
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
837
|
+
const node = parent.childNodes[i];
|
|
838
|
+
const removed = shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes);
|
|
839
|
+
if (removed) {
|
|
840
|
+
nodesToRemove.push(node);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return nodesToRemove;
|
|
844
|
+
}
|
|
845
|
+
function removeNodes(parent, nodes, cacheManager) {
|
|
846
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
847
|
+
const node = nodes[i];
|
|
848
|
+
if (node.parentNode === parent) {
|
|
849
|
+
if (cacheManager && (node instanceof HTMLElement || node instanceof SVGElement)) {
|
|
850
|
+
const metadata = cacheManager.getMetadata(node);
|
|
851
|
+
const refCallback = metadata?.ref;
|
|
852
|
+
if (typeof refCallback === "function") {
|
|
853
|
+
try {
|
|
854
|
+
refCallback(null);
|
|
855
|
+
} catch {
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
parent.removeChild(node);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
function reinsertPreservedElements(parent, preservedElements) {
|
|
864
|
+
for (const element of preservedElements) {
|
|
865
|
+
if (element.parentNode !== parent) {
|
|
866
|
+
parent.appendChild(element);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
function flattenChildrenSafe(children) {
|
|
871
|
+
return flattenChildren(children);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// src/utils/element-update.ts
|
|
875
|
+
function removeProp(element, key, oldValue, tag) {
|
|
876
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
877
|
+
if (key === "ref") {
|
|
878
|
+
if (typeof oldValue === "function") {
|
|
879
|
+
try {
|
|
880
|
+
oldValue(null);
|
|
881
|
+
} catch {
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
if (key === "className" || key === "class") {
|
|
887
|
+
if (isSVG) {
|
|
888
|
+
element.removeAttribute("class");
|
|
889
|
+
} else {
|
|
890
|
+
element.className = "";
|
|
891
|
+
}
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
if (key === "style") {
|
|
895
|
+
element.removeAttribute("style");
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
if (key.startsWith("on") && typeof oldValue === "function") {
|
|
899
|
+
const eventName = key.slice(2).toLowerCase();
|
|
900
|
+
const listenerKey = `__wsxListener_${eventName}`;
|
|
901
|
+
const savedListener = element[listenerKey];
|
|
902
|
+
if (savedListener) {
|
|
903
|
+
element.removeEventListener(eventName, savedListener);
|
|
904
|
+
delete element[listenerKey];
|
|
905
|
+
}
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
if (key === "value") {
|
|
909
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
910
|
+
element.value = "";
|
|
911
|
+
} else {
|
|
912
|
+
const attributeName2 = isSVG ? getSVGAttributeName(key) : key;
|
|
913
|
+
element.removeAttribute(attributeName2);
|
|
914
|
+
}
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
if (isFrameworkInternalProp(key)) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
921
|
+
element.removeAttribute(attributeName);
|
|
922
|
+
try {
|
|
923
|
+
delete element[key];
|
|
924
|
+
} catch {
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
function applySingleProp2(element, key, value, tag, isSVG) {
|
|
928
|
+
if (value === null || value === void 0) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (key === "ref" && typeof value === "function") {
|
|
932
|
+
value(element);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
if (key === "className" || key === "class") {
|
|
936
|
+
if (isSVG) {
|
|
937
|
+
element.setAttribute("class", value);
|
|
938
|
+
} else {
|
|
939
|
+
element.className = value;
|
|
940
|
+
}
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
if (key === "style" && typeof value === "string") {
|
|
944
|
+
element.setAttribute("style", value);
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
948
|
+
const eventName = key.slice(2).toLowerCase();
|
|
949
|
+
const listenerKey = `__wsxListener_${eventName}`;
|
|
950
|
+
const oldListener = element[listenerKey];
|
|
951
|
+
if (oldListener) {
|
|
952
|
+
element.removeEventListener(eventName, oldListener);
|
|
953
|
+
}
|
|
954
|
+
element.addEventListener(eventName, value);
|
|
955
|
+
element[listenerKey] = value;
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
if (typeof value === "boolean") {
|
|
959
|
+
if (value) {
|
|
960
|
+
element.setAttribute(key, "");
|
|
961
|
+
if (element instanceof HTMLInputElement) {
|
|
962
|
+
if (key === "checked") {
|
|
963
|
+
element.checked = true;
|
|
964
|
+
} else if (key === "disabled") {
|
|
965
|
+
element.disabled = true;
|
|
966
|
+
} else if (key === "readonly") {
|
|
967
|
+
element.readOnly = true;
|
|
968
|
+
}
|
|
969
|
+
} else if (element instanceof HTMLOptionElement && key === "selected") {
|
|
970
|
+
element.selected = true;
|
|
971
|
+
}
|
|
972
|
+
} else {
|
|
973
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
974
|
+
element.removeAttribute(attributeName);
|
|
975
|
+
if (element instanceof HTMLInputElement) {
|
|
976
|
+
if (key === "checked") {
|
|
977
|
+
element.checked = false;
|
|
978
|
+
} else if (key === "disabled") {
|
|
979
|
+
element.disabled = false;
|
|
980
|
+
} else if (key === "readonly") {
|
|
981
|
+
element.readOnly = false;
|
|
982
|
+
}
|
|
983
|
+
} else if (element instanceof HTMLOptionElement && key === "selected") {
|
|
984
|
+
element.selected = false;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
if (key === "value") {
|
|
990
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
991
|
+
element.value = String(value);
|
|
992
|
+
} else {
|
|
993
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
994
|
+
element.setAttribute(attributeName, String(value));
|
|
995
|
+
}
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
if (isFrameworkInternalProp(key)) {
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
setSmartProperty(element, key, value, tag);
|
|
1002
|
+
}
|
|
1003
|
+
function updateProps(element, oldProps, newProps, tag) {
|
|
1004
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
1005
|
+
const old = oldProps || {};
|
|
1006
|
+
const new_ = newProps || {};
|
|
1007
|
+
for (const key in old) {
|
|
1008
|
+
if (!(key in new_)) {
|
|
1009
|
+
removeProp(element, key, old[key], tag);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
for (const key in new_) {
|
|
1013
|
+
const oldValue = old[key];
|
|
1014
|
+
const newValue = new_[key];
|
|
1015
|
+
if (oldValue === newValue) {
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
if (oldValue === void 0) {
|
|
1019
|
+
applySingleProp2(element, key, newValue, tag, isSVG);
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
if (typeof oldValue === "object" && oldValue !== null && typeof newValue === "object" && newValue !== null) {
|
|
1023
|
+
try {
|
|
1024
|
+
const oldJson = JSON.stringify(oldValue);
|
|
1025
|
+
const newJson = JSON.stringify(newValue);
|
|
1026
|
+
if (oldJson === newJson) {
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
} catch {
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
applySingleProp2(element, key, newValue, tag, isSVG);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
function updateChildren(element, oldChildren, newChildren, _cacheManager) {
|
|
1036
|
+
const flatOld = flattenChildrenSafe(oldChildren);
|
|
1037
|
+
const flatNew = flattenChildrenSafe(newChildren);
|
|
1038
|
+
const preservedElements = collectPreservedElements(element);
|
|
1039
|
+
const minLength = Math.min(flatOld.length, flatNew.length);
|
|
1040
|
+
const domIndex = { value: 0 };
|
|
1041
|
+
const insertionIndex = { value: 0 };
|
|
1042
|
+
const processedNodes = /* @__PURE__ */ new Set();
|
|
1043
|
+
for (let i = 0; i < minLength; i++) {
|
|
1044
|
+
const oldChild = flatOld[i];
|
|
1045
|
+
const newChild = flatNew[i];
|
|
1046
|
+
let oldNode = null;
|
|
1047
|
+
if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
|
|
1048
|
+
oldNode = findElementNode(oldChild, element);
|
|
1049
|
+
if (oldNode && oldNode.parentNode === element) {
|
|
1050
|
+
const nodeIndex = Array.from(element.childNodes).indexOf(oldNode);
|
|
1051
|
+
if (nodeIndex !== -1 && nodeIndex >= domIndex.value) {
|
|
1052
|
+
domIndex.value = nodeIndex + 1;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
} else if (typeof oldChild === "string" || typeof oldChild === "number") {
|
|
1056
|
+
if (shouldPreserveElement(element)) {
|
|
1057
|
+
oldNode = null;
|
|
1058
|
+
} else {
|
|
1059
|
+
oldNode = findTextNode(element, domIndex, processedNodes);
|
|
1060
|
+
if (oldNode) {
|
|
1061
|
+
const nodeIndex = Array.from(element.childNodes).indexOf(oldNode);
|
|
1062
|
+
if (nodeIndex !== -1 && nodeIndex >= domIndex.value) {
|
|
1063
|
+
domIndex.value = nodeIndex + 1;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
if (typeof oldChild === "string" || typeof oldChild === "number") {
|
|
1069
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
1070
|
+
const newText = String(newChild);
|
|
1071
|
+
if (shouldPreserveElement(element)) {
|
|
1072
|
+
continue;
|
|
1073
|
+
}
|
|
1074
|
+
const insertBeforeNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
|
|
1075
|
+
const updatedNode = updateOrCreateTextNode(
|
|
1076
|
+
element,
|
|
1077
|
+
oldNode,
|
|
1078
|
+
newText,
|
|
1079
|
+
insertBeforeNode
|
|
1080
|
+
);
|
|
1081
|
+
if (updatedNode) {
|
|
1082
|
+
processedNodes.add(updatedNode);
|
|
1083
|
+
insertionIndex.value++;
|
|
1084
|
+
}
|
|
1085
|
+
} else {
|
|
1086
|
+
const targetNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
|
|
1087
|
+
if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
1088
|
+
if (oldNode && oldNode === targetNode && oldNode.parentNode === element) {
|
|
1089
|
+
element.replaceChild(newChild, oldNode);
|
|
1090
|
+
} else {
|
|
1091
|
+
element.insertBefore(newChild, targetNode);
|
|
1092
|
+
removeNodeIfNotPreserved(element, oldNode);
|
|
1093
|
+
}
|
|
1094
|
+
processedNodes.add(newChild);
|
|
1095
|
+
insertionIndex.value++;
|
|
1096
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
1097
|
+
if (processedNodes) {
|
|
1098
|
+
for (let i2 = 0; i2 < newChild.childNodes.length; i2++) {
|
|
1099
|
+
processedNodes.add(newChild.childNodes[i2]);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
element.insertBefore(newChild, targetNode);
|
|
1103
|
+
removeNodeIfNotPreserved(element, oldNode);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
} else if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
|
|
1107
|
+
if (oldNode && shouldPreserveElement(oldNode)) {
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
1111
|
+
const insertBeforeNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
|
|
1112
|
+
if (newChild === oldNode) {
|
|
1113
|
+
if (newChild.nextSibling !== insertBeforeNode) {
|
|
1114
|
+
element.insertBefore(newChild, insertBeforeNode);
|
|
1115
|
+
}
|
|
1116
|
+
} else {
|
|
1117
|
+
element.insertBefore(newChild, insertBeforeNode);
|
|
1118
|
+
}
|
|
1119
|
+
processedNodes.add(newChild);
|
|
1120
|
+
insertionIndex.value++;
|
|
1121
|
+
} else {
|
|
1122
|
+
const targetNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
|
|
1123
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
1124
|
+
const newTextNode = document.createTextNode(String(newChild));
|
|
1125
|
+
newTextNode.__wsxManaged = true;
|
|
1126
|
+
if (oldNode && oldNode === targetNode && oldNode.parentNode === element) {
|
|
1127
|
+
element.replaceChild(newTextNode, oldNode);
|
|
1128
|
+
} else {
|
|
1129
|
+
element.insertBefore(newTextNode, targetNode);
|
|
1130
|
+
removeNodeIfNotPreserved(element, oldNode);
|
|
1131
|
+
}
|
|
1132
|
+
processedNodes.add(newTextNode);
|
|
1133
|
+
insertionIndex.value++;
|
|
1134
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
1135
|
+
if (processedNodes) {
|
|
1136
|
+
for (let i2 = 0; i2 < newChild.childNodes.length; i2++) {
|
|
1137
|
+
processedNodes.add(newChild.childNodes[i2]);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
element.insertBefore(newChild, targetNode);
|
|
1141
|
+
removeNodeIfNotPreserved(element, oldNode);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
for (let i = minLength; i < flatNew.length; i++) {
|
|
1147
|
+
appendNewChild(element, flatNew[i], processedNodes);
|
|
1148
|
+
}
|
|
1149
|
+
const { elementSet, cacheKeyMap } = buildNewChildrenMaps(flatNew);
|
|
1150
|
+
deduplicateCacheKeys(element, cacheKeyMap);
|
|
1151
|
+
const nodesToRemove = collectNodesToRemove(element, elementSet, cacheKeyMap, processedNodes);
|
|
1152
|
+
removeNodes(element, nodesToRemove, _cacheManager);
|
|
1153
|
+
reinsertPreservedElements(element, preservedElements);
|
|
1154
|
+
}
|
|
1155
|
+
function updateElement(element, newProps, newChildren, tag, cacheManager) {
|
|
1156
|
+
const oldMetadata = cacheManager.getMetadata(element);
|
|
1157
|
+
const oldProps = oldMetadata?.props || null;
|
|
1158
|
+
const oldChildren = oldMetadata?.children || [];
|
|
1159
|
+
cacheManager.setMetadata(element, {
|
|
1160
|
+
props: newProps || {},
|
|
1161
|
+
children: newChildren
|
|
1162
|
+
});
|
|
1163
|
+
updateProps(element, oldProps, newProps, tag);
|
|
1164
|
+
updateChildren(element, oldChildren, newChildren, cacheManager);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/jsx-factory.ts
|
|
1168
|
+
var logger3 = createLogger("JSX Factory");
|
|
1169
|
+
function h(tag, props = {}, ...children) {
|
|
1170
|
+
if (typeof tag === "function") {
|
|
1171
|
+
return tag(props, children);
|
|
1172
|
+
}
|
|
1173
|
+
const context = RenderContext.getCurrentComponent();
|
|
1174
|
+
const cacheManager = context ? RenderContext.getDOMCache() : null;
|
|
1175
|
+
if (context && cacheManager) {
|
|
1176
|
+
return tryUseCacheOrCreate(tag, props, children, context, cacheManager);
|
|
1177
|
+
}
|
|
1178
|
+
try {
|
|
1179
|
+
const nodeEnv = typeof globalThis.process !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1180
|
+
globalThis.process.env?.NODE_ENV;
|
|
1181
|
+
if (nodeEnv === "development") {
|
|
1182
|
+
if (!context) {
|
|
1183
|
+
logger3.debug(
|
|
1184
|
+
`h() called without render context. Tag: "${tag}", ComponentId: "${getComponentId()}"`,
|
|
1185
|
+
{
|
|
1186
|
+
tag,
|
|
1187
|
+
props: props ? Object.keys(props) : [],
|
|
1188
|
+
hasCacheManager: !!cacheManager
|
|
1189
|
+
}
|
|
1190
|
+
);
|
|
1191
|
+
} else if (!cacheManager) {
|
|
1192
|
+
logger3.debug(
|
|
1193
|
+
`h() called with context but no cache manager. Tag: "${tag}", Component: "${context.constructor.name}"`,
|
|
1194
|
+
{
|
|
1195
|
+
tag,
|
|
1196
|
+
component: context.constructor.name
|
|
1197
|
+
}
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
} catch {
|
|
1202
|
+
}
|
|
1203
|
+
const element = createElementWithPropsAndChildren(tag, props, children);
|
|
1204
|
+
const componentId = getComponentId();
|
|
1205
|
+
const cacheKey = generateCacheKey(tag, props, componentId, context || void 0);
|
|
1206
|
+
markElement(element, cacheKey);
|
|
1207
|
+
return element;
|
|
1208
|
+
}
|
|
1209
|
+
function tryUseCacheOrCreate(tag, props, children, context, cacheManager) {
|
|
1210
|
+
try {
|
|
1211
|
+
const componentId = getComponentId();
|
|
1212
|
+
const cacheKey = generateCacheKey(tag, props, componentId, context);
|
|
1213
|
+
const cachedElement = cacheManager.get(cacheKey);
|
|
1214
|
+
if (cachedElement) {
|
|
1215
|
+
const element2 = cachedElement;
|
|
1216
|
+
updateElement(element2, props, children, tag, cacheManager);
|
|
1217
|
+
const isCustomElement = tag.includes("-") && customElements.get(tag);
|
|
1218
|
+
if (isCustomElement && element2.isConnected) {
|
|
1219
|
+
const parent = element2.parentNode;
|
|
1220
|
+
if (parent) {
|
|
1221
|
+
parent.removeChild(element2);
|
|
1222
|
+
parent.appendChild(element2);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
return element2;
|
|
1226
|
+
}
|
|
1227
|
+
const element = createElementWithPropsAndChildren(tag, props, children);
|
|
1228
|
+
cacheManager.set(cacheKey, element);
|
|
1229
|
+
markElement(element, cacheKey);
|
|
1230
|
+
cacheManager.setMetadata(element, {
|
|
1231
|
+
props: props || {},
|
|
1232
|
+
children
|
|
1233
|
+
});
|
|
1234
|
+
return element;
|
|
1235
|
+
} catch (error) {
|
|
1236
|
+
return handleCacheError(error, tag, props, children);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
function handleCacheError(error, tag, props, children) {
|
|
1240
|
+
try {
|
|
1241
|
+
const nodeEnv = typeof globalThis.process !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1242
|
+
globalThis.process.env?.NODE_ENV;
|
|
1243
|
+
if (nodeEnv === "development") {
|
|
1244
|
+
logger3.warn("[WSX DOM Cache] Cache error, falling back to create new element:", error);
|
|
1245
|
+
}
|
|
1246
|
+
} catch {
|
|
1247
|
+
}
|
|
1248
|
+
const element = createElementWithPropsAndChildren(tag, props, children);
|
|
1249
|
+
const context = RenderContext.getCurrentComponent();
|
|
1250
|
+
const componentId = getComponentId();
|
|
1251
|
+
const cacheKey = generateCacheKey(tag, props, componentId, context || void 0);
|
|
1252
|
+
markElement(element, cacheKey);
|
|
1253
|
+
return element;
|
|
1254
|
+
}
|
|
1255
|
+
function Fragment(_props, children) {
|
|
1256
|
+
const fragment = document.createDocumentFragment();
|
|
1257
|
+
const flatChildren = flattenChildren(children);
|
|
1258
|
+
flatChildren.forEach((child) => {
|
|
1259
|
+
if (child === null || child === void 0 || child === false) {
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
1263
|
+
const textNode = document.createTextNode(String(child));
|
|
1264
|
+
textNode.__wsxManaged = true;
|
|
1265
|
+
fragment.appendChild(textNode);
|
|
1266
|
+
} else if (child instanceof HTMLElement || child instanceof SVGElement) {
|
|
1267
|
+
fragment.appendChild(child);
|
|
1268
|
+
} else if (child instanceof DocumentFragment) {
|
|
1269
|
+
fragment.appendChild(child);
|
|
1270
|
+
}
|
|
1271
|
+
});
|
|
1272
|
+
return fragment;
|
|
1273
|
+
}
|
|
1274
|
+
function jsx(tag, props) {
|
|
1275
|
+
if (!props) {
|
|
1276
|
+
return h(tag, null);
|
|
1277
|
+
}
|
|
1278
|
+
const { children, ...restProps } = props;
|
|
1279
|
+
if (children !== void 0 && children !== null) {
|
|
1280
|
+
const childrenArray = Array.isArray(children) ? children : [children];
|
|
1281
|
+
return h(tag, restProps, ...childrenArray);
|
|
1282
|
+
}
|
|
1283
|
+
return h(tag, restProps);
|
|
1284
|
+
}
|
|
1285
|
+
function jsxs(tag, props) {
|
|
1286
|
+
if (!props) {
|
|
1287
|
+
return h(tag, null);
|
|
1288
|
+
}
|
|
1289
|
+
const { children, ...restProps } = props;
|
|
1290
|
+
if (Array.isArray(children)) {
|
|
1291
|
+
return h(tag, restProps, ...children);
|
|
1292
|
+
} else if (children !== void 0 && children !== null) {
|
|
1293
|
+
return h(tag, restProps, children);
|
|
1294
|
+
}
|
|
1295
|
+
return h(tag, restProps);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
export {
|
|
1299
|
+
RenderContext,
|
|
1300
|
+
shouldPreserveElement,
|
|
1301
|
+
createLogger,
|
|
1302
|
+
h,
|
|
1303
|
+
Fragment,
|
|
1304
|
+
jsx,
|
|
1305
|
+
jsxs
|
|
1306
|
+
};
|