@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.js +265 -40
- package/dist/index.mjs +265 -40
- package/package.json +2 -2
- package/src/babel-plugin-wsx-state.ts +338 -72
- package/src/vite-plugin-wsx-babel.ts +82 -72
|
@@ -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: ${
|
|
147
|
+
` - Property: ${propertyName}, decorators: ${decoratorCount}, hasValue: ${hasValue}, isUndefined: ${isUndefined}, value type: ${valueType}, value name: ${valueName}`
|
|
66
148
|
);
|
|
67
149
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
130
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
|
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
|
|
314
|
+
return true;
|
|
155
315
|
}
|
|
156
|
-
return
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
|
172
|
+
`[WSX Plugin] Babel transform returned no code for ${id}. ` +
|
|
155
173
|
`@state decorators will NOT be processed and will cause runtime errors. ` +
|
|
156
|
-
|
|
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
|
|