@wsxjs/wsx-vite-plugin 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.
@@ -31,6 +31,52 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
31
31
  return {
32
32
  name: "babel-plugin-wsx-state",
33
33
  visitor: {
34
+ Program: {
35
+ enter(_path, state) {
36
+ // CRITICAL: Debug log to check state.opts
37
+ console.info(
38
+ `[Babel Plugin WSX State] DEBUG: state.opts keys = ${Object.keys(state.opts || {}).join(", ")}`
39
+ );
40
+ console.info(
41
+ `[Babel Plugin WSX State] DEBUG: state.opts = ${JSON.stringify(state.opts).substring(0, 200)}`
42
+ );
43
+
44
+ // Store original source code for later checking
45
+ // First try to get from plugin options (passed from vite plugin)
46
+ const opts = state.opts as Record<string, unknown> | undefined;
47
+ if (opts?.originalSource) {
48
+ (state as Record<string, unknown>).originalSource = opts.originalSource;
49
+ console.info(
50
+ `[Babel Plugin WSX State] ✅ Stored original source from options (length: ${String(opts.originalSource).length})`
51
+ );
52
+ } else {
53
+ console.warn(
54
+ `[Babel Plugin WSX State] ⚠️ state.opts.originalSource not found, trying fallback...`
55
+ );
56
+ // Fallback: try to get from file
57
+ const file = state.file;
58
+ if (file) {
59
+ const sourceCode = (file as unknown as Record<string, unknown>).code as
60
+ | string
61
+ | undefined;
62
+ if (sourceCode) {
63
+ (state as Record<string, unknown>).originalSource = sourceCode;
64
+ console.info(
65
+ `[Babel Plugin WSX State] ✅ Stored original source from file (length: ${sourceCode.length})`
66
+ );
67
+ } else {
68
+ console.error(
69
+ `[Babel Plugin WSX State] ❌ ERROR: Could not get original source code from state.opts or state.file!`
70
+ );
71
+ }
72
+ } else {
73
+ console.error(
74
+ `[Babel Plugin WSX State] ❌ ERROR: state.file is undefined!`
75
+ );
76
+ }
77
+ }
78
+ },
79
+ },
34
80
  ClassDeclaration(path) {
35
81
  const classBody = path.node.body;
36
82
  const stateProperties: Array<{
@@ -46,6 +92,13 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
46
92
  `[Babel Plugin WSX State] Processing class ${path.node.id?.name || "anonymous"}, members: ${classBody.body.length}`
47
93
  );
48
94
 
95
+ // CRITICAL: Log all decorators at class level to debug
96
+ if (path.node.decorators && path.node.decorators.length > 0) {
97
+ console.info(
98
+ `[Babel Plugin WSX State] Class-level decorators: ${path.node.decorators.length}`
99
+ );
100
+ }
101
+
49
102
  for (const member of classBody.body) {
50
103
  // Debug: log member type
51
104
 
@@ -61,106 +114,319 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
61
114
  member.key.type === "Identifier"
62
115
  ) {
63
116
  // Debug: log all class properties
117
+ const propertyName = member.key.name;
118
+ const decoratorCount = member.decorators?.length || 0;
119
+ // CRITICAL: undefined value means no initial value
120
+ // TypeScript optional properties (count?) are converted to count: undefined
121
+ // We should treat undefined as no initial value
122
+ const hasValue =
123
+ !!member.value &&
124
+ !(
125
+ member.value.type === "Identifier" &&
126
+ (member.value as t.Identifier).name === "undefined"
127
+ ) &&
128
+ !(
129
+ member.value.type === "UnaryExpression" &&
130
+ member.value.operator === "void"
131
+ );
132
+ // Check if value is explicitly undefined
133
+ const isUndefined = !!(
134
+ member.value &&
135
+ ((member.value.type === "Identifier" &&
136
+ (member.value as t.Identifier).name === "undefined") ||
137
+ (member.value.type === "UnaryExpression" &&
138
+ member.value.operator === "void"))
139
+ );
140
+ const valueType = member.value ? member.value.type : "none";
141
+ const valueName =
142
+ member.value && member.value.type === "Identifier"
143
+ ? (member.value as t.Identifier).name
144
+ : "none";
145
+
64
146
  console.info(
65
- ` - Property: ${member.key.name}, decorators: ${member.decorators?.length || 0}, hasValue: ${!!member.value}`
147
+ ` - Property: ${propertyName}, decorators: ${decoratorCount}, hasValue: ${hasValue}, isUndefined: ${isUndefined}, value type: ${valueType}, value name: ${valueName}`
66
148
  );
67
149
 
68
- if (member.decorators && member.decorators.length > 0) {
69
- // Debug: log decorator names
70
- member.decorators.forEach((decorator) => {
71
- if (decorator.expression.type === "Identifier") {
72
- console.info(` Decorator: ${decorator.expression.name}`);
73
- } else if (
74
- decorator.expression.type === "CallExpression" &&
75
- decorator.expression.callee.type === "Identifier"
76
- ) {
77
- console.debug(
78
- ` Decorator: ${decorator.expression.callee.name}()`
150
+ // Wrap the entire detection logic in try-catch to pinpoint crash location
151
+ try {
152
+ // CRITICAL: If property has undefined value, check source code for @state decorator
153
+ // This handles the case where decorator was removed but property has undefined from optional syntax
154
+ if (isUndefined) {
155
+ const originalSource = (path.state as Record<string, unknown>)
156
+ ?.originalSource as string | undefined;
157
+ if (originalSource) {
158
+ // Escape special regex characters in property name
159
+ const escapedPropertyName = propertyName.replace(
160
+ /[.*+?^${}()|[\]\\]/g,
161
+ "\\$&"
79
162
  );
163
+ const propertyPattern = new RegExp(
164
+ `@state\\s+(?:private|protected|public)?\\s+${escapedPropertyName}\\s*[?;]`,
165
+ "m"
166
+ );
167
+ if (propertyPattern.test(originalSource)) {
168
+ console.error(
169
+ `[Babel Plugin WSX State] ERROR: Found @state decorator in source for property '${propertyName}' but value is undefined (from optional property syntax)!`
170
+ );
171
+ const error = path.buildCodeFrameError(
172
+ `@state decorator on property '${propertyName}' requires an initial value.\n` +
173
+ `\n` +
174
+ `The property has undefined value (from optional property syntax '${propertyName}?'), ` +
175
+ `but @state decorator needs a real value to decide whether to use useState (primitive) or reactive (object/array).\n` +
176
+ `\n` +
177
+ `Examples:\n` +
178
+ ` @state private ${propertyName} = ""; // for string\n` +
179
+ ` @state private ${propertyName} = 0; // for number\n` +
180
+ ` @state private ${propertyName} = {}; // for object\n` +
181
+ ` @state private ${propertyName} = []; // for array\n` +
182
+ ` @state private ${propertyName} = undefined; // explicitly undefined\n` +
183
+ `\n` +
184
+ `Current code: @state private ${propertyName}?;\n` +
185
+ `\n` +
186
+ `Fix: Change to '@state private ${propertyName} = undefined;' or provide a real initial value.`
187
+ );
188
+ console.error(
189
+ `[Babel Plugin WSX State] ERROR: ${error.message}`
190
+ );
191
+ throw error;
192
+ }
80
193
  }
81
- });
82
- }
83
-
84
- // Check if has @state decorator
85
- const hasStateDecorator = member.decorators?.some(
86
- (decorator: t.Decorator) => {
87
- if (
88
- decorator.expression.type === "Identifier" &&
89
- decorator.expression.name === "state"
90
- ) {
91
- return true;
92
- }
93
- if (
94
- decorator.expression.type === "CallExpression" &&
95
- decorator.expression.callee.type === "Identifier" &&
96
- decorator.expression.callee.name === "state"
97
- ) {
98
- return true;
99
- }
100
- return false;
101
194
  }
102
- );
103
195
 
104
- if (hasStateDecorator) {
105
- const key = member.key.name;
106
-
107
- // @state decorator requires an initial value
108
- // This is opinionated: we need the value to determine if it's primitive or object/array
109
- if (!member.value) {
110
- throw path.buildCodeFrameError(
111
- `@state decorator on property '${key}' requires an initial value.\n` +
112
- `\n` +
113
- `Examples:\n` +
114
- ` @state private ${key} = ""; // for string\n` +
115
- ` @state private ${key} = 0; // for number\n` +
116
- ` @state private ${key} = {}; // for object\n` +
117
- ` @state private ${key} = []; // for array\n` +
118
- ` @state private ${key} = undefined; // for optional\n`
196
+ // CRITICAL: Check ALL properties for @state decorator in source code
197
+ // Even if decorators array is empty (decorators: 0), the decorator might exist in source
198
+ // This is the main way to detect @state when decorators are removed before our plugin runs
199
+
200
+ // DEBUG: Check path.state structure
201
+ if (propertyName === "count") {
202
+ console.info(
203
+ `[Babel Plugin WSX State] DEBUG path.state keys for '${propertyName}': ${path.state ? Object.keys(path.state).join(", ") : "null"}`
204
+ );
205
+ console.info(
206
+ `[Babel Plugin WSX State] DEBUG path.state.originalSource type: ${path.state ? typeof (path.state as any).originalSource : "path.state is null"}`
119
207
  );
120
208
  }
121
209
 
122
- const initialValue = member.value as t.Expression;
210
+ const originalSource = (path.state as Record<string, unknown>)
211
+ ?.originalSource as string | undefined;
212
+
213
+ if (originalSource) {
214
+ // Check if there's a pattern like "@state private count?" or "@state private count;" in source
215
+ // Look for @state followed by private/protected/public, then property name, then optional ? or ;
216
+ // Escape special regex characters in property name
217
+ const escapedPropertyName = propertyName.replace(
218
+ /[.*+?^${}()|[\]\\]/g,
219
+ "\\$&"
220
+ );
221
+ const propertyPattern = new RegExp(
222
+ `@state\\s+(?:private|protected|public)?\\s+${escapedPropertyName}\\s*[?;]`,
223
+ "m"
224
+ );
225
+
226
+ const hasStateInSource = propertyPattern.test(originalSource);
227
+ if (hasStateInSource) {
228
+ console.info(
229
+ `[Babel Plugin WSX State] Found @state in source for property '${propertyName}' (decorators array was empty: ${decoratorCount})`
230
+ );
231
+ // Found @state in source but decorators array is empty
232
+ // Check if it has an initial value in source (not just undefined from TypeScript)
233
+ // Escape special regex characters in property name (already escaped above, reuse)
234
+ const hasInitialValueInSource = new RegExp(
235
+ `@state\\s+(?:private|protected|public)?\\s+${escapedPropertyName}\\s*=\\s*[^;]+`,
236
+ "m"
237
+ ).test(originalSource);
123
238
 
124
- // Determine if it's an object/array
125
- const isObject =
126
- initialValue.type === "ObjectExpression" ||
127
- initialValue.type === "ArrayExpression";
239
+ if (!hasInitialValueInSource) {
240
+ console.error(
241
+ `[Babel Plugin WSX State] ERROR: Found @state decorator in source for property '${propertyName}' but no initial value!`
242
+ );
128
243
 
129
- // Check if it's specifically an array
130
- const isArray = initialValue.type === "ArrayExpression";
244
+ const error = path.buildCodeFrameError(
245
+ `@state decorator on property '${propertyName}' requires an initial value.\n` +
246
+ `\n` +
247
+ `The @state decorator needs a real value to decide whether to use useState (primitive) or reactive (object/array).\n` +
248
+ `\n` +
249
+ `Examples:\n` +
250
+ ` @state private ${propertyName} = ""; // for string\n` +
251
+ ` @state private ${propertyName} = 0; // for number\n` +
252
+ ` @state private ${propertyName} = {}; // for object\n` +
253
+ ` @state private ${propertyName} = []; // for array\n` +
254
+ ` @state private ${propertyName} = undefined; // explicitly undefined\n` +
255
+ `\n` +
256
+ `Current code: @state private ${propertyName}?;\n` +
257
+ `\n` +
258
+ `Fix: Change to '@state private ${propertyName} = undefined;' or provide a real initial value.`
259
+ );
260
+ console.error(
261
+ `[Babel Plugin WSX State] ERROR: ${error.message}`
262
+ );
263
+ throw error;
264
+ }
265
+ }
266
+ } else {
267
+ // Log when originalSource is not available for debugging
268
+ if (propertyName === "count") {
269
+ console.warn(
270
+ `[Babel Plugin WSX State] WARNING: originalSource not available for property '${propertyName}'`
271
+ );
272
+ }
273
+ }
131
274
 
132
- stateProperties.push({
133
- key,
134
- initialValue,
135
- isObject,
136
- isArray, // Add isArray flag
137
- });
275
+ if (member.decorators && member.decorators.length > 0) {
276
+ // Debug: log decorator names
277
+ member.decorators.forEach((decorator) => {
278
+ if (decorator.expression.type === "Identifier") {
279
+ console.info(` Decorator: ${decorator.expression.name}`);
280
+ } else if (
281
+ decorator.expression.type === "CallExpression" &&
282
+ decorator.expression.callee.type === "Identifier"
283
+ ) {
284
+ console.info(
285
+ ` Decorator: ${decorator.expression.callee.name}()`
286
+ );
287
+ } else {
288
+ console.info(` Decorator: ${decorator.expression.type}`);
289
+ }
290
+ });
291
+ } else {
292
+ // Check if this property might have had @state decorator but it was removed
293
+ // This can happen if TypeScript preset or another plugin processed it first
294
+ // For now, we can't detect this case, but we log it for debugging
295
+ // In the future, we might need to check the original source or use a different approach
296
+ }
138
297
 
139
- // Remove @state decorator - but keep other decorators
140
- if (member.decorators) {
141
- member.decorators = member.decorators.filter(
142
- (decorator: t.Decorator) => {
298
+ // Check if has @state decorator
299
+ let hasStateDecorator = false;
300
+ try {
301
+ hasStateDecorator =
302
+ member.decorators?.some((decorator: t.Decorator) => {
143
303
  if (
144
304
  decorator.expression.type === "Identifier" &&
145
305
  decorator.expression.name === "state"
146
306
  ) {
147
- return false; // Remove @state decorator
307
+ return true;
148
308
  }
149
309
  if (
150
310
  decorator.expression.type === "CallExpression" &&
151
311
  decorator.expression.callee.type === "Identifier" &&
152
312
  decorator.expression.callee.name === "state"
153
313
  ) {
154
- return false; // Remove @state() decorator
314
+ return true;
155
315
  }
156
- return true; // Keep other decorators
157
- }
316
+ return false;
317
+ }) || false;
318
+ } catch (error) {
319
+ console.error(
320
+ `[Babel Plugin WSX State] ERROR in hasStateDecorator check for ${propertyName}: ${error}`
158
321
  );
322
+ throw error;
159
323
  }
160
324
 
161
- // Remove initial value - it will be set in constructor via this.reactive()
162
- // Keep the property declaration but without initial value
163
- member.value = undefined;
325
+ if (hasStateDecorator) {
326
+ const key = member.key.name;
327
+
328
+ // @state decorator requires an initial value
329
+ // This is opinionated: we need the value to determine if it's primitive or object/array
330
+ // CRITICAL: undefined value means no initial value
331
+ // TypeScript optional properties (count?) are converted to count: undefined
332
+ // We should treat undefined as no initial value
333
+ const hasInitialValue = !!(
334
+ member.value &&
335
+ !(
336
+ member.value.type === "Identifier" &&
337
+ (member.value as t.Identifier).name === "undefined"
338
+ ) &&
339
+ !(
340
+ member.value.type === "UnaryExpression" &&
341
+ member.value.operator === "void"
342
+ )
343
+ );
344
+
345
+ // DEBUG: Log hasInitialValue for count
346
+ if (key === "count") {
347
+ console.error(
348
+ `[Babel Plugin WSX State] DEBUG: hasInitialValue for 'count' = ${hasInitialValue}, type = ${typeof hasInitialValue}`
349
+ );
350
+ console.error(
351
+ `[Babel Plugin WSX State] DEBUG: member.value = ${member.value}, !hasInitialValue = ${!hasInitialValue}`
352
+ );
353
+ }
354
+
355
+ if (!hasInitialValue) {
356
+ // CRITICAL: This error should be thrown during build time
357
+ // If this error is not thrown, it means the plugin didn't detect the decorator
358
+ // or the file wasn't processed by Babel
359
+ const error = path.buildCodeFrameError(
360
+ `@state decorator on property '${key}' requires an initial value.\n` +
361
+ `\n` +
362
+ `Examples:\n` +
363
+ ` @state private ${key} = ""; // for string\n` +
364
+ ` @state private ${key} = 0; // for number\n` +
365
+ ` @state private ${key} = {}; // for object\n` +
366
+ ` @state private ${key} = []; // for array\n` +
367
+ ` @state private ${key} = undefined; // for optional\n` +
368
+ `\n` +
369
+ `Current code: @state private ${key};\n` +
370
+ `\n` +
371
+ `This error should be caught during build time. ` +
372
+ `If you see this at runtime, it means the Babel plugin did not process this file.`
373
+ );
374
+ console.error(
375
+ `[Babel Plugin WSX State] ERROR: ${error.message}`
376
+ );
377
+ throw error;
378
+ }
379
+
380
+ const initialValue = member.value as t.Expression;
381
+
382
+ // Determine if it's an object/array
383
+ const isObject =
384
+ initialValue.type === "ObjectExpression" ||
385
+ initialValue.type === "ArrayExpression";
386
+
387
+ // Check if it's specifically an array
388
+ const isArray = initialValue.type === "ArrayExpression";
389
+
390
+ stateProperties.push({
391
+ key,
392
+ initialValue,
393
+ isObject,
394
+ isArray, // Add isArray flag
395
+ });
396
+
397
+ // Remove @state decorator - but keep other decorators
398
+ if (member.decorators) {
399
+ member.decorators = member.decorators.filter(
400
+ (decorator: t.Decorator) => {
401
+ if (
402
+ decorator.expression.type === "Identifier" &&
403
+ decorator.expression.name === "state"
404
+ ) {
405
+ return false; // Remove @state decorator
406
+ }
407
+ if (
408
+ decorator.expression.type === "CallExpression" &&
409
+ decorator.expression.callee.type === "Identifier" &&
410
+ decorator.expression.callee.name === "state"
411
+ ) {
412
+ return false; // Remove @state() decorator
413
+ }
414
+ return true; // Keep other decorators
415
+ }
416
+ );
417
+ }
418
+
419
+ // Remove initial value - it will be set in constructor via this.reactive()
420
+ // Keep the property declaration but without initial value
421
+ member.value = undefined;
422
+ }
423
+ } catch (error) {
424
+ console.error(
425
+ `[Babel Plugin WSX State] CRASH in member processing for '${propertyName}':`,
426
+ error
427
+ );
428
+ console.error(`Stack trace:`, (error as Error).stack);
429
+ throw error;
164
430
  }
165
431
  }
166
432
  }
@@ -85,83 +85,93 @@ export function vitePluginWSXWithBabel(options: WSXPluginOptions = {}): Plugin {
85
85
  }
86
86
 
87
87
  // 2. Use Babel to preprocess decorators
88
- try {
89
- const babelResult = transformSync(transformedCode, {
90
- filename: id, // Pass the actual filename so Babel knows it's .wsx
91
- presets: [
92
- [
93
- "@babel/preset-typescript",
94
- {
95
- isTSX: true, // Enable JSX syntax
96
- allExtensions: true, // Process all extensions, including .wsx
97
- },
98
- ],
99
- ],
88
+ const babelResult = transformSync(transformedCode, {
89
+ filename: id, // Pass the actual filename so Babel knows it's .wsx
90
+ // CRITICAL: Configure parser to preserve decorators
91
+ // @babel/preset-typescript should preserve decorators by default,
92
+ // but we explicitly enable decorator parsing to be sure
93
+ parserOpts: {
100
94
  plugins: [
101
- // CRITICAL: Style injection plugin must run FIRST
102
- // This ensures _autoStyles property exists before state transformations
103
- ...(autoStyleInjection && cssFileExists
104
- ? [
105
- [
106
- babelPluginWSXStyle,
107
- {
108
- cssFileExists,
109
- cssFilePath,
110
- componentName,
111
- },
112
- ],
113
- ]
114
- : []),
115
- // Focus key generation plugin runs early to add data-wsx-key attributes
116
- // This must run before JSX is transformed to h() calls
117
- babelPluginWSXFocus,
118
- // State decorator transformation runs after style injection
95
+ ["decorators", { decoratorsBeforeExport: true }], // Stage 3 decorators
96
+ "typescript",
97
+ "jsx",
98
+ ],
99
+ },
100
+ presets: [
101
+ [
102
+ "@babel/preset-typescript",
103
+ {
104
+ isTSX: true, // Enable JSX syntax
105
+ allExtensions: true, // Process all extensions, including .wsx
106
+ // CRITICAL: onlyRemoveTypeImports only affects import statements
107
+ // Decorators are preserved by default in @babel/preset-typescript
108
+ // They are only removed if we explicitly configure it
109
+ onlyRemoveTypeImports: false, // Remove all type-only imports
110
+ },
111
+ ],
112
+ ],
113
+ plugins: [
114
+ // CRITICAL: Decorator plugin must run FIRST to parse decorators correctly
115
+ // This ensures decorators are properly parsed before our custom plugins try to process them
116
+ // However, we need to use a custom visitor that doesn't transform decorators yet
117
+ // Actually, we should NOT run decorator plugin first because it transforms decorators
118
+ // Instead, we rely on TypeScript preset to parse but not transform decorators
119
+ // CRITICAL: Style injection plugin must run FIRST
120
+ // This ensures _autoStyles property exists before state transformations
121
+ ...(autoStyleInjection && cssFileExists
122
+ ? [
123
+ [
124
+ babelPluginWSXStyle,
125
+ {
126
+ cssFileExists,
127
+ cssFilePath,
128
+ componentName,
129
+ },
130
+ ],
131
+ ]
132
+ : []),
133
+ // Focus key generation plugin runs early to add data-wsx-key attributes
134
+ // This must run before JSX is transformed to h() calls
135
+ babelPluginWSXFocus,
136
+ // CRITICAL: State decorator transformation must run BEFORE @babel/plugin-proposal-decorators
137
+ // This allows the plugin to detect @state decorators in their original form and throw errors if needed
138
+ // The plugin removes @state decorators after processing, so the decorator plugin won't see them
139
+ [
119
140
  babelPluginWSXState,
120
- [
121
- "@babel/plugin-proposal-decorators",
122
- {
123
- version: "2023-05",
124
- decoratorsBeforeExport: true,
125
- },
126
- ],
127
- [
128
- "@babel/plugin-proposal-class-properties",
129
- {
130
- loose: false,
131
- },
132
- ],
133
- "@babel/plugin-transform-class-static-block", // Support static class blocks
141
+ {
142
+ // Pass ORIGINAL source code (before JSX import injection) to plugin
143
+ // This ensures we can detect @state decorators even if they're removed by TypeScript preset
144
+ originalSource: code, // Use original code, not transformedCode
145
+ },
134
146
  ],
135
- // parserOpts not needed - @babel/preset-typescript and plugins handle it
136
- });
137
-
138
- if (babelResult && babelResult.code) {
139
- transformedCode = babelResult.code;
140
- } else {
141
- // Babel returned no code - critical error
142
- throw new Error(
143
- `[WSX Plugin] Babel transform returned no code for ${id}. ` +
144
- `@state decorators will NOT be processed and will cause runtime errors. ` +
145
- `Please check Babel configuration and plugin setup.`
146
- );
147
- }
148
- } catch (error) {
149
- // Babel transform failed - this is critical
150
- // If Babel fails, @state decorators won't be processed and will cause runtime errors
151
- // Don't silently fallback - throw error to make it obvious
152
- const errorMessage = error instanceof Error ? error.message : String(error);
147
+ // Decorator plugin runs after our custom plugins
148
+ // This transforms remaining decorators (like @autoRegister) to runtime calls
149
+ [
150
+ "@babel/plugin-proposal-decorators",
151
+ {
152
+ version: "2023-05",
153
+ decoratorsBeforeExport: true,
154
+ },
155
+ ],
156
+ [
157
+ "@babel/plugin-proposal-class-properties",
158
+ {
159
+ loose: false,
160
+ },
161
+ ],
162
+ "@babel/plugin-transform-class-static-block", // Support static class blocks
163
+ ],
164
+ // parserOpts not needed - @babel/preset-typescript and plugins handle it
165
+ });
166
+
167
+ if (babelResult && babelResult.code) {
168
+ transformedCode = babelResult.code;
169
+ } else {
170
+ // Babel returned no code - critical error
153
171
  throw new Error(
154
- `[WSX Plugin] Babel transform failed for ${id}. ` +
172
+ `[WSX Plugin] Babel transform returned no code for ${id}. ` +
155
173
  `@state decorators will NOT be processed and will cause runtime errors. ` +
156
- `\n\n` +
157
- `Babel Error: ${errorMessage}` +
158
- `\n\n` +
159
- `This usually means:` +
160
- `\n1. Babel plugins are not installed correctly` +
161
- `\n2. Babel configuration is invalid` +
162
- `\n3. File contains syntax errors that Babel cannot parse` +
163
- `\n\n` +
164
- `Please fix the Babel error above before continuing.`
174
+ `Please check Babel configuration and plugin setup.`
165
175
  );
166
176
  }
167
177