@wsxjs/wsx-core 0.0.10 → 0.0.12

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/package.json CHANGED
@@ -1,10 +1,17 @@
1
1
  {
2
2
  "name": "@wsxjs/wsx-core",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "Core WSX Framework - Web Components with JSX syntax",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./types/index.d.ts",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/wsxjs/wsxjs.git",
11
+ "directory": "packages/core"
12
+ },
13
+ "author": "Albert Li <albert.li@wsxjs.com>",
14
+ "license": "MIT",
8
15
  "exports": {
9
16
  ".": {
10
17
  "types": "./types/index.d.ts",
@@ -38,6 +45,9 @@
38
45
  "peerDependencies": {
39
46
  "typescript": ">=4.7.0"
40
47
  },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
41
51
  "scripts": {
42
52
  "build": "tsup src/index.ts src/jsx.ts src/jsx-runtime.ts --format cjs,esm",
43
53
  "build:dev": "NODE_ENV=development tsup src/index.ts src/jsx.ts src/jsx-runtime.ts --format cjs,esm --sourcemap",
@@ -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
+ ): void | DecoratorContext {
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;