@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 +85 -1
- package/dist/index.mjs +85 -1
- package/package.json +1 -1
- package/src/reactive-decorator.ts +166 -10
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(
|
|
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(
|
|
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
|
@@ -33,20 +33,176 @@
|
|
|
33
33
|
* }
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
|
-
|
|
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
|
|
39
|
-
* Babel plugin
|
|
40
|
-
*
|
|
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
|
-
*
|
|
46
|
-
*
|
|
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
|
-
//
|
|
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;
|