@wsxjs/wsx-vite-plugin 0.0.13 → 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.mjs CHANGED
@@ -11,68 +11,255 @@ function babelPluginWSXState() {
11
11
  return {
12
12
  name: "babel-plugin-wsx-state",
13
13
  visitor: {
14
+ Program: {
15
+ enter(_path, state) {
16
+ console.info(
17
+ `[Babel Plugin WSX State] DEBUG: state.opts keys = ${Object.keys(state.opts || {}).join(", ")}`
18
+ );
19
+ console.info(
20
+ `[Babel Plugin WSX State] DEBUG: state.opts = ${JSON.stringify(state.opts).substring(0, 200)}`
21
+ );
22
+ const opts = state.opts;
23
+ if (opts?.originalSource) {
24
+ state.originalSource = opts.originalSource;
25
+ console.info(
26
+ `[Babel Plugin WSX State] \u2705 Stored original source from options (length: ${String(opts.originalSource).length})`
27
+ );
28
+ } else {
29
+ console.warn(
30
+ `[Babel Plugin WSX State] \u26A0\uFE0F state.opts.originalSource not found, trying fallback...`
31
+ );
32
+ const file = state.file;
33
+ if (file) {
34
+ const sourceCode = file.code;
35
+ if (sourceCode) {
36
+ state.originalSource = sourceCode;
37
+ console.info(
38
+ `[Babel Plugin WSX State] \u2705 Stored original source from file (length: ${sourceCode.length})`
39
+ );
40
+ } else {
41
+ console.error(
42
+ `[Babel Plugin WSX State] \u274C ERROR: Could not get original source code from state.opts or state.file!`
43
+ );
44
+ }
45
+ } else {
46
+ console.error(
47
+ `[Babel Plugin WSX State] \u274C ERROR: state.file is undefined!`
48
+ );
49
+ }
50
+ }
51
+ }
52
+ },
14
53
  ClassDeclaration(path) {
15
54
  const classBody = path.node.body;
16
55
  const stateProperties = [];
17
56
  console.info(
18
57
  `[Babel Plugin WSX State] Processing class ${path.node.id?.name || "anonymous"}, members: ${classBody.body.length}`
19
58
  );
59
+ if (path.node.decorators && path.node.decorators.length > 0) {
60
+ console.info(
61
+ `[Babel Plugin WSX State] Class-level decorators: ${path.node.decorators.length}`
62
+ );
63
+ }
20
64
  for (const member of classBody.body) {
21
65
  console.info(
22
66
  ` - Member type: ${member.type}, key: ${member.type === "ClassProperty" || member.type === "ClassPrivateProperty" ? member.key?.name : "N/A"}`
23
67
  );
24
68
  if ((member.type === "ClassProperty" || member.type === "ClassPrivateProperty") && member.key.type === "Identifier") {
69
+ const propertyName = member.key.name;
70
+ const decoratorCount = member.decorators?.length || 0;
71
+ const hasValue = !!member.value && !(member.value.type === "Identifier" && member.value.name === "undefined") && !(member.value.type === "UnaryExpression" && member.value.operator === "void");
72
+ const isUndefined = !!(member.value && (member.value.type === "Identifier" && member.value.name === "undefined" || member.value.type === "UnaryExpression" && member.value.operator === "void"));
73
+ const valueType = member.value ? member.value.type : "none";
74
+ const valueName = member.value && member.value.type === "Identifier" ? member.value.name : "none";
25
75
  console.info(
26
- ` - Property: ${member.key.name}, decorators: ${member.decorators?.length || 0}, hasValue: ${!!member.value}`
76
+ ` - Property: ${propertyName}, decorators: ${decoratorCount}, hasValue: ${hasValue}, isUndefined: ${isUndefined}, value type: ${valueType}, value name: ${valueName}`
27
77
  );
28
- if (member.decorators && member.decorators.length > 0) {
29
- member.decorators.forEach((decorator) => {
30
- if (decorator.expression.type === "Identifier") {
31
- console.info(` Decorator: ${decorator.expression.name}`);
32
- } else if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier") {
33
- console.debug(
34
- ` Decorator: ${decorator.expression.callee.name}()`
78
+ try {
79
+ if (isUndefined) {
80
+ const originalSource2 = path.state?.originalSource;
81
+ if (originalSource2) {
82
+ const propertyPattern = new RegExp(
83
+ `@state\\s+(?:private|protected|public)?\\s+${propertyName}\\s*[?;]`,
84
+ "m"
35
85
  );
86
+ if (propertyPattern.test(originalSource2)) {
87
+ console.error(
88
+ `[Babel Plugin WSX State] ERROR: Found @state decorator in source for property '${propertyName}' but value is undefined (from optional property syntax)!`
89
+ );
90
+ const error = path.buildCodeFrameError(
91
+ `@state decorator on property '${propertyName}' requires an initial value.
92
+
93
+ The property has undefined value (from optional property syntax '${propertyName}?'), but @state decorator needs a real value to decide whether to use useState (primitive) or reactive (object/array).
94
+
95
+ Examples:
96
+ @state private ${propertyName} = ""; // for string
97
+ @state private ${propertyName} = 0; // for number
98
+ @state private ${propertyName} = {}; // for object
99
+ @state private ${propertyName} = []; // for array
100
+ @state private ${propertyName} = undefined; // explicitly undefined
101
+
102
+ Current code: @state private ${propertyName}?;
103
+
104
+ Fix: Change to '@state private ${propertyName} = undefined;' or provide a real initial value.`
105
+ );
106
+ console.error(
107
+ `[Babel Plugin WSX State] ERROR: ${error.message}`
108
+ );
109
+ throw error;
110
+ }
36
111
  }
37
- });
38
- }
39
- const hasStateDecorator = member.decorators?.some(
40
- (decorator) => {
41
- if (decorator.expression.type === "Identifier" && decorator.expression.name === "state") {
42
- return true;
112
+ }
113
+ if (propertyName === "count") {
114
+ console.info(
115
+ `[Babel Plugin WSX State] DEBUG path.state keys for '${propertyName}': ${path.state ? Object.keys(path.state).join(", ") : "null"}`
116
+ );
117
+ console.info(
118
+ `[Babel Plugin WSX State] DEBUG path.state.originalSource type: ${path.state ? typeof path.state.originalSource : "path.state is null"}`
119
+ );
120
+ }
121
+ const originalSource = path.state?.originalSource;
122
+ if (originalSource) {
123
+ const propertyPattern = new RegExp(
124
+ `@state\\s+(?:private|protected|public)?\\s+${propertyName}\\s*[?;]`,
125
+ "m"
126
+ );
127
+ const hasStateInSource = propertyPattern.test(originalSource);
128
+ if (hasStateInSource) {
129
+ console.info(
130
+ `[Babel Plugin WSX State] Found @state in source for property '${propertyName}' (decorators array was empty: ${decoratorCount})`
131
+ );
132
+ const hasInitialValueInSource = new RegExp(
133
+ `@state\\s+(?:private|protected|public)?\\s+${propertyName}\\s*=\\s*[^;]+`,
134
+ "m"
135
+ ).test(originalSource);
136
+ if (!hasInitialValueInSource) {
137
+ console.error(
138
+ `[Babel Plugin WSX State] ERROR: Found @state decorator in source for property '${propertyName}' but no initial value!`
139
+ );
140
+ const error = path.buildCodeFrameError(
141
+ `@state decorator on property '${propertyName}' requires an initial value.
142
+
143
+ The @state decorator needs a real value to decide whether to use useState (primitive) or reactive (object/array).
144
+
145
+ Examples:
146
+ @state private ${propertyName} = ""; // for string
147
+ @state private ${propertyName} = 0; // for number
148
+ @state private ${propertyName} = {}; // for object
149
+ @state private ${propertyName} = []; // for array
150
+ @state private ${propertyName} = undefined; // explicitly undefined
151
+
152
+ Current code: @state private ${propertyName}?;
153
+
154
+ Fix: Change to '@state private ${propertyName} = undefined;' or provide a real initial value.`
155
+ );
156
+ console.error(
157
+ `[Babel Plugin WSX State] ERROR: ${error.message}`
158
+ );
159
+ throw error;
160
+ }
43
161
  }
44
- if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier" && decorator.expression.callee.name === "state") {
45
- return true;
162
+ } else {
163
+ if (propertyName === "count") {
164
+ console.warn(
165
+ `[Babel Plugin WSX State] WARNING: originalSource not available for property '${propertyName}'`
166
+ );
46
167
  }
47
- return false;
48
168
  }
49
- );
50
- if (hasStateDecorator && member.value) {
51
- const key = member.key.name;
52
- const initialValue = member.value;
53
- const isObject = initialValue.type === "ObjectExpression" || initialValue.type === "ArrayExpression";
54
- const isArray = initialValue.type === "ArrayExpression";
55
- stateProperties.push({
56
- key,
57
- initialValue,
58
- isObject,
59
- isArray
60
- // Add isArray flag
61
- });
62
- if (member.decorators) {
63
- member.decorators = member.decorators.filter(
169
+ if (member.decorators && member.decorators.length > 0) {
170
+ member.decorators.forEach((decorator) => {
171
+ if (decorator.expression.type === "Identifier") {
172
+ console.info(` Decorator: ${decorator.expression.name}`);
173
+ } else if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier") {
174
+ console.info(
175
+ ` Decorator: ${decorator.expression.callee.name}()`
176
+ );
177
+ } else {
178
+ console.info(` Decorator: ${decorator.expression.type}`);
179
+ }
180
+ });
181
+ } else {
182
+ }
183
+ let hasStateDecorator = false;
184
+ try {
185
+ hasStateDecorator = member.decorators?.some(
64
186
  (decorator) => {
65
187
  if (decorator.expression.type === "Identifier" && decorator.expression.name === "state") {
66
- return false;
188
+ return true;
67
189
  }
68
190
  if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier" && decorator.expression.callee.name === "state") {
69
- return false;
191
+ return true;
70
192
  }
71
- return true;
193
+ return false;
72
194
  }
195
+ ) || false;
196
+ } catch (error) {
197
+ console.error(
198
+ `[Babel Plugin WSX State] ERROR in hasStateDecorator check for ${propertyName}: ${error}`
73
199
  );
200
+ throw error;
74
201
  }
75
- member.value = void 0;
202
+ if (hasStateDecorator) {
203
+ const key = member.key.name;
204
+ const hasInitialValue = !!(member.value && !(member.value.type === "Identifier" && member.value.name === "undefined") && !(member.value.type === "UnaryExpression" && member.value.operator === "void"));
205
+ if (key === "count") {
206
+ console.error(
207
+ `[Babel Plugin WSX State] DEBUG: hasInitialValue for 'count' = ${hasInitialValue}, type = ${typeof hasInitialValue}`
208
+ );
209
+ console.error(
210
+ `[Babel Plugin WSX State] DEBUG: member.value = ${member.value}, !hasInitialValue = ${!hasInitialValue}`
211
+ );
212
+ }
213
+ if (!hasInitialValue) {
214
+ const error = path.buildCodeFrameError(
215
+ `@state decorator on property '${key}' requires an initial value.
216
+
217
+ Examples:
218
+ @state private ${key} = ""; // for string
219
+ @state private ${key} = 0; // for number
220
+ @state private ${key} = {}; // for object
221
+ @state private ${key} = []; // for array
222
+ @state private ${key} = undefined; // for optional
223
+
224
+ Current code: @state private ${key};
225
+
226
+ This error should be caught during build time. If you see this at runtime, it means the Babel plugin did not process this file.`
227
+ );
228
+ console.error(`[Babel Plugin WSX State] ERROR: ${error.message}`);
229
+ throw error;
230
+ }
231
+ const initialValue = member.value;
232
+ const isObject = initialValue.type === "ObjectExpression" || initialValue.type === "ArrayExpression";
233
+ const isArray = initialValue.type === "ArrayExpression";
234
+ stateProperties.push({
235
+ key,
236
+ initialValue,
237
+ isObject,
238
+ isArray
239
+ // Add isArray flag
240
+ });
241
+ if (member.decorators) {
242
+ member.decorators = member.decorators.filter(
243
+ (decorator) => {
244
+ if (decorator.expression.type === "Identifier" && decorator.expression.name === "state") {
245
+ return false;
246
+ }
247
+ if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier" && decorator.expression.callee.name === "state") {
248
+ return false;
249
+ }
250
+ return true;
251
+ }
252
+ );
253
+ }
254
+ member.value = void 0;
255
+ }
256
+ } catch (error) {
257
+ console.error(
258
+ `[Babel Plugin WSX State] CRASH in member processing for '${propertyName}':`,
259
+ error
260
+ );
261
+ console.error(`Stack trace:`, error.stack);
262
+ throw error;
76
263
  }
77
264
  }
78
265
  }
@@ -536,18 +723,39 @@ function vitePluginWSXWithBabel(options = {}) {
536
723
  const babelResult = transformSync(transformedCode, {
537
724
  filename: id,
538
725
  // Pass the actual filename so Babel knows it's .wsx
726
+ // CRITICAL: Configure parser to preserve decorators
727
+ // @babel/preset-typescript should preserve decorators by default,
728
+ // but we explicitly enable decorator parsing to be sure
729
+ parserOpts: {
730
+ plugins: [
731
+ ["decorators", { decoratorsBeforeExport: true }],
732
+ // Stage 3 decorators
733
+ "typescript",
734
+ "jsx"
735
+ ]
736
+ },
539
737
  presets: [
540
738
  [
541
739
  "@babel/preset-typescript",
542
740
  {
543
741
  isTSX: true,
544
742
  // Enable JSX syntax
545
- allExtensions: true
743
+ allExtensions: true,
546
744
  // Process all extensions, including .wsx
745
+ // CRITICAL: onlyRemoveTypeImports only affects import statements
746
+ // Decorators are preserved by default in @babel/preset-typescript
747
+ // They are only removed if we explicitly configure it
748
+ onlyRemoveTypeImports: false
749
+ // Remove all type-only imports
547
750
  }
548
751
  ]
549
752
  ],
550
753
  plugins: [
754
+ // CRITICAL: Decorator plugin must run FIRST to parse decorators correctly
755
+ // This ensures decorators are properly parsed before our custom plugins try to process them
756
+ // However, we need to use a custom visitor that doesn't transform decorators yet
757
+ // Actually, we should NOT run decorator plugin first because it transforms decorators
758
+ // Instead, we rely on TypeScript preset to parse but not transform decorators
551
759
  // CRITICAL: Style injection plugin must run FIRST
552
760
  // This ensures _autoStyles property exists before state transformations
553
761
  ...autoStyleInjection && cssFileExists ? [
@@ -563,8 +771,20 @@ function vitePluginWSXWithBabel(options = {}) {
563
771
  // Focus key generation plugin runs early to add data-wsx-key attributes
564
772
  // This must run before JSX is transformed to h() calls
565
773
  babelPluginWSXFocus,
566
- // State decorator transformation runs after style injection
567
- babelPluginWSXState,
774
+ // CRITICAL: State decorator transformation must run BEFORE @babel/plugin-proposal-decorators
775
+ // This allows the plugin to detect @state decorators in their original form and throw errors if needed
776
+ // The plugin removes @state decorators after processing, so the decorator plugin won't see them
777
+ [
778
+ babelPluginWSXState,
779
+ {
780
+ // Pass ORIGINAL source code (before JSX import injection) to plugin
781
+ // This ensures we can detect @state decorators even if they're removed by TypeScript preset
782
+ originalSource: code
783
+ // Use original code, not transformedCode
784
+ }
785
+ ],
786
+ // Decorator plugin runs after our custom plugins
787
+ // This transforms remaining decorators (like @autoRegister) to runtime calls
568
788
  [
569
789
  "@babel/plugin-proposal-decorators",
570
790
  {
@@ -585,8 +805,13 @@ function vitePluginWSXWithBabel(options = {}) {
585
805
  });
586
806
  if (babelResult && babelResult.code) {
587
807
  transformedCode = babelResult.code;
808
+ } else {
809
+ throw new Error(
810
+ `[WSX Plugin] Babel transform returned no code for ${id}. @state decorators will NOT be processed and will cause runtime errors. Please check Babel configuration and plugin setup.`
811
+ );
588
812
  }
589
- } catch {
813
+ } catch (error) {
814
+ throw error;
590
815
  }
591
816
  const hasJSXAfterBabel = transformedCode.includes('from "@wsxjs/wsx-core"') && (new RegExp(`[{,]\\s*${jsxFactory}\\s*[},]`).test(transformedCode) || new RegExp(`[{,]\\s*${jsxFragment}\\s*[},]`).test(transformedCode));
592
817
  if ((transformedCode.includes("<") || transformedCode.includes("Fragment")) && !hasJSXAfterBabel) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wsxjs/wsx-vite-plugin",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "description": "Vite plugin for WSX Framework",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -31,7 +31,7 @@
31
31
  "@babel/plugin-transform-class-static-block": "^7.28.0",
32
32
  "@babel/preset-typescript": "^7.28.5",
33
33
  "@babel/types": "^7.28.1",
34
- "@wsxjs/wsx-core": "0.0.13"
34
+ "@wsxjs/wsx-core": "0.0.14"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@babel/traverse": "^7.28.5",