@wsxjs/wsx-core 0.0.16 → 0.0.18

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.js CHANGED
@@ -23,14 +23,15 @@ __export(index_exports, {
23
23
  Fragment: () => Fragment,
24
24
  LightComponent: () => LightComponent,
25
25
  StyleManager: () => StyleManager,
26
- WSXLogger: () => WSXLogger,
26
+ WSXLogger: () => import_wsx_logger4.WSXLogger,
27
27
  WebComponent: () => WebComponent,
28
28
  autoRegister: () => autoRegister,
29
- createLogger: () => createLogger,
29
+ createLogger: () => import_wsx_logger4.createLogger,
30
+ createLoggerWithConfig: () => import_wsx_logger4.createLoggerWithConfig,
30
31
  h: () => h,
31
32
  jsx: () => h,
32
33
  jsxs: () => h,
33
- logger: () => logger,
34
+ logger: () => import_wsx_logger4.logger,
34
35
  registerComponent: () => registerComponent,
35
36
  state: () => state
36
37
  });
@@ -148,6 +149,20 @@ function getSVGAttributeName(attributeName) {
148
149
  return SVG_ATTRIBUTE_MAP.get(attributeName) || attributeName;
149
150
  }
150
151
 
152
+ // src/utils/dom-utils.ts
153
+ function parseHTMLToNodes(html) {
154
+ if (!html) return [];
155
+ const temp = document.createElement("div");
156
+ temp.innerHTML = html;
157
+ return Array.from(temp.childNodes).map((node) => {
158
+ if (node instanceof HTMLElement || node instanceof SVGElement) {
159
+ return node;
160
+ } else {
161
+ return node.textContent || "";
162
+ }
163
+ });
164
+ }
165
+
151
166
  // src/jsx-factory.ts
152
167
  function h(tag, props = {}, ...children) {
153
168
  if (typeof tag === "function") {
@@ -205,13 +220,53 @@ function h(tag, props = {}, ...children) {
205
220
  });
206
221
  return element;
207
222
  }
208
- function flattenChildren(children) {
223
+ function isHTMLString(str) {
224
+ const trimmed = str.trim();
225
+ if (!trimmed) return false;
226
+ const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
227
+ const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
228
+ if (looksLikeMath) return false;
229
+ return htmlTagPattern.test(trimmed);
230
+ }
231
+ function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
232
+ if (depth > 10) {
233
+ console.warn(
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
+ );
239
+ }
209
240
  const result = [];
210
241
  for (const child of children) {
211
242
  if (child === null || child === void 0 || child === false) {
212
243
  continue;
213
244
  } else if (Array.isArray(child)) {
214
- result.push(...flattenChildren(child));
245
+ result.push(...flattenChildren(child, skipHTMLDetection, depth + 1));
246
+ } else if (typeof child === "string") {
247
+ if (skipHTMLDetection) {
248
+ result.push(child);
249
+ } else if (isHTMLString(child)) {
250
+ try {
251
+ const nodes = parseHTMLToNodes(child);
252
+ if (nodes.length > 0) {
253
+ for (const node of nodes) {
254
+ if (typeof node === "string") {
255
+ result.push(node);
256
+ } else {
257
+ result.push(node);
258
+ }
259
+ }
260
+ } else {
261
+ result.push(child);
262
+ }
263
+ } catch (error) {
264
+ console.warn("[WSX] Failed to parse HTML string, treating as text:", error);
265
+ result.push(child);
266
+ }
267
+ } else {
268
+ result.push(child);
269
+ }
215
270
  } else {
216
271
  result.push(child);
217
272
  }
@@ -276,48 +331,9 @@ var StyleManager = class {
276
331
  };
277
332
  StyleManager.styleSheets = /* @__PURE__ */ new Map();
278
333
 
279
- // src/utils/logger.ts
280
- var WSXLogger = class {
281
- constructor(prefix = "[WSX]", enabled = true, level = "info") {
282
- this.prefix = prefix;
283
- this.enabled = enabled;
284
- this.level = level;
285
- }
286
- shouldLog(level) {
287
- if (!this.enabled) return false;
288
- const levels = ["debug", "info", "warn", "error"];
289
- const currentLevelIndex = levels.indexOf(this.level);
290
- const messageLevelIndex = levels.indexOf(level);
291
- return messageLevelIndex >= currentLevelIndex;
292
- }
293
- debug(message, ...args) {
294
- if (this.shouldLog("debug")) {
295
- console.debug(`${this.prefix} ${message}`, ...args);
296
- }
297
- }
298
- info(message, ...args) {
299
- if (this.shouldLog("info")) {
300
- console.info(`${this.prefix} ${message}`, ...args);
301
- }
302
- }
303
- warn(message, ...args) {
304
- if (this.shouldLog("warn")) {
305
- console.warn(`${this.prefix} ${message}`, ...args);
306
- }
307
- }
308
- error(message, ...args) {
309
- if (this.shouldLog("error")) {
310
- console.error(`${this.prefix} ${message}`, ...args);
311
- }
312
- }
313
- };
314
- var logger = new WSXLogger();
315
- function createLogger(componentName) {
316
- return new WSXLogger(`[WSX:${componentName}]`);
317
- }
318
-
319
334
  // src/utils/reactive.ts
320
- var logger2 = createLogger("ReactiveSystem");
335
+ var import_wsx_logger = require("@wsxjs/wsx-logger");
336
+ var logger = (0, import_wsx_logger.createLogger)("ReactiveSystem");
321
337
  var UpdateScheduler = class {
322
338
  constructor() {
323
339
  this.pendingCallbacks = /* @__PURE__ */ new Set();
@@ -346,7 +362,7 @@ var UpdateScheduler = class {
346
362
  try {
347
363
  callback();
348
364
  } catch (error) {
349
- logger2.error("[WSX Reactive] Error in callback:", error);
365
+ logger.error("[WSX Reactive] Error in callback:", error);
350
366
  }
351
367
  });
352
368
  }
@@ -488,7 +504,7 @@ var ReactiveDebug = {
488
504
  */
489
505
  log(message, ...args) {
490
506
  if (this.isEnabled()) {
491
- logger2.info(`[WSX Reactive] ${message}`, ...args);
507
+ logger.info(`[WSX Reactive] ${message}`, ...args);
492
508
  }
493
509
  }
494
510
  };
@@ -558,6 +574,11 @@ var BaseComponent = class extends HTMLElement {
558
574
  * @internal
559
575
  */
560
576
  this._pendingRerender = false;
577
+ /**
578
+ * 正在渲染标志(防止在 _rerender() 执行期间再次触发 scheduleRerender())
579
+ * @internal
580
+ */
581
+ this._isRendering = false;
561
582
  /**
562
583
  * 处理 blur 事件,在用户停止输入时执行待处理的重渲染
563
584
  * @internal
@@ -572,9 +593,10 @@ var BaseComponent = class extends HTMLElement {
572
593
  this._rerenderDebounceTimer = null;
573
594
  }
574
595
  requestAnimationFrame(() => {
575
- if (this._pendingRerender && this.connected) {
596
+ if (this._pendingRerender && this.connected && !this._isRendering) {
576
597
  this._pendingRerender = false;
577
- this.rerender();
598
+ this._isRendering = true;
599
+ this._rerender();
578
600
  }
579
601
  });
580
602
  }
@@ -653,6 +675,9 @@ var BaseComponent = class extends HTMLElement {
653
675
  }
654
676
  return;
655
677
  }
678
+ if (this._isRendering) {
679
+ return;
680
+ }
656
681
  const root = this.getActiveRoot();
657
682
  let activeElement = null;
658
683
  if (root instanceof ShadowRoot) {
@@ -678,12 +703,27 @@ var BaseComponent = class extends HTMLElement {
678
703
  if (this._pendingRerender) {
679
704
  this._pendingRerender = false;
680
705
  }
681
- queueMicrotask(() => {
682
- if (this.connected) {
683
- this.rerender();
706
+ requestAnimationFrame(() => {
707
+ if (this.connected && !this._isRendering) {
708
+ this._isRendering = true;
709
+ this._rerender();
710
+ } else if (!this.connected) {
711
+ this._isRendering = false;
684
712
  }
685
713
  });
686
714
  }
715
+ /**
716
+ * 调度重渲染(公开 API)
717
+ *
718
+ * 与 scheduleRerender() 对齐:所有重渲染都通过统一的调度机制
719
+ * 使用异步调度机制,自动处理防抖和批量更新
720
+ *
721
+ * 注意:此方法现在是异步的,使用调度机制
722
+ * 如果需要同步执行,使用 _rerender()(不推荐,仅内部使用)
723
+ */
724
+ rerender() {
725
+ this.scheduleRerender();
726
+ }
687
727
  /**
688
728
  * 清理资源(在组件断开连接时调用)
689
729
  * @internal
@@ -889,7 +929,8 @@ var BaseComponent = class extends HTMLElement {
889
929
  };
890
930
 
891
931
  // src/web-component.ts
892
- var logger3 = createLogger("WebComponent");
932
+ var import_wsx_logger2 = require("@wsxjs/wsx-logger");
933
+ var logger2 = (0, import_wsx_logger2.createLogger)("WebComponent");
893
934
  var WebComponent = class extends BaseComponent {
894
935
  // Initialized by BaseComponent constructor
895
936
  constructor(config = {}) {
@@ -932,8 +973,13 @@ var WebComponent = class extends BaseComponent {
932
973
  }
933
974
  this.initializeEventListeners();
934
975
  this.onConnected?.();
976
+ if (hasActualContent === false || hasErrorElement) {
977
+ requestAnimationFrame(() => {
978
+ this.onRendered?.();
979
+ });
980
+ }
935
981
  } catch (error) {
936
- logger3.error(`Error in connectedCallback:`, error);
982
+ logger2.error(`Error in connectedCallback:`, error);
937
983
  this.renderError(error);
938
984
  }
939
985
  }
@@ -964,11 +1010,15 @@ var WebComponent = class extends BaseComponent {
964
1010
  return this.shadowRoot.querySelectorAll(selector);
965
1011
  }
966
1012
  /**
967
- * 重新渲染组件
1013
+ * 内部重渲染实现
1014
+ * 包含从 rerender() 方法迁移的实际渲染逻辑
1015
+ * WebComponent 使用 Shadow DOM,不存在 JSX children 问题
1016
+ *
1017
+ * @override
968
1018
  */
969
- rerender() {
1019
+ _rerender() {
970
1020
  if (!this.connected) {
971
- logger3.warn("Component is not connected, skipping rerender.");
1021
+ this._isRendering = false;
972
1022
  return;
973
1023
  }
974
1024
  const focusState = this.captureFocusState();
@@ -1005,11 +1055,14 @@ var WebComponent = class extends BaseComponent {
1005
1055
  requestAnimationFrame(() => {
1006
1056
  this.restoreFocusState(focusState);
1007
1057
  this._pendingFocusState = null;
1058
+ this.onRendered?.();
1059
+ this._isRendering = false;
1008
1060
  });
1009
1061
  });
1010
1062
  } catch (error) {
1011
- logger3.error("Error in rerender:", error);
1063
+ logger2.error("Error in _rerender:", error);
1012
1064
  this.renderError(error);
1065
+ this._isRendering = false;
1013
1066
  }
1014
1067
  }
1015
1068
  /**
@@ -1034,7 +1087,8 @@ var WebComponent = class extends BaseComponent {
1034
1087
  };
1035
1088
 
1036
1089
  // src/light-component.ts
1037
- var logger4 = createLogger("LightComponent");
1090
+ var import_wsx_logger3 = require("@wsxjs/wsx-logger");
1091
+ var logger3 = (0, import_wsx_logger3.createLogger)("LightComponent");
1038
1092
  var LightComponent = class extends BaseComponent {
1039
1093
  // Initialized by BaseComponent constructor
1040
1094
  constructor(config = {}) {
@@ -1064,9 +1118,10 @@ var LightComponent = class extends BaseComponent {
1064
1118
  (child) => child instanceof HTMLElement && child !== styleElement && child.style.color === "red" && child.textContent?.includes("Component Error")
1065
1119
  );
1066
1120
  const hasActualContent = Array.from(this.children).some(
1067
- (child) => child !== styleElement
1121
+ (child) => child !== styleElement && !(child instanceof HTMLSlotElement)
1068
1122
  );
1069
1123
  if (hasActualContent && !hasErrorElement) {
1124
+ this.markJSXChildren();
1070
1125
  if (styleElement && styleElement !== this.firstChild) {
1071
1126
  this.insertBefore(styleElement, this.firstChild);
1072
1127
  }
@@ -1083,8 +1138,13 @@ var LightComponent = class extends BaseComponent {
1083
1138
  }
1084
1139
  this.initializeEventListeners();
1085
1140
  this.onConnected?.();
1141
+ if (hasActualContent === false || hasErrorElement) {
1142
+ requestAnimationFrame(() => {
1143
+ this.onRendered?.();
1144
+ });
1145
+ }
1086
1146
  } catch (error) {
1087
- logger4.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
1147
+ logger3.error(`[${this.constructor.name}] Error in connectedCallback:`, error);
1088
1148
  this.renderError(error);
1089
1149
  }
1090
1150
  }
@@ -1117,17 +1177,20 @@ var LightComponent = class extends BaseComponent {
1117
1177
  return HTMLElement.prototype.querySelectorAll.call(this, selector);
1118
1178
  }
1119
1179
  /**
1120
- * 重新渲染组件
1180
+ * 内部重渲染实现
1181
+ * 包含从 rerender() 方法迁移的实际渲染逻辑
1182
+ * 处理 JSX children 的保留(Light DOM 特有)
1183
+ *
1184
+ * @override
1121
1185
  */
1122
- rerender() {
1186
+ _rerender() {
1123
1187
  if (!this.connected) {
1124
- logger4.warn(
1125
- `[${this.constructor.name}] Component is not connected, skipping rerender.`
1126
- );
1188
+ this._isRendering = false;
1127
1189
  return;
1128
1190
  }
1129
1191
  const focusState = this.captureFocusState();
1130
1192
  this._pendingFocusState = focusState;
1193
+ const jsxChildren = this.getJSXChildren();
1131
1194
  try {
1132
1195
  const content = this.render();
1133
1196
  if (focusState && focusState.key && focusState.value !== void 0) {
@@ -1141,8 +1204,8 @@ var LightComponent = class extends BaseComponent {
1141
1204
  }
1142
1205
  }
1143
1206
  const stylesToApply = this._autoStyles || this.config.styles;
1207
+ const styleName = this.config.styleName || this.constructor.name;
1144
1208
  if (stylesToApply) {
1145
- const styleName = this.config.styleName || this.constructor.name;
1146
1209
  let styleElement = this.querySelector(
1147
1210
  `style[data-wsx-light-component="${styleName}"]`
1148
1211
  );
@@ -1161,7 +1224,10 @@ var LightComponent = class extends BaseComponent {
1161
1224
  if (child === content) {
1162
1225
  return false;
1163
1226
  }
1164
- if (stylesToApply && child instanceof HTMLStyleElement && child.getAttribute("data-wsx-light-component") === (this.config.styleName || this.constructor.name)) {
1227
+ if (stylesToApply && child instanceof HTMLStyleElement && child.getAttribute("data-wsx-light-component") === styleName) {
1228
+ return false;
1229
+ }
1230
+ if (child instanceof HTMLElement && jsxChildren.includes(child)) {
1165
1231
  return false;
1166
1232
  }
1167
1233
  return true;
@@ -1169,7 +1235,7 @@ var LightComponent = class extends BaseComponent {
1169
1235
  oldChildren.forEach((child) => child.remove());
1170
1236
  if (stylesToApply && this.children.length > 1) {
1171
1237
  const styleElement = this.querySelector(
1172
- `style[data-wsx-light-component="${this.config.styleName || this.constructor.name}"]`
1238
+ `style[data-wsx-light-component="${styleName}"]`
1173
1239
  );
1174
1240
  if (styleElement && styleElement !== this.firstChild) {
1175
1241
  this.insertBefore(styleElement, this.firstChild);
@@ -1178,13 +1244,42 @@ var LightComponent = class extends BaseComponent {
1178
1244
  requestAnimationFrame(() => {
1179
1245
  this.restoreFocusState(focusState);
1180
1246
  this._pendingFocusState = null;
1247
+ this.onRendered?.();
1248
+ this._isRendering = false;
1181
1249
  });
1182
1250
  });
1183
1251
  } catch (error) {
1184
- logger4.error(`[${this.constructor.name}] Error in rerender:`, error);
1252
+ logger3.error(`[${this.constructor.name}] Error in _rerender:`, error);
1185
1253
  this.renderError(error);
1254
+ this._isRendering = false;
1186
1255
  }
1187
1256
  }
1257
+ /**
1258
+ * 获取 JSX children(通过 JSX factory 直接添加的 children)
1259
+ *
1260
+ * 在 Light DOM 中,JSX children 是通过 JSX factory 直接添加到组件元素的
1261
+ * 这些 children 不是 render() 返回的内容,应该保留
1262
+ */
1263
+ getJSXChildren() {
1264
+ const jsxChildren = Array.from(this.children).filter(
1265
+ (child) => child instanceof HTMLElement && child.getAttribute("data-wsx-jsx-child") === "true"
1266
+ ).map((child) => child);
1267
+ return jsxChildren;
1268
+ }
1269
+ /**
1270
+ * 标记 JSX children(在 connectedCallback 中调用)
1271
+ */
1272
+ markJSXChildren() {
1273
+ const styleName = this.config.styleName || this.constructor.name;
1274
+ const styleElement = this.querySelector(
1275
+ `style[data-wsx-light-component="${styleName}"]`
1276
+ );
1277
+ Array.from(this.children).forEach((child) => {
1278
+ if (child instanceof HTMLElement && child !== styleElement && !(child instanceof HTMLSlotElement)) {
1279
+ child.setAttribute("data-wsx-jsx-child", "true");
1280
+ }
1281
+ });
1282
+ }
1188
1283
  /**
1189
1284
  * 渲染错误信息
1190
1285
  *
@@ -1254,6 +1349,9 @@ function deriveTagName(className, prefix) {
1254
1349
  return prefix ? `${prefix}${kebabCase}` : kebabCase;
1255
1350
  }
1256
1351
 
1352
+ // src/index.ts
1353
+ var import_wsx_logger4 = require("@wsxjs/wsx-logger");
1354
+
1257
1355
  // src/reactive-decorator.ts
1258
1356
  function createBabelPluginError(propertyName) {
1259
1357
  return `@state decorator on property "${propertyName}" MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
@@ -1335,6 +1433,7 @@ function state(targetOrContext, propertyKey) {
1335
1433
  WebComponent,
1336
1434
  autoRegister,
1337
1435
  createLogger,
1436
+ createLoggerWithConfig,
1338
1437
  h,
1339
1438
  jsx,
1340
1439
  jsxs,