@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/index.js
CHANGED
|
@@ -37,6 +37,170 @@ __export(index_exports, {
|
|
|
37
37
|
});
|
|
38
38
|
module.exports = __toCommonJS(index_exports);
|
|
39
39
|
|
|
40
|
+
// src/utils/dom-utils.ts
|
|
41
|
+
function parseHTMLToNodes(html) {
|
|
42
|
+
if (!html) return [];
|
|
43
|
+
const temp = document.createElement("div");
|
|
44
|
+
temp.innerHTML = html;
|
|
45
|
+
return Array.from(temp.childNodes).map((node) => {
|
|
46
|
+
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
47
|
+
return node;
|
|
48
|
+
} else {
|
|
49
|
+
return node.textContent || "";
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function isHTMLString(str) {
|
|
54
|
+
const trimmed = str.trim();
|
|
55
|
+
if (!trimmed) return false;
|
|
56
|
+
const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
|
|
57
|
+
const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
|
|
58
|
+
if (looksLikeMath) return false;
|
|
59
|
+
return htmlTagPattern.test(trimmed);
|
|
60
|
+
}
|
|
61
|
+
function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
|
|
62
|
+
if (depth > 10) {
|
|
63
|
+
console.warn(
|
|
64
|
+
"[WSX] flattenChildren: Maximum depth exceeded, treating remaining children as text"
|
|
65
|
+
);
|
|
66
|
+
return children.filter(
|
|
67
|
+
(child) => typeof child === "string" || typeof child === "number"
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const result = [];
|
|
71
|
+
for (const child of children) {
|
|
72
|
+
if (child === null || child === void 0 || child === false) {
|
|
73
|
+
continue;
|
|
74
|
+
} else if (Array.isArray(child)) {
|
|
75
|
+
result.push(...flattenChildren(child, skipHTMLDetection, depth + 1));
|
|
76
|
+
} else if (typeof child === "string") {
|
|
77
|
+
if (skipHTMLDetection) {
|
|
78
|
+
result.push(child);
|
|
79
|
+
} else if (isHTMLString(child)) {
|
|
80
|
+
try {
|
|
81
|
+
const nodes = parseHTMLToNodes(child);
|
|
82
|
+
if (nodes.length > 0) {
|
|
83
|
+
for (const node of nodes) {
|
|
84
|
+
if (typeof node === "string") {
|
|
85
|
+
result.push(node);
|
|
86
|
+
} else {
|
|
87
|
+
result.push(node);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
result.push(child);
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.warn("[WSX] Failed to parse HTML string, treating as text:", error);
|
|
95
|
+
result.push(child);
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
result.push(child);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
result.push(child);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/render-context.ts
|
|
108
|
+
var _RenderContext = class _RenderContext {
|
|
109
|
+
/**
|
|
110
|
+
* Executes a function within the context of a component.
|
|
111
|
+
* @param component The component instance currently rendering.
|
|
112
|
+
* @param fn The function to execute (usually the render method).
|
|
113
|
+
*/
|
|
114
|
+
static runInContext(component, fn) {
|
|
115
|
+
const prev = _RenderContext.current;
|
|
116
|
+
_RenderContext.current = component;
|
|
117
|
+
try {
|
|
118
|
+
return fn();
|
|
119
|
+
} finally {
|
|
120
|
+
_RenderContext.current = prev;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Gets the currently rendering component.
|
|
125
|
+
*/
|
|
126
|
+
static getCurrentComponent() {
|
|
127
|
+
return _RenderContext.current;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Gets the current component's DOM cache.
|
|
131
|
+
*/
|
|
132
|
+
static getDOMCache() {
|
|
133
|
+
return _RenderContext.current?.getDomCache();
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
_RenderContext.current = null;
|
|
137
|
+
var RenderContext = _RenderContext;
|
|
138
|
+
|
|
139
|
+
// src/utils/cache-key.ts
|
|
140
|
+
var POSITION_ID_KEY = "__wsxPositionId";
|
|
141
|
+
var INDEX_KEY = "__wsxIndex";
|
|
142
|
+
var componentElementCounters = /* @__PURE__ */ new WeakMap();
|
|
143
|
+
var componentIdCache = /* @__PURE__ */ new WeakMap();
|
|
144
|
+
function generateCacheKey(tag, props, componentId, component) {
|
|
145
|
+
const positionId = props?.[POSITION_ID_KEY];
|
|
146
|
+
const userKey = props?.key;
|
|
147
|
+
const index = props?.[INDEX_KEY];
|
|
148
|
+
if (userKey !== void 0 && userKey !== null) {
|
|
149
|
+
return `${componentId}:${tag}:key-${String(userKey)}`;
|
|
150
|
+
}
|
|
151
|
+
if (index !== void 0 && index !== null) {
|
|
152
|
+
return `${componentId}:${tag}:idx-${String(index)}`;
|
|
153
|
+
}
|
|
154
|
+
if (positionId !== void 0 && positionId !== null && positionId !== "no-id") {
|
|
155
|
+
return `${componentId}:${tag}:${String(positionId)}`;
|
|
156
|
+
}
|
|
157
|
+
if (component) {
|
|
158
|
+
let counter = componentElementCounters.get(component) || 0;
|
|
159
|
+
counter++;
|
|
160
|
+
componentElementCounters.set(component, counter);
|
|
161
|
+
return `${componentId}:${tag}:auto-${counter}`;
|
|
162
|
+
}
|
|
163
|
+
return `${componentId}:${tag}:fallback-${Date.now()}-${Math.random()}`;
|
|
164
|
+
}
|
|
165
|
+
function getComponentId() {
|
|
166
|
+
const component = RenderContext.getCurrentComponent();
|
|
167
|
+
if (component) {
|
|
168
|
+
let cachedId = componentIdCache.get(component);
|
|
169
|
+
if (cachedId) {
|
|
170
|
+
return cachedId;
|
|
171
|
+
}
|
|
172
|
+
const instanceId = component._instanceId || "default";
|
|
173
|
+
cachedId = `${component.constructor.name}:${instanceId}`;
|
|
174
|
+
componentIdCache.set(component, cachedId);
|
|
175
|
+
return cachedId;
|
|
176
|
+
}
|
|
177
|
+
return "unknown";
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/utils/element-marking.ts
|
|
181
|
+
var CACHE_KEY_PROP = "__wsxCacheKey";
|
|
182
|
+
function markElement(element, cacheKey) {
|
|
183
|
+
element[CACHE_KEY_PROP] = cacheKey;
|
|
184
|
+
}
|
|
185
|
+
function isCreatedByH(element) {
|
|
186
|
+
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
return element[CACHE_KEY_PROP] !== void 0;
|
|
190
|
+
}
|
|
191
|
+
function shouldPreserveElement(element) {
|
|
192
|
+
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
if (!isCreatedByH(element)) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
if (element.hasAttribute("data-wsx-preserve")) {
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
40
204
|
// src/utils/svg-utils.ts
|
|
41
205
|
var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
42
206
|
var SVG_ONLY_ELEMENTS = /* @__PURE__ */ new Set([
|
|
@@ -149,62 +313,301 @@ function getSVGAttributeName(attributeName) {
|
|
|
149
313
|
return SVG_ATTRIBUTE_MAP.get(attributeName) || attributeName;
|
|
150
314
|
}
|
|
151
315
|
|
|
152
|
-
// src/utils/
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
316
|
+
// src/utils/logger.ts
|
|
317
|
+
var WSXLogger = class {
|
|
318
|
+
constructor(prefix = "[WSX]", enabled = true, level = "info") {
|
|
319
|
+
this.prefix = prefix;
|
|
320
|
+
this.enabled = enabled;
|
|
321
|
+
this.level = level;
|
|
322
|
+
}
|
|
323
|
+
shouldLog(level) {
|
|
324
|
+
if (!this.enabled) return false;
|
|
325
|
+
const levels = ["debug", "info", "warn", "error"];
|
|
326
|
+
const currentLevelIndex = levels.indexOf(this.level);
|
|
327
|
+
const messageLevelIndex = levels.indexOf(level);
|
|
328
|
+
return messageLevelIndex >= currentLevelIndex;
|
|
329
|
+
}
|
|
330
|
+
debug(message, ...args) {
|
|
331
|
+
if (this.shouldLog("debug")) {
|
|
332
|
+
console.debug(`${this.prefix} ${message}`, ...args);
|
|
162
333
|
}
|
|
163
|
-
}
|
|
334
|
+
}
|
|
335
|
+
info(message, ...args) {
|
|
336
|
+
if (this.shouldLog("info")) {
|
|
337
|
+
console.info(`${this.prefix} ${message}`, ...args);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
warn(message, ...args) {
|
|
341
|
+
if (this.shouldLog("warn")) {
|
|
342
|
+
console.warn(`${this.prefix} ${message}`, ...args);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
error(message, ...args) {
|
|
346
|
+
if (this.shouldLog("error")) {
|
|
347
|
+
console.error(`${this.prefix} ${message}`, ...args);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
var logger = new WSXLogger();
|
|
352
|
+
function createLogger(componentName) {
|
|
353
|
+
return new WSXLogger(`[WSX:${componentName}]`);
|
|
164
354
|
}
|
|
165
355
|
|
|
166
|
-
// src/
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
356
|
+
// src/utils/props-utils.ts
|
|
357
|
+
var logger2 = createLogger("Props Utilities");
|
|
358
|
+
function isFrameworkInternalProp(key) {
|
|
359
|
+
if (key === "key") {
|
|
360
|
+
return true;
|
|
170
361
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
362
|
+
if (key === "__wsxPositionId" || key === "__wsxIndex") {
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
if (key === "__testId") {
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
if (key === "ref") {
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
function isStandardHTMLAttribute(key) {
|
|
374
|
+
const standardAttributes = /* @__PURE__ */ new Set([
|
|
375
|
+
// 全局属性
|
|
376
|
+
"id",
|
|
377
|
+
"class",
|
|
378
|
+
"className",
|
|
379
|
+
"style",
|
|
380
|
+
"title",
|
|
381
|
+
"lang",
|
|
382
|
+
"dir",
|
|
383
|
+
"hidden",
|
|
384
|
+
"tabindex",
|
|
385
|
+
"accesskey",
|
|
386
|
+
"contenteditable",
|
|
387
|
+
"draggable",
|
|
388
|
+
"spellcheck",
|
|
389
|
+
"translate",
|
|
390
|
+
"autocapitalize",
|
|
391
|
+
"autocorrect",
|
|
392
|
+
// 表单属性
|
|
393
|
+
"name",
|
|
394
|
+
"value",
|
|
395
|
+
"type",
|
|
396
|
+
"placeholder",
|
|
397
|
+
"required",
|
|
398
|
+
"disabled",
|
|
399
|
+
"readonly",
|
|
400
|
+
"checked",
|
|
401
|
+
"selected",
|
|
402
|
+
"multiple",
|
|
403
|
+
"min",
|
|
404
|
+
"max",
|
|
405
|
+
"step",
|
|
406
|
+
"autocomplete",
|
|
407
|
+
"autofocus",
|
|
408
|
+
"form",
|
|
409
|
+
"formaction",
|
|
410
|
+
"formenctype",
|
|
411
|
+
"formmethod",
|
|
412
|
+
"formnovalidate",
|
|
413
|
+
"formtarget",
|
|
414
|
+
// 链接属性
|
|
415
|
+
"href",
|
|
416
|
+
"target",
|
|
417
|
+
"rel",
|
|
418
|
+
"download",
|
|
419
|
+
"hreflang",
|
|
420
|
+
"ping",
|
|
421
|
+
// 媒体属性
|
|
422
|
+
"src",
|
|
423
|
+
"alt",
|
|
424
|
+
"width",
|
|
425
|
+
"height",
|
|
426
|
+
"poster",
|
|
427
|
+
"preload",
|
|
428
|
+
"controls",
|
|
429
|
+
"autoplay",
|
|
430
|
+
"loop",
|
|
431
|
+
"muted",
|
|
432
|
+
"playsinline",
|
|
433
|
+
"crossorigin",
|
|
434
|
+
// ARIA 属性(部分常见)
|
|
435
|
+
"role"
|
|
436
|
+
]);
|
|
437
|
+
const lowerKey = key.toLowerCase();
|
|
438
|
+
if (standardAttributes.has(lowerKey)) {
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
if (lowerKey.startsWith("data-")) {
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
if (lowerKey.startsWith("aria-")) {
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
if (key.startsWith("xml:") || key.startsWith("xlink:")) {
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
function isSpecialProperty(key, value) {
|
|
453
|
+
return key === "ref" || key === "className" || key === "class" || key === "style" || key.startsWith("on") && typeof value === "function" || typeof value === "boolean" || key === "value";
|
|
454
|
+
}
|
|
455
|
+
function setSmartProperty(element, key, value, tag) {
|
|
456
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
457
|
+
if (isSpecialProperty(key, value)) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (isStandardHTMLAttribute(key)) {
|
|
461
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
462
|
+
if (typeof value === "object" && value !== null) {
|
|
463
|
+
try {
|
|
464
|
+
const serialized = JSON.stringify(value);
|
|
465
|
+
if (serialized.length > 1024 * 1024) {
|
|
466
|
+
logger2.warn(
|
|
467
|
+
`[WSX] Attribute "${key}" value too large, consider using a non-standard property name instead`
|
|
468
|
+
);
|
|
185
469
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
470
|
+
element.setAttribute(attributeName, serialized);
|
|
471
|
+
} catch (error) {
|
|
472
|
+
logger2.warn(`Cannot serialize attribute "${key}":`, error);
|
|
473
|
+
}
|
|
474
|
+
} else {
|
|
475
|
+
element.setAttribute(attributeName, String(value));
|
|
476
|
+
}
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (element instanceof SVGElement) {
|
|
480
|
+
const attributeName = getSVGAttributeName(key);
|
|
481
|
+
if (typeof value === "object" && value !== null) {
|
|
482
|
+
try {
|
|
483
|
+
const serialized = JSON.stringify(value);
|
|
484
|
+
element.setAttribute(attributeName, serialized);
|
|
485
|
+
} catch (error) {
|
|
486
|
+
logger2.warn(`Cannot serialize SVG attribute "${key}":`, error);
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
element.setAttribute(attributeName, String(value));
|
|
490
|
+
}
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const hasProperty = key in element || Object.prototype.hasOwnProperty.call(element, key);
|
|
494
|
+
if (hasProperty) {
|
|
495
|
+
let isReadOnly = false;
|
|
496
|
+
try {
|
|
497
|
+
const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), key);
|
|
498
|
+
if (descriptor) {
|
|
499
|
+
isReadOnly = descriptor.get !== void 0 && descriptor.set === void 0 || descriptor.writable === false && descriptor.set === void 0;
|
|
500
|
+
}
|
|
501
|
+
} catch {
|
|
502
|
+
}
|
|
503
|
+
if (isReadOnly) {
|
|
504
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
505
|
+
if (typeof value === "object" && value !== null) {
|
|
506
|
+
try {
|
|
507
|
+
const serialized = JSON.stringify(value);
|
|
508
|
+
element.setAttribute(attributeName, serialized);
|
|
509
|
+
} catch (error) {
|
|
510
|
+
logger2.warn(`Cannot serialize readonly property "${key}":`, error);
|
|
194
511
|
}
|
|
195
|
-
} else
|
|
196
|
-
|
|
197
|
-
|
|
512
|
+
} else {
|
|
513
|
+
element.setAttribute(attributeName, String(value));
|
|
514
|
+
}
|
|
515
|
+
} else {
|
|
516
|
+
try {
|
|
517
|
+
element[key] = value;
|
|
518
|
+
} catch {
|
|
519
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
520
|
+
if (typeof value === "object" && value !== null) {
|
|
521
|
+
try {
|
|
522
|
+
const serialized = JSON.stringify(value);
|
|
523
|
+
element.setAttribute(attributeName, serialized);
|
|
524
|
+
} catch (error) {
|
|
525
|
+
logger2.warn(
|
|
526
|
+
`[WSX] Cannot serialize property "${key}" for attribute:`,
|
|
527
|
+
error
|
|
528
|
+
);
|
|
529
|
+
}
|
|
198
530
|
} else {
|
|
199
|
-
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
200
531
|
element.setAttribute(attributeName, String(value));
|
|
201
532
|
}
|
|
202
|
-
} else {
|
|
203
|
-
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
204
|
-
element.setAttribute(attributeName, String(value));
|
|
205
533
|
}
|
|
206
|
-
}
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
537
|
+
if (typeof value === "object" && value !== null) {
|
|
538
|
+
try {
|
|
539
|
+
const serialized = JSON.stringify(value);
|
|
540
|
+
if (serialized.length > 1024 * 1024) {
|
|
541
|
+
logger2.warn(
|
|
542
|
+
`[WSX] Property "${key}" value too large for attribute, consider using a JavaScript property instead`
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
element.setAttribute(attributeName, serialized);
|
|
546
|
+
} catch (error) {
|
|
547
|
+
logger2.warn(`Cannot serialize property "${key}" for attribute:`, error);
|
|
548
|
+
}
|
|
549
|
+
} else {
|
|
550
|
+
element.setAttribute(attributeName, String(value));
|
|
551
|
+
}
|
|
207
552
|
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// src/utils/element-creation.ts
|
|
556
|
+
function applySingleProp(element, key, value, tag, isSVG) {
|
|
557
|
+
if (value === null || value === void 0 || value === false) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (key === "ref" && typeof value === "function") {
|
|
561
|
+
value(element);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
if (key === "className" || key === "class") {
|
|
565
|
+
if (isSVG) {
|
|
566
|
+
element.setAttribute("class", value);
|
|
567
|
+
} else {
|
|
568
|
+
element.className = value;
|
|
569
|
+
}
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (key === "style" && typeof value === "string") {
|
|
573
|
+
element.setAttribute("style", value);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
577
|
+
const eventName = key.slice(2).toLowerCase();
|
|
578
|
+
element.addEventListener(eventName, value);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
if (typeof value === "boolean") {
|
|
582
|
+
if (value) {
|
|
583
|
+
element.setAttribute(key, "");
|
|
584
|
+
}
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (key === "value") {
|
|
588
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
589
|
+
element.value = String(value);
|
|
590
|
+
} else {
|
|
591
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
592
|
+
element.setAttribute(attributeName, String(value));
|
|
593
|
+
}
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
if (isFrameworkInternalProp(key)) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
setSmartProperty(element, key, value, tag);
|
|
600
|
+
}
|
|
601
|
+
function applyPropsToElement(element, props, tag) {
|
|
602
|
+
if (!props) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
606
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
607
|
+
applySingleProp(element, key, value, tag, isSVG);
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
function appendChildrenToElement(element, children) {
|
|
208
611
|
const flatChildren = flattenChildren(children);
|
|
209
612
|
flatChildren.forEach((child) => {
|
|
210
613
|
if (child === null || child === void 0 || child === false) {
|
|
@@ -218,60 +621,278 @@ function h(tag, props = {}, ...children) {
|
|
|
218
621
|
element.appendChild(child);
|
|
219
622
|
}
|
|
220
623
|
});
|
|
624
|
+
}
|
|
625
|
+
function createElementWithPropsAndChildren(tag, props, children) {
|
|
626
|
+
const element = createElement(tag);
|
|
627
|
+
applyPropsToElement(element, props, tag);
|
|
628
|
+
appendChildrenToElement(element, children);
|
|
221
629
|
return element;
|
|
222
630
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
631
|
+
|
|
632
|
+
// src/utils/element-update.ts
|
|
633
|
+
function removeProp(element, key, oldValue, tag) {
|
|
634
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
635
|
+
if (key === "ref") {
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
if (key === "className" || key === "class") {
|
|
639
|
+
if (isSVG) {
|
|
640
|
+
element.removeAttribute("class");
|
|
641
|
+
} else {
|
|
642
|
+
element.className = "";
|
|
643
|
+
}
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (key === "style") {
|
|
647
|
+
element.removeAttribute("style");
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (key.startsWith("on") && typeof oldValue === "function") {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (key === "value") {
|
|
654
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
655
|
+
element.value = "";
|
|
656
|
+
} else {
|
|
657
|
+
const attributeName2 = isSVG ? getSVGAttributeName(key) : key;
|
|
658
|
+
element.removeAttribute(attributeName2);
|
|
659
|
+
}
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (isFrameworkInternalProp(key)) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
666
|
+
element.removeAttribute(attributeName);
|
|
667
|
+
try {
|
|
668
|
+
delete element[key];
|
|
669
|
+
} catch {
|
|
670
|
+
}
|
|
230
671
|
}
|
|
231
|
-
function
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
"[WSX] flattenChildren: Maximum depth exceeded, treating remaining children as text"
|
|
235
|
-
);
|
|
236
|
-
return children.filter(
|
|
237
|
-
(child) => typeof child === "string" || typeof child === "number"
|
|
238
|
-
);
|
|
672
|
+
function applySingleProp2(element, key, value, tag, isSVG) {
|
|
673
|
+
if (value === null || value === void 0 || value === false) {
|
|
674
|
+
return;
|
|
239
675
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
676
|
+
if (key === "ref" && typeof value === "function") {
|
|
677
|
+
value(element);
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
if (key === "className" || key === "class") {
|
|
681
|
+
if (isSVG) {
|
|
682
|
+
element.setAttribute("class", value);
|
|
683
|
+
} else {
|
|
684
|
+
element.className = value;
|
|
685
|
+
}
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
if (key === "style" && typeof value === "string") {
|
|
689
|
+
element.setAttribute("style", value);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
693
|
+
const eventName = key.slice(2).toLowerCase();
|
|
694
|
+
element.addEventListener(eventName, value);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
if (typeof value === "boolean") {
|
|
698
|
+
if (value) {
|
|
699
|
+
element.setAttribute(key, "");
|
|
700
|
+
}
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
if (key === "value") {
|
|
704
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
705
|
+
element.value = String(value);
|
|
706
|
+
} else {
|
|
707
|
+
const attributeName = isSVG ? getSVGAttributeName(key) : key;
|
|
708
|
+
element.setAttribute(attributeName, String(value));
|
|
709
|
+
}
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (isFrameworkInternalProp(key)) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
setSmartProperty(element, key, value, tag);
|
|
716
|
+
}
|
|
717
|
+
function updateProps(element, oldProps, newProps, tag) {
|
|
718
|
+
const isSVG = shouldUseSVGNamespace(tag);
|
|
719
|
+
const old = oldProps || {};
|
|
720
|
+
const new_ = newProps || {};
|
|
721
|
+
for (const key in old) {
|
|
722
|
+
if (!(key in new_)) {
|
|
723
|
+
removeProp(element, key, old[key], tag);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
for (const key in new_) {
|
|
727
|
+
const oldValue = old[key];
|
|
728
|
+
const newValue = new_[key];
|
|
729
|
+
if (oldValue === newValue) {
|
|
243
730
|
continue;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
731
|
+
}
|
|
732
|
+
if (typeof oldValue === "object" && oldValue !== null && typeof newValue === "object" && newValue !== null) {
|
|
733
|
+
if (JSON.stringify(oldValue) === JSON.stringify(newValue)) {
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
applySingleProp2(element, key, newValue, tag, isSVG);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
function updateChildren(element, oldChildren, newChildren) {
|
|
741
|
+
const flatOld = flattenChildren(oldChildren);
|
|
742
|
+
const flatNew = flattenChildren(newChildren);
|
|
743
|
+
const minLength = Math.min(flatOld.length, flatNew.length);
|
|
744
|
+
for (let i = 0; i < minLength; i++) {
|
|
745
|
+
const oldChild = flatOld[i];
|
|
746
|
+
const newChild = flatNew[i];
|
|
747
|
+
if (typeof oldChild === "string" || typeof oldChild === "number") {
|
|
748
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
749
|
+
const textNode = element.childNodes[i];
|
|
750
|
+
if (textNode && textNode.nodeType === Node.TEXT_NODE) {
|
|
751
|
+
textNode.textContent = String(newChild);
|
|
752
|
+
} else {
|
|
753
|
+
const newTextNode = document.createTextNode(String(newChild));
|
|
754
|
+
if (textNode) {
|
|
755
|
+
element.replaceChild(newTextNode, textNode);
|
|
756
|
+
} else {
|
|
757
|
+
element.appendChild(newTextNode);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
} else {
|
|
761
|
+
const textNode = element.childNodes[i];
|
|
762
|
+
if (textNode) {
|
|
763
|
+
if (!shouldPreserveElement(textNode)) {
|
|
764
|
+
element.removeChild(textNode);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
768
|
+
element.appendChild(document.createTextNode(String(newChild)));
|
|
769
|
+
} else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
770
|
+
element.appendChild(newChild);
|
|
771
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
772
|
+
element.appendChild(newChild);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
} else if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
|
|
776
|
+
if (newChild === oldChild) {
|
|
777
|
+
continue;
|
|
778
|
+
} else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
779
|
+
const oldNode = element.childNodes[i];
|
|
780
|
+
if (oldNode) {
|
|
781
|
+
if (!shouldPreserveElement(oldNode)) {
|
|
782
|
+
if (oldNode !== newChild) {
|
|
783
|
+
element.replaceChild(newChild, oldNode);
|
|
259
784
|
}
|
|
260
785
|
} else {
|
|
261
|
-
|
|
786
|
+
if (newChild.parentNode !== element) {
|
|
787
|
+
element.appendChild(newChild);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
} else {
|
|
791
|
+
if (newChild.parentNode !== element) {
|
|
792
|
+
element.appendChild(newChild);
|
|
262
793
|
}
|
|
263
|
-
} catch (error) {
|
|
264
|
-
console.warn("[WSX] Failed to parse HTML string, treating as text:", error);
|
|
265
|
-
result.push(child);
|
|
266
794
|
}
|
|
267
795
|
} else {
|
|
268
|
-
|
|
796
|
+
const oldNode = element.childNodes[i];
|
|
797
|
+
if (oldNode) {
|
|
798
|
+
if (!shouldPreserveElement(oldNode)) {
|
|
799
|
+
element.removeChild(oldNode);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
803
|
+
element.appendChild(document.createTextNode(String(newChild)));
|
|
804
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
805
|
+
element.appendChild(newChild);
|
|
806
|
+
}
|
|
269
807
|
}
|
|
270
|
-
} else {
|
|
271
|
-
result.push(child);
|
|
272
808
|
}
|
|
273
809
|
}
|
|
274
|
-
|
|
810
|
+
for (let i = minLength; i < flatNew.length; i++) {
|
|
811
|
+
const newChild = flatNew[i];
|
|
812
|
+
if (newChild === null || newChild === void 0 || newChild === false) {
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (typeof newChild === "string" || typeof newChild === "number") {
|
|
816
|
+
element.appendChild(document.createTextNode(String(newChild)));
|
|
817
|
+
} else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
|
|
818
|
+
if (newChild.parentNode !== element) {
|
|
819
|
+
element.appendChild(newChild);
|
|
820
|
+
}
|
|
821
|
+
} else if (newChild instanceof DocumentFragment) {
|
|
822
|
+
element.appendChild(newChild);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
const nodesToRemove = [];
|
|
826
|
+
for (let i = flatNew.length; i < element.childNodes.length; i++) {
|
|
827
|
+
const child = element.childNodes[i];
|
|
828
|
+
if (!shouldPreserveElement(child)) {
|
|
829
|
+
nodesToRemove.push(child);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
for (let i = nodesToRemove.length - 1; i >= 0; i--) {
|
|
833
|
+
const node = nodesToRemove[i];
|
|
834
|
+
if (node.parentNode === element) {
|
|
835
|
+
element.removeChild(node);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
function updateElement(element, newProps, newChildren, tag, cacheManager) {
|
|
840
|
+
const oldMetadata = cacheManager.getMetadata(element);
|
|
841
|
+
const oldProps = oldMetadata?.props || null;
|
|
842
|
+
const oldChildren = oldMetadata?.children || [];
|
|
843
|
+
updateProps(element, oldProps, newProps, tag);
|
|
844
|
+
updateChildren(element, oldChildren, newChildren);
|
|
845
|
+
cacheManager.setMetadata(element, {
|
|
846
|
+
props: newProps || {},
|
|
847
|
+
children: newChildren
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// src/jsx-factory.ts
|
|
852
|
+
var logger3 = createLogger("JSX Factory");
|
|
853
|
+
function h(tag, props = {}, ...children) {
|
|
854
|
+
if (typeof tag === "function") {
|
|
855
|
+
return tag(props, children);
|
|
856
|
+
}
|
|
857
|
+
const context = RenderContext.getCurrentComponent();
|
|
858
|
+
const cacheManager = context ? RenderContext.getDOMCache() : null;
|
|
859
|
+
if (context && cacheManager) {
|
|
860
|
+
return tryUseCacheOrCreate(tag, props, children, context, cacheManager);
|
|
861
|
+
}
|
|
862
|
+
return createElementWithPropsAndChildren(tag, props, children);
|
|
863
|
+
}
|
|
864
|
+
function tryUseCacheOrCreate(tag, props, children, context, cacheManager) {
|
|
865
|
+
try {
|
|
866
|
+
const componentId = getComponentId();
|
|
867
|
+
const cacheKey = generateCacheKey(tag, props, componentId, context);
|
|
868
|
+
const cachedElement = cacheManager.get(cacheKey);
|
|
869
|
+
if (cachedElement) {
|
|
870
|
+
const element2 = cachedElement;
|
|
871
|
+
updateElement(element2, props, children, tag, cacheManager);
|
|
872
|
+
return element2;
|
|
873
|
+
}
|
|
874
|
+
const element = createElementWithPropsAndChildren(tag, props, children);
|
|
875
|
+
cacheManager.set(cacheKey, element);
|
|
876
|
+
markElement(element, cacheKey);
|
|
877
|
+
cacheManager.setMetadata(element, {
|
|
878
|
+
props: props || {},
|
|
879
|
+
children
|
|
880
|
+
});
|
|
881
|
+
return element;
|
|
882
|
+
} catch (error) {
|
|
883
|
+
return handleCacheError(error, tag, props, children);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
function handleCacheError(error, tag, props, children) {
|
|
887
|
+
try {
|
|
888
|
+
const nodeEnv = typeof globalThis.process !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
889
|
+
globalThis.process.env?.NODE_ENV;
|
|
890
|
+
if (nodeEnv === "development") {
|
|
891
|
+
logger3.warn("[WSX DOM Cache] Cache error, falling back to create new element:", error);
|
|
892
|
+
}
|
|
893
|
+
} catch {
|
|
894
|
+
}
|
|
895
|
+
return createElementWithPropsAndChildren(tag, props, children);
|
|
275
896
|
}
|
|
276
897
|
function Fragment(_props, children) {
|
|
277
898
|
const fragment = document.createDocumentFragment();
|
|
@@ -333,7 +954,7 @@ StyleManager.styleSheets = /* @__PURE__ */ new Map();
|
|
|
333
954
|
|
|
334
955
|
// src/utils/reactive.ts
|
|
335
956
|
var import_wsx_logger = require("@wsxjs/wsx-logger");
|
|
336
|
-
var
|
|
957
|
+
var logger4 = (0, import_wsx_logger.createLogger)("ReactiveSystem");
|
|
337
958
|
var UpdateScheduler = class {
|
|
338
959
|
constructor() {
|
|
339
960
|
this.pendingCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -362,7 +983,7 @@ var UpdateScheduler = class {
|
|
|
362
983
|
try {
|
|
363
984
|
callback();
|
|
364
985
|
} catch (error) {
|
|
365
|
-
|
|
986
|
+
logger4.error("[WSX Reactive] Error in callback:", error);
|
|
366
987
|
}
|
|
367
988
|
});
|
|
368
989
|
}
|
|
@@ -504,7 +1125,7 @@ var ReactiveDebug = {
|
|
|
504
1125
|
*/
|
|
505
1126
|
log(message, ...args) {
|
|
506
1127
|
if (this.isEnabled()) {
|
|
507
|
-
|
|
1128
|
+
logger4.info(`[WSX Reactive] ${message}`, ...args);
|
|
508
1129
|
}
|
|
509
1130
|
}
|
|
510
1131
|
};
|
|
@@ -552,6 +1173,108 @@ function reactiveWithDebug(obj, onChange, debugName) {
|
|
|
552
1173
|
});
|
|
553
1174
|
}
|
|
554
1175
|
|
|
1176
|
+
// src/dom-cache-manager.ts
|
|
1177
|
+
var logger5 = createLogger("DOMCacheManager");
|
|
1178
|
+
var DOMCacheManager = class {
|
|
1179
|
+
constructor() {
|
|
1180
|
+
// Map<CacheKey, DOMElement>
|
|
1181
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
1182
|
+
// Map<DOMElement, Metadata>
|
|
1183
|
+
// Stores metadata (props, children) for cached elements to support diffing
|
|
1184
|
+
this.metadata = /* @__PURE__ */ new WeakMap();
|
|
1185
|
+
// Track key-parent relationships to detect duplicate keys in all environments
|
|
1186
|
+
// Map<CacheKey, ParentInfo>
|
|
1187
|
+
this.keyParentMap = /* @__PURE__ */ new Map();
|
|
1188
|
+
// Flag to enable duplicate key warnings (enabled by default, critical for correctness)
|
|
1189
|
+
this.warnDuplicateKeys = true;
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Retrieves an element from the cache.
|
|
1193
|
+
* @param key The unique cache key.
|
|
1194
|
+
*/
|
|
1195
|
+
get(key) {
|
|
1196
|
+
return this.cache.get(key);
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* Stores an element in the cache.
|
|
1200
|
+
* @param key The unique cache key.
|
|
1201
|
+
* @param element The DOM element to cache.
|
|
1202
|
+
*/
|
|
1203
|
+
set(key, element) {
|
|
1204
|
+
if (this.warnDuplicateKeys) {
|
|
1205
|
+
this.checkDuplicateKey(key, element);
|
|
1206
|
+
}
|
|
1207
|
+
this.cache.set(key, element);
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Checks if a cache key is being reused in a different parent container.
|
|
1211
|
+
* Runs in all environments to help developers catch key conflicts early.
|
|
1212
|
+
* This is critical for correctness and helps prevent subtle bugs.
|
|
1213
|
+
*/
|
|
1214
|
+
checkDuplicateKey(key, element) {
|
|
1215
|
+
const existing = this.keyParentMap.get(key);
|
|
1216
|
+
const currentParent = element.parentElement;
|
|
1217
|
+
if (existing && currentParent) {
|
|
1218
|
+
const currentParentInfo = this.getParentInfo(currentParent);
|
|
1219
|
+
const existingParentInfo = `${existing.parentTag}${existing.parentClass ? "." + existing.parentClass : ""}`;
|
|
1220
|
+
if (currentParentInfo !== existingParentInfo) {
|
|
1221
|
+
logger5.warn(
|
|
1222
|
+
`Duplicate key "${key}" detected in different parent containers!
|
|
1223
|
+
Previous parent: ${existingParentInfo}
|
|
1224
|
+
Current parent: ${currentParentInfo}
|
|
1225
|
+
|
|
1226
|
+
This may cause elements to appear in wrong containers or be moved unexpectedly.
|
|
1227
|
+
|
|
1228
|
+
Solution: Use unique key prefixes for different locations:
|
|
1229
|
+
Example: <wsx-link key="nav-0"> vs <wsx-link key="overflow-0">
|
|
1230
|
+
|
|
1231
|
+
See https://wsxjs.dev/docs/guide/DOM_CACHE_GUIDE for best practices.`
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
if (currentParent) {
|
|
1236
|
+
this.keyParentMap.set(key, {
|
|
1237
|
+
parentTag: currentParent.tagName.toLowerCase(),
|
|
1238
|
+
parentClass: currentParent.className,
|
|
1239
|
+
element
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Gets a formatted parent container description.
|
|
1245
|
+
*/
|
|
1246
|
+
getParentInfo(parent) {
|
|
1247
|
+
const tag = parent.tagName.toLowerCase();
|
|
1248
|
+
const className = parent.className;
|
|
1249
|
+
return `${tag}${className ? "." + className.split(" ")[0] : ""}`;
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Checks if a key exists in the cache.
|
|
1253
|
+
*/
|
|
1254
|
+
has(key) {
|
|
1255
|
+
return this.cache.has(key);
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Clears the cache.
|
|
1259
|
+
* Should be called when component is disconnected or cache is invalidated.
|
|
1260
|
+
*/
|
|
1261
|
+
clear() {
|
|
1262
|
+
this.cache.clear();
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Stores metadata for an element (e.g. previous props).
|
|
1266
|
+
*/
|
|
1267
|
+
setMetadata(element, meta) {
|
|
1268
|
+
this.metadata.set(element, meta);
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* Retrieves metadata for an element.
|
|
1272
|
+
*/
|
|
1273
|
+
getMetadata(element) {
|
|
1274
|
+
return this.metadata.get(element);
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
|
|
555
1278
|
// src/base-component.ts
|
|
556
1279
|
var BaseComponent = class extends HTMLElement {
|
|
557
1280
|
constructor(config = {}) {
|
|
@@ -559,6 +1282,11 @@ var BaseComponent = class extends HTMLElement {
|
|
|
559
1282
|
this.connected = false;
|
|
560
1283
|
this._isDebugEnabled = false;
|
|
561
1284
|
this._reactiveStates = /* @__PURE__ */ new Map();
|
|
1285
|
+
/**
|
|
1286
|
+
* DOM Cache Manager for fine-grained updates (RFC 0037)
|
|
1287
|
+
* @internal
|
|
1288
|
+
*/
|
|
1289
|
+
this._domCache = new DOMCacheManager();
|
|
562
1290
|
/**
|
|
563
1291
|
* 当前捕获的焦点状态(用于在 render 时使用捕获的值)
|
|
564
1292
|
* @internal - 由 rerender() 方法管理
|
|
@@ -623,6 +1351,13 @@ var BaseComponent = class extends HTMLElement {
|
|
|
623
1351
|
static get observedAttributes() {
|
|
624
1352
|
return [];
|
|
625
1353
|
}
|
|
1354
|
+
/**
|
|
1355
|
+
* Gets the DOMCacheManager instance.
|
|
1356
|
+
* @internal
|
|
1357
|
+
*/
|
|
1358
|
+
getDomCache() {
|
|
1359
|
+
return this._domCache;
|
|
1360
|
+
}
|
|
626
1361
|
/**
|
|
627
1362
|
* Web Component生命周期:属性变化
|
|
628
1363
|
*/
|
|
@@ -930,7 +1665,7 @@ var BaseComponent = class extends HTMLElement {
|
|
|
930
1665
|
|
|
931
1666
|
// src/web-component.ts
|
|
932
1667
|
var import_wsx_logger2 = require("@wsxjs/wsx-logger");
|
|
933
|
-
var
|
|
1668
|
+
var logger6 = (0, import_wsx_logger2.createLogger)("WebComponent");
|
|
934
1669
|
var WebComponent = class extends BaseComponent {
|
|
935
1670
|
// Initialized by BaseComponent constructor
|
|
936
1671
|
constructor(config = {}) {
|
|
@@ -979,7 +1714,7 @@ var WebComponent = class extends BaseComponent {
|
|
|
979
1714
|
});
|
|
980
1715
|
}
|
|
981
1716
|
} catch (error) {
|
|
982
|
-
|
|
1717
|
+
logger6.error(`Error in connectedCallback:`, error);
|
|
983
1718
|
this.renderError(error);
|
|
984
1719
|
}
|
|
985
1720
|
}
|
|
@@ -1032,7 +1767,7 @@ var WebComponent = class extends BaseComponent {
|
|
|
1032
1767
|
StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
|
|
1033
1768
|
}
|
|
1034
1769
|
}
|
|
1035
|
-
const content = this.render();
|
|
1770
|
+
const content = RenderContext.runInContext(this, () => this.render());
|
|
1036
1771
|
if (focusState && focusState.key && focusState.value !== void 0) {
|
|
1037
1772
|
const target = content.querySelector(
|
|
1038
1773
|
`[data-wsx-key="${focusState.key}"]`
|
|
@@ -1060,7 +1795,7 @@ var WebComponent = class extends BaseComponent {
|
|
|
1060
1795
|
});
|
|
1061
1796
|
});
|
|
1062
1797
|
} catch (error) {
|
|
1063
|
-
|
|
1798
|
+
logger6.error("Error in _rerender:", error);
|
|
1064
1799
|
this.renderError(error);
|
|
1065
1800
|
this._isRendering = false;
|
|
1066
1801
|
}
|
|
@@ -1088,7 +1823,7 @@ var WebComponent = class extends BaseComponent {
|
|
|
1088
1823
|
|
|
1089
1824
|
// src/light-component.ts
|
|
1090
1825
|
var import_wsx_logger3 = require("@wsxjs/wsx-logger");
|
|
1091
|
-
var
|
|
1826
|
+
var logger7 = (0, import_wsx_logger3.createLogger)("LightComponent");
|
|
1092
1827
|
var LightComponent = class extends BaseComponent {
|
|
1093
1828
|
// Initialized by BaseComponent constructor
|
|
1094
1829
|
constructor(config = {}) {
|
|
@@ -1130,7 +1865,7 @@ var LightComponent = class extends BaseComponent {
|
|
|
1130
1865
|
(child) => child !== styleElement
|
|
1131
1866
|
);
|
|
1132
1867
|
childrenToRemove.forEach((child) => child.remove());
|
|
1133
|
-
const content = this.render();
|
|
1868
|
+
const content = RenderContext.runInContext(this, () => this.render());
|
|
1134
1869
|
this.appendChild(content);
|
|
1135
1870
|
if (styleElement && styleElement !== this.firstChild) {
|
|
1136
1871
|
this.insertBefore(styleElement, this.firstChild);
|
|
@@ -1144,7 +1879,7 @@ var LightComponent = class extends BaseComponent {
|
|
|
1144
1879
|
});
|
|
1145
1880
|
}
|
|
1146
1881
|
} catch (error) {
|
|
1147
|
-
|
|
1882
|
+
logger7.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
|
|
1148
1883
|
this.renderError(error);
|
|
1149
1884
|
}
|
|
1150
1885
|
}
|
|
@@ -1192,7 +1927,7 @@ var LightComponent = class extends BaseComponent {
|
|
|
1192
1927
|
this._pendingFocusState = focusState;
|
|
1193
1928
|
const jsxChildren = this.getJSXChildren();
|
|
1194
1929
|
try {
|
|
1195
|
-
const content = this.render();
|
|
1930
|
+
const content = RenderContext.runInContext(this, () => this.render());
|
|
1196
1931
|
if (focusState && focusState.key && focusState.value !== void 0) {
|
|
1197
1932
|
const target = content.querySelector(
|
|
1198
1933
|
`[data-wsx-key="${focusState.key}"]`
|
|
@@ -1249,7 +1984,7 @@ var LightComponent = class extends BaseComponent {
|
|
|
1249
1984
|
});
|
|
1250
1985
|
});
|
|
1251
1986
|
} catch (error) {
|
|
1252
|
-
|
|
1987
|
+
logger7.error(`[${this.constructor.name}] Error in _rerender:`, error);
|
|
1253
1988
|
this.renderError(error);
|
|
1254
1989
|
this._isRendering = false;
|
|
1255
1990
|
}
|
|
@@ -1371,6 +2106,10 @@ To fix this, please:
|
|
|
1371
2106
|
See: https://github.com/wsxjs/wsxjs#setup for more details.`;
|
|
1372
2107
|
}
|
|
1373
2108
|
function state(targetOrContext, propertyKey) {
|
|
2109
|
+
const globalProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
2110
|
+
if (globalProcess?.env?.NODE_ENV === "test") {
|
|
2111
|
+
return;
|
|
2112
|
+
}
|
|
1374
2113
|
let propertyName = "unknown";
|
|
1375
2114
|
const propertyKeyIsObject = typeof propertyKey === "object" && propertyKey !== null;
|
|
1376
2115
|
const targetIsObject = typeof targetOrContext === "object" && targetOrContext !== null;
|