@wsxjs/wsx-core 0.0.20 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-OXFZ575O.mjs +1091 -0
- package/dist/index.js +869 -113
- package/dist/index.mjs +155 -22
- package/dist/jsx-runtime.js +723 -97
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/jsx.js +723 -97
- package/dist/jsx.mjs +1 -1
- package/package.json +2 -2
- package/src/base-component.ts +15 -0
- package/src/dom-cache-manager.ts +135 -0
- package/src/jsx-factory.ts +133 -447
- package/src/light-component.ts +12 -4
- package/src/reactive-decorator.ts +9 -0
- package/src/render-context.ts +40 -0
- package/src/utils/cache-key.ts +114 -0
- package/src/utils/dom-utils.ts +119 -0
- package/src/utils/element-creation.ts +140 -0
- package/src/utils/element-marking.ts +80 -0
- package/src/utils/element-update.ts +633 -0
- package/src/utils/props-utils.ts +307 -0
- package/src/web-component.ts +24 -6
- package/dist/chunk-7FXISNME.mjs +0 -462
- package/dist/tsconfig.tsbuildinfo +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Fragment,
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
RenderContext,
|
|
4
|
+
createLogger,
|
|
5
|
+
h,
|
|
6
|
+
shouldPreserveElement
|
|
7
|
+
} from "./chunk-OXFZ575O.mjs";
|
|
5
8
|
|
|
6
9
|
// src/styles/style-manager.ts
|
|
7
10
|
var StyleManager = class {
|
|
@@ -44,8 +47,8 @@ var StyleManager = class {
|
|
|
44
47
|
StyleManager.styleSheets = /* @__PURE__ */ new Map();
|
|
45
48
|
|
|
46
49
|
// src/utils/reactive.ts
|
|
47
|
-
import { createLogger } from "@wsxjs/wsx-logger";
|
|
48
|
-
var logger =
|
|
50
|
+
import { createLogger as createLogger2 } from "@wsxjs/wsx-logger";
|
|
51
|
+
var logger = createLogger2("ReactiveSystem");
|
|
49
52
|
var UpdateScheduler = class {
|
|
50
53
|
constructor() {
|
|
51
54
|
this.pendingCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -264,6 +267,108 @@ function reactiveWithDebug(obj, onChange, debugName) {
|
|
|
264
267
|
});
|
|
265
268
|
}
|
|
266
269
|
|
|
270
|
+
// src/dom-cache-manager.ts
|
|
271
|
+
var logger2 = createLogger("DOMCacheManager");
|
|
272
|
+
var DOMCacheManager = class {
|
|
273
|
+
constructor() {
|
|
274
|
+
// Map<CacheKey, DOMElement>
|
|
275
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
276
|
+
// Map<DOMElement, Metadata>
|
|
277
|
+
// Stores metadata (props, children) for cached elements to support diffing
|
|
278
|
+
this.metadata = /* @__PURE__ */ new WeakMap();
|
|
279
|
+
// Track key-parent relationships to detect duplicate keys in all environments
|
|
280
|
+
// Map<CacheKey, ParentInfo>
|
|
281
|
+
this.keyParentMap = /* @__PURE__ */ new Map();
|
|
282
|
+
// Flag to enable duplicate key warnings (enabled by default, critical for correctness)
|
|
283
|
+
this.warnDuplicateKeys = true;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Retrieves an element from the cache.
|
|
287
|
+
* @param key The unique cache key.
|
|
288
|
+
*/
|
|
289
|
+
get(key) {
|
|
290
|
+
return this.cache.get(key);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Stores an element in the cache.
|
|
294
|
+
* @param key The unique cache key.
|
|
295
|
+
* @param element The DOM element to cache.
|
|
296
|
+
*/
|
|
297
|
+
set(key, element) {
|
|
298
|
+
if (this.warnDuplicateKeys) {
|
|
299
|
+
this.checkDuplicateKey(key, element);
|
|
300
|
+
}
|
|
301
|
+
this.cache.set(key, element);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Checks if a cache key is being reused in a different parent container.
|
|
305
|
+
* Runs in all environments to help developers catch key conflicts early.
|
|
306
|
+
* This is critical for correctness and helps prevent subtle bugs.
|
|
307
|
+
*/
|
|
308
|
+
checkDuplicateKey(key, element) {
|
|
309
|
+
const existing = this.keyParentMap.get(key);
|
|
310
|
+
const currentParent = element.parentElement;
|
|
311
|
+
if (existing && currentParent) {
|
|
312
|
+
const currentParentInfo = this.getParentInfo(currentParent);
|
|
313
|
+
const existingParentInfo = `${existing.parentTag}${existing.parentClass ? "." + existing.parentClass : ""}`;
|
|
314
|
+
if (currentParentInfo !== existingParentInfo) {
|
|
315
|
+
logger2.warn(
|
|
316
|
+
`Duplicate key "${key}" detected in different parent containers!
|
|
317
|
+
Previous parent: ${existingParentInfo}
|
|
318
|
+
Current parent: ${currentParentInfo}
|
|
319
|
+
|
|
320
|
+
This may cause elements to appear in wrong containers or be moved unexpectedly.
|
|
321
|
+
|
|
322
|
+
Solution: Use unique key prefixes for different locations:
|
|
323
|
+
Example: <wsx-link key="nav-0"> vs <wsx-link key="overflow-0">
|
|
324
|
+
|
|
325
|
+
See https://wsxjs.dev/docs/guide/DOM_CACHE_GUIDE for best practices.`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (currentParent) {
|
|
330
|
+
this.keyParentMap.set(key, {
|
|
331
|
+
parentTag: currentParent.tagName.toLowerCase(),
|
|
332
|
+
parentClass: currentParent.className,
|
|
333
|
+
element
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Gets a formatted parent container description.
|
|
339
|
+
*/
|
|
340
|
+
getParentInfo(parent) {
|
|
341
|
+
const tag = parent.tagName.toLowerCase();
|
|
342
|
+
const className = parent.className;
|
|
343
|
+
return `${tag}${className ? "." + className.split(" ")[0] : ""}`;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Checks if a key exists in the cache.
|
|
347
|
+
*/
|
|
348
|
+
has(key) {
|
|
349
|
+
return this.cache.has(key);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Clears the cache.
|
|
353
|
+
* Should be called when component is disconnected or cache is invalidated.
|
|
354
|
+
*/
|
|
355
|
+
clear() {
|
|
356
|
+
this.cache.clear();
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Stores metadata for an element (e.g. previous props).
|
|
360
|
+
*/
|
|
361
|
+
setMetadata(element, meta) {
|
|
362
|
+
this.metadata.set(element, meta);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Retrieves metadata for an element.
|
|
366
|
+
*/
|
|
367
|
+
getMetadata(element) {
|
|
368
|
+
return this.metadata.get(element);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
267
372
|
// src/base-component.ts
|
|
268
373
|
var BaseComponent = class extends HTMLElement {
|
|
269
374
|
constructor(config = {}) {
|
|
@@ -271,6 +376,11 @@ var BaseComponent = class extends HTMLElement {
|
|
|
271
376
|
this.connected = false;
|
|
272
377
|
this._isDebugEnabled = false;
|
|
273
378
|
this._reactiveStates = /* @__PURE__ */ new Map();
|
|
379
|
+
/**
|
|
380
|
+
* DOM Cache Manager for fine-grained updates (RFC 0037)
|
|
381
|
+
* @internal
|
|
382
|
+
*/
|
|
383
|
+
this._domCache = new DOMCacheManager();
|
|
274
384
|
/**
|
|
275
385
|
* 当前捕获的焦点状态(用于在 render 时使用捕获的值)
|
|
276
386
|
* @internal - 由 rerender() 方法管理
|
|
@@ -335,6 +445,13 @@ var BaseComponent = class extends HTMLElement {
|
|
|
335
445
|
static get observedAttributes() {
|
|
336
446
|
return [];
|
|
337
447
|
}
|
|
448
|
+
/**
|
|
449
|
+
* Gets the DOMCacheManager instance.
|
|
450
|
+
* @internal
|
|
451
|
+
*/
|
|
452
|
+
getDomCache() {
|
|
453
|
+
return this._domCache;
|
|
454
|
+
}
|
|
338
455
|
/**
|
|
339
456
|
* Web Component生命周期:属性变化
|
|
340
457
|
*/
|
|
@@ -641,8 +758,8 @@ var BaseComponent = class extends HTMLElement {
|
|
|
641
758
|
};
|
|
642
759
|
|
|
643
760
|
// src/web-component.ts
|
|
644
|
-
import { createLogger as
|
|
645
|
-
var
|
|
761
|
+
import { createLogger as createLogger3 } from "@wsxjs/wsx-logger";
|
|
762
|
+
var logger3 = createLogger3("WebComponent");
|
|
646
763
|
var WebComponent = class extends BaseComponent {
|
|
647
764
|
// Initialized by BaseComponent constructor
|
|
648
765
|
constructor(config = {}) {
|
|
@@ -680,7 +797,7 @@ var WebComponent = class extends BaseComponent {
|
|
|
680
797
|
const styleName = this.config.styleName || this.constructor.name;
|
|
681
798
|
StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
|
|
682
799
|
}
|
|
683
|
-
const content = this.render();
|
|
800
|
+
const content = RenderContext.runInContext(this, () => this.render());
|
|
684
801
|
this.shadowRoot.appendChild(content);
|
|
685
802
|
}
|
|
686
803
|
this.initializeEventListeners();
|
|
@@ -691,7 +808,7 @@ var WebComponent = class extends BaseComponent {
|
|
|
691
808
|
});
|
|
692
809
|
}
|
|
693
810
|
} catch (error) {
|
|
694
|
-
|
|
811
|
+
logger3.error(`Error in connectedCallback:`, error);
|
|
695
812
|
this.renderError(error);
|
|
696
813
|
}
|
|
697
814
|
}
|
|
@@ -744,7 +861,7 @@ var WebComponent = class extends BaseComponent {
|
|
|
744
861
|
StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
|
|
745
862
|
}
|
|
746
863
|
}
|
|
747
|
-
const content = this.render();
|
|
864
|
+
const content = RenderContext.runInContext(this, () => this.render());
|
|
748
865
|
if (focusState && focusState.key && focusState.value !== void 0) {
|
|
749
866
|
const target = content.querySelector(
|
|
750
867
|
`[data-wsx-key="${focusState.key}"]`
|
|
@@ -760,9 +877,18 @@ var WebComponent = class extends BaseComponent {
|
|
|
760
877
|
}
|
|
761
878
|
requestAnimationFrame(() => {
|
|
762
879
|
this.shadowRoot.appendChild(content);
|
|
763
|
-
const oldChildren = Array.from(this.shadowRoot.children).filter(
|
|
764
|
-
(child
|
|
765
|
-
|
|
880
|
+
const oldChildren = Array.from(this.shadowRoot.children).filter((child) => {
|
|
881
|
+
if (child === content) {
|
|
882
|
+
return false;
|
|
883
|
+
}
|
|
884
|
+
if (child instanceof HTMLStyleElement) {
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
if (shouldPreserveElement(child)) {
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
return true;
|
|
891
|
+
});
|
|
766
892
|
oldChildren.forEach((child) => child.remove());
|
|
767
893
|
requestAnimationFrame(() => {
|
|
768
894
|
this.restoreFocusState(focusState);
|
|
@@ -772,7 +898,7 @@ var WebComponent = class extends BaseComponent {
|
|
|
772
898
|
});
|
|
773
899
|
});
|
|
774
900
|
} catch (error) {
|
|
775
|
-
|
|
901
|
+
logger3.error("Error in _rerender:", error);
|
|
776
902
|
this.renderError(error);
|
|
777
903
|
this._isRendering = false;
|
|
778
904
|
}
|
|
@@ -799,8 +925,8 @@ var WebComponent = class extends BaseComponent {
|
|
|
799
925
|
};
|
|
800
926
|
|
|
801
927
|
// src/light-component.ts
|
|
802
|
-
import { createLogger as
|
|
803
|
-
var
|
|
928
|
+
import { createLogger as createLogger4 } from "@wsxjs/wsx-logger";
|
|
929
|
+
var logger4 = createLogger4("LightComponent");
|
|
804
930
|
var LightComponent = class extends BaseComponent {
|
|
805
931
|
// Initialized by BaseComponent constructor
|
|
806
932
|
constructor(config = {}) {
|
|
@@ -842,7 +968,7 @@ var LightComponent = class extends BaseComponent {
|
|
|
842
968
|
(child) => child !== styleElement
|
|
843
969
|
);
|
|
844
970
|
childrenToRemove.forEach((child) => child.remove());
|
|
845
|
-
const content = this.render();
|
|
971
|
+
const content = RenderContext.runInContext(this, () => this.render());
|
|
846
972
|
this.appendChild(content);
|
|
847
973
|
if (styleElement && styleElement !== this.firstChild) {
|
|
848
974
|
this.insertBefore(styleElement, this.firstChild);
|
|
@@ -856,7 +982,7 @@ var LightComponent = class extends BaseComponent {
|
|
|
856
982
|
});
|
|
857
983
|
}
|
|
858
984
|
} catch (error) {
|
|
859
|
-
|
|
985
|
+
logger4.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
|
|
860
986
|
this.renderError(error);
|
|
861
987
|
}
|
|
862
988
|
}
|
|
@@ -904,7 +1030,7 @@ var LightComponent = class extends BaseComponent {
|
|
|
904
1030
|
this._pendingFocusState = focusState;
|
|
905
1031
|
const jsxChildren = this.getJSXChildren();
|
|
906
1032
|
try {
|
|
907
|
-
const content = this.render();
|
|
1033
|
+
const content = RenderContext.runInContext(this, () => this.render());
|
|
908
1034
|
if (focusState && focusState.key && focusState.value !== void 0) {
|
|
909
1035
|
const target = content.querySelector(
|
|
910
1036
|
`[data-wsx-key="${focusState.key}"]`
|
|
@@ -942,6 +1068,9 @@ var LightComponent = class extends BaseComponent {
|
|
|
942
1068
|
if (child instanceof HTMLElement && jsxChildren.includes(child)) {
|
|
943
1069
|
return false;
|
|
944
1070
|
}
|
|
1071
|
+
if (shouldPreserveElement(child)) {
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
945
1074
|
return true;
|
|
946
1075
|
});
|
|
947
1076
|
oldChildren.forEach((child) => child.remove());
|
|
@@ -961,7 +1090,7 @@ var LightComponent = class extends BaseComponent {
|
|
|
961
1090
|
});
|
|
962
1091
|
});
|
|
963
1092
|
} catch (error) {
|
|
964
|
-
|
|
1093
|
+
logger4.error(`[${this.constructor.name}] Error in _rerender:`, error);
|
|
965
1094
|
this.renderError(error);
|
|
966
1095
|
this._isRendering = false;
|
|
967
1096
|
}
|
|
@@ -1062,7 +1191,7 @@ function deriveTagName(className, prefix) {
|
|
|
1062
1191
|
}
|
|
1063
1192
|
|
|
1064
1193
|
// src/index.ts
|
|
1065
|
-
import { WSXLogger, logger as
|
|
1194
|
+
import { WSXLogger, logger as logger5, createLogger as createLogger5, createLoggerWithConfig } from "@wsxjs/wsx-logger";
|
|
1066
1195
|
|
|
1067
1196
|
// src/reactive-decorator.ts
|
|
1068
1197
|
function createBabelPluginError(propertyName) {
|
|
@@ -1083,6 +1212,10 @@ To fix this, please:
|
|
|
1083
1212
|
See: https://github.com/wsxjs/wsxjs#setup for more details.`;
|
|
1084
1213
|
}
|
|
1085
1214
|
function state(targetOrContext, propertyKey) {
|
|
1215
|
+
const globalProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
1216
|
+
if (globalProcess?.env?.NODE_ENV === "test") {
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1086
1219
|
let propertyName = "unknown";
|
|
1087
1220
|
const propertyKeyIsObject = typeof propertyKey === "object" && propertyKey !== null;
|
|
1088
1221
|
const targetIsObject = typeof targetOrContext === "object" && targetOrContext !== null;
|
|
@@ -1143,12 +1276,12 @@ export {
|
|
|
1143
1276
|
WSXLogger,
|
|
1144
1277
|
WebComponent,
|
|
1145
1278
|
autoRegister,
|
|
1146
|
-
|
|
1279
|
+
createLogger5 as createLogger,
|
|
1147
1280
|
createLoggerWithConfig,
|
|
1148
1281
|
h,
|
|
1149
1282
|
h as jsx,
|
|
1150
1283
|
h as jsxs,
|
|
1151
|
-
|
|
1284
|
+
logger5 as logger,
|
|
1152
1285
|
registerComponent,
|
|
1153
1286
|
state
|
|
1154
1287
|
};
|