@wsxjs/wsx-core 0.0.12 → 0.0.14

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
@@ -1206,17 +1206,177 @@ function deriveTagName(className, prefix) {
1206
1206
  }
1207
1207
 
1208
1208
  // src/reactive-decorator.ts
1209
- function state(target, propertyKey) {
1209
+ function state(targetOrContext, propertyKey) {
1210
+ let propertyName;
1211
+ const propertyKeyIsObject = typeof propertyKey === "object" && propertyKey !== null;
1212
+ const targetIsObject = typeof targetOrContext === "object" && targetOrContext !== null;
1213
+ const hasStage3Indicators = targetIsObject && ("kind" in targetOrContext || "addInitializer" in targetOrContext || "access" in targetOrContext);
1214
+ const hasName = hasStage3Indicators && "name" in targetOrContext;
1215
+ const isStage3Decorator = propertyKeyIsObject || hasName || hasStage3Indicators && (propertyKey === void 0 || propertyKey === null) || targetIsObject && "addInitializer" in targetOrContext;
1216
+ if (isStage3Decorator) {
1217
+ const context = targetOrContext;
1218
+ if (context.name) {
1219
+ propertyName = typeof context.name === "string" ? context.name : context.name.toString();
1220
+ } else if (propertyKeyIsObject) {
1221
+ if ("name" in propertyKey) {
1222
+ const keyObj = propertyKey;
1223
+ propertyName = keyObj.name ? typeof keyObj.name === "string" ? keyObj.name : keyObj.name.toString() : "unknown";
1224
+ } else if ("key" in propertyKey) {
1225
+ const keyObj = propertyKey;
1226
+ propertyName = keyObj.key ? typeof keyObj.key === "string" ? keyObj.key : keyObj.key.toString() : "unknown";
1227
+ } else {
1228
+ const keyStr = String(propertyKey);
1229
+ if (keyStr !== "[object Object]") {
1230
+ propertyName = keyStr;
1231
+ } else {
1232
+ propertyName = "unknown";
1233
+ }
1234
+ }
1235
+ } else {
1236
+ propertyName = "unknown";
1237
+ }
1238
+ } else {
1239
+ if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
1240
+ propertyName = typeof propertyKey === "string" ? propertyKey : propertyKey.toString();
1241
+ } else if (propertyKey != null) {
1242
+ const propertyKeyStr = String(propertyKey);
1243
+ if (propertyKeyStr === "[object Object]") {
1244
+ propertyName = "unknown";
1245
+ } else {
1246
+ propertyName = propertyKeyStr;
1247
+ }
1248
+ } else {
1249
+ propertyName = "unknown";
1250
+ }
1251
+ }
1252
+ console.warn(
1253
+ `[WSX] @state decorator is using runtime fallback. Property "${propertyName}" will work but with reduced performance.
1254
+
1255
+ To fix this and enable compile-time processing, please:
1256
+ 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1257
+ 2. Configure it in vite.config.ts:
1258
+ import { wsx } from '@wsxjs/wsx-vite-plugin';
1259
+ export default defineConfig({ plugins: [wsx()] });
1260
+ 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1261
+ npm install --save-dev @wsxjs/wsx-tsconfig
1262
+ Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1263
+ Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1264
+
1265
+ See: https://github.com/wsxjs/wsxjs#setup for more details.`
1266
+ );
1267
+ if (isStage3Decorator) {
1268
+ let context;
1269
+ if (hasStage3Indicators) {
1270
+ context = targetOrContext;
1271
+ } else if (propertyKeyIsObject) {
1272
+ const keyObj = propertyKey;
1273
+ const targetObj = targetOrContext;
1274
+ const nameValue = targetObj.name || keyObj.name || keyObj.key || "unknown";
1275
+ context = {
1276
+ kind: targetObj.kind || "field",
1277
+ name: nameValue,
1278
+ addInitializer: targetObj.addInitializer || keyObj.addInitializer,
1279
+ access: targetObj.access || keyObj.access
1280
+ };
1281
+ } else {
1282
+ context = targetOrContext;
1283
+ }
1284
+ if (context.kind && context.kind !== "field") {
1285
+ const nameStr = typeof context.name === "string" ? context.name : context.name.toString();
1286
+ throw new Error(
1287
+ `@state decorator can only be used on class fields, not ${context.kind}. Property: "${nameStr}"`
1288
+ );
1289
+ }
1290
+ if (!context.addInitializer) {
1291
+ console.warn(
1292
+ `[WSX] @state decorator: addInitializer not available for property "${propertyName}". The property will not be reactive. This usually means the decorator system is not properly configured.`
1293
+ );
1294
+ } else {
1295
+ context.addInitializer(function() {
1296
+ if (!this || typeof this.reactive !== "function" || typeof this.useState !== "function") {
1297
+ throw new Error(
1298
+ `@state decorator runtime fallback: Component does not extend WebComponent or LightComponent. Property "${propertyName}" cannot be made reactive.
1299
+
1300
+ The @state decorator can only be used in classes that extend WebComponent or LightComponent. Please ensure your component class extends one of these base classes.`
1301
+ );
1302
+ }
1303
+ let initialValue = void 0;
1304
+ if (context.access?.get) {
1305
+ try {
1306
+ initialValue = context.access.get();
1307
+ } catch {
1308
+ initialValue = this[propertyName];
1309
+ }
1310
+ } else {
1311
+ initialValue = this[propertyName];
1312
+ }
1313
+ const isObject = initialValue !== null && initialValue !== void 0 && (typeof initialValue === "object" || Array.isArray(initialValue));
1314
+ if (isObject) {
1315
+ let reactiveValue = this.reactive(initialValue);
1316
+ Object.defineProperty(this, propertyName, {
1317
+ get: () => reactiveValue,
1318
+ set: (newValue) => {
1319
+ if (newValue !== null && newValue !== void 0 && (typeof newValue === "object" || Array.isArray(newValue))) {
1320
+ reactiveValue = this.reactive(newValue);
1321
+ this.scheduleRerender();
1322
+ } else {
1323
+ reactiveValue = newValue;
1324
+ this.scheduleRerender();
1325
+ }
1326
+ },
1327
+ enumerable: true,
1328
+ configurable: true
1329
+ });
1330
+ } else {
1331
+ const [getState, setState] = this.useState(propertyName, initialValue);
1332
+ Object.defineProperty(this, propertyName, {
1333
+ get: getState,
1334
+ set: setState,
1335
+ enumerable: true,
1336
+ configurable: true
1337
+ });
1338
+ }
1339
+ });
1340
+ }
1341
+ return context;
1342
+ }
1343
+ const target = targetOrContext;
1210
1344
  let normalizedPropertyKey;
1211
1345
  if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
1212
1346
  normalizedPropertyKey = propertyKey;
1213
- } else {
1347
+ } else if (propertyKey != null) {
1214
1348
  const propertyKeyStr = String(propertyKey);
1215
1349
  if (propertyKeyStr === "[object Object]") {
1350
+ if (typeof targetOrContext === "object" && targetOrContext !== null && ("kind" in targetOrContext || "addInitializer" in targetOrContext)) {
1351
+ console.warn(
1352
+ `[WSX] @state decorator: Detected potential Stage 3 decorator format but with unexpected propertyKey. This might be a compatibility issue. The decorator will attempt to work in runtime fallback mode.`
1353
+ );
1354
+ const context = targetOrContext;
1355
+ if (context.name) {
1356
+ const name = typeof context.name === "string" ? context.name : context.name.toString();
1357
+ throw new Error(
1358
+ `@state decorator: Detected Stage 3 decorator format but with invalid propertyKey. Property name: "${name}".
1359
+
1360
+ This usually means the decorator is being called in an unexpected format. Please ensure you have configured the Babel plugin correctly.
1361
+
1362
+ To fix this, please:
1363
+ 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1364
+ 2. Configure it in vite.config.ts:
1365
+ import { wsx } from '@wsxjs/wsx-vite-plugin';
1366
+ export default defineConfig({ plugins: [wsx()] });
1367
+ 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1368
+ npm install --save-dev @wsxjs/wsx-tsconfig
1369
+ Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1370
+ Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1371
+
1372
+ See: https://github.com/wsxjs/wsxjs#setup for more details.`
1373
+ );
1374
+ }
1375
+ }
1216
1376
  throw new Error(
1217
- `@state decorator: Invalid propertyKey detected.
1377
+ `@state decorator: Invalid propertyKey detected (received object instead of string/symbol).
1218
1378
 
1219
- The @state decorator MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
1379
+ The @state decorator MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
1220
1380
 
1221
1381
  To fix this, please:
1222
1382
  1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
@@ -1232,6 +1392,10 @@ See: https://github.com/wsxjs/wsxjs#setup for more details.`
1232
1392
  );
1233
1393
  }
1234
1394
  normalizedPropertyKey = propertyKeyStr;
1395
+ } else {
1396
+ throw new Error(
1397
+ `@state decorator: propertyKey is missing. This usually means the decorator is not being called correctly. Please ensure you're using @state on a class field, not a method or other construct.`
1398
+ );
1235
1399
  }
1236
1400
  if (target == null) {
1237
1401
  const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
package/dist/index.mjs CHANGED
@@ -973,17 +973,177 @@ function deriveTagName(className, prefix) {
973
973
  }
974
974
 
975
975
  // src/reactive-decorator.ts
976
- function state(target, propertyKey) {
976
+ function state(targetOrContext, propertyKey) {
977
+ let propertyName;
978
+ const propertyKeyIsObject = typeof propertyKey === "object" && propertyKey !== null;
979
+ const targetIsObject = typeof targetOrContext === "object" && targetOrContext !== null;
980
+ const hasStage3Indicators = targetIsObject && ("kind" in targetOrContext || "addInitializer" in targetOrContext || "access" in targetOrContext);
981
+ const hasName = hasStage3Indicators && "name" in targetOrContext;
982
+ const isStage3Decorator = propertyKeyIsObject || hasName || hasStage3Indicators && (propertyKey === void 0 || propertyKey === null) || targetIsObject && "addInitializer" in targetOrContext;
983
+ if (isStage3Decorator) {
984
+ const context = targetOrContext;
985
+ if (context.name) {
986
+ propertyName = typeof context.name === "string" ? context.name : context.name.toString();
987
+ } else if (propertyKeyIsObject) {
988
+ if ("name" in propertyKey) {
989
+ const keyObj = propertyKey;
990
+ propertyName = keyObj.name ? typeof keyObj.name === "string" ? keyObj.name : keyObj.name.toString() : "unknown";
991
+ } else if ("key" in propertyKey) {
992
+ const keyObj = propertyKey;
993
+ propertyName = keyObj.key ? typeof keyObj.key === "string" ? keyObj.key : keyObj.key.toString() : "unknown";
994
+ } else {
995
+ const keyStr = String(propertyKey);
996
+ if (keyStr !== "[object Object]") {
997
+ propertyName = keyStr;
998
+ } else {
999
+ propertyName = "unknown";
1000
+ }
1001
+ }
1002
+ } else {
1003
+ propertyName = "unknown";
1004
+ }
1005
+ } else {
1006
+ if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
1007
+ propertyName = typeof propertyKey === "string" ? propertyKey : propertyKey.toString();
1008
+ } else if (propertyKey != null) {
1009
+ const propertyKeyStr = String(propertyKey);
1010
+ if (propertyKeyStr === "[object Object]") {
1011
+ propertyName = "unknown";
1012
+ } else {
1013
+ propertyName = propertyKeyStr;
1014
+ }
1015
+ } else {
1016
+ propertyName = "unknown";
1017
+ }
1018
+ }
1019
+ console.warn(
1020
+ `[WSX] @state decorator is using runtime fallback. Property "${propertyName}" will work but with reduced performance.
1021
+
1022
+ To fix this and enable compile-time processing, please:
1023
+ 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1024
+ 2. Configure it in vite.config.ts:
1025
+ import { wsx } from '@wsxjs/wsx-vite-plugin';
1026
+ export default defineConfig({ plugins: [wsx()] });
1027
+ 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1028
+ npm install --save-dev @wsxjs/wsx-tsconfig
1029
+ Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1030
+ Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1031
+
1032
+ See: https://github.com/wsxjs/wsxjs#setup for more details.`
1033
+ );
1034
+ if (isStage3Decorator) {
1035
+ let context;
1036
+ if (hasStage3Indicators) {
1037
+ context = targetOrContext;
1038
+ } else if (propertyKeyIsObject) {
1039
+ const keyObj = propertyKey;
1040
+ const targetObj = targetOrContext;
1041
+ const nameValue = targetObj.name || keyObj.name || keyObj.key || "unknown";
1042
+ context = {
1043
+ kind: targetObj.kind || "field",
1044
+ name: nameValue,
1045
+ addInitializer: targetObj.addInitializer || keyObj.addInitializer,
1046
+ access: targetObj.access || keyObj.access
1047
+ };
1048
+ } else {
1049
+ context = targetOrContext;
1050
+ }
1051
+ if (context.kind && context.kind !== "field") {
1052
+ const nameStr = typeof context.name === "string" ? context.name : context.name.toString();
1053
+ throw new Error(
1054
+ `@state decorator can only be used on class fields, not ${context.kind}. Property: "${nameStr}"`
1055
+ );
1056
+ }
1057
+ if (!context.addInitializer) {
1058
+ console.warn(
1059
+ `[WSX] @state decorator: addInitializer not available for property "${propertyName}". The property will not be reactive. This usually means the decorator system is not properly configured.`
1060
+ );
1061
+ } else {
1062
+ context.addInitializer(function() {
1063
+ if (!this || typeof this.reactive !== "function" || typeof this.useState !== "function") {
1064
+ throw new Error(
1065
+ `@state decorator runtime fallback: Component does not extend WebComponent or LightComponent. Property "${propertyName}" cannot be made reactive.
1066
+
1067
+ The @state decorator can only be used in classes that extend WebComponent or LightComponent. Please ensure your component class extends one of these base classes.`
1068
+ );
1069
+ }
1070
+ let initialValue = void 0;
1071
+ if (context.access?.get) {
1072
+ try {
1073
+ initialValue = context.access.get();
1074
+ } catch {
1075
+ initialValue = this[propertyName];
1076
+ }
1077
+ } else {
1078
+ initialValue = this[propertyName];
1079
+ }
1080
+ const isObject = initialValue !== null && initialValue !== void 0 && (typeof initialValue === "object" || Array.isArray(initialValue));
1081
+ if (isObject) {
1082
+ let reactiveValue = this.reactive(initialValue);
1083
+ Object.defineProperty(this, propertyName, {
1084
+ get: () => reactiveValue,
1085
+ set: (newValue) => {
1086
+ if (newValue !== null && newValue !== void 0 && (typeof newValue === "object" || Array.isArray(newValue))) {
1087
+ reactiveValue = this.reactive(newValue);
1088
+ this.scheduleRerender();
1089
+ } else {
1090
+ reactiveValue = newValue;
1091
+ this.scheduleRerender();
1092
+ }
1093
+ },
1094
+ enumerable: true,
1095
+ configurable: true
1096
+ });
1097
+ } else {
1098
+ const [getState, setState] = this.useState(propertyName, initialValue);
1099
+ Object.defineProperty(this, propertyName, {
1100
+ get: getState,
1101
+ set: setState,
1102
+ enumerable: true,
1103
+ configurable: true
1104
+ });
1105
+ }
1106
+ });
1107
+ }
1108
+ return context;
1109
+ }
1110
+ const target = targetOrContext;
977
1111
  let normalizedPropertyKey;
978
1112
  if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
979
1113
  normalizedPropertyKey = propertyKey;
980
- } else {
1114
+ } else if (propertyKey != null) {
981
1115
  const propertyKeyStr = String(propertyKey);
982
1116
  if (propertyKeyStr === "[object Object]") {
1117
+ if (typeof targetOrContext === "object" && targetOrContext !== null && ("kind" in targetOrContext || "addInitializer" in targetOrContext)) {
1118
+ console.warn(
1119
+ `[WSX] @state decorator: Detected potential Stage 3 decorator format but with unexpected propertyKey. This might be a compatibility issue. The decorator will attempt to work in runtime fallback mode.`
1120
+ );
1121
+ const context = targetOrContext;
1122
+ if (context.name) {
1123
+ const name = typeof context.name === "string" ? context.name : context.name.toString();
1124
+ throw new Error(
1125
+ `@state decorator: Detected Stage 3 decorator format but with invalid propertyKey. Property name: "${name}".
1126
+
1127
+ This usually means the decorator is being called in an unexpected format. Please ensure you have configured the Babel plugin correctly.
1128
+
1129
+ To fix this, please:
1130
+ 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1131
+ 2. Configure it in vite.config.ts:
1132
+ import { wsx } from '@wsxjs/wsx-vite-plugin';
1133
+ export default defineConfig({ plugins: [wsx()] });
1134
+ 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1135
+ npm install --save-dev @wsxjs/wsx-tsconfig
1136
+ Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1137
+ Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1138
+
1139
+ See: https://github.com/wsxjs/wsxjs#setup for more details.`
1140
+ );
1141
+ }
1142
+ }
983
1143
  throw new Error(
984
- `@state decorator: Invalid propertyKey detected.
1144
+ `@state decorator: Invalid propertyKey detected (received object instead of string/symbol).
985
1145
 
986
- The @state decorator MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
1146
+ The @state decorator MUST be processed by Babel plugin at compile time. It appears the Babel plugin is not configured in your build setup.
987
1147
 
988
1148
  To fix this, please:
989
1149
  1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
@@ -999,6 +1159,10 @@ See: https://github.com/wsxjs/wsxjs#setup for more details.`
999
1159
  );
1000
1160
  }
1001
1161
  normalizedPropertyKey = propertyKeyStr;
1162
+ } else {
1163
+ throw new Error(
1164
+ `@state decorator: propertyKey is missing. This usually means the decorator is not being called correctly. Please ensure you're using @state on a class field, not a method or other construct.`
1165
+ );
1002
1166
  }
1003
1167
  if (target == null) {
1004
1168
  const propertyKeyStr = typeof normalizedPropertyKey === "string" ? normalizedPropertyKey : normalizedPropertyKey.toString();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wsxjs/wsx-core",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "description": "Core WSX Framework - Web Components with JSX syntax",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -48,241 +48,136 @@ interface DecoratorContext {
48
48
  addInitializer?(initializer: () => void): void;
49
49
  }
50
50
 
51
+ /**
52
+ * Helper function to create error message for missing Babel plugin
53
+ */
54
+ function createBabelPluginError(propertyName: string): string {
55
+ return (
56
+ `@state decorator on property "${propertyName}" MUST be processed by Babel plugin at compile time. ` +
57
+ `It appears the Babel plugin is not configured in your build setup. ` +
58
+ `\n\n` +
59
+ `The @state decorator cannot work without the Babel plugin. ` +
60
+ `\n\n` +
61
+ `To fix this, please:` +
62
+ `\n1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin` +
63
+ `\n2. Configure it in vite.config.ts:` +
64
+ `\n import { wsx } from '@wsxjs/wsx-vite-plugin';` +
65
+ `\n export default defineConfig({ plugins: [wsx()] });` +
66
+ `\n3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):` +
67
+ `\n npm install --save-dev @wsxjs/wsx-tsconfig` +
68
+ `\n Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }` +
69
+ `\n Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }` +
70
+ `\n\n` +
71
+ `See: https://github.com/wsxjs/wsxjs#setup for more details.`
72
+ );
73
+ }
74
+
51
75
  export function state(
52
76
  targetOrContext: unknown | DecoratorContext,
53
77
  propertyKey?: string | symbol | unknown
54
- ): void | DecoratorContext {
78
+ // Compatibility with Babel plugin which is required for this decorator to work properly
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
+ ): any {
55
81
  /**
56
- * @state decorator supports both:
57
- * 1. Compile-time processing by Babel plugin (preferred) - decorator is removed at compile time
58
- * 2. Runtime fallback when Babel plugin is not configured - decorator executes at runtime
59
- *
82
+ * @state decorator MUST be processed by Babel plugin at compile time.
60
83
  * If this function is executed at runtime, it means Babel plugin did not process it.
61
- * We should warn the user and provide fallback functionality.
84
+ * We MUST throw an error - no fallback is allowed.
62
85
  */
63
86
 
64
- // Determine property name for warning message
65
- let propertyName: string;
66
- if (
67
- typeof targetOrContext === "object" &&
68
- targetOrContext !== null &&
69
- "kind" in targetOrContext &&
70
- "name" in targetOrContext
71
- ) {
72
- // Stage 3 decorator format
73
- const context = targetOrContext as DecoratorContext;
74
- propertyName = typeof context.name === "string" ? context.name : context.name.toString();
75
- } else {
76
- // Legacy decorator format
77
- if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
78
- propertyName = typeof propertyKey === "string" ? propertyKey : propertyKey.toString();
79
- } else {
80
- propertyName = String(propertyKey);
81
- }
82
- }
83
-
84
- // Show warning immediately when decorator is executed at runtime
85
- // This means Babel plugin did not process it
86
- console.warn(
87
- `[WSX] @state decorator is using runtime fallback. ` +
88
- `Property "${propertyName}" will work but with reduced performance. ` +
89
- `\n\n` +
90
- `To fix this and enable compile-time processing, please:` +
91
- `\n1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin` +
92
- `\n2. Configure it in vite.config.ts:` +
93
- `\n import { wsx } from '@wsxjs/wsx-vite-plugin';` +
94
- `\n export default defineConfig({ plugins: [wsx()] });` +
95
- `\n3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):` +
96
- `\n npm install --save-dev @wsxjs/wsx-tsconfig` +
97
- `\n Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }` +
98
- `\n Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }` +
99
- `\n\n` +
100
- `See: https://github.com/wsxjs/wsxjs#setup for more details.`
101
- );
87
+ // Determine property name for error message
88
+ let propertyName: string = "unknown";
102
89
 
103
90
  // Check if this is a Stage 3 decorator (context object)
104
- if (
105
- typeof targetOrContext === "object" &&
106
- targetOrContext !== null &&
107
- "kind" in targetOrContext &&
108
- "name" in targetOrContext
109
- ) {
110
- const context = targetOrContext as DecoratorContext;
111
-
112
- // Only support field decorators
113
- if (context.kind !== "field") {
114
- const nameStr =
115
- typeof context.name === "string" ? context.name : context.name.toString();
116
- throw new Error(
117
- `@state decorator can only be used on class fields, not ${context.kind}. Property: "${nameStr}"`
118
- );
119
- }
120
-
121
- // Runtime fallback: Use addInitializer to set up reactive state
122
- if (context.addInitializer) {
123
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
124
- context.addInitializer(function (this: any) {
125
- // 'this' refers to the component instance
126
- if (
127
- !this ||
128
- typeof this.reactive !== "function" ||
129
- typeof this.useState !== "function"
130
- ) {
131
- throw new Error(
132
- `@state decorator runtime fallback: Component does not extend WebComponent or LightComponent. ` +
133
- `Property "${propertyName}" cannot be made reactive. ` +
134
- `\n\n` +
135
- `The @state decorator can only be used in classes that extend WebComponent or LightComponent. ` +
136
- `Please ensure your component class extends one of these base classes.`
137
- );
91
+ const propertyKeyIsObject = typeof propertyKey === "object" && propertyKey !== null;
92
+ const targetIsObject = typeof targetOrContext === "object" && targetOrContext !== null;
93
+
94
+ const hasStage3Indicators =
95
+ targetIsObject &&
96
+ ("kind" in targetOrContext ||
97
+ "addInitializer" in targetOrContext ||
98
+ "access" in targetOrContext);
99
+
100
+ const hasName = hasStage3Indicators && "name" in targetOrContext;
101
+
102
+ const isStage3Decorator =
103
+ propertyKeyIsObject ||
104
+ hasName ||
105
+ (hasStage3Indicators && (propertyKey === undefined || propertyKey === null)) ||
106
+ (targetIsObject && "addInitializer" in targetOrContext);
107
+
108
+ if (isStage3Decorator) {
109
+ // Stage 3 decorator format - determine property name safely
110
+ if (hasStage3Indicators && targetOrContext && typeof targetOrContext === "object") {
111
+ const context = targetOrContext as DecoratorContext;
112
+ if (context.name) {
113
+ propertyName =
114
+ typeof context.name === "string" ? context.name : context.name.toString();
115
+ }
116
+ } else if (propertyKeyIsObject) {
117
+ // If propertyKey is an object, try to extract name from it
118
+ const keyObj = propertyKey as Record<string, unknown>;
119
+ const targetObj = targetOrContext as Record<string, unknown> | null;
120
+
121
+ // Try to get name from multiple sources
122
+ const nameValue =
123
+ (targetObj?.name as string | symbol | undefined) ||
124
+ (keyObj.name as string | symbol | undefined) ||
125
+ (keyObj.key as string | symbol | undefined);
126
+
127
+ if (nameValue) {
128
+ propertyName = typeof nameValue === "string" ? nameValue : nameValue.toString();
129
+ } else {
130
+ const keyStr = String(propertyKey);
131
+ if (keyStr !== "[object Object]") {
132
+ propertyName = keyStr;
138
133
  }
134
+ }
135
+ }
139
136
 
140
- // Get initial value - try from access.get() first, then from property
141
- let initialValue: unknown = undefined;
142
- if (context.access?.get) {
143
- try {
144
- initialValue = context.access.get();
145
- } catch {
146
- // Access might not be available yet, try property directly
147
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
148
- initialValue = (this as any)[propertyName];
149
- }
150
- } else {
151
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
- initialValue = (this as any)[propertyName];
153
- }
154
-
155
- // Determine if it's an object/array
156
- const isObject =
157
- initialValue !== null &&
158
- initialValue !== undefined &&
159
- (typeof initialValue === "object" || Array.isArray(initialValue));
160
-
161
- if (isObject) {
162
- // For objects/arrays: use reactive()
163
- let reactiveValue = this.reactive(initialValue);
164
- Object.defineProperty(this, propertyName, {
165
- get: () => reactiveValue,
166
- set: (newValue: unknown) => {
167
- // Auto-wrap new values in reactive if they're objects/arrays
168
- if (
169
- newValue !== null &&
170
- newValue !== undefined &&
171
- (typeof newValue === "object" || Array.isArray(newValue))
172
- ) {
173
- // Create new reactive value
174
- reactiveValue = this.reactive(newValue);
175
- this.scheduleRerender();
176
- } else {
177
- // For primitives, just assign (but this shouldn't happen for object properties)
178
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
- reactiveValue = newValue as any;
180
- this.scheduleRerender();
181
- }
182
- },
183
- enumerable: true,
184
- configurable: true,
185
- });
186
- } else {
187
- // For primitives: use useState
188
- const [getState, setState] = this.useState(propertyName, initialValue);
189
- Object.defineProperty(this, propertyName, {
190
- get: getState,
191
- set: setState,
192
- enumerable: true,
193
- configurable: true,
194
- });
195
- }
196
- });
137
+ // Only support field decorators (if kind is specified)
138
+ if (targetIsObject && "kind" in targetOrContext) {
139
+ const context = targetOrContext as DecoratorContext;
140
+ if (context.kind && context.kind !== "field") {
141
+ const nameStr =
142
+ typeof context.name === "string" ? context.name : context.name.toString();
143
+ throw new Error(
144
+ `@state decorator can only be used on class fields, not ${context.kind}. Property: "${nameStr}"`
145
+ );
146
+ }
197
147
  }
198
148
 
199
- // Return context for Stage 3 decorators
200
- return context;
149
+ // If we reach here, the decorator is being executed at runtime
150
+ // This means Babel plugin did not process it - throw error immediately
151
+ throw new Error(createBabelPluginError(propertyName));
201
152
  }
202
153
 
203
154
  // Legacy decorator format (experimentalDecorators: true)
204
155
  // This should ideally be removed by Babel plugin
205
- const target = targetOrContext;
206
- let normalizedPropertyKey: string | symbol;
207
156
  if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
208
- normalizedPropertyKey = propertyKey;
209
- } else {
157
+ propertyName = typeof propertyKey === "string" ? propertyKey : propertyKey.toString();
158
+ } else if (propertyKey != null) {
210
159
  const propertyKeyStr = String(propertyKey);
211
160
  if (propertyKeyStr === "[object Object]") {
212
- // Invalid propertyKey - Babel plugin was not configured
213
- throw new Error(
214
- `@state decorator: Invalid propertyKey detected. ` +
215
- `\n\n` +
216
- `The @state decorator MUST be processed by Babel plugin at compile time. ` +
217
- `It appears the Babel plugin is not configured in your build setup.` +
218
- `\n\n` +
219
- `To fix this, please:` +
220
- `\n1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin` +
221
- `\n2. Configure it in vite.config.ts:` +
222
- `\n import { wsx } from '@wsxjs/wsx-vite-plugin';` +
223
- `\n export default defineConfig({ plugins: [wsx()] });` +
224
- `\n3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):` +
225
- `\n npm install --save-dev @wsxjs/wsx-tsconfig` +
226
- `\n Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }` +
227
- `\n Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }` +
228
- `\n\n` +
229
- `See: https://github.com/wsxjs/wsxjs#setup for more details.`
230
- );
161
+ // Invalid propertyKey - this might happen if decorator is called in unexpected format
162
+ // Check if targetOrContext might actually be a Stage 3 context that we missed
163
+ if (
164
+ typeof targetOrContext === "object" &&
165
+ targetOrContext !== null &&
166
+ ("kind" in targetOrContext || "addInitializer" in targetOrContext)
167
+ ) {
168
+ // This looks like it might be a Stage 3 decorator that we didn't detect properly
169
+ const context = targetOrContext as Partial<DecoratorContext>;
170
+ if (context.name) {
171
+ propertyName =
172
+ typeof context.name === "string" ? context.name : context.name.toString();
173
+ }
174
+ }
175
+ } else {
176
+ propertyName = propertyKeyStr;
231
177
  }
232
- normalizedPropertyKey = propertyKeyStr;
233
- }
234
-
235
- // Basic validation: ensure target is valid
236
- if (target == null) {
237
- const propertyKeyStr =
238
- typeof normalizedPropertyKey === "string"
239
- ? normalizedPropertyKey
240
- : normalizedPropertyKey.toString();
241
- throw new Error(
242
- `@state decorator: Cannot access property "${propertyKeyStr}". ` +
243
- `Target is ${target === null ? "null" : "undefined"}.` +
244
- `\n\n` +
245
- `The @state decorator MUST be processed by Babel plugin at compile time. ` +
246
- `It appears the Babel plugin is not configured in your build setup.` +
247
- `\n\n` +
248
- `To fix this, please:` +
249
- `\n1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin` +
250
- `\n2. Configure it in vite.config.ts:` +
251
- `\n import { wsx } from '@wsxjs/wsx-vite-plugin';` +
252
- `\n export default defineConfig({ plugins: [wsx()] });` +
253
- `\n3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):` +
254
- `\n npm install --save-dev @wsxjs/wsx-tsconfig` +
255
- `\n Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }` +
256
- `\n Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }` +
257
- `\n\n` +
258
- `See: https://github.com/wsxjs/wsxjs#setup for more details.`
259
- );
260
- }
261
-
262
- if (typeof target !== "object") {
263
- const propertyKeyStr =
264
- typeof normalizedPropertyKey === "string"
265
- ? normalizedPropertyKey
266
- : normalizedPropertyKey.toString();
267
- throw new Error(
268
- `@state decorator: Cannot be used on "${propertyKeyStr}". ` +
269
- `@state is for properties only, not methods.`
270
- );
271
- }
272
-
273
- // Validate that property has an initial value
274
- const descriptor = Object.getOwnPropertyDescriptor(target, normalizedPropertyKey);
275
- if (descriptor?.get) {
276
- const propertyKeyStr =
277
- typeof normalizedPropertyKey === "string"
278
- ? normalizedPropertyKey
279
- : normalizedPropertyKey.toString();
280
- throw new Error(
281
- `@state decorator cannot be used with getter properties. Property: "${propertyKeyStr}"`
282
- );
283
178
  }
284
179
 
285
- // Note: We don't store metadata or remove the property here.
286
- // Babel plugin handles everything at compile time.
287
- // If this function is called at runtime, it means Babel plugin didn't process the decorator.
180
+ // If we reach here, the decorator is being executed at runtime
181
+ // This means Babel plugin did not process it - throw error immediately
182
+ throw new Error(createBabelPluginError(propertyName));
288
183
  }