@wsxjs/wsx-vite-plugin 0.0.7 → 0.0.8

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.d.mts CHANGED
@@ -1,39 +1,19 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
3
  /**
4
- * Vite Plugin for WSX (Web Component JSX)
4
+ * Vite Plugin for WSX with Babel decorator support
5
5
  *
6
- * 专门处理.wsx文件:
7
- * - 自动添加JSX pragma
8
- * - 支持TypeScript编译
9
- * - 完全隔离,不影响主项目React配置
6
+ * Uses Babel to preprocess decorators before esbuild transformation
7
+ * This ensures decorators work correctly even with esbuild's limitations
10
8
  */
11
9
 
12
10
  interface WSXPluginOptions {
13
- /**
14
- * JSX工厂函数名
15
- * @default 'h'
16
- */
17
11
  jsxFactory?: string;
18
- /**
19
- * JSX Fragment函数名
20
- * @default 'Fragment'
21
- */
22
12
  jsxFragment?: string;
23
- /**
24
- * 是否启用调试日志
25
- * @default false
26
- */
27
13
  debug?: boolean;
28
- /**
29
- * 文件扩展名
30
- * @default ['.wsx']
31
- */
32
14
  extensions?: string[];
15
+ autoStyleInjection?: boolean;
33
16
  }
34
- /**
35
- * WSX Vite插件
36
- */
37
- declare function vitePluginWSX(options?: WSXPluginOptions): Plugin;
17
+ declare function vitePluginWSXWithBabel(options?: WSXPluginOptions): Plugin;
38
18
 
39
- export { vitePluginWSX as wsx };
19
+ export { vitePluginWSXWithBabel as wsx };
package/dist/index.d.ts CHANGED
@@ -1,39 +1,19 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
3
  /**
4
- * Vite Plugin for WSX (Web Component JSX)
4
+ * Vite Plugin for WSX with Babel decorator support
5
5
  *
6
- * 专门处理.wsx文件:
7
- * - 自动添加JSX pragma
8
- * - 支持TypeScript编译
9
- * - 完全隔离,不影响主项目React配置
6
+ * Uses Babel to preprocess decorators before esbuild transformation
7
+ * This ensures decorators work correctly even with esbuild's limitations
10
8
  */
11
9
 
12
10
  interface WSXPluginOptions {
13
- /**
14
- * JSX工厂函数名
15
- * @default 'h'
16
- */
17
11
  jsxFactory?: string;
18
- /**
19
- * JSX Fragment函数名
20
- * @default 'Fragment'
21
- */
22
12
  jsxFragment?: string;
23
- /**
24
- * 是否启用调试日志
25
- * @default false
26
- */
27
13
  debug?: boolean;
28
- /**
29
- * 文件扩展名
30
- * @default ['.wsx']
31
- */
32
14
  extensions?: string[];
15
+ autoStyleInjection?: boolean;
33
16
  }
34
- /**
35
- * WSX Vite插件
36
- */
37
- declare function vitePluginWSX(options?: WSXPluginOptions): Plugin;
17
+ declare function vitePluginWSXWithBabel(options?: WSXPluginOptions): Plugin;
38
18
 
39
- export { vitePluginWSX as wsx };
19
+ export { vitePluginWSXWithBabel as wsx };
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,104 +17,426 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
23
- wsx: () => vite_plugin_wsx_default
33
+ wsx: () => vitePluginWSXWithBabel
24
34
  });
25
35
  module.exports = __toCommonJS(index_exports);
26
36
 
27
- // src/vite-plugin-wsx.ts
37
+ // src/vite-plugin-wsx-babel.ts
28
38
  var import_esbuild = require("esbuild");
39
+ var import_core = require("@babel/core");
40
+ var import_fs = require("fs");
41
+ var import_path = require("path");
42
+
43
+ // src/babel-plugin-wsx-state.ts
44
+ var tModule = __toESM(require("@babel/types"));
45
+ function babelPluginWSXState() {
46
+ const t = tModule;
47
+ return {
48
+ name: "babel-plugin-wsx-state",
49
+ visitor: {
50
+ ClassDeclaration(path) {
51
+ const classBody = path.node.body;
52
+ const stateProperties = [];
53
+ console.info(
54
+ `[Babel Plugin WSX State] Processing class ${path.node.id?.name || "anonymous"}, members: ${classBody.body.length}`
55
+ );
56
+ for (const member of classBody.body) {
57
+ console.info(
58
+ ` - Member type: ${member.type}, key: ${member.type === "ClassProperty" || member.type === "ClassPrivateProperty" ? member.key?.name : "N/A"}`
59
+ );
60
+ if ((member.type === "ClassProperty" || member.type === "ClassPrivateProperty") && member.key.type === "Identifier") {
61
+ console.info(
62
+ ` - Property: ${member.key.name}, decorators: ${member.decorators?.length || 0}, hasValue: ${!!member.value}`
63
+ );
64
+ if (member.decorators && member.decorators.length > 0) {
65
+ member.decorators.forEach((decorator) => {
66
+ if (decorator.expression.type === "Identifier") {
67
+ console.info(` Decorator: ${decorator.expression.name}`);
68
+ } else if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier") {
69
+ console.debug(
70
+ ` Decorator: ${decorator.expression.callee.name}()`
71
+ );
72
+ }
73
+ });
74
+ }
75
+ const hasStateDecorator = member.decorators?.some(
76
+ (decorator) => {
77
+ if (decorator.expression.type === "Identifier" && decorator.expression.name === "state") {
78
+ return true;
79
+ }
80
+ if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier" && decorator.expression.callee.name === "state") {
81
+ return true;
82
+ }
83
+ return false;
84
+ }
85
+ );
86
+ if (hasStateDecorator && member.value) {
87
+ const key = member.key.name;
88
+ const initialValue = member.value;
89
+ const isObject = initialValue.type === "ObjectExpression" || initialValue.type === "ArrayExpression";
90
+ stateProperties.push({
91
+ key,
92
+ initialValue,
93
+ isObject
94
+ });
95
+ if (member.decorators) {
96
+ member.decorators = member.decorators.filter(
97
+ (decorator) => {
98
+ if (decorator.expression.type === "Identifier" && decorator.expression.name === "state") {
99
+ return false;
100
+ }
101
+ if (decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier" && decorator.expression.callee.name === "state") {
102
+ return false;
103
+ }
104
+ return true;
105
+ }
106
+ );
107
+ }
108
+ member.value = void 0;
109
+ }
110
+ }
111
+ }
112
+ if (stateProperties.length === 0) {
113
+ return;
114
+ }
115
+ let constructor = classBody.body.find(
116
+ (member) => member.type === "ClassMethod" && member.kind === "constructor"
117
+ );
118
+ if (!constructor) {
119
+ constructor = t.classMethod(
120
+ "constructor",
121
+ t.identifier("constructor"),
122
+ [],
123
+ t.blockStatement([])
124
+ );
125
+ classBody.body.unshift(constructor);
126
+ }
127
+ const statements = [];
128
+ const hasSuper = constructor.body.body.some(
129
+ (stmt) => stmt.type === "ExpressionStatement" && stmt.expression.type === "CallExpression" && stmt.expression.callee.type === "Super"
130
+ );
131
+ if (!hasSuper) {
132
+ statements.push(t.expressionStatement(t.callExpression(t.super(), [])));
133
+ }
134
+ for (const { key, initialValue, isObject } of stateProperties) {
135
+ if (isObject) {
136
+ statements.push(
137
+ t.expressionStatement(
138
+ t.assignmentExpression(
139
+ "=",
140
+ t.memberExpression(t.thisExpression(), t.identifier(key)),
141
+ t.callExpression(
142
+ t.memberExpression(
143
+ t.thisExpression(),
144
+ t.identifier("reactive")
145
+ ),
146
+ [initialValue]
147
+ )
148
+ )
149
+ )
150
+ );
151
+ } else {
152
+ const getterId = t.identifier(`_get${key}`);
153
+ const setterId = t.identifier(`_set${key}`);
154
+ statements.push(
155
+ t.variableDeclaration("const", [
156
+ t.variableDeclarator(
157
+ t.arrayPattern([getterId, setterId]),
158
+ t.callExpression(
159
+ t.memberExpression(
160
+ t.thisExpression(),
161
+ t.identifier("useState")
162
+ ),
163
+ [t.stringLiteral(key), initialValue]
164
+ )
165
+ )
166
+ ])
167
+ );
168
+ statements.push(
169
+ t.expressionStatement(
170
+ t.callExpression(
171
+ t.memberExpression(
172
+ t.identifier("Object"),
173
+ t.identifier("defineProperty")
174
+ ),
175
+ [
176
+ t.thisExpression(),
177
+ t.stringLiteral(key),
178
+ t.objectExpression([
179
+ t.objectProperty(t.identifier("get"), getterId),
180
+ t.objectProperty(t.identifier("set"), setterId),
181
+ t.objectProperty(
182
+ t.identifier("enumerable"),
183
+ t.booleanLiteral(true)
184
+ ),
185
+ t.objectProperty(
186
+ t.identifier("configurable"),
187
+ t.booleanLiteral(true)
188
+ )
189
+ ])
190
+ ]
191
+ )
192
+ )
193
+ );
194
+ }
195
+ }
196
+ constructor.body.body.push(...statements);
197
+ }
198
+ }
199
+ };
200
+ }
201
+
202
+ // src/babel-plugin-wsx-style.ts
203
+ var tModule2 = __toESM(require("@babel/types"));
204
+ function hasStylesImport(program) {
205
+ for (const node of program.body) {
206
+ if (node.type === "ImportDeclaration") {
207
+ const source = node.source.value;
208
+ if (typeof source === "string" && (source.endsWith(".css?inline") || source.endsWith(".css"))) {
209
+ const defaultSpecifier = node.specifiers.find(
210
+ (spec) => spec.type === "ImportDefaultSpecifier"
211
+ );
212
+ if (defaultSpecifier) {
213
+ return true;
214
+ }
215
+ }
216
+ }
217
+ }
218
+ return false;
219
+ }
220
+ function hasAutoStylesProperty(classBody) {
221
+ for (const member of classBody.body) {
222
+ if ((member.type === "ClassProperty" || member.type === "ClassPrivateProperty") && member.key.type === "Identifier" && member.key.name === "_autoStyles") {
223
+ return true;
224
+ }
225
+ }
226
+ return false;
227
+ }
228
+ function babelPluginWSXStyle() {
229
+ const t = tModule2;
230
+ return {
231
+ name: "babel-plugin-wsx-style",
232
+ visitor: {
233
+ Program(path, state) {
234
+ const { cssFileExists, cssFilePath, componentName } = state.opts;
235
+ if (!cssFileExists) {
236
+ return;
237
+ }
238
+ if (hasStylesImport(path.node)) {
239
+ console.info(
240
+ `[Babel Plugin WSX Style] Skipping ${componentName}: styles already manually imported`
241
+ );
242
+ return;
243
+ }
244
+ console.info(
245
+ `[Babel Plugin WSX Style] Injecting CSS import for ${componentName}: ${cssFilePath}`
246
+ );
247
+ const importStatement = t.importDeclaration(
248
+ [t.importDefaultSpecifier(t.identifier("styles"))],
249
+ t.stringLiteral(cssFilePath)
250
+ );
251
+ let insertIndex = 0;
252
+ for (let i = 0; i < path.node.body.length; i++) {
253
+ const node = path.node.body[i];
254
+ if (node.type === "ImportDeclaration") {
255
+ insertIndex = i + 1;
256
+ } else {
257
+ break;
258
+ }
259
+ }
260
+ path.node.body.splice(insertIndex, 0, importStatement);
261
+ },
262
+ ClassDeclaration(path, state) {
263
+ const { cssFileExists } = state.opts;
264
+ if (!cssFileExists) {
265
+ return;
266
+ }
267
+ const classBody = path.node.body;
268
+ if (hasAutoStylesProperty(classBody)) {
269
+ return;
270
+ }
271
+ const autoStylesProperty = t.classProperty(
272
+ t.identifier("_autoStyles"),
273
+ t.identifier("styles"),
274
+ null,
275
+ // typeAnnotation
276
+ [],
277
+ // decorators
278
+ false,
279
+ // computed
280
+ false
281
+ // static
282
+ );
283
+ classBody.body.unshift(autoStylesProperty);
284
+ }
285
+ }
286
+ };
287
+ }
288
+
289
+ // src/vite-plugin-wsx-babel.ts
29
290
  function getJSXFactoryImportPath(_options) {
30
291
  return "@wsxjs/wsx-core";
31
292
  }
32
- function vitePluginWSX(options = {}) {
293
+ function vitePluginWSXWithBabel(options = {}) {
33
294
  const {
34
295
  jsxFactory = "h",
35
296
  jsxFragment = "Fragment",
36
297
  debug = false,
37
- extensions = [".wsx"]
298
+ extensions = [".wsx"],
299
+ autoStyleInjection = true
38
300
  } = options;
39
301
  return {
40
- name: "vite-plugin-wsx",
302
+ name: "vite-plugin-wsx-babel",
41
303
  enforce: "pre",
42
- // 确保在 React 插件之前执行
43
- // 处理 .wsx 文件加载
44
- load(id) {
45
- const isWSXFile = extensions.some((ext) => id.endsWith(ext));
46
- if (!isWSXFile) {
47
- return null;
48
- }
49
- if (debug) {
50
- console.log(`[WSX Plugin] Loading: ${id}`);
51
- }
52
- return null;
53
- },
54
- // 在transform阶段处理文件
55
304
  async transform(code, id) {
56
305
  const isWSXFile = extensions.some((ext) => id.endsWith(ext));
57
306
  if (!isWSXFile) {
58
307
  return null;
59
308
  }
60
309
  if (debug) {
61
- console.log(`[WSX Plugin] Processing: ${id}`);
310
+ console.log(`[WSX Plugin Babel] Processing: ${id}`);
311
+ }
312
+ let cssFileExists = false;
313
+ let cssFilePath = "";
314
+ let componentName = "";
315
+ if (autoStyleInjection) {
316
+ const fileDir = (0, import_path.dirname)(id);
317
+ const fileName = (0, import_path.basename)(id, extensions.find((ext) => id.endsWith(ext)) || "");
318
+ const cssFilePathWithoutQuery = (0, import_path.join)(fileDir, `${fileName}.css`);
319
+ cssFileExists = (0, import_fs.existsSync)(cssFilePathWithoutQuery);
320
+ componentName = fileName;
321
+ if (cssFileExists) {
322
+ cssFilePath = `./${fileName}.css?inline`;
323
+ }
324
+ if (cssFileExists) {
325
+ console.log(
326
+ `[WSX Plugin Babel] Found CSS file for auto-injection: ${cssFilePathWithoutQuery}, will inject: ${cssFilePath}`
327
+ );
328
+ }
62
329
  }
63
330
  let transformedCode = code;
64
331
  const hasWSXCoreImport = code.includes('from "@wsxjs/wsx-core"');
65
332
  const hasJSXInImport = hasWSXCoreImport && (new RegExp(`[{,]\\s*${jsxFactory}\\s*[},]`).test(code) || new RegExp(`[{,]\\s*${jsxFragment}\\s*[},]`).test(code));
66
- if (debug) {
67
- console.log(`[WSX Plugin] Checking JSX imports for: ${id}`);
68
- console.log(` - hasWSXCoreImport: ${hasWSXCoreImport}`);
69
- console.log(` - hasJSXInImport: ${hasJSXInImport}`);
70
- console.log(` - has < character: ${code.includes("<")}`);
71
- console.log(` - has Fragment: ${code.includes("Fragment")}`);
72
- }
73
333
  if ((code.includes("<") || code.includes("Fragment")) && !hasJSXInImport) {
74
334
  const importPath = getJSXFactoryImportPath(options);
75
335
  const importStatement = `import { ${jsxFactory}, ${jsxFragment} } from "${importPath}";
336
+ `;
337
+ transformedCode = importStatement + transformedCode;
338
+ }
339
+ try {
340
+ const babelResult = (0, import_core.transformSync)(transformedCode, {
341
+ filename: id,
342
+ // Pass the actual filename so Babel knows it's .wsx
343
+ presets: [
344
+ [
345
+ "@babel/preset-typescript",
346
+ {
347
+ isTSX: true,
348
+ // Enable JSX syntax
349
+ allExtensions: true
350
+ // Process all extensions, including .wsx
351
+ }
352
+ ]
353
+ ],
354
+ plugins: [
355
+ // CRITICAL: Style injection plugin must run FIRST
356
+ // This ensures _autoStyles property exists before state transformations
357
+ ...autoStyleInjection && cssFileExists ? [
358
+ [
359
+ babelPluginWSXStyle,
360
+ {
361
+ cssFileExists,
362
+ cssFilePath,
363
+ componentName
364
+ }
365
+ ]
366
+ ] : [],
367
+ // State decorator transformation runs after style injection
368
+ babelPluginWSXState,
369
+ [
370
+ "@babel/plugin-proposal-decorators",
371
+ {
372
+ version: "2023-05",
373
+ decoratorsBeforeExport: true
374
+ }
375
+ ],
376
+ [
377
+ "@babel/plugin-proposal-class-properties",
378
+ {
379
+ loose: false
380
+ }
381
+ ],
382
+ "@babel/plugin-transform-class-static-block"
383
+ // Support static class blocks
384
+ ]
385
+ // parserOpts not needed - @babel/preset-typescript and plugins handle it
386
+ });
387
+ if (babelResult && babelResult.code) {
388
+ transformedCode = babelResult.code;
389
+ if (debug) {
390
+ console.log(`[WSX Plugin Babel] Decorators preprocessed: ${id}`);
391
+ if (transformedCode.includes("this.reactive") || transformedCode.includes("this.useState")) {
392
+ console.log(
393
+ `[WSX Plugin Babel] Generated reactive code found in: ${id}
394
+ ` + transformedCode.split("\n").filter(
395
+ (line) => line.includes("this.reactive") || line.includes("this.useState")
396
+ ).join("\n")
397
+ );
398
+ }
399
+ }
400
+ }
401
+ } catch (error) {
402
+ console.warn(
403
+ `[WSX Plugin Babel] Babel transform failed for ${id}, falling back to esbuild only:`,
404
+ error
405
+ );
406
+ }
407
+ const hasJSXAfterBabel = transformedCode.includes('from "@wsxjs/wsx-core"') && (new RegExp(`[{,]\\s*${jsxFactory}\\s*[},]`).test(transformedCode) || new RegExp(`[{,]\\s*${jsxFragment}\\s*[},]`).test(transformedCode));
408
+ if ((transformedCode.includes("<") || transformedCode.includes("Fragment")) && !hasJSXAfterBabel) {
409
+ const importPath = getJSXFactoryImportPath(options);
410
+ const importStatement = `import { ${jsxFactory}, ${jsxFragment} } from "${importPath}";
76
411
  `;
77
412
  transformedCode = importStatement + transformedCode;
78
413
  if (debug) {
79
- console.log(`[WSX Plugin] Added JSX factory import to: ${id}`);
414
+ console.log(
415
+ `[WSX Plugin Babel] Re-added JSX imports after Babel transform: ${id}`
416
+ );
80
417
  }
81
418
  }
82
419
  try {
83
420
  const result = await (0, import_esbuild.transform)(transformedCode, {
84
- loader: "tsx",
421
+ loader: "jsx",
422
+ // Already TypeScript-transformed by Babel
85
423
  jsx: "transform",
86
424
  jsxFactory,
87
425
  jsxFragment,
88
426
  target: "es2020",
89
427
  format: "esm"
90
- // Esbuild supports decorators natively with tsx loader
91
428
  });
92
- if (debug) {
93
- console.log(`[WSX Plugin] JSX transformed: ${id}`);
94
- }
95
429
  return {
96
430
  code: result.code,
97
431
  map: null
98
432
  };
99
433
  } catch (error) {
100
- console.error(`[WSX Plugin] Transform error for ${id}:`, error);
434
+ console.error(`[WSX Plugin Babel] Transform error for ${id}:`, error);
101
435
  throw error;
102
436
  }
103
- },
104
- // We handle JSX transformation directly in the transform hook
105
- // No need to modify global esbuild config
106
- // 构建开始时的日志
107
- buildStart() {
108
- if (debug) {
109
- console.log(`[WSX Plugin] Build started with extensions: ${extensions.join(", ")}`);
110
- console.log(`[WSX Plugin] JSX Factory: ${jsxFactory}, Fragment: ${jsxFragment}`);
111
- }
112
437
  }
113
438
  };
114
439
  }
115
- var vite_plugin_wsx_default = vitePluginWSX;
116
440
  // Annotate the CommonJS export names for ESM import in node:
117
441
  0 && (module.exports = {
118
442
  wsx