@wsxjs/wsx-vite-plugin 0.0.23 → 0.0.25
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 +91 -59
- package/dist/index.mjs +91 -59
- package/package.json +2 -2
- package/src/babel-plugin-wsx-focus.ts +5 -0
- package/src/babel-plugin-wsx-state.ts +86 -59
- package/src/babel-plugin-wsx-style.ts +12 -7
- package/src/vite-plugin-wsx-babel.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -49,30 +49,40 @@ function babelPluginWSXState() {
|
|
|
49
49
|
visitor: {
|
|
50
50
|
Program: {
|
|
51
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
52
|
const opts = state.opts;
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
const debug = !!opts?.debug;
|
|
54
|
+
state.showDebugLogs = debug;
|
|
55
|
+
if (debug) {
|
|
61
56
|
console.info(
|
|
62
|
-
`[Babel Plugin WSX State]
|
|
57
|
+
`[Babel Plugin WSX State] DEBUG: state.opts keys = ${Object.keys(state.opts || {}).join(", ")}`
|
|
63
58
|
);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
`[Babel Plugin WSX State] \u26A0\uFE0F state.opts.originalSource not found, trying fallback...`
|
|
59
|
+
console.info(
|
|
60
|
+
`[Babel Plugin WSX State] DEBUG: state.opts = ${JSON.stringify(state.opts).substring(0, 200)}`
|
|
67
61
|
);
|
|
62
|
+
}
|
|
63
|
+
if (opts?.originalSource) {
|
|
64
|
+
state.originalSource = opts.originalSource;
|
|
65
|
+
if (debug) {
|
|
66
|
+
console.info(
|
|
67
|
+
`[Babel Plugin WSX State] \u2705 Stored original source from options (length: ${String(opts.originalSource).length})`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
if (debug) {
|
|
72
|
+
console.warn(
|
|
73
|
+
`[Babel Plugin WSX State] \u26A0\uFE0F state.opts.originalSource not found, trying fallback...`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
68
76
|
const file = state.file;
|
|
69
77
|
if (file) {
|
|
70
78
|
const sourceCode = file.code;
|
|
71
79
|
if (sourceCode) {
|
|
72
80
|
state.originalSource = sourceCode;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
if (debug) {
|
|
82
|
+
console.info(
|
|
83
|
+
`[Babel Plugin WSX State] \u2705 Stored original source from file (length: ${sourceCode.length})`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
76
86
|
} else {
|
|
77
87
|
console.error(
|
|
78
88
|
`[Babel Plugin WSX State] \u274C ERROR: Could not get original source code from state.opts or state.file!`
|
|
@@ -86,21 +96,29 @@ function babelPluginWSXState() {
|
|
|
86
96
|
}
|
|
87
97
|
}
|
|
88
98
|
},
|
|
89
|
-
ClassDeclaration(path) {
|
|
99
|
+
ClassDeclaration(path, state) {
|
|
100
|
+
const debug = !!state.showDebugLogs;
|
|
101
|
+
const logInfo = (msg) => {
|
|
102
|
+
if (debug) console.info(msg);
|
|
103
|
+
};
|
|
90
104
|
const classBody = path.node.body;
|
|
91
105
|
const stateProperties = [];
|
|
92
|
-
|
|
93
|
-
|
|
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}`
|
|
106
|
+
if (debug) {
|
|
107
|
+
logInfo(
|
|
108
|
+
`[Babel Plugin WSX State] Processing class ${path.node.id?.name || "anonymous"}, members: ${classBody.body.length}`
|
|
98
109
|
);
|
|
110
|
+
if (path.node.decorators && path.node.decorators.length > 0) {
|
|
111
|
+
logInfo(
|
|
112
|
+
`[Babel Plugin WSX State] Class-level decorators: ${path.node.decorators.length}`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
99
115
|
}
|
|
100
116
|
for (const member of classBody.body) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
117
|
+
if (debug) {
|
|
118
|
+
logInfo(
|
|
119
|
+
` - Member type: ${member.type}, key: ${member.type === "ClassProperty" || member.type === "ClassPrivateProperty" ? member.key?.name : "N/A"}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
104
122
|
if ((member.type === "ClassProperty" || member.type === "ClassPrivateProperty") && member.key.type === "Identifier") {
|
|
105
123
|
const propertyName = member.key.name;
|
|
106
124
|
const decoratorCount = member.decorators?.length || 0;
|
|
@@ -108,12 +126,14 @@ function babelPluginWSXState() {
|
|
|
108
126
|
const isUndefined = !!(member.value && (member.value.type === "Identifier" && member.value.name === "undefined" || member.value.type === "UnaryExpression" && member.value.operator === "void"));
|
|
109
127
|
const valueType = member.value ? member.value.type : "none";
|
|
110
128
|
const valueName = member.value && member.value.type === "Identifier" ? member.value.name : "none";
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
129
|
+
if (debug) {
|
|
130
|
+
logInfo(
|
|
131
|
+
` - Property: ${propertyName}, decorators: ${decoratorCount}, hasValue: ${hasValue}, isUndefined: ${isUndefined}, value type: ${valueType}, value name: ${valueName}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
114
134
|
try {
|
|
115
135
|
if (isUndefined) {
|
|
116
|
-
const originalSource2 =
|
|
136
|
+
const originalSource2 = state?.originalSource;
|
|
117
137
|
if (originalSource2) {
|
|
118
138
|
const escapedPropertyName = propertyName.replace(
|
|
119
139
|
/[.*+?^${}()|[\]\\]/g,
|
|
@@ -150,15 +170,15 @@ Fix: Change to '@state private ${propertyName} = undefined;' or provide a real i
|
|
|
150
170
|
}
|
|
151
171
|
}
|
|
152
172
|
}
|
|
153
|
-
if (propertyName === "count") {
|
|
154
|
-
|
|
155
|
-
`[Babel Plugin WSX State] DEBUG
|
|
173
|
+
if (propertyName === "count" && debug) {
|
|
174
|
+
logInfo(
|
|
175
|
+
`[Babel Plugin WSX State] DEBUG state keys for '${propertyName}': ${state ? Object.keys(state).join(", ") : "null"}`
|
|
156
176
|
);
|
|
157
|
-
|
|
158
|
-
`[Babel Plugin WSX State] DEBUG
|
|
177
|
+
logInfo(
|
|
178
|
+
`[Babel Plugin WSX State] DEBUG state.originalSource type: ${state ? typeof state.originalSource : "state is null"}`
|
|
159
179
|
);
|
|
160
180
|
}
|
|
161
|
-
const originalSource =
|
|
181
|
+
const originalSource = state?.originalSource;
|
|
162
182
|
if (originalSource) {
|
|
163
183
|
const escapedPropertyName = propertyName.replace(
|
|
164
184
|
/[.*+?^${}()|[\]\\]/g,
|
|
@@ -170,9 +190,11 @@ Fix: Change to '@state private ${propertyName} = undefined;' or provide a real i
|
|
|
170
190
|
);
|
|
171
191
|
const hasStateInSource = propertyPattern.test(originalSource);
|
|
172
192
|
if (hasStateInSource) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
193
|
+
if (debug) {
|
|
194
|
+
logInfo(
|
|
195
|
+
`[Babel Plugin WSX State] Found @state in source for property '${propertyName}' (decorators array was empty: ${decoratorCount})`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
176
198
|
const hasInitialValueInSource = new RegExp(
|
|
177
199
|
`@state\\s+(?:private|protected|public)?\\s+${escapedPropertyName}\\s*=\\s*[^;]+`,
|
|
178
200
|
"m"
|
|
@@ -204,24 +226,26 @@ Fix: Change to '@state private ${propertyName} = undefined;' or provide a real i
|
|
|
204
226
|
}
|
|
205
227
|
}
|
|
206
228
|
} else {
|
|
207
|
-
if (propertyName === "count") {
|
|
229
|
+
if (propertyName === "count" && debug) {
|
|
208
230
|
console.warn(
|
|
209
231
|
`[Babel Plugin WSX State] WARNING: originalSource not available for property '${propertyName}'`
|
|
210
232
|
);
|
|
211
233
|
}
|
|
212
234
|
}
|
|
213
235
|
if (member.decorators && member.decorators.length > 0) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
236
|
+
if (debug) {
|
|
237
|
+
member.decorators.forEach((decorator) => {
|
|
238
|
+
if (decorator.expression.type === "Identifier") {
|
|
239
|
+
logInfo(` Decorator: ${decorator.expression.name}`);
|
|
240
|
+
} else if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier") {
|
|
241
|
+
logInfo(
|
|
242
|
+
` Decorator: ${decorator.expression.callee.name}()`
|
|
243
|
+
);
|
|
244
|
+
} else {
|
|
245
|
+
logInfo(` Decorator: ${decorator.expression.type}`);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
225
249
|
} else {
|
|
226
250
|
}
|
|
227
251
|
let hasStateDecorator = false;
|
|
@@ -244,7 +268,7 @@ Fix: Change to '@state private ${propertyName} = undefined;' or provide a real i
|
|
|
244
268
|
if (hasStateDecorator) {
|
|
245
269
|
const key = member.key.name;
|
|
246
270
|
const hasInitialValue = !!(member.value && !(member.value.type === "Identifier" && member.value.name === "undefined") && !(member.value.type === "UnaryExpression" && member.value.operator === "void"));
|
|
247
|
-
if (key === "count") {
|
|
271
|
+
if (key === "count" && debug) {
|
|
248
272
|
console.error(
|
|
249
273
|
`[Babel Plugin WSX State] DEBUG: hasInitialValue for 'count' = ${hasInitialValue}, type = ${typeof hasInitialValue}`
|
|
250
274
|
);
|
|
@@ -549,19 +573,23 @@ function babelPluginWSXStyle() {
|
|
|
549
573
|
name: "babel-plugin-wsx-style",
|
|
550
574
|
visitor: {
|
|
551
575
|
Program(path, state) {
|
|
552
|
-
const { cssFileExists, cssFilePath, componentName } = state.opts;
|
|
576
|
+
const { cssFileExists, cssFilePath, componentName, debug } = state.opts;
|
|
553
577
|
if (!cssFileExists) {
|
|
554
578
|
return;
|
|
555
579
|
}
|
|
556
580
|
if (hasStylesImport(path.node)) {
|
|
581
|
+
if (debug) {
|
|
582
|
+
console.info(
|
|
583
|
+
`[Babel Plugin WSX Style] Skipping ${componentName}: styles already manually imported`
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (debug) {
|
|
557
589
|
console.info(
|
|
558
|
-
`[Babel Plugin WSX Style]
|
|
590
|
+
`[Babel Plugin WSX Style] Injecting CSS import for ${componentName}: ${cssFilePath}`
|
|
559
591
|
);
|
|
560
|
-
return;
|
|
561
592
|
}
|
|
562
|
-
console.info(
|
|
563
|
-
`[Babel Plugin WSX Style] Injecting CSS import for ${componentName}: ${cssFilePath}`
|
|
564
|
-
);
|
|
565
593
|
const importStatement = t.importDeclaration(
|
|
566
594
|
[t.importDefaultSpecifier(t.identifier("styles"))],
|
|
567
595
|
t.stringLiteral(cssFilePath)
|
|
@@ -808,7 +836,9 @@ function vitePluginWSXWithBabel(options = {}) {
|
|
|
808
836
|
{
|
|
809
837
|
cssFileExists,
|
|
810
838
|
cssFilePath,
|
|
811
|
-
componentName
|
|
839
|
+
componentName,
|
|
840
|
+
debug: options.debug
|
|
841
|
+
// Pass debug flag
|
|
812
842
|
}
|
|
813
843
|
]
|
|
814
844
|
] : [],
|
|
@@ -823,8 +853,10 @@ function vitePluginWSXWithBabel(options = {}) {
|
|
|
823
853
|
{
|
|
824
854
|
// Pass ORIGINAL source code (before JSX import injection) to plugin
|
|
825
855
|
// This ensures we can detect @state decorators even if they're removed by TypeScript preset
|
|
826
|
-
originalSource: code
|
|
856
|
+
originalSource: code,
|
|
827
857
|
// Use original code, not transformedCode
|
|
858
|
+
debug: options.debug
|
|
859
|
+
// Pass debug flag
|
|
828
860
|
}
|
|
829
861
|
],
|
|
830
862
|
// Decorator plugin runs after our custom plugins
|
package/dist/index.mjs
CHANGED
|
@@ -13,30 +13,40 @@ function babelPluginWSXState() {
|
|
|
13
13
|
visitor: {
|
|
14
14
|
Program: {
|
|
15
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
16
|
const opts = state.opts;
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
const debug = !!opts?.debug;
|
|
18
|
+
state.showDebugLogs = debug;
|
|
19
|
+
if (debug) {
|
|
25
20
|
console.info(
|
|
26
|
-
`[Babel Plugin WSX State]
|
|
21
|
+
`[Babel Plugin WSX State] DEBUG: state.opts keys = ${Object.keys(state.opts || {}).join(", ")}`
|
|
27
22
|
);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
`[Babel Plugin WSX State] \u26A0\uFE0F state.opts.originalSource not found, trying fallback...`
|
|
23
|
+
console.info(
|
|
24
|
+
`[Babel Plugin WSX State] DEBUG: state.opts = ${JSON.stringify(state.opts).substring(0, 200)}`
|
|
31
25
|
);
|
|
26
|
+
}
|
|
27
|
+
if (opts?.originalSource) {
|
|
28
|
+
state.originalSource = opts.originalSource;
|
|
29
|
+
if (debug) {
|
|
30
|
+
console.info(
|
|
31
|
+
`[Babel Plugin WSX State] \u2705 Stored original source from options (length: ${String(opts.originalSource).length})`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
if (debug) {
|
|
36
|
+
console.warn(
|
|
37
|
+
`[Babel Plugin WSX State] \u26A0\uFE0F state.opts.originalSource not found, trying fallback...`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
32
40
|
const file = state.file;
|
|
33
41
|
if (file) {
|
|
34
42
|
const sourceCode = file.code;
|
|
35
43
|
if (sourceCode) {
|
|
36
44
|
state.originalSource = sourceCode;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
if (debug) {
|
|
46
|
+
console.info(
|
|
47
|
+
`[Babel Plugin WSX State] \u2705 Stored original source from file (length: ${sourceCode.length})`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
40
50
|
} else {
|
|
41
51
|
console.error(
|
|
42
52
|
`[Babel Plugin WSX State] \u274C ERROR: Could not get original source code from state.opts or state.file!`
|
|
@@ -50,21 +60,29 @@ function babelPluginWSXState() {
|
|
|
50
60
|
}
|
|
51
61
|
}
|
|
52
62
|
},
|
|
53
|
-
ClassDeclaration(path) {
|
|
63
|
+
ClassDeclaration(path, state) {
|
|
64
|
+
const debug = !!state.showDebugLogs;
|
|
65
|
+
const logInfo = (msg) => {
|
|
66
|
+
if (debug) console.info(msg);
|
|
67
|
+
};
|
|
54
68
|
const classBody = path.node.body;
|
|
55
69
|
const stateProperties = [];
|
|
56
|
-
|
|
57
|
-
|
|
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}`
|
|
70
|
+
if (debug) {
|
|
71
|
+
logInfo(
|
|
72
|
+
`[Babel Plugin WSX State] Processing class ${path.node.id?.name || "anonymous"}, members: ${classBody.body.length}`
|
|
62
73
|
);
|
|
74
|
+
if (path.node.decorators && path.node.decorators.length > 0) {
|
|
75
|
+
logInfo(
|
|
76
|
+
`[Babel Plugin WSX State] Class-level decorators: ${path.node.decorators.length}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
63
79
|
}
|
|
64
80
|
for (const member of classBody.body) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
if (debug) {
|
|
82
|
+
logInfo(
|
|
83
|
+
` - Member type: ${member.type}, key: ${member.type === "ClassProperty" || member.type === "ClassPrivateProperty" ? member.key?.name : "N/A"}`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
68
86
|
if ((member.type === "ClassProperty" || member.type === "ClassPrivateProperty") && member.key.type === "Identifier") {
|
|
69
87
|
const propertyName = member.key.name;
|
|
70
88
|
const decoratorCount = member.decorators?.length || 0;
|
|
@@ -72,12 +90,14 @@ function babelPluginWSXState() {
|
|
|
72
90
|
const isUndefined = !!(member.value && (member.value.type === "Identifier" && member.value.name === "undefined" || member.value.type === "UnaryExpression" && member.value.operator === "void"));
|
|
73
91
|
const valueType = member.value ? member.value.type : "none";
|
|
74
92
|
const valueName = member.value && member.value.type === "Identifier" ? member.value.name : "none";
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
93
|
+
if (debug) {
|
|
94
|
+
logInfo(
|
|
95
|
+
` - Property: ${propertyName}, decorators: ${decoratorCount}, hasValue: ${hasValue}, isUndefined: ${isUndefined}, value type: ${valueType}, value name: ${valueName}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
78
98
|
try {
|
|
79
99
|
if (isUndefined) {
|
|
80
|
-
const originalSource2 =
|
|
100
|
+
const originalSource2 = state?.originalSource;
|
|
81
101
|
if (originalSource2) {
|
|
82
102
|
const escapedPropertyName = propertyName.replace(
|
|
83
103
|
/[.*+?^${}()|[\]\\]/g,
|
|
@@ -114,15 +134,15 @@ Fix: Change to '@state private ${propertyName} = undefined;' or provide a real i
|
|
|
114
134
|
}
|
|
115
135
|
}
|
|
116
136
|
}
|
|
117
|
-
if (propertyName === "count") {
|
|
118
|
-
|
|
119
|
-
`[Babel Plugin WSX State] DEBUG
|
|
137
|
+
if (propertyName === "count" && debug) {
|
|
138
|
+
logInfo(
|
|
139
|
+
`[Babel Plugin WSX State] DEBUG state keys for '${propertyName}': ${state ? Object.keys(state).join(", ") : "null"}`
|
|
120
140
|
);
|
|
121
|
-
|
|
122
|
-
`[Babel Plugin WSX State] DEBUG
|
|
141
|
+
logInfo(
|
|
142
|
+
`[Babel Plugin WSX State] DEBUG state.originalSource type: ${state ? typeof state.originalSource : "state is null"}`
|
|
123
143
|
);
|
|
124
144
|
}
|
|
125
|
-
const originalSource =
|
|
145
|
+
const originalSource = state?.originalSource;
|
|
126
146
|
if (originalSource) {
|
|
127
147
|
const escapedPropertyName = propertyName.replace(
|
|
128
148
|
/[.*+?^${}()|[\]\\]/g,
|
|
@@ -134,9 +154,11 @@ Fix: Change to '@state private ${propertyName} = undefined;' or provide a real i
|
|
|
134
154
|
);
|
|
135
155
|
const hasStateInSource = propertyPattern.test(originalSource);
|
|
136
156
|
if (hasStateInSource) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
157
|
+
if (debug) {
|
|
158
|
+
logInfo(
|
|
159
|
+
`[Babel Plugin WSX State] Found @state in source for property '${propertyName}' (decorators array was empty: ${decoratorCount})`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
140
162
|
const hasInitialValueInSource = new RegExp(
|
|
141
163
|
`@state\\s+(?:private|protected|public)?\\s+${escapedPropertyName}\\s*=\\s*[^;]+`,
|
|
142
164
|
"m"
|
|
@@ -168,24 +190,26 @@ Fix: Change to '@state private ${propertyName} = undefined;' or provide a real i
|
|
|
168
190
|
}
|
|
169
191
|
}
|
|
170
192
|
} else {
|
|
171
|
-
if (propertyName === "count") {
|
|
193
|
+
if (propertyName === "count" && debug) {
|
|
172
194
|
console.warn(
|
|
173
195
|
`[Babel Plugin WSX State] WARNING: originalSource not available for property '${propertyName}'`
|
|
174
196
|
);
|
|
175
197
|
}
|
|
176
198
|
}
|
|
177
199
|
if (member.decorators && member.decorators.length > 0) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
200
|
+
if (debug) {
|
|
201
|
+
member.decorators.forEach((decorator) => {
|
|
202
|
+
if (decorator.expression.type === "Identifier") {
|
|
203
|
+
logInfo(` Decorator: ${decorator.expression.name}`);
|
|
204
|
+
} else if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier") {
|
|
205
|
+
logInfo(
|
|
206
|
+
` Decorator: ${decorator.expression.callee.name}()`
|
|
207
|
+
);
|
|
208
|
+
} else {
|
|
209
|
+
logInfo(` Decorator: ${decorator.expression.type}`);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
189
213
|
} else {
|
|
190
214
|
}
|
|
191
215
|
let hasStateDecorator = false;
|
|
@@ -208,7 +232,7 @@ Fix: Change to '@state private ${propertyName} = undefined;' or provide a real i
|
|
|
208
232
|
if (hasStateDecorator) {
|
|
209
233
|
const key = member.key.name;
|
|
210
234
|
const hasInitialValue = !!(member.value && !(member.value.type === "Identifier" && member.value.name === "undefined") && !(member.value.type === "UnaryExpression" && member.value.operator === "void"));
|
|
211
|
-
if (key === "count") {
|
|
235
|
+
if (key === "count" && debug) {
|
|
212
236
|
console.error(
|
|
213
237
|
`[Babel Plugin WSX State] DEBUG: hasInitialValue for 'count' = ${hasInitialValue}, type = ${typeof hasInitialValue}`
|
|
214
238
|
);
|
|
@@ -513,19 +537,23 @@ function babelPluginWSXStyle() {
|
|
|
513
537
|
name: "babel-plugin-wsx-style",
|
|
514
538
|
visitor: {
|
|
515
539
|
Program(path, state) {
|
|
516
|
-
const { cssFileExists, cssFilePath, componentName } = state.opts;
|
|
540
|
+
const { cssFileExists, cssFilePath, componentName, debug } = state.opts;
|
|
517
541
|
if (!cssFileExists) {
|
|
518
542
|
return;
|
|
519
543
|
}
|
|
520
544
|
if (hasStylesImport(path.node)) {
|
|
545
|
+
if (debug) {
|
|
546
|
+
console.info(
|
|
547
|
+
`[Babel Plugin WSX Style] Skipping ${componentName}: styles already manually imported`
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
if (debug) {
|
|
521
553
|
console.info(
|
|
522
|
-
`[Babel Plugin WSX Style]
|
|
554
|
+
`[Babel Plugin WSX Style] Injecting CSS import for ${componentName}: ${cssFilePath}`
|
|
523
555
|
);
|
|
524
|
-
return;
|
|
525
556
|
}
|
|
526
|
-
console.info(
|
|
527
|
-
`[Babel Plugin WSX Style] Injecting CSS import for ${componentName}: ${cssFilePath}`
|
|
528
|
-
);
|
|
529
557
|
const importStatement = t.importDeclaration(
|
|
530
558
|
[t.importDefaultSpecifier(t.identifier("styles"))],
|
|
531
559
|
t.stringLiteral(cssFilePath)
|
|
@@ -772,7 +800,9 @@ function vitePluginWSXWithBabel(options = {}) {
|
|
|
772
800
|
{
|
|
773
801
|
cssFileExists,
|
|
774
802
|
cssFilePath,
|
|
775
|
-
componentName
|
|
803
|
+
componentName,
|
|
804
|
+
debug: options.debug
|
|
805
|
+
// Pass debug flag
|
|
776
806
|
}
|
|
777
807
|
]
|
|
778
808
|
] : [],
|
|
@@ -787,8 +817,10 @@ function vitePluginWSXWithBabel(options = {}) {
|
|
|
787
817
|
{
|
|
788
818
|
// Pass ORIGINAL source code (before JSX import injection) to plugin
|
|
789
819
|
// This ensures we can detect @state decorators even if they're removed by TypeScript preset
|
|
790
|
-
originalSource: code
|
|
820
|
+
originalSource: code,
|
|
791
821
|
// Use original code, not transformedCode
|
|
822
|
+
debug: options.debug
|
|
823
|
+
// Pass debug flag
|
|
792
824
|
}
|
|
793
825
|
],
|
|
794
826
|
// Decorator plugin runs after our custom plugins
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wsxjs/wsx-vite-plugin",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"description": "Vite plugin for WSXJS",
|
|
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.
|
|
34
|
+
"@wsxjs/wsx-core": "0.0.25"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@babel/traverse": "^7.28.5",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Babel plugin to automatically add data-wsx-key attributes to focusable elements
|
|
3
|
+
* for focus preservation only (RFC-0046).
|
|
3
4
|
*
|
|
4
5
|
* Transforms:
|
|
5
6
|
* <input value={this.name} onInput={this.handleInput} />
|
|
@@ -12,6 +13,10 @@
|
|
|
12
13
|
* />
|
|
13
14
|
*
|
|
14
15
|
* This enables automatic focus preservation during component rerenders.
|
|
16
|
+
*
|
|
17
|
+
* IMPORTANT: This plugin ONLY handles focus preservation. It does NOT generate
|
|
18
|
+
* cache keys or position IDs. The framework handles cache key generation using
|
|
19
|
+
* user-provided `key` prop or runtime counters (following React/Vue design).
|
|
15
20
|
*/
|
|
16
21
|
|
|
17
22
|
import type { PluginObj, NodePath } from "@babel/core";
|
|
@@ -23,6 +23,7 @@ interface WSXStatePluginPass extends PluginPass {
|
|
|
23
23
|
isObject: boolean;
|
|
24
24
|
isArray?: boolean; // Add isArray flag
|
|
25
25
|
}>;
|
|
26
|
+
showDebugLogs?: boolean;
|
|
26
27
|
reactiveMethodName: string;
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -33,26 +34,36 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
|
|
|
33
34
|
visitor: {
|
|
34
35
|
Program: {
|
|
35
36
|
enter(_path, state) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
const opts = state.opts as Record<string, unknown> | undefined;
|
|
38
|
+
const debug = !!opts?.debug;
|
|
39
|
+
// Store debug flag in state for other visitors
|
|
40
|
+
(state as any).showDebugLogs = debug;
|
|
41
|
+
|
|
42
|
+
if (debug) {
|
|
43
|
+
// CRITICAL: Debug log to check state.opts
|
|
44
|
+
console.info(
|
|
45
|
+
`[Babel Plugin WSX State] DEBUG: state.opts keys = ${Object.keys(state.opts || {}).join(", ")}`
|
|
46
|
+
);
|
|
47
|
+
console.info(
|
|
48
|
+
`[Babel Plugin WSX State] DEBUG: state.opts = ${JSON.stringify(state.opts).substring(0, 200)}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
43
51
|
|
|
44
52
|
// Store original source code for later checking
|
|
45
53
|
// First try to get from plugin options (passed from vite plugin)
|
|
46
|
-
const opts = state.opts as Record<string, unknown> | undefined;
|
|
47
54
|
if (opts?.originalSource) {
|
|
48
55
|
(state as Record<string, unknown>).originalSource = opts.originalSource;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
if (debug) {
|
|
57
|
+
console.info(
|
|
58
|
+
`[Babel Plugin WSX State] ✅ Stored original source from options (length: ${String(opts.originalSource).length})`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
52
61
|
} else {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
if (debug) {
|
|
63
|
+
console.warn(
|
|
64
|
+
`[Babel Plugin WSX State] ⚠️ state.opts.originalSource not found, trying fallback...`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
56
67
|
// Fallback: try to get from file
|
|
57
68
|
const file = state.file;
|
|
58
69
|
if (file) {
|
|
@@ -61,9 +72,11 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
|
|
|
61
72
|
| undefined;
|
|
62
73
|
if (sourceCode) {
|
|
63
74
|
(state as Record<string, unknown>).originalSource = sourceCode;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
if (debug) {
|
|
76
|
+
console.info(
|
|
77
|
+
`[Babel Plugin WSX State] ✅ Stored original source from file (length: ${sourceCode.length})`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
67
80
|
} else {
|
|
68
81
|
console.error(
|
|
69
82
|
`[Babel Plugin WSX State] ❌ ERROR: Could not get original source code from state.opts or state.file!`
|
|
@@ -77,7 +90,11 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
|
|
|
77
90
|
}
|
|
78
91
|
},
|
|
79
92
|
},
|
|
80
|
-
ClassDeclaration(path) {
|
|
93
|
+
ClassDeclaration(path, state) {
|
|
94
|
+
const debug = !!(state as any).showDebugLogs;
|
|
95
|
+
const logInfo = (msg: string) => {
|
|
96
|
+
if (debug) console.info(msg);
|
|
97
|
+
};
|
|
81
98
|
const classBody = path.node.body;
|
|
82
99
|
const stateProperties: Array<{
|
|
83
100
|
key: string;
|
|
@@ -88,23 +105,27 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
|
|
|
88
105
|
|
|
89
106
|
// Find all @state decorated properties
|
|
90
107
|
// Debug: log all class members
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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}`
|
|
108
|
+
if (debug) {
|
|
109
|
+
logInfo(
|
|
110
|
+
`[Babel Plugin WSX State] Processing class ${path.node.id?.name || "anonymous"}, members: ${classBody.body.length}`
|
|
99
111
|
);
|
|
112
|
+
|
|
113
|
+
// CRITICAL: Log all decorators at class level to debug
|
|
114
|
+
if (path.node.decorators && path.node.decorators.length > 0) {
|
|
115
|
+
logInfo(
|
|
116
|
+
`[Babel Plugin WSX State] Class-level decorators: ${path.node.decorators.length}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
100
119
|
}
|
|
101
120
|
|
|
102
121
|
for (const member of classBody.body) {
|
|
103
122
|
// Debug: log member type
|
|
104
123
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
124
|
+
if (debug) {
|
|
125
|
+
logInfo(
|
|
126
|
+
` - Member type: ${member.type}, key: ${member.type === "ClassProperty" || member.type === "ClassPrivateProperty" ? (member.key as any)?.name : "N/A"}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
108
129
|
|
|
109
130
|
// Check both ClassProperty and ClassPrivateProperty
|
|
110
131
|
// @babel/plugin-proposal-class-properties might convert them
|
|
@@ -143,16 +164,18 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
|
|
|
143
164
|
? (member.value as t.Identifier).name
|
|
144
165
|
: "none";
|
|
145
166
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
167
|
+
if (debug) {
|
|
168
|
+
logInfo(
|
|
169
|
+
` - Property: ${propertyName}, decorators: ${decoratorCount}, hasValue: ${hasValue}, isUndefined: ${isUndefined}, value type: ${valueType}, value name: ${valueName}`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
149
172
|
|
|
150
173
|
// Wrap the entire detection logic in try-catch to pinpoint crash location
|
|
151
174
|
try {
|
|
152
175
|
// CRITICAL: If property has undefined value, check source code for @state decorator
|
|
153
176
|
// This handles the case where decorator was removed but property has undefined from optional syntax
|
|
154
177
|
if (isUndefined) {
|
|
155
|
-
const originalSource = (
|
|
178
|
+
const originalSource = (state as Record<string, unknown>)
|
|
156
179
|
?.originalSource as string | undefined;
|
|
157
180
|
if (originalSource) {
|
|
158
181
|
// Escape special regex characters in property name
|
|
@@ -197,17 +220,17 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
|
|
|
197
220
|
// Even if decorators array is empty (decorators: 0), the decorator might exist in source
|
|
198
221
|
// This is the main way to detect @state when decorators are removed before our plugin runs
|
|
199
222
|
|
|
200
|
-
// DEBUG: Check
|
|
201
|
-
if (propertyName === "count") {
|
|
202
|
-
|
|
203
|
-
`[Babel Plugin WSX State] DEBUG
|
|
223
|
+
// DEBUG: Check state structure
|
|
224
|
+
if (propertyName === "count" && debug) {
|
|
225
|
+
logInfo(
|
|
226
|
+
`[Babel Plugin WSX State] DEBUG state keys for '${propertyName}': ${state ? Object.keys(state).join(", ") : "null"}`
|
|
204
227
|
);
|
|
205
|
-
|
|
206
|
-
`[Babel Plugin WSX State] DEBUG
|
|
228
|
+
logInfo(
|
|
229
|
+
`[Babel Plugin WSX State] DEBUG state.originalSource type: ${state ? typeof (state as any).originalSource : "state is null"}`
|
|
207
230
|
);
|
|
208
231
|
}
|
|
209
232
|
|
|
210
|
-
const originalSource = (
|
|
233
|
+
const originalSource = (state as Record<string, unknown>)
|
|
211
234
|
?.originalSource as string | undefined;
|
|
212
235
|
|
|
213
236
|
if (originalSource) {
|
|
@@ -225,9 +248,11 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
|
|
|
225
248
|
|
|
226
249
|
const hasStateInSource = propertyPattern.test(originalSource);
|
|
227
250
|
if (hasStateInSource) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
251
|
+
if (debug) {
|
|
252
|
+
logInfo(
|
|
253
|
+
`[Babel Plugin WSX State] Found @state in source for property '${propertyName}' (decorators array was empty: ${decoratorCount})`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
231
256
|
// Found @state in source but decorators array is empty
|
|
232
257
|
// Check if it has an initial value in source (not just undefined from TypeScript)
|
|
233
258
|
// Escape special regex characters in property name (already escaped above, reuse)
|
|
@@ -265,7 +290,7 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
|
|
|
265
290
|
}
|
|
266
291
|
} else {
|
|
267
292
|
// Log when originalSource is not available for debugging
|
|
268
|
-
if (propertyName === "count") {
|
|
293
|
+
if (propertyName === "count" && debug) {
|
|
269
294
|
console.warn(
|
|
270
295
|
`[Babel Plugin WSX State] WARNING: originalSource not available for property '${propertyName}'`
|
|
271
296
|
);
|
|
@@ -274,20 +299,22 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
|
|
|
274
299
|
|
|
275
300
|
if (member.decorators && member.decorators.length > 0) {
|
|
276
301
|
// Debug: log decorator names
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
302
|
+
if (debug) {
|
|
303
|
+
member.decorators.forEach((decorator) => {
|
|
304
|
+
if (decorator.expression.type === "Identifier") {
|
|
305
|
+
logInfo(` Decorator: ${decorator.expression.name}`);
|
|
306
|
+
} else if (
|
|
307
|
+
decorator.expression.type === "CallExpression" &&
|
|
308
|
+
decorator.expression.callee.type === "Identifier"
|
|
309
|
+
) {
|
|
310
|
+
logInfo(
|
|
311
|
+
` Decorator: ${decorator.expression.callee.name}()`
|
|
312
|
+
);
|
|
313
|
+
} else {
|
|
314
|
+
logInfo(` Decorator: ${decorator.expression.type}`);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
291
318
|
} else {
|
|
292
319
|
// Check if this property might have had @state decorator but it was removed
|
|
293
320
|
// This can happen if TypeScript preset or another plugin processed it first
|
|
@@ -343,7 +370,7 @@ export default function babelPluginWSXState(): PluginObj<WSXStatePluginPass> {
|
|
|
343
370
|
);
|
|
344
371
|
|
|
345
372
|
// DEBUG: Log hasInitialValue for count
|
|
346
|
-
if (key === "count") {
|
|
373
|
+
if (key === "count" && debug) {
|
|
347
374
|
console.error(
|
|
348
375
|
`[Babel Plugin WSX State] DEBUG: hasInitialValue for 'count' = ${hasInitialValue}, type = ${typeof hasInitialValue}`
|
|
349
376
|
);
|
|
@@ -21,6 +21,7 @@ interface WSXStylePluginPass extends PluginPass {
|
|
|
21
21
|
cssFileExists: boolean;
|
|
22
22
|
cssFilePath: string;
|
|
23
23
|
componentName: string;
|
|
24
|
+
debug?: boolean;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -70,7 +71,7 @@ export default function babelPluginWSXStyle(): PluginObj<WSXStylePluginPass> {
|
|
|
70
71
|
name: "babel-plugin-wsx-style",
|
|
71
72
|
visitor: {
|
|
72
73
|
Program(path, state) {
|
|
73
|
-
const { cssFileExists, cssFilePath, componentName } =
|
|
74
|
+
const { cssFileExists, cssFilePath, componentName, debug } =
|
|
74
75
|
state.opts as WSXStylePluginPass;
|
|
75
76
|
|
|
76
77
|
// Skip if CSS file doesn't exist
|
|
@@ -80,15 +81,19 @@ export default function babelPluginWSXStyle(): PluginObj<WSXStylePluginPass> {
|
|
|
80
81
|
|
|
81
82
|
// Check if styles are already manually imported
|
|
82
83
|
if (hasStylesImport(path.node)) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
if (debug) {
|
|
85
|
+
console.info(
|
|
86
|
+
`[Babel Plugin WSX Style] Skipping ${componentName}: styles already manually imported`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
86
89
|
return; // Skip auto-injection if manual import exists
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
if (debug) {
|
|
93
|
+
console.info(
|
|
94
|
+
`[Babel Plugin WSX Style] Injecting CSS import for ${componentName}: ${cssFilePath}`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
92
97
|
|
|
93
98
|
// Add CSS import at the top of the file
|
|
94
99
|
const importStatement = t.importDeclaration(
|
|
@@ -126,6 +126,7 @@ export function vitePluginWSXWithBabel(options: WSXPluginOptions = {}): Plugin {
|
|
|
126
126
|
cssFileExists,
|
|
127
127
|
cssFilePath,
|
|
128
128
|
componentName,
|
|
129
|
+
debug: options.debug, // Pass debug flag
|
|
129
130
|
},
|
|
130
131
|
],
|
|
131
132
|
]
|
|
@@ -142,6 +143,7 @@ export function vitePluginWSXWithBabel(options: WSXPluginOptions = {}): Plugin {
|
|
|
142
143
|
// Pass ORIGINAL source code (before JSX import injection) to plugin
|
|
143
144
|
// This ensures we can detect @state decorators even if they're removed by TypeScript preset
|
|
144
145
|
originalSource: code, // Use original code, not transformedCode
|
|
146
|
+
debug: options.debug, // Pass debug flag
|
|
145
147
|
},
|
|
146
148
|
],
|
|
147
149
|
// Decorator plugin runs after our custom plugins
|