@wsxjs/wsx-core 0.0.20 → 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/index.mjs CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  Fragment,
3
+ RenderContext,
4
+ createLogger,
3
5
  h
4
- } from "./chunk-7FXISNME.mjs";
6
+ } from "./chunk-AR3DIDLV.mjs";
5
7
 
6
8
  // src/styles/style-manager.ts
7
9
  var StyleManager = class {
@@ -44,8 +46,8 @@ var StyleManager = class {
44
46
  StyleManager.styleSheets = /* @__PURE__ */ new Map();
45
47
 
46
48
  // src/utils/reactive.ts
47
- import { createLogger } from "@wsxjs/wsx-logger";
48
- var logger = createLogger("ReactiveSystem");
49
+ import { createLogger as createLogger2 } from "@wsxjs/wsx-logger";
50
+ var logger = createLogger2("ReactiveSystem");
49
51
  var UpdateScheduler = class {
50
52
  constructor() {
51
53
  this.pendingCallbacks = /* @__PURE__ */ new Set();
@@ -264,6 +266,108 @@ function reactiveWithDebug(obj, onChange, debugName) {
264
266
  });
265
267
  }
266
268
 
269
+ // src/dom-cache-manager.ts
270
+ var logger2 = createLogger("DOMCacheManager");
271
+ var DOMCacheManager = class {
272
+ constructor() {
273
+ // Map<CacheKey, DOMElement>
274
+ this.cache = /* @__PURE__ */ new Map();
275
+ // Map<DOMElement, Metadata>
276
+ // Stores metadata (props, children) for cached elements to support diffing
277
+ this.metadata = /* @__PURE__ */ new WeakMap();
278
+ // Track key-parent relationships to detect duplicate keys in all environments
279
+ // Map<CacheKey, ParentInfo>
280
+ this.keyParentMap = /* @__PURE__ */ new Map();
281
+ // Flag to enable duplicate key warnings (enabled by default, critical for correctness)
282
+ this.warnDuplicateKeys = true;
283
+ }
284
+ /**
285
+ * Retrieves an element from the cache.
286
+ * @param key The unique cache key.
287
+ */
288
+ get(key) {
289
+ return this.cache.get(key);
290
+ }
291
+ /**
292
+ * Stores an element in the cache.
293
+ * @param key The unique cache key.
294
+ * @param element The DOM element to cache.
295
+ */
296
+ set(key, element) {
297
+ if (this.warnDuplicateKeys) {
298
+ this.checkDuplicateKey(key, element);
299
+ }
300
+ this.cache.set(key, element);
301
+ }
302
+ /**
303
+ * Checks if a cache key is being reused in a different parent container.
304
+ * Runs in all environments to help developers catch key conflicts early.
305
+ * This is critical for correctness and helps prevent subtle bugs.
306
+ */
307
+ checkDuplicateKey(key, element) {
308
+ const existing = this.keyParentMap.get(key);
309
+ const currentParent = element.parentElement;
310
+ if (existing && currentParent) {
311
+ const currentParentInfo = this.getParentInfo(currentParent);
312
+ const existingParentInfo = `${existing.parentTag}${existing.parentClass ? "." + existing.parentClass : ""}`;
313
+ if (currentParentInfo !== existingParentInfo) {
314
+ logger2.warn(
315
+ `Duplicate key "${key}" detected in different parent containers!
316
+ Previous parent: ${existingParentInfo}
317
+ Current parent: ${currentParentInfo}
318
+
319
+ This may cause elements to appear in wrong containers or be moved unexpectedly.
320
+
321
+ Solution: Use unique key prefixes for different locations:
322
+ Example: <wsx-link key="nav-0"> vs <wsx-link key="overflow-0">
323
+
324
+ See https://wsxjs.dev/docs/guide/DOM_CACHE_GUIDE for best practices.`
325
+ );
326
+ }
327
+ }
328
+ if (currentParent) {
329
+ this.keyParentMap.set(key, {
330
+ parentTag: currentParent.tagName.toLowerCase(),
331
+ parentClass: currentParent.className,
332
+ element
333
+ });
334
+ }
335
+ }
336
+ /**
337
+ * Gets a formatted parent container description.
338
+ */
339
+ getParentInfo(parent) {
340
+ const tag = parent.tagName.toLowerCase();
341
+ const className = parent.className;
342
+ return `${tag}${className ? "." + className.split(" ")[0] : ""}`;
343
+ }
344
+ /**
345
+ * Checks if a key exists in the cache.
346
+ */
347
+ has(key) {
348
+ return this.cache.has(key);
349
+ }
350
+ /**
351
+ * Clears the cache.
352
+ * Should be called when component is disconnected or cache is invalidated.
353
+ */
354
+ clear() {
355
+ this.cache.clear();
356
+ }
357
+ /**
358
+ * Stores metadata for an element (e.g. previous props).
359
+ */
360
+ setMetadata(element, meta) {
361
+ this.metadata.set(element, meta);
362
+ }
363
+ /**
364
+ * Retrieves metadata for an element.
365
+ */
366
+ getMetadata(element) {
367
+ return this.metadata.get(element);
368
+ }
369
+ };
370
+
267
371
  // src/base-component.ts
268
372
  var BaseComponent = class extends HTMLElement {
269
373
  constructor(config = {}) {
@@ -271,6 +375,11 @@ var BaseComponent = class extends HTMLElement {
271
375
  this.connected = false;
272
376
  this._isDebugEnabled = false;
273
377
  this._reactiveStates = /* @__PURE__ */ new Map();
378
+ /**
379
+ * DOM Cache Manager for fine-grained updates (RFC 0037)
380
+ * @internal
381
+ */
382
+ this._domCache = new DOMCacheManager();
274
383
  /**
275
384
  * 当前捕获的焦点状态(用于在 render 时使用捕获的值)
276
385
  * @internal - 由 rerender() 方法管理
@@ -335,6 +444,13 @@ var BaseComponent = class extends HTMLElement {
335
444
  static get observedAttributes() {
336
445
  return [];
337
446
  }
447
+ /**
448
+ * Gets the DOMCacheManager instance.
449
+ * @internal
450
+ */
451
+ getDomCache() {
452
+ return this._domCache;
453
+ }
338
454
  /**
339
455
  * Web Component生命周期:属性变化
340
456
  */
@@ -641,8 +757,8 @@ var BaseComponent = class extends HTMLElement {
641
757
  };
642
758
 
643
759
  // src/web-component.ts
644
- import { createLogger as createLogger2 } from "@wsxjs/wsx-logger";
645
- var logger2 = createLogger2("WebComponent");
760
+ import { createLogger as createLogger3 } from "@wsxjs/wsx-logger";
761
+ var logger3 = createLogger3("WebComponent");
646
762
  var WebComponent = class extends BaseComponent {
647
763
  // Initialized by BaseComponent constructor
648
764
  constructor(config = {}) {
@@ -691,7 +807,7 @@ var WebComponent = class extends BaseComponent {
691
807
  });
692
808
  }
693
809
  } catch (error) {
694
- logger2.error(`Error in connectedCallback:`, error);
810
+ logger3.error(`Error in connectedCallback:`, error);
695
811
  this.renderError(error);
696
812
  }
697
813
  }
@@ -744,7 +860,7 @@ var WebComponent = class extends BaseComponent {
744
860
  StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
745
861
  }
746
862
  }
747
- const content = this.render();
863
+ const content = RenderContext.runInContext(this, () => this.render());
748
864
  if (focusState && focusState.key && focusState.value !== void 0) {
749
865
  const target = content.querySelector(
750
866
  `[data-wsx-key="${focusState.key}"]`
@@ -772,7 +888,7 @@ var WebComponent = class extends BaseComponent {
772
888
  });
773
889
  });
774
890
  } catch (error) {
775
- logger2.error("Error in _rerender:", error);
891
+ logger3.error("Error in _rerender:", error);
776
892
  this.renderError(error);
777
893
  this._isRendering = false;
778
894
  }
@@ -799,8 +915,8 @@ var WebComponent = class extends BaseComponent {
799
915
  };
800
916
 
801
917
  // src/light-component.ts
802
- import { createLogger as createLogger3 } from "@wsxjs/wsx-logger";
803
- var logger3 = createLogger3("LightComponent");
918
+ import { createLogger as createLogger4 } from "@wsxjs/wsx-logger";
919
+ var logger4 = createLogger4("LightComponent");
804
920
  var LightComponent = class extends BaseComponent {
805
921
  // Initialized by BaseComponent constructor
806
922
  constructor(config = {}) {
@@ -842,7 +958,7 @@ var LightComponent = class extends BaseComponent {
842
958
  (child) => child !== styleElement
843
959
  );
844
960
  childrenToRemove.forEach((child) => child.remove());
845
- const content = this.render();
961
+ const content = RenderContext.runInContext(this, () => this.render());
846
962
  this.appendChild(content);
847
963
  if (styleElement && styleElement !== this.firstChild) {
848
964
  this.insertBefore(styleElement, this.firstChild);
@@ -856,7 +972,7 @@ var LightComponent = class extends BaseComponent {
856
972
  });
857
973
  }
858
974
  } catch (error) {
859
- logger3.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
975
+ logger4.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
860
976
  this.renderError(error);
861
977
  }
862
978
  }
@@ -904,7 +1020,7 @@ var LightComponent = class extends BaseComponent {
904
1020
  this._pendingFocusState = focusState;
905
1021
  const jsxChildren = this.getJSXChildren();
906
1022
  try {
907
- const content = this.render();
1023
+ const content = RenderContext.runInContext(this, () => this.render());
908
1024
  if (focusState && focusState.key && focusState.value !== void 0) {
909
1025
  const target = content.querySelector(
910
1026
  `[data-wsx-key="${focusState.key}"]`
@@ -961,7 +1077,7 @@ var LightComponent = class extends BaseComponent {
961
1077
  });
962
1078
  });
963
1079
  } catch (error) {
964
- logger3.error(`[${this.constructor.name}] Error in _rerender:`, error);
1080
+ logger4.error(`[${this.constructor.name}] Error in _rerender:`, error);
965
1081
  this.renderError(error);
966
1082
  this._isRendering = false;
967
1083
  }
@@ -1062,7 +1178,7 @@ function deriveTagName(className, prefix) {
1062
1178
  }
1063
1179
 
1064
1180
  // src/index.ts
1065
- import { WSXLogger, logger as logger4, createLogger as createLogger4, createLoggerWithConfig } from "@wsxjs/wsx-logger";
1181
+ import { WSXLogger, logger as logger5, createLogger as createLogger5, createLoggerWithConfig } from "@wsxjs/wsx-logger";
1066
1182
 
1067
1183
  // src/reactive-decorator.ts
1068
1184
  function createBabelPluginError(propertyName) {
@@ -1083,6 +1199,10 @@ To fix this, please:
1083
1199
  See: https://github.com/wsxjs/wsxjs#setup for more details.`;
1084
1200
  }
1085
1201
  function state(targetOrContext, propertyKey) {
1202
+ const globalProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
1203
+ if (globalProcess?.env?.NODE_ENV === "test") {
1204
+ return;
1205
+ }
1086
1206
  let propertyName = "unknown";
1087
1207
  const propertyKeyIsObject = typeof propertyKey === "object" && propertyKey !== null;
1088
1208
  const targetIsObject = typeof targetOrContext === "object" && targetOrContext !== null;
@@ -1143,12 +1263,12 @@ export {
1143
1263
  WSXLogger,
1144
1264
  WebComponent,
1145
1265
  autoRegister,
1146
- createLogger4 as createLogger,
1266
+ createLogger5 as createLogger,
1147
1267
  createLoggerWithConfig,
1148
1268
  h,
1149
1269
  h as jsx,
1150
1270
  h as jsxs,
1151
- logger4 as logger,
1271
+ logger5 as logger,
1152
1272
  registerComponent,
1153
1273
  state
1154
1274
  };