@wsxjs/wsx-core 0.0.11 → 0.0.13

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,7 +1206,91 @@ 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
+ if (typeof targetOrContext === "object" && targetOrContext !== null && "kind" in targetOrContext && "name" in targetOrContext) {
1212
+ const context = targetOrContext;
1213
+ propertyName = typeof context.name === "string" ? context.name : context.name.toString();
1214
+ } else {
1215
+ if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
1216
+ propertyName = typeof propertyKey === "string" ? propertyKey : propertyKey.toString();
1217
+ } else {
1218
+ propertyName = String(propertyKey);
1219
+ }
1220
+ }
1221
+ console.warn(
1222
+ `[WSX] @state decorator is using runtime fallback. Property "${propertyName}" will work but with reduced performance.
1223
+
1224
+ To fix this and enable compile-time processing, please:
1225
+ 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
1226
+ 2. Configure it in vite.config.ts:
1227
+ import { wsx } from '@wsxjs/wsx-vite-plugin';
1228
+ export default defineConfig({ plugins: [wsx()] });
1229
+ 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
1230
+ npm install --save-dev @wsxjs/wsx-tsconfig
1231
+ Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
1232
+ Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1233
+
1234
+ See: https://github.com/wsxjs/wsxjs#setup for more details.`
1235
+ );
1236
+ if (typeof targetOrContext === "object" && targetOrContext !== null && "kind" in targetOrContext && "name" in targetOrContext) {
1237
+ const context = targetOrContext;
1238
+ if (context.kind !== "field") {
1239
+ const nameStr = typeof context.name === "string" ? context.name : context.name.toString();
1240
+ throw new Error(
1241
+ `@state decorator can only be used on class fields, not ${context.kind}. Property: "${nameStr}"`
1242
+ );
1243
+ }
1244
+ if (context.addInitializer) {
1245
+ context.addInitializer(function() {
1246
+ if (!this || typeof this.reactive !== "function" || typeof this.useState !== "function") {
1247
+ throw new Error(
1248
+ `@state decorator runtime fallback: Component does not extend WebComponent or LightComponent. Property "${propertyName}" cannot be made reactive.
1249
+
1250
+ 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.`
1251
+ );
1252
+ }
1253
+ let initialValue = void 0;
1254
+ if (context.access?.get) {
1255
+ try {
1256
+ initialValue = context.access.get();
1257
+ } catch {
1258
+ initialValue = this[propertyName];
1259
+ }
1260
+ } else {
1261
+ initialValue = this[propertyName];
1262
+ }
1263
+ const isObject = initialValue !== null && initialValue !== void 0 && (typeof initialValue === "object" || Array.isArray(initialValue));
1264
+ if (isObject) {
1265
+ let reactiveValue = this.reactive(initialValue);
1266
+ Object.defineProperty(this, propertyName, {
1267
+ get: () => reactiveValue,
1268
+ set: (newValue) => {
1269
+ if (newValue !== null && newValue !== void 0 && (typeof newValue === "object" || Array.isArray(newValue))) {
1270
+ reactiveValue = this.reactive(newValue);
1271
+ this.scheduleRerender();
1272
+ } else {
1273
+ reactiveValue = newValue;
1274
+ this.scheduleRerender();
1275
+ }
1276
+ },
1277
+ enumerable: true,
1278
+ configurable: true
1279
+ });
1280
+ } else {
1281
+ const [getState, setState] = this.useState(propertyName, initialValue);
1282
+ Object.defineProperty(this, propertyName, {
1283
+ get: getState,
1284
+ set: setState,
1285
+ enumerable: true,
1286
+ configurable: true
1287
+ });
1288
+ }
1289
+ });
1290
+ }
1291
+ return context;
1292
+ }
1293
+ const target = targetOrContext;
1210
1294
  let normalizedPropertyKey;
1211
1295
  if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
1212
1296
  normalizedPropertyKey = propertyKey;
package/dist/index.mjs CHANGED
@@ -973,7 +973,91 @@ 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
+ if (typeof targetOrContext === "object" && targetOrContext !== null && "kind" in targetOrContext && "name" in targetOrContext) {
979
+ const context = targetOrContext;
980
+ propertyName = typeof context.name === "string" ? context.name : context.name.toString();
981
+ } else {
982
+ if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
983
+ propertyName = typeof propertyKey === "string" ? propertyKey : propertyKey.toString();
984
+ } else {
985
+ propertyName = String(propertyKey);
986
+ }
987
+ }
988
+ console.warn(
989
+ `[WSX] @state decorator is using runtime fallback. Property "${propertyName}" will work but with reduced performance.
990
+
991
+ To fix this and enable compile-time processing, please:
992
+ 1. Install @wsxjs/wsx-vite-plugin: npm install @wsxjs/wsx-vite-plugin
993
+ 2. Configure it in vite.config.ts:
994
+ import { wsx } from '@wsxjs/wsx-vite-plugin';
995
+ export default defineConfig({ plugins: [wsx()] });
996
+ 3. Configure TypeScript (recommended: use @wsxjs/wsx-tsconfig):
997
+ npm install --save-dev @wsxjs/wsx-tsconfig
998
+ Then in tsconfig.json: { "extends": "@wsxjs/wsx-tsconfig/tsconfig.base.json" }
999
+ Or manually: { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
1000
+
1001
+ See: https://github.com/wsxjs/wsxjs#setup for more details.`
1002
+ );
1003
+ if (typeof targetOrContext === "object" && targetOrContext !== null && "kind" in targetOrContext && "name" in targetOrContext) {
1004
+ const context = targetOrContext;
1005
+ if (context.kind !== "field") {
1006
+ const nameStr = typeof context.name === "string" ? context.name : context.name.toString();
1007
+ throw new Error(
1008
+ `@state decorator can only be used on class fields, not ${context.kind}. Property: "${nameStr}"`
1009
+ );
1010
+ }
1011
+ if (context.addInitializer) {
1012
+ context.addInitializer(function() {
1013
+ if (!this || typeof this.reactive !== "function" || typeof this.useState !== "function") {
1014
+ throw new Error(
1015
+ `@state decorator runtime fallback: Component does not extend WebComponent or LightComponent. Property "${propertyName}" cannot be made reactive.
1016
+
1017
+ 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.`
1018
+ );
1019
+ }
1020
+ let initialValue = void 0;
1021
+ if (context.access?.get) {
1022
+ try {
1023
+ initialValue = context.access.get();
1024
+ } catch {
1025
+ initialValue = this[propertyName];
1026
+ }
1027
+ } else {
1028
+ initialValue = this[propertyName];
1029
+ }
1030
+ const isObject = initialValue !== null && initialValue !== void 0 && (typeof initialValue === "object" || Array.isArray(initialValue));
1031
+ if (isObject) {
1032
+ let reactiveValue = this.reactive(initialValue);
1033
+ Object.defineProperty(this, propertyName, {
1034
+ get: () => reactiveValue,
1035
+ set: (newValue) => {
1036
+ if (newValue !== null && newValue !== void 0 && (typeof newValue === "object" || Array.isArray(newValue))) {
1037
+ reactiveValue = this.reactive(newValue);
1038
+ this.scheduleRerender();
1039
+ } else {
1040
+ reactiveValue = newValue;
1041
+ this.scheduleRerender();
1042
+ }
1043
+ },
1044
+ enumerable: true,
1045
+ configurable: true
1046
+ });
1047
+ } else {
1048
+ const [getState, setState] = this.useState(propertyName, initialValue);
1049
+ Object.defineProperty(this, propertyName, {
1050
+ get: getState,
1051
+ set: setState,
1052
+ enumerable: true,
1053
+ configurable: true
1054
+ });
1055
+ }
1056
+ });
1057
+ }
1058
+ return context;
1059
+ }
1060
+ const target = targetOrContext;
977
1061
  let normalizedPropertyKey;
978
1062
  if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
979
1063
  normalizedPropertyKey = propertyKey;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wsxjs/wsx-core",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "Core WSX Framework - Web Components with JSX syntax",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -33,20 +33,176 @@
33
33
  * }
34
34
  * ```
35
35
  */
36
- export function state(target: unknown, propertyKey: string | symbol | unknown): void {
36
+ /**
37
+ * Stage 3 Decorator Context interface
38
+ */
39
+ interface DecoratorContext {
40
+ kind: "class" | "method" | "getter" | "setter" | "field" | "accessor";
41
+ name: string | symbol;
42
+ access?: {
43
+ get?(): unknown;
44
+ set?(value: unknown): void;
45
+ };
46
+ private?: boolean;
47
+ static?: boolean;
48
+ addInitializer?(initializer: () => void): void;
49
+ }
50
+
51
+ export function state(
52
+ targetOrContext: unknown | DecoratorContext,
53
+ propertyKey?: string | symbol | unknown
54
+ ): unknown {
37
55
  /**
38
- * @state decorator is a compile-time marker for Babel plugin.
39
- * Babel plugin will:
40
- * 1. Detect @state decorator on properties
41
- * 2. Extract initial value from AST
42
- * 3. Remove @state decorator
43
- * 4. Generate initialization code in constructor (this.state = this.reactive(...) or useState)
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
44
59
  *
45
- * This runtime function only performs basic validation.
46
- * If Babel plugin is not configured, this will throw an error.
60
+ * 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.
47
62
  */
48
63
 
49
- // Normalize propertyKey
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
+ );
102
+
103
+ // 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
+ );
138
+ }
139
+
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
+ });
197
+ }
198
+
199
+ // Return context for Stage 3 decorators
200
+ return context;
201
+ }
202
+
203
+ // Legacy decorator format (experimentalDecorators: true)
204
+ // This should ideally be removed by Babel plugin
205
+ const target = targetOrContext;
50
206
  let normalizedPropertyKey: string | symbol;
51
207
  if (typeof propertyKey === "string" || typeof propertyKey === "symbol") {
52
208
  normalizedPropertyKey = propertyKey;