@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.
- 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
package/dist/index.js
CHANGED
|
@@ -47,68 +47,255 @@ function babelPluginWSXState() {
|
|
|
47
47
|
return {
|
|
48
48
|
name: "babel-plugin-wsx-state",
|
|
49
49
|
visitor: {
|
|
50
|
+
Program: {
|
|
51
|
+
enter(_path, state) {
|
|
52
|
+
console.info(
|
|
53
|
+
`[Babel Plugin WSX State] DEBUG: state.opts keys = ${Object.keys(state.opts || {}).join(", ")}`
|
|
54
|
+
);
|
|
55
|
+
console.info(
|
|
56
|
+
`[Babel Plugin WSX State] DEBUG: state.opts = ${JSON.stringify(state.opts).substring(0, 200)}`
|
|
57
|
+
);
|
|
58
|
+
const opts = state.opts;
|
|
59
|
+
if (opts?.originalSource) {
|
|
60
|
+
state.originalSource = opts.originalSource;
|
|
61
|
+
console.info(
|
|
62
|
+
`[Babel Plugin WSX State] \u2705 Stored original source from options (length: ${String(opts.originalSource).length})`
|
|
63
|
+
);
|
|
64
|
+
} else {
|
|
65
|
+
console.warn(
|
|
66
|
+
`[Babel Plugin WSX State] \u26A0\uFE0F state.opts.originalSource not found, trying fallback...`
|
|
67
|
+
);
|
|
68
|
+
const file = state.file;
|
|
69
|
+
if (file) {
|
|
70
|
+
const sourceCode = file.code;
|
|
71
|
+
if (sourceCode) {
|
|
72
|
+
state.originalSource = sourceCode;
|
|
73
|
+
console.info(
|
|
74
|
+
`[Babel Plugin WSX State] \u2705 Stored original source from file (length: ${sourceCode.length})`
|
|
75
|
+
);
|
|
76
|
+
} else {
|
|
77
|
+
console.error(
|
|
78
|
+
`[Babel Plugin WSX State] \u274C ERROR: Could not get original source code from state.opts or state.file!`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
console.error(
|
|
83
|
+
`[Babel Plugin WSX State] \u274C ERROR: state.file is undefined!`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
50
89
|
ClassDeclaration(path) {
|
|
51
90
|
const classBody = path.node.body;
|
|
52
91
|
const stateProperties = [];
|
|
53
92
|
console.info(
|
|
54
93
|
`[Babel Plugin WSX State] Processing class ${path.node.id?.name || "anonymous"}, members: ${classBody.body.length}`
|
|
55
94
|
);
|
|
95
|
+
if (path.node.decorators && path.node.decorators.length > 0) {
|
|
96
|
+
console.info(
|
|
97
|
+
`[Babel Plugin WSX State] Class-level decorators: ${path.node.decorators.length}`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
56
100
|
for (const member of classBody.body) {
|
|
57
101
|
console.info(
|
|
58
102
|
` - Member type: ${member.type}, key: ${member.type === "ClassProperty" || member.type === "ClassPrivateProperty" ? member.key?.name : "N/A"}`
|
|
59
103
|
);
|
|
60
104
|
if ((member.type === "ClassProperty" || member.type === "ClassPrivateProperty") && member.key.type === "Identifier") {
|
|
105
|
+
const propertyName = member.key.name;
|
|
106
|
+
const decoratorCount = member.decorators?.length || 0;
|
|
107
|
+
const hasValue = !!member.value && !(member.value.type === "Identifier" && member.value.name === "undefined") && !(member.value.type === "UnaryExpression" && member.value.operator === "void");
|
|
108
|
+
const isUndefined = !!(member.value && (member.value.type === "Identifier" && member.value.name === "undefined" || member.value.type === "UnaryExpression" && member.value.operator === "void"));
|
|
109
|
+
const valueType = member.value ? member.value.type : "none";
|
|
110
|
+
const valueName = member.value && member.value.type === "Identifier" ? member.value.name : "none";
|
|
61
111
|
console.info(
|
|
62
|
-
` - Property: ${
|
|
112
|
+
` - Property: ${propertyName}, decorators: ${decoratorCount}, hasValue: ${hasValue}, isUndefined: ${isUndefined}, value type: ${valueType}, value name: ${valueName}`
|
|
63
113
|
);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
114
|
+
try {
|
|
115
|
+
if (isUndefined) {
|
|
116
|
+
const originalSource2 = path.state?.originalSource;
|
|
117
|
+
if (originalSource2) {
|
|
118
|
+
const propertyPattern = new RegExp(
|
|
119
|
+
`@state\\s+(?:private|protected|public)?\\s+${propertyName}\\s*[?;]`,
|
|
120
|
+
"m"
|
|
71
121
|
);
|
|
122
|
+
if (propertyPattern.test(originalSource2)) {
|
|
123
|
+
console.error(
|
|
124
|
+
`[Babel Plugin WSX State] ERROR: Found @state decorator in source for property '${propertyName}' but value is undefined (from optional property syntax)!`
|
|
125
|
+
);
|
|
126
|
+
const error = path.buildCodeFrameError(
|
|
127
|
+
`@state decorator on property '${propertyName}' requires an initial value.
|
|
128
|
+
|
|
129
|
+
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).
|
|
130
|
+
|
|
131
|
+
Examples:
|
|
132
|
+
@state private ${propertyName} = ""; // for string
|
|
133
|
+
@state private ${propertyName} = 0; // for number
|
|
134
|
+
@state private ${propertyName} = {}; // for object
|
|
135
|
+
@state private ${propertyName} = []; // for array
|
|
136
|
+
@state private ${propertyName} = undefined; // explicitly undefined
|
|
137
|
+
|
|
138
|
+
Current code: @state private ${propertyName}?;
|
|
139
|
+
|
|
140
|
+
Fix: Change to '@state private ${propertyName} = undefined;' or provide a real initial value.`
|
|
141
|
+
);
|
|
142
|
+
console.error(
|
|
143
|
+
`[Babel Plugin WSX State] ERROR: ${error.message}`
|
|
144
|
+
);
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
72
147
|
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
148
|
+
}
|
|
149
|
+
if (propertyName === "count") {
|
|
150
|
+
console.info(
|
|
151
|
+
`[Babel Plugin WSX State] DEBUG path.state keys for '${propertyName}': ${path.state ? Object.keys(path.state).join(", ") : "null"}`
|
|
152
|
+
);
|
|
153
|
+
console.info(
|
|
154
|
+
`[Babel Plugin WSX State] DEBUG path.state.originalSource type: ${path.state ? typeof path.state.originalSource : "path.state is null"}`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
const originalSource = path.state?.originalSource;
|
|
158
|
+
if (originalSource) {
|
|
159
|
+
const propertyPattern = new RegExp(
|
|
160
|
+
`@state\\s+(?:private|protected|public)?\\s+${propertyName}\\s*[?;]`,
|
|
161
|
+
"m"
|
|
162
|
+
);
|
|
163
|
+
const hasStateInSource = propertyPattern.test(originalSource);
|
|
164
|
+
if (hasStateInSource) {
|
|
165
|
+
console.info(
|
|
166
|
+
`[Babel Plugin WSX State] Found @state in source for property '${propertyName}' (decorators array was empty: ${decoratorCount})`
|
|
167
|
+
);
|
|
168
|
+
const hasInitialValueInSource = new RegExp(
|
|
169
|
+
`@state\\s+(?:private|protected|public)?\\s+${propertyName}\\s*=\\s*[^;]+`,
|
|
170
|
+
"m"
|
|
171
|
+
).test(originalSource);
|
|
172
|
+
if (!hasInitialValueInSource) {
|
|
173
|
+
console.error(
|
|
174
|
+
`[Babel Plugin WSX State] ERROR: Found @state decorator in source for property '${propertyName}' but no initial value!`
|
|
175
|
+
);
|
|
176
|
+
const error = path.buildCodeFrameError(
|
|
177
|
+
`@state decorator on property '${propertyName}' requires an initial value.
|
|
178
|
+
|
|
179
|
+
The @state decorator needs a real value to decide whether to use useState (primitive) or reactive (object/array).
|
|
180
|
+
|
|
181
|
+
Examples:
|
|
182
|
+
@state private ${propertyName} = ""; // for string
|
|
183
|
+
@state private ${propertyName} = 0; // for number
|
|
184
|
+
@state private ${propertyName} = {}; // for object
|
|
185
|
+
@state private ${propertyName} = []; // for array
|
|
186
|
+
@state private ${propertyName} = undefined; // explicitly undefined
|
|
187
|
+
|
|
188
|
+
Current code: @state private ${propertyName}?;
|
|
189
|
+
|
|
190
|
+
Fix: Change to '@state private ${propertyName} = undefined;' or provide a real initial value.`
|
|
191
|
+
);
|
|
192
|
+
console.error(
|
|
193
|
+
`[Babel Plugin WSX State] ERROR: ${error.message}`
|
|
194
|
+
);
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
79
197
|
}
|
|
80
|
-
|
|
81
|
-
|
|
198
|
+
} else {
|
|
199
|
+
if (propertyName === "count") {
|
|
200
|
+
console.warn(
|
|
201
|
+
`[Babel Plugin WSX State] WARNING: originalSource not available for property '${propertyName}'`
|
|
202
|
+
);
|
|
82
203
|
}
|
|
83
|
-
return false;
|
|
84
204
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
205
|
+
if (member.decorators && member.decorators.length > 0) {
|
|
206
|
+
member.decorators.forEach((decorator) => {
|
|
207
|
+
if (decorator.expression.type === "Identifier") {
|
|
208
|
+
console.info(` Decorator: ${decorator.expression.name}`);
|
|
209
|
+
} else if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier") {
|
|
210
|
+
console.info(
|
|
211
|
+
` Decorator: ${decorator.expression.callee.name}()`
|
|
212
|
+
);
|
|
213
|
+
} else {
|
|
214
|
+
console.info(` Decorator: ${decorator.expression.type}`);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
} else {
|
|
218
|
+
}
|
|
219
|
+
let hasStateDecorator = false;
|
|
220
|
+
try {
|
|
221
|
+
hasStateDecorator = member.decorators?.some(
|
|
100
222
|
(decorator) => {
|
|
101
223
|
if (decorator.expression.type === "Identifier" && decorator.expression.name === "state") {
|
|
102
|
-
return
|
|
224
|
+
return true;
|
|
103
225
|
}
|
|
104
226
|
if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier" && decorator.expression.callee.name === "state") {
|
|
105
|
-
return
|
|
227
|
+
return true;
|
|
106
228
|
}
|
|
107
|
-
return
|
|
229
|
+
return false;
|
|
108
230
|
}
|
|
231
|
+
) || false;
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error(
|
|
234
|
+
`[Babel Plugin WSX State] ERROR in hasStateDecorator check for ${propertyName}: ${error}`
|
|
109
235
|
);
|
|
236
|
+
throw error;
|
|
110
237
|
}
|
|
111
|
-
|
|
238
|
+
if (hasStateDecorator) {
|
|
239
|
+
const key = member.key.name;
|
|
240
|
+
const hasInitialValue = !!(member.value && !(member.value.type === "Identifier" && member.value.name === "undefined") && !(member.value.type === "UnaryExpression" && member.value.operator === "void"));
|
|
241
|
+
if (key === "count") {
|
|
242
|
+
console.error(
|
|
243
|
+
`[Babel Plugin WSX State] DEBUG: hasInitialValue for 'count' = ${hasInitialValue}, type = ${typeof hasInitialValue}`
|
|
244
|
+
);
|
|
245
|
+
console.error(
|
|
246
|
+
`[Babel Plugin WSX State] DEBUG: member.value = ${member.value}, !hasInitialValue = ${!hasInitialValue}`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
if (!hasInitialValue) {
|
|
250
|
+
const error = path.buildCodeFrameError(
|
|
251
|
+
`@state decorator on property '${key}' requires an initial value.
|
|
252
|
+
|
|
253
|
+
Examples:
|
|
254
|
+
@state private ${key} = ""; // for string
|
|
255
|
+
@state private ${key} = 0; // for number
|
|
256
|
+
@state private ${key} = {}; // for object
|
|
257
|
+
@state private ${key} = []; // for array
|
|
258
|
+
@state private ${key} = undefined; // for optional
|
|
259
|
+
|
|
260
|
+
Current code: @state private ${key};
|
|
261
|
+
|
|
262
|
+
This error should be caught during build time. If you see this at runtime, it means the Babel plugin did not process this file.`
|
|
263
|
+
);
|
|
264
|
+
console.error(`[Babel Plugin WSX State] ERROR: ${error.message}`);
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
const initialValue = member.value;
|
|
268
|
+
const isObject = initialValue.type === "ObjectExpression" || initialValue.type === "ArrayExpression";
|
|
269
|
+
const isArray = initialValue.type === "ArrayExpression";
|
|
270
|
+
stateProperties.push({
|
|
271
|
+
key,
|
|
272
|
+
initialValue,
|
|
273
|
+
isObject,
|
|
274
|
+
isArray
|
|
275
|
+
// Add isArray flag
|
|
276
|
+
});
|
|
277
|
+
if (member.decorators) {
|
|
278
|
+
member.decorators = member.decorators.filter(
|
|
279
|
+
(decorator) => {
|
|
280
|
+
if (decorator.expression.type === "Identifier" && decorator.expression.name === "state") {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier" && decorator.expression.callee.name === "state") {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
member.value = void 0;
|
|
291
|
+
}
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error(
|
|
294
|
+
`[Babel Plugin WSX State] CRASH in member processing for '${propertyName}':`,
|
|
295
|
+
error
|
|
296
|
+
);
|
|
297
|
+
console.error(`Stack trace:`, error.stack);
|
|
298
|
+
throw error;
|
|
112
299
|
}
|
|
113
300
|
}
|
|
114
301
|
}
|
|
@@ -572,18 +759,39 @@ function vitePluginWSXWithBabel(options = {}) {
|
|
|
572
759
|
const babelResult = (0, import_core.transformSync)(transformedCode, {
|
|
573
760
|
filename: id,
|
|
574
761
|
// Pass the actual filename so Babel knows it's .wsx
|
|
762
|
+
// CRITICAL: Configure parser to preserve decorators
|
|
763
|
+
// @babel/preset-typescript should preserve decorators by default,
|
|
764
|
+
// but we explicitly enable decorator parsing to be sure
|
|
765
|
+
parserOpts: {
|
|
766
|
+
plugins: [
|
|
767
|
+
["decorators", { decoratorsBeforeExport: true }],
|
|
768
|
+
// Stage 3 decorators
|
|
769
|
+
"typescript",
|
|
770
|
+
"jsx"
|
|
771
|
+
]
|
|
772
|
+
},
|
|
575
773
|
presets: [
|
|
576
774
|
[
|
|
577
775
|
"@babel/preset-typescript",
|
|
578
776
|
{
|
|
579
777
|
isTSX: true,
|
|
580
778
|
// Enable JSX syntax
|
|
581
|
-
allExtensions: true
|
|
779
|
+
allExtensions: true,
|
|
582
780
|
// Process all extensions, including .wsx
|
|
781
|
+
// CRITICAL: onlyRemoveTypeImports only affects import statements
|
|
782
|
+
// Decorators are preserved by default in @babel/preset-typescript
|
|
783
|
+
// They are only removed if we explicitly configure it
|
|
784
|
+
onlyRemoveTypeImports: false
|
|
785
|
+
// Remove all type-only imports
|
|
583
786
|
}
|
|
584
787
|
]
|
|
585
788
|
],
|
|
586
789
|
plugins: [
|
|
790
|
+
// CRITICAL: Decorator plugin must run FIRST to parse decorators correctly
|
|
791
|
+
// This ensures decorators are properly parsed before our custom plugins try to process them
|
|
792
|
+
// However, we need to use a custom visitor that doesn't transform decorators yet
|
|
793
|
+
// Actually, we should NOT run decorator plugin first because it transforms decorators
|
|
794
|
+
// Instead, we rely on TypeScript preset to parse but not transform decorators
|
|
587
795
|
// CRITICAL: Style injection plugin must run FIRST
|
|
588
796
|
// This ensures _autoStyles property exists before state transformations
|
|
589
797
|
...autoStyleInjection && cssFileExists ? [
|
|
@@ -599,8 +807,20 @@ function vitePluginWSXWithBabel(options = {}) {
|
|
|
599
807
|
// Focus key generation plugin runs early to add data-wsx-key attributes
|
|
600
808
|
// This must run before JSX is transformed to h() calls
|
|
601
809
|
babelPluginWSXFocus,
|
|
602
|
-
// State decorator transformation
|
|
603
|
-
|
|
810
|
+
// CRITICAL: State decorator transformation must run BEFORE @babel/plugin-proposal-decorators
|
|
811
|
+
// This allows the plugin to detect @state decorators in their original form and throw errors if needed
|
|
812
|
+
// The plugin removes @state decorators after processing, so the decorator plugin won't see them
|
|
813
|
+
[
|
|
814
|
+
babelPluginWSXState,
|
|
815
|
+
{
|
|
816
|
+
// Pass ORIGINAL source code (before JSX import injection) to plugin
|
|
817
|
+
// This ensures we can detect @state decorators even if they're removed by TypeScript preset
|
|
818
|
+
originalSource: code
|
|
819
|
+
// Use original code, not transformedCode
|
|
820
|
+
}
|
|
821
|
+
],
|
|
822
|
+
// Decorator plugin runs after our custom plugins
|
|
823
|
+
// This transforms remaining decorators (like @autoRegister) to runtime calls
|
|
604
824
|
[
|
|
605
825
|
"@babel/plugin-proposal-decorators",
|
|
606
826
|
{
|
|
@@ -621,8 +841,13 @@ function vitePluginWSXWithBabel(options = {}) {
|
|
|
621
841
|
});
|
|
622
842
|
if (babelResult && babelResult.code) {
|
|
623
843
|
transformedCode = babelResult.code;
|
|
844
|
+
} else {
|
|
845
|
+
throw new Error(
|
|
846
|
+
`[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.`
|
|
847
|
+
);
|
|
624
848
|
}
|
|
625
|
-
} catch {
|
|
849
|
+
} catch (error) {
|
|
850
|
+
throw error;
|
|
626
851
|
}
|
|
627
852
|
const hasJSXAfterBabel = transformedCode.includes('from "@wsxjs/wsx-core"') && (new RegExp(`[{,]\\s*${jsxFactory}\\s*[},]`).test(transformedCode) || new RegExp(`[{,]\\s*${jsxFragment}\\s*[},]`).test(transformedCode));
|
|
628
853
|
if ((transformedCode.includes("<") || transformedCode.includes("Fragment")) && !hasJSXAfterBabel) {
|