@wsxjs/wsx-core 0.0.19 → 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 +837 -98
- package/dist/index.mjs +137 -17
- package/dist/jsx-runtime.js +707 -86
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/jsx.js +707 -86
- 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 -190
- 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-UH5BDYGI.mjs +0 -283
package/dist/jsx-runtime.js
CHANGED
|
@@ -26,6 +26,170 @@ __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 isCreatedByH(element) {
|
|
175
|
+
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
return element[CACHE_KEY_PROP] !== void 0;
|
|
179
|
+
}
|
|
180
|
+
function shouldPreserveElement(element) {
|
|
181
|
+
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
if (!isCreatedByH(element)) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
if (element.hasAttribute("data-wsx-preserve")) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
29
193
|
// src/utils/svg-utils.ts
|
|
30
194
|
var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
31
195
|
var SVG_ONLY_ELEMENTS = /* @__PURE__ */ new Set([
|
|
@@ -138,62 +302,301 @@ function getSVGAttributeName(attributeName) {
|
|
|
138
302
|
return SVG_ATTRIBUTE_MAP.get(attributeName) || attributeName;
|
|
139
303
|
}
|
|
140
304
|
|
|
141
|
-
// src/utils/
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
305
|
+
// src/utils/logger.ts
|
|
306
|
+
var WSXLogger = class {
|
|
307
|
+
constructor(prefix = "[WSX]", enabled = true, level = "info") {
|
|
308
|
+
this.prefix = prefix;
|
|
309
|
+
this.enabled = enabled;
|
|
310
|
+
this.level = level;
|
|
311
|
+
}
|
|
312
|
+
shouldLog(level) {
|
|
313
|
+
if (!this.enabled) return false;
|
|
314
|
+
const levels = ["debug", "info", "warn", "error"];
|
|
315
|
+
const currentLevelIndex = levels.indexOf(this.level);
|
|
316
|
+
const messageLevelIndex = levels.indexOf(level);
|
|
317
|
+
return messageLevelIndex >= currentLevelIndex;
|
|
318
|
+
}
|
|
319
|
+
debug(message, ...args) {
|
|
320
|
+
if (this.shouldLog("debug")) {
|
|
321
|
+
console.debug(`${this.prefix} ${message}`, ...args);
|
|
151
322
|
}
|
|
152
|
-
}
|
|
323
|
+
}
|
|
324
|
+
info(message, ...args) {
|
|
325
|
+
if (this.shouldLog("info")) {
|
|
326
|
+
console.info(`${this.prefix} ${message}`, ...args);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
warn(message, ...args) {
|
|
330
|
+
if (this.shouldLog("warn")) {
|
|
331
|
+
console.warn(`${this.prefix} ${message}`, ...args);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
error(message, ...args) {
|
|
335
|
+
if (this.shouldLog("error")) {
|
|
336
|
+
console.error(`${this.prefix} ${message}`, ...args);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
var logger = new WSXLogger();
|
|
341
|
+
function createLogger(componentName) {
|
|
342
|
+
return new WSXLogger(`[WSX:${componentName}]`);
|
|
153
343
|
}
|
|
154
344
|
|
|
155
|
-
// src/
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
345
|
+
// src/utils/props-utils.ts
|
|
346
|
+
var logger2 = createLogger("Props Utilities");
|
|
347
|
+
function isFrameworkInternalProp(key) {
|
|
348
|
+
if (key === "key") {
|
|
349
|
+
return true;
|
|
159
350
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
351
|
+
if (key === "__wsxPositionId" || key === "__wsxIndex") {
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
if (key === "__testId") {
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
if (key === "ref") {
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
function isStandardHTMLAttribute(key) {
|
|
363
|
+
const standardAttributes = /* @__PURE__ */ new Set([
|
|
364
|
+
// 全局属性
|
|
365
|
+
"id",
|
|
366
|
+
"class",
|
|
367
|
+
"className",
|
|
368
|
+
"style",
|
|
369
|
+
"title",
|
|
370
|
+
"lang",
|
|
371
|
+
"dir",
|
|
372
|
+
"hidden",
|
|
373
|
+
"tabindex",
|
|
374
|
+
"accesskey",
|
|
375
|
+
"contenteditable",
|
|
376
|
+
"draggable",
|
|
377
|
+
"spellcheck",
|
|
378
|
+
"translate",
|
|
379
|
+
"autocapitalize",
|
|
380
|
+
"autocorrect",
|
|
381
|
+
// 表单属性
|
|
382
|
+
"name",
|
|
383
|
+
"value",
|
|
384
|
+
"type",
|
|
385
|
+
"placeholder",
|
|
386
|
+
"required",
|
|
387
|
+
"disabled",
|
|
388
|
+
"readonly",
|
|
389
|
+
"checked",
|
|
390
|
+
"selected",
|
|
391
|
+
"multiple",
|
|
392
|
+
"min",
|
|
393
|
+
"max",
|
|
394
|
+
"step",
|
|
395
|
+
"autocomplete",
|
|
396
|
+
"autofocus",
|
|
397
|
+
"form",
|
|
398
|
+
"formaction",
|
|
399
|
+
"formenctype",
|
|
400
|
+
"formmethod",
|
|
401
|
+
"formnovalidate",
|
|
402
|
+
"formtarget",
|
|
403
|
+
// 链接属性
|
|
404
|
+
"href",
|
|
405
|
+
"target",
|
|
406
|
+
"rel",
|
|
407
|
+
"download",
|
|
408
|
+
"hreflang",
|
|
409
|
+
"ping",
|
|
410
|
+
// 媒体属性
|
|
411
|
+
"src",
|
|
412
|
+
"alt",
|
|
413
|
+
"width",
|
|
414
|
+
"height",
|
|
415
|
+
"poster",
|
|
416
|
+
"preload",
|
|
417
|
+
"controls",
|
|
418
|
+
"autoplay",
|
|
419
|
+
"loop",
|
|
420
|
+
"muted",
|
|
421
|
+
"playsinline",
|
|
422
|
+
"crossorigin",
|
|
423
|
+
// ARIA 属性(部分常见)
|
|
424
|
+
"role"
|
|
425
|
+
]);
|
|
426
|
+
const lowerKey = key.toLowerCase();
|
|
427
|
+
if (standardAttributes.has(lowerKey)) {
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
if (lowerKey.startsWith("data-")) {
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
if (lowerKey.startsWith("aria-")) {
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
if (key.startsWith("xml:") || key.startsWith("xlink:")) {
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
function isSpecialProperty(key, value) {
|
|
442
|
+
return key === "ref" || key === "className" || key === "class" || key === "style" || key.startsWith("on") && typeof value === "function" || typeof value === "boolean" || key === "value";
|
|
443
|
+
}
|
|
444
|
+
function setSmartProperty(element, key, value, tag) {
|
|
445
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
446
|
+
if (isSpecialProperty(key, value)) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (isStandardHTMLAttribute(key)) {
|
|
450
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
451
|
+
if (typeof value === "object" && value !== null) {
|
|
452
|
+
try {
|
|
453
|
+
const serialized = JSON.stringify(value);
|
|
454
|
+
if (serialized.length > 1024 * 1024) {
|
|
455
|
+
logger2.warn(
|
|
456
|
+
`[WSX] Attribute "${key}" value too large, consider using a non-standard property name instead`
|
|
457
|
+
);
|
|
174
458
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
459
|
+
element.setAttribute(attributeName, serialized);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
logger2.warn(`Cannot serialize attribute "${key}":`, error);
|
|
462
|
+
}
|
|
463
|
+
} else {
|
|
464
|
+
element.setAttribute(attributeName, String(value));
|
|
465
|
+
}
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (element instanceof SVGElement) {
|
|
469
|
+
const attributeName = getSVGAttributeName(key);
|
|
470
|
+
if (typeof value === "object" && value !== null) {
|
|
471
|
+
try {
|
|
472
|
+
const serialized = JSON.stringify(value);
|
|
473
|
+
element.setAttribute(attributeName, serialized);
|
|
474
|
+
} catch (error) {
|
|
475
|
+
logger2.warn(`Cannot serialize SVG attribute "${key}":`, error);
|
|
476
|
+
}
|
|
477
|
+
} else {
|
|
478
|
+
element.setAttribute(attributeName, String(value));
|
|
479
|
+
}
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const hasProperty = key in element || Object.prototype.hasOwnProperty.call(element, key);
|
|
483
|
+
if (hasProperty) {
|
|
484
|
+
let isReadOnly = false;
|
|
485
|
+
try {
|
|
486
|
+
const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), key);
|
|
487
|
+
if (descriptor) {
|
|
488
|
+
isReadOnly = descriptor.get !== void 0 && descriptor.set === void 0 || descriptor.writable === false && descriptor.set === void 0;
|
|
489
|
+
}
|
|
490
|
+
} catch {
|
|
491
|
+
}
|
|
492
|
+
if (isReadOnly) {
|
|
493
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
494
|
+
if (typeof value === "object" && value !== null) {
|
|
495
|
+
try {
|
|
496
|
+
const serialized = JSON.stringify(value);
|
|
497
|
+
element.setAttribute(attributeName, serialized);
|
|
498
|
+
} catch (error) {
|
|
499
|
+
logger2.warn(`Cannot serialize readonly property "${key}":`, error);
|
|
183
500
|
}
|
|
184
|
-
} else
|
|
185
|
-
|
|
186
|
-
|
|
501
|
+
} else {
|
|
502
|
+
element.setAttribute(attributeName, String(value));
|
|
503
|
+
}
|
|
504
|
+
} else {
|
|
505
|
+
try {
|
|
506
|
+
element[key] = value;
|
|
507
|
+
} catch {
|
|
508
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
509
|
+
if (typeof value === "object" && value !== null) {
|
|
510
|
+
try {
|
|
511
|
+
const serialized = JSON.stringify(value);
|
|
512
|
+
element.setAttribute(attributeName, serialized);
|
|
513
|
+
} catch (error) {
|
|
514
|
+
logger2.warn(
|
|
515
|
+
`[WSX] Cannot serialize property "${key}" for attribute:`,
|
|
516
|
+
error
|
|
517
|
+
);
|
|
518
|
+
}
|
|
187
519
|
} else {
|
|
188
|
-
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
189
520
|
element.setAttribute(attributeName, String(value));
|
|
190
521
|
}
|
|
191
|
-
} else {
|
|
192
|
-
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
193
|
-
element.setAttribute(attributeName, String(value));
|
|
194
522
|
}
|
|
195
|
-
}
|
|
523
|
+
}
|
|
524
|
+
} else {
|
|
525
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
526
|
+
if (typeof value === "object" && value !== null) {
|
|
527
|
+
try {
|
|
528
|
+
const serialized = JSON.stringify(value);
|
|
529
|
+
if (serialized.length > 1024 * 1024) {
|
|
530
|
+
logger2.warn(
|
|
531
|
+
`[WSX] Property "${key}" value too large for attribute, consider using a JavaScript property instead`
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
element.setAttribute(attributeName, serialized);
|
|
535
|
+
} catch (error) {
|
|
536
|
+
logger2.warn(`Cannot serialize property "${key}" for attribute:`, error);
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
element.setAttribute(attributeName, String(value));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/utils/element-creation.ts
|
|
545
|
+
function applySingleProp(element, key, value, tag, isSVG) {
|
|
546
|
+
if (value === null || value === void 0 || value === false) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (key === "ref" && typeof value === "function") {
|
|
550
|
+
value(element);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (key === "className" || key === "class") {
|
|
554
|
+
if (isSVG) {
|
|
555
|
+
element.setAttribute("class", value);
|
|
556
|
+
} else {
|
|
557
|
+
element.className = value;
|
|
558
|
+
}
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
if (key === "style" && typeof value === "string") {
|
|
562
|
+
element.setAttribute("style", value);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
566
|
+
const eventName = key.slice(2).toLowerCase();
|
|
567
|
+
element.addEventListener(eventName, value);
|
|
568
|
+
return;
|
|
196
569
|
}
|
|
570
|
+
if (typeof value === "boolean") {
|
|
571
|
+
if (value) {
|
|
572
|
+
element.setAttribute(key, "");
|
|
573
|
+
}
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
if (key === "value") {
|
|
577
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
578
|
+
element.value = String(value);
|
|
579
|
+
} else {
|
|
580
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
581
|
+
element.setAttribute(attributeName, String(value));
|
|
582
|
+
}
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (isFrameworkInternalProp(key)) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
setSmartProperty(element, key, value, tag);
|
|
589
|
+
}
|
|
590
|
+
function applyPropsToElement(element, props, tag) {
|
|
591
|
+
if (!props) {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
595
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
596
|
+
applySingleProp(element, key, value, tag, isSVG);
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
function appendChildrenToElement(element, children) {
|
|
197
600
|
const flatChildren = flattenChildren(children);
|
|
198
601
|
flatChildren.forEach((child) => {
|
|
199
602
|
if (child === null || child === void 0 || child === false) {
|
|
@@ -207,60 +610,278 @@ function h(tag, props = {}, ...children) {
|
|
|
207
610
|
element.appendChild(child);
|
|
208
611
|
}
|
|
209
612
|
});
|
|
613
|
+
}
|
|
614
|
+
function createElementWithPropsAndChildren(tag, props, children) {
|
|
615
|
+
const element = createElement(tag);
|
|
616
|
+
applyPropsToElement(element, props, tag);
|
|
617
|
+
appendChildrenToElement(element, children);
|
|
210
618
|
return element;
|
|
211
619
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
620
|
+
|
|
621
|
+
// src/utils/element-update.ts
|
|
622
|
+
function removeProp(element, key, oldValue, tag) {
|
|
623
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
624
|
+
if (key === "ref") {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
if (key === "className" || key === "class") {
|
|
628
|
+
if (isSVG) {
|
|
629
|
+
element.removeAttribute("class");
|
|
630
|
+
} else {
|
|
631
|
+
element.className = "";
|
|
632
|
+
}
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
if (key === "style") {
|
|
636
|
+
element.removeAttribute("style");
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
if (key.startsWith("on") && typeof oldValue === "function") {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (key === "value") {
|
|
643
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
644
|
+
element.value = "";
|
|
645
|
+
} else {
|
|
646
|
+
const attributeName2 = isSVG ? getSVGAttributeName(key) : key;
|
|
647
|
+
element.removeAttribute(attributeName2);
|
|
648
|
+
}
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
if (isFrameworkInternalProp(key)) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
655
|
+
element.removeAttribute(attributeName);
|
|
656
|
+
try {
|
|
657
|
+
delete element[key];
|
|
658
|
+
} catch {
|
|
659
|
+
}
|
|
219
660
|
}
|
|
220
|
-
function
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
"[WSX] flattenChildren: Maximum depth exceeded, treating remaining children as text"
|
|
224
|
-
);
|
|
225
|
-
return children.filter(
|
|
226
|
-
(child) => typeof child === "string" || typeof child === "number"
|
|
227
|
-
);
|
|
661
|
+
function applySingleProp2(element, key, value, tag, isSVG) {
|
|
662
|
+
if (value === null || value === void 0 || value === false) {
|
|
663
|
+
return;
|
|
228
664
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
665
|
+
if (key === "ref" && typeof value === "function") {
|
|
666
|
+
value(element);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (key === "className" || key === "class") {
|
|
670
|
+
if (isSVG) {
|
|
671
|
+
element.setAttribute("class", value);
|
|
672
|
+
} else {
|
|
673
|
+
element.className = value;
|
|
674
|
+
}
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
if (key === "style" && typeof value === "string") {
|
|
678
|
+
element.setAttribute("style", value);
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
682
|
+
const eventName = key.slice(2).toLowerCase();
|
|
683
|
+
element.addEventListener(eventName, value);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
if (typeof value === "boolean") {
|
|
687
|
+
if (value) {
|
|
688
|
+
element.setAttribute(key, "");
|
|
689
|
+
}
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
if (key === "value") {
|
|
693
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
694
|
+
element.value = String(value);
|
|
695
|
+
} else {
|
|
696
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
697
|
+
element.setAttribute(attributeName, String(value));
|
|
698
|
+
}
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
if (isFrameworkInternalProp(key)) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
setSmartProperty(element, key, value, tag);
|
|
705
|
+
}
|
|
706
|
+
function updateProps(element, oldProps, newProps, tag) {
|
|
707
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
708
|
+
const old = oldProps || {};
|
|
709
|
+
const new_ = newProps || {};
|
|
710
|
+
for (const key in old) {
|
|
711
|
+
if (!(key in new_)) {
|
|
712
|
+
removeProp(element, key, old[key], tag);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
for (const key in new_) {
|
|
716
|
+
const oldValue = old[key];
|
|
717
|
+
const newValue = new_[key];
|
|
718
|
+
if (oldValue === newValue) {
|
|
232
719
|
continue;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
720
|
+
}
|
|
721
|
+
if (typeof oldValue === "object" && oldValue !== null && typeof newValue === "object" && newValue !== null) {
|
|
722
|
+
if (JSON.stringify(oldValue) === JSON.stringify(newValue)) {
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
applySingleProp2(element, key, newValue, tag, isSVG);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
function updateChildren(element, oldChildren, newChildren) {
|
|
730
|
+
const flatOld = flattenChildren(oldChildren);
|
|
731
|
+
const flatNew = flattenChildren(newChildren);
|
|
732
|
+
const minLength = Math.min(flatOld.length, flatNew.length);
|
|
733
|
+
for (let i = 0; i < minLength; i++) {
|
|
734
|
+
const oldChild = flatOld[i];
|
|
735
|
+
const newChild = flatNew[i];
|
|
736
|
+
if (typeof oldChild === "string" || typeof oldChild === "number") {
|
|
737
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
738
|
+
const textNode = element.childNodes[i];
|
|
739
|
+
if (textNode && textNode.nodeType === Node.TEXT_NODE) {
|
|
740
|
+
textNode.textContent = String(newChild);
|
|
741
|
+
} else {
|
|
742
|
+
const newTextNode = document.createTextNode(String(newChild));
|
|
743
|
+
if (textNode) {
|
|
744
|
+
element.replaceChild(newTextNode, textNode);
|
|
745
|
+
} else {
|
|
746
|
+
element.appendChild(newTextNode);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
} else {
|
|
750
|
+
const textNode = element.childNodes[i];
|
|
751
|
+
if (textNode) {
|
|
752
|
+
if (!shouldPreserveElement(textNode)) {
|
|
753
|
+
element.removeChild(textNode);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
757
|
+
element.appendChild(document.createTextNode(String(newChild)));
|
|
758
|
+
} else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
759
|
+
element.appendChild(newChild);
|
|
760
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
761
|
+
element.appendChild(newChild);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
} else if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
|
|
765
|
+
if (newChild === oldChild) {
|
|
766
|
+
continue;
|
|
767
|
+
} else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
768
|
+
const oldNode = element.childNodes[i];
|
|
769
|
+
if (oldNode) {
|
|
770
|
+
if (!shouldPreserveElement(oldNode)) {
|
|
771
|
+
if (oldNode !== newChild) {
|
|
772
|
+
element.replaceChild(newChild, oldNode);
|
|
248
773
|
}
|
|
249
774
|
} else {
|
|
250
|
-
|
|
775
|
+
if (newChild.parentNode !== element) {
|
|
776
|
+
element.appendChild(newChild);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
} else {
|
|
780
|
+
if (newChild.parentNode !== element) {
|
|
781
|
+
element.appendChild(newChild);
|
|
251
782
|
}
|
|
252
|
-
} catch (error) {
|
|
253
|
-
console.warn("[WSX] Failed to parse HTML string, treating as text:", error);
|
|
254
|
-
result.push(child);
|
|
255
783
|
}
|
|
256
784
|
} else {
|
|
257
|
-
|
|
785
|
+
const oldNode = element.childNodes[i];
|
|
786
|
+
if (oldNode) {
|
|
787
|
+
if (!shouldPreserveElement(oldNode)) {
|
|
788
|
+
element.removeChild(oldNode);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
792
|
+
element.appendChild(document.createTextNode(String(newChild)));
|
|
793
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
794
|
+
element.appendChild(newChild);
|
|
795
|
+
}
|
|
258
796
|
}
|
|
259
|
-
} else {
|
|
260
|
-
result.push(child);
|
|
261
797
|
}
|
|
262
798
|
}
|
|
263
|
-
|
|
799
|
+
for (let i = minLength; i < flatNew.length; i++) {
|
|
800
|
+
const newChild = flatNew[i];
|
|
801
|
+
if (newChild === null || newChild === void 0 || newChild === false) {
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
805
|
+
element.appendChild(document.createTextNode(String(newChild)));
|
|
806
|
+
} else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
807
|
+
if (newChild.parentNode !== element) {
|
|
808
|
+
element.appendChild(newChild);
|
|
809
|
+
}
|
|
810
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
811
|
+
element.appendChild(newChild);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
const nodesToRemove = [];
|
|
815
|
+
for (let i = flatNew.length; i < element.childNodes.length; i++) {
|
|
816
|
+
const child = element.childNodes[i];
|
|
817
|
+
if (!shouldPreserveElement(child)) {
|
|
818
|
+
nodesToRemove.push(child);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
for (let i = nodesToRemove.length - 1; i >= 0; i--) {
|
|
822
|
+
const node = nodesToRemove[i];
|
|
823
|
+
if (node.parentNode === element) {
|
|
824
|
+
element.removeChild(node);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
function updateElement(element, newProps, newChildren, tag, cacheManager) {
|
|
829
|
+
const oldMetadata = cacheManager.getMetadata(element);
|
|
830
|
+
const oldProps = oldMetadata?.props || null;
|
|
831
|
+
const oldChildren = oldMetadata?.children || [];
|
|
832
|
+
updateProps(element, oldProps, newProps, tag);
|
|
833
|
+
updateChildren(element, oldChildren, newChildren);
|
|
834
|
+
cacheManager.setMetadata(element, {
|
|
835
|
+
props: newProps || {},
|
|
836
|
+
children: newChildren
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// src/jsx-factory.ts
|
|
841
|
+
var logger3 = createLogger("JSX Factory");
|
|
842
|
+
function h(tag, props = {}, ...children) {
|
|
843
|
+
if (typeof tag === "function") {
|
|
844
|
+
return tag(props, children);
|
|
845
|
+
}
|
|
846
|
+
const context = RenderContext.getCurrentComponent();
|
|
847
|
+
const cacheManager = context ? RenderContext.getDOMCache() : null;
|
|
848
|
+
if (context && cacheManager) {
|
|
849
|
+
return tryUseCacheOrCreate(tag, props, children, context, cacheManager);
|
|
850
|
+
}
|
|
851
|
+
return createElementWithPropsAndChildren(tag, props, children);
|
|
852
|
+
}
|
|
853
|
+
function tryUseCacheOrCreate(tag, props, children, context, cacheManager) {
|
|
854
|
+
try {
|
|
855
|
+
const componentId = getComponentId();
|
|
856
|
+
const cacheKey = generateCacheKey(tag, props, componentId, context);
|
|
857
|
+
const cachedElement = cacheManager.get(cacheKey);
|
|
858
|
+
if (cachedElement) {
|
|
859
|
+
const element2 = cachedElement;
|
|
860
|
+
updateElement(element2, props, children, tag, cacheManager);
|
|
861
|
+
return element2;
|
|
862
|
+
}
|
|
863
|
+
const element = createElementWithPropsAndChildren(tag, props, children);
|
|
864
|
+
cacheManager.set(cacheKey, element);
|
|
865
|
+
markElement(element, cacheKey);
|
|
866
|
+
cacheManager.setMetadata(element, {
|
|
867
|
+
props: props || {},
|
|
868
|
+
children
|
|
869
|
+
});
|
|
870
|
+
return element;
|
|
871
|
+
} catch (error) {
|
|
872
|
+
return handleCacheError(error, tag, props, children);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function handleCacheError(error, tag, props, children) {
|
|
876
|
+
try {
|
|
877
|
+
const nodeEnv = typeof globalThis.process !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
878
|
+
globalThis.process.env?.NODE_ENV;
|
|
879
|
+
if (nodeEnv === "development") {
|
|
880
|
+
logger3.warn("[WSX DOM Cache] Cache error, falling back to create new element:", error);
|
|
881
|
+
}
|
|
882
|
+
} catch {
|
|
883
|
+
}
|
|
884
|
+
return createElementWithPropsAndChildren(tag, props, children);
|
|
264
885
|
}
|
|
265
886
|
function Fragment(_props, children) {
|
|
266
887
|
const fragment = document.createDocumentFragment();
|