@praxisjs/vite-plugin 3.0.0 → 4.0.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @praxisjs/vite-plugin
2
2
 
3
+ ## 4.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 22d9a4e: Move `esbuild` from `devDependencies` to `dependencies`. It's imported at runtime by the CSS extraction step, so it was missing from `node_modules` in consumer projects (e.g. `create-praxisjs` scaffolds), causing `ERR_MODULE_NOT_FOUND: esbuild` on `vite dev`/`vite build`.
8
+
9
+ ## 4.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - 9c2a7e0: Upgraded to Vite 8 (rolldown-vite). Vite 8's oxc-based transform doesn't lower TC39 (non-legacy) decorators yet ([oxc-project/oxc#9170](https://github.com/oxc-project/oxc/issues/9170)), so `praxisjs()` now uses the TypeScript compiler (`ts.transpileModule`) to lower decorator syntax into runtime-executable code, leaving JSX untouched for oxc's own JSX pipeline. No third-party decorator transform is bundled — TypeScript's decorator lowering keeps class fields as native ES2022 fields, so a decorated field's `addInitializer` callback already runs after `super()` in a derived class's explicit constructor without any extra fix-up pass.
14
+
15
+ The `esbuild` config key (deprecated in Vite 8) was replaced with `oxc` in the plugin's own `config()` hook.
16
+
17
+ `typescript` moved from a `devDependency` to a regular `dependency`, since the decorator-lowering pass now calls it at runtime — it's pulled in automatically, no action needed.
18
+
19
+ **Breaking:** `@praxisjs/vite-plugin` now requires Vite `^8.0.0`. Apps still on Vite 7 should stay on the previous `@praxisjs/vite-plugin` major version.
20
+
21
+ **Also requires `useDefineForClassFields: true`** in your `tsconfig.json` (previously `false`) — see the updated [Getting Started](/docs/guide/getting-started) guide. With `false`, oxc silently drops a class field's decorator during its own TypeScript stripping, before the lowering step ever sees it.
22
+
23
+ **`decoratorLoweringPlugin()` is now exported** alongside `praxisjs()`, so it can be registered directly in `vitest.config.ts` (or any other Vite-based tool) to lower decorator syntax outside of the full `praxisjs()` plugin — see [Using decorators in Vitest](/docs/tooling/vite-plugin#using-decorators-in-vitest).
24
+
3
25
  ## 3.0.0
4
26
 
5
27
  ### Patch Changes
@@ -1,68 +1,249 @@
1
+ import ts from "typescript";
1
2
  import { describe, it, expect, vi } from "vitest";
2
- import { praxisjs } from "../index";
3
+ import { praxisjs, decoratorLoweringPlugin } from "../index";
4
+ // `ts.transpileModule`'s own types mark `diagnostics` and `sourceMapText` as
5
+ // optional regardless of the options passed in — wrapping it lets a couple
6
+ // of tests below simulate that contract even though it never happens with
7
+ // the fixed options `decoratorLoweringPlugin()` always calls it with.
8
+ vi.mock("typescript", async (importOriginal) => {
9
+ const actual = (await importOriginal());
10
+ return {
11
+ ...actual,
12
+ default: {
13
+ ...actual.default,
14
+ transpileModule: vi.fn(actual.default.transpileModule),
15
+ },
16
+ };
17
+ });
18
+ function transformedCode(result) {
19
+ if (result == null || typeof result === "string")
20
+ return result;
21
+ return result.code;
22
+ }
23
+ function findPlugin(plugins, name) {
24
+ const plugin = plugins.find((p) => p.name === name);
25
+ if (!plugin)
26
+ throw new Error(`plugin "${name}" not found`);
27
+ return plugin;
28
+ }
3
29
  describe("praxisjs() plugin factory", () => {
4
- it("returns two plugins by default (core + hmr)", () => {
30
+ it("returns three plugins by default (decorators + core + hmr)", () => {
5
31
  const plugins = praxisjs();
6
- expect(plugins).toHaveLength(2);
32
+ expect(plugins).toHaveLength(3);
7
33
  });
8
- it("returns one plugin when hmr is disabled", () => {
34
+ it("returns two plugins when hmr is disabled", () => {
9
35
  const plugins = praxisjs({ hmr: false });
10
- expect(plugins).toHaveLength(1);
36
+ expect(plugins).toHaveLength(2);
11
37
  });
12
38
  it("core plugin has the correct name", () => {
13
- const [core] = praxisjs();
39
+ const core = findPlugin(praxisjs(), "praxisjs:core");
14
40
  expect(core.name).toBe("praxisjs:core");
15
41
  });
16
42
  it("core plugin enforces 'pre'", () => {
17
- const [core] = praxisjs();
43
+ const core = findPlugin(praxisjs(), "praxisjs:core");
18
44
  expect(core.enforce).toBe("pre");
19
45
  });
20
46
  it("hmr plugin has the correct name", () => {
21
- const [, hmr] = praxisjs();
47
+ const hmr = findPlugin(praxisjs(), "praxisjs:hmr");
22
48
  expect(hmr.name).toBe("praxisjs:hmr");
23
49
  });
24
50
  it("hmr plugin enforces 'post'", () => {
25
- const [, hmr] = praxisjs();
51
+ const hmr = findPlugin(praxisjs(), "praxisjs:hmr");
26
52
  expect(hmr.enforce).toBe("post");
27
53
  });
54
+ it("omits the hmr plugin when hmr is disabled", () => {
55
+ const plugins = praxisjs({ hmr: false });
56
+ expect(plugins.find((p) => p.name === "praxisjs:hmr")).toBeUndefined();
57
+ });
58
+ it("includes the decorator-lowering plugin", () => {
59
+ const plugins = praxisjs();
60
+ expect(findPlugin(plugins, "praxisjs:decorators")).toBeDefined();
61
+ });
62
+ });
63
+ describe("decoratorLoweringPlugin() standalone export", () => {
64
+ it("is exported so it can be reused outside of praxisjs(), e.g. in vitest.config.ts", () => {
65
+ const plugin = decoratorLoweringPlugin();
66
+ expect(plugin.name).toBe("praxisjs:decorators");
67
+ });
68
+ it("lowers decorators the same way whether reused standalone or via praxisjs()", () => {
69
+ const code = `
70
+ function Dec() { return function (v, ctx) {}; }
71
+ class Foo {
72
+ @Dec()
73
+ accent = "red";
74
+ }
75
+ `;
76
+ const standalone = decoratorLoweringPlugin();
77
+ const result = transformedCode(standalone.transform?.(code, "component.ts"));
78
+ expect(result).toBeTruthy();
79
+ expect(result).not.toContain("@Dec()");
80
+ });
81
+ });
82
+ describe("praxisjs:decorators transform()", () => {
83
+ it("lowers a real decorator into runtime-executable code", () => {
84
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
85
+ const code = `
86
+ function Dec() { return function (v, ctx) {}; }
87
+ class Foo {
88
+ @Dec()
89
+ accent = "red";
90
+ }
91
+ `;
92
+ const result = transformedCode(decorators.transform?.(code, "component.ts"));
93
+ expect(result).toBeTruthy();
94
+ expect(result).not.toContain("@Dec()");
95
+ });
96
+ it("returns null for a file with '@' only in a comment (no real decorator)", () => {
97
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
98
+ const code = `
99
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
+ export function identity(signal) { return signal; }
101
+ `;
102
+ const result = decorators.transform?.(code, "utils.ts");
103
+ expect(result).toBeNull();
104
+ });
105
+ it("returns null for non-JS/TS files", () => {
106
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
107
+ const result = decorators.transform?.("@import 'x.css';", "styles.css");
108
+ expect(result).toBeNull();
109
+ });
110
+ it("lowers a real decorator in a .tsx file, leaving JSX untouched", () => {
111
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
112
+ const code = `
113
+ function Dec() { return function (v, ctx) {}; }
114
+ class Foo {
115
+ @Dec()
116
+ accent = "red";
117
+ render() { return <div>{this.accent}</div>; }
118
+ }
119
+ `;
120
+ const result = transformedCode(decorators.transform?.(code, "component.tsx"));
121
+ expect(result).toBeTruthy();
122
+ expect(result).not.toContain("@Dec()");
123
+ expect(result).toContain("<div>{this.accent}</div>");
124
+ });
125
+ it("lowers a real decorator in a .jsx file, leaving JSX untouched", () => {
126
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
127
+ const code = `
128
+ function Dec() { return function (v, ctx) {}; }
129
+ class Foo {
130
+ @Dec()
131
+ accent = "red";
132
+ render() { return <div>{this.accent}</div>; }
133
+ }
134
+ `;
135
+ const result = transformedCode(decorators.transform?.(code, "component.jsx"));
136
+ expect(result).toBeTruthy();
137
+ expect(result).not.toContain("@Dec()");
138
+ expect(result).toContain("<div>{this.accent}</div>");
139
+ });
140
+ it("lowers a real decorator in a plain .js file", () => {
141
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
142
+ const code = `
143
+ function Dec() { return function (v, ctx) {}; }
144
+ class Foo {
145
+ @Dec()
146
+ accent = "red";
147
+ }
148
+ `;
149
+ const result = transformedCode(decorators.transform?.(code, "component.js"));
150
+ expect(result).toBeTruthy();
151
+ expect(result).not.toContain("@Dec()");
152
+ });
153
+ it("keeps a field decorator's addInitializer running after super() in a derived class", () => {
154
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
155
+ const code = `
156
+ function Dec() {
157
+ return function (value, ctx) {
158
+ ctx.addInitializer(function () { this.order.push("field:" + ctx.name); });
159
+ };
160
+ }
161
+ class Base {
162
+ constructor(order) { this.order = order; this.order.push("super"); }
163
+ }
164
+ class Comp extends Base {
165
+ @Dec()
166
+ accent = "red";
167
+ constructor(order) {
168
+ super(order);
169
+ order.push("after-super");
170
+ }
171
+ }
172
+ `;
173
+ const result = transformedCode(decorators.transform?.(code, "component.ts"));
174
+ expect(result).toBeTruthy();
175
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
176
+ const factory = new Function(`${result}\nreturn Comp;`);
177
+ const Comp = factory();
178
+ const order = [];
179
+ new Comp(order);
180
+ expect(order).toEqual(["super", "field:accent", "after-super"]);
181
+ });
182
+ it("throws on invalid decorator syntax instead of silently emitting broken code", () => {
183
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
184
+ const code = `
185
+ class Foo {
186
+ @ accent = "red";
187
+ }
188
+ `;
189
+ // An absolute id (as Vite always passes) makes TypeScript canonicalize
190
+ // the file name while formatting the diagnostic message.
191
+ expect(() => decorators.transform?.(code, "/src/component.ts")).toThrow();
192
+ });
193
+ it("returns a null map when ts.transpileModule omits sourceMapText", () => {
194
+ const transpileModule = ts.transpileModule;
195
+ transpileModule.mockReturnValueOnce({
196
+ outputText: "class Foo {}",
197
+ diagnostics: [],
198
+ sourceMapText: undefined,
199
+ });
200
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
201
+ const result = decorators.transform?.("class Foo { @Dec() x = 1; }", "component.ts");
202
+ expect(result).toEqual({ code: "class Foo {}", map: null });
203
+ });
204
+ it("does not throw when ts.transpileModule omits diagnostics", () => {
205
+ const transpileModule = ts.transpileModule;
206
+ transpileModule.mockReturnValueOnce({
207
+ outputText: "class Foo {}",
208
+ diagnostics: undefined,
209
+ sourceMapText: undefined,
210
+ });
211
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
212
+ expect(() => decorators.transform?.("class Foo { @Dec() x = 1; }", "component.ts")).not.toThrow();
213
+ });
28
214
  });
29
215
  describe("praxisjs:core config()", () => {
30
- it("sets esbuild target to es2022", () => {
31
- const [core] = praxisjs();
32
- const config = core.config();
33
- expect(config).toEqual({ esbuild: { target: "es2022" } });
216
+ it("sets oxc target to es2022", () => {
217
+ const core = findPlugin(praxisjs(), "praxisjs:core");
218
+ const config = core.config?.();
219
+ expect(config).toEqual({ oxc: { target: "es2022" } });
34
220
  });
35
221
  });
36
222
  describe("praxisjs:core transform()", () => {
37
223
  it("returns null for .tsx files when autoImport is enabled", () => {
38
- const [core] = praxisjs({ autoImport: true });
39
- const transform = core.transform;
40
- const result = transform("const x = 1;", "component.tsx");
224
+ const core = findPlugin(praxisjs({ autoImport: true }), "praxisjs:core");
225
+ const result = core.transform?.("const x = 1;", "component.tsx");
41
226
  expect(result).toBeNull();
42
227
  });
43
228
  it("returns null for .jsx files when autoImport is enabled", () => {
44
- const [core] = praxisjs({ autoImport: true });
45
- const transform = core.transform;
46
- const result = transform("const x = 1;", "component.jsx");
229
+ const core = findPlugin(praxisjs({ autoImport: true }), "praxisjs:core");
230
+ const result = core.transform?.("const x = 1;", "component.jsx");
47
231
  expect(result).toBeNull();
48
232
  });
49
233
  it("returns undefined (no-op) for non-.tsx/.jsx files", () => {
50
- const [core] = praxisjs({ autoImport: true });
51
- const transform = core.transform;
52
- const result = transform("const x = 1;", "module.ts");
234
+ const core = findPlugin(praxisjs({ autoImport: true }), "praxisjs:core");
235
+ const result = core.transform?.("const x = 1;", "module.ts");
53
236
  expect(result).toBeUndefined();
54
237
  });
55
238
  it("returns undefined when autoImport is false", () => {
56
- const [core] = praxisjs({ autoImport: false });
57
- const transform = core.transform;
58
- const result = transform("const x = 1;", "component.tsx");
239
+ const core = findPlugin(praxisjs({ autoImport: false }), "praxisjs:core");
240
+ const result = core.transform?.("const x = 1;", "component.tsx");
59
241
  expect(result).toBeUndefined();
60
242
  });
61
243
  it("returns undefined when code already imports @praxisjs/jsx/jsx-runtime", () => {
62
- const [core] = praxisjs({ autoImport: true });
63
- const transform = core.transform;
244
+ const core = findPlugin(praxisjs({ autoImport: true }), "praxisjs:core");
64
245
  const code = 'import "@praxisjs/jsx/jsx-runtime"; const x = 1;';
65
- const result = transform(code, "component.tsx");
246
+ const result = core.transform?.(code, "component.tsx");
66
247
  expect(result).toBeUndefined();
67
248
  });
68
249
  });
@@ -75,12 +256,9 @@ describe("praxisjs:hmr handleHotUpdate()", () => {
75
256
  };
76
257
  }
77
258
  it("sends custom event for .ts files", () => {
78
- const [, hmr] = praxisjs({ hmr: true });
259
+ const hmr = findPlugin(praxisjs({ hmr: true }), "praxisjs:hmr");
79
260
  const server = makeMockServer();
80
- hmr.handleHotUpdate({
81
- file: "src/app.ts",
82
- server,
83
- });
261
+ hmr.handleHotUpdate?.({ file: "src/app.ts", server });
84
262
  expect(server.ws.send).toHaveBeenCalledWith({
85
263
  type: "custom",
86
264
  event: "praxisjs:component-update",
@@ -88,12 +266,9 @@ describe("praxisjs:hmr handleHotUpdate()", () => {
88
266
  });
89
267
  });
90
268
  it("sends custom event for .tsx files", () => {
91
- const [, hmr] = praxisjs({ hmr: true });
269
+ const hmr = findPlugin(praxisjs({ hmr: true }), "praxisjs:hmr");
92
270
  const server = makeMockServer();
93
- hmr.handleHotUpdate({
94
- file: "src/component.tsx",
95
- server,
96
- });
271
+ hmr.handleHotUpdate?.({ file: "src/component.tsx", server });
97
272
  expect(server.ws.send).toHaveBeenCalledWith({
98
273
  type: "custom",
99
274
  event: "praxisjs:component-update",
@@ -101,21 +276,15 @@ describe("praxisjs:hmr handleHotUpdate()", () => {
101
276
  });
102
277
  });
103
278
  it("does not send event for non-.ts/.tsx files", () => {
104
- const [, hmr] = praxisjs({ hmr: true });
279
+ const hmr = findPlugin(praxisjs({ hmr: true }), "praxisjs:hmr");
105
280
  const server = makeMockServer();
106
- hmr.handleHotUpdate({
107
- file: "src/style.css",
108
- server,
109
- });
281
+ hmr.handleHotUpdate?.({ file: "src/style.css", server });
110
282
  expect(server.ws.send).not.toHaveBeenCalled();
111
283
  });
112
284
  it("does not send event for .js files", () => {
113
- const [, hmr] = praxisjs({ hmr: true });
285
+ const hmr = findPlugin(praxisjs({ hmr: true }), "praxisjs:hmr");
114
286
  const server = makeMockServer();
115
- hmr.handleHotUpdate({
116
- file: "src/utils.js",
117
- server,
118
- });
287
+ hmr.handleHotUpdate?.({ file: "src/utils.js", server });
119
288
  expect(server.ws.send).not.toHaveBeenCalled();
120
289
  });
121
290
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAElD,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,OAAO,GAAG,QAAQ,EAAE,CAAC;QAC3B,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,OAAO,GAAG,QAAQ,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QACzC,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAI,IAA6C,CAAC,MAAM,EAAE,CAAC;QACvE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IAGzC,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAI,IAAmC,CAAC,SAAS,CAAC;QACjE,MAAM,MAAM,GAAG,SAAS,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAI,IAAmC,CAAC,SAAS,CAAC;QACjE,MAAM,MAAM,GAAG,SAAS,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAI,IAAmC,CAAC,SAAS,CAAC;QACjE,MAAM,MAAM,GAAG,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAI,IAAmC,CAAC,SAAS,CAAC;QACjE,MAAM,MAAM,GAAG,SAAS,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAI,IAAmC,CAAC,SAAS,CAAC;QACjE,MAAM,IAAI,GAAG,kDAAkD,CAAC;QAChE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAG9C,SAAS,cAAc;QACrB,OAAO;YACL,EAAE,EAAE;gBACF,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE;aACd;SACF,CAAC;IACJ,CAAC;IAED,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;QAC/B,GAAuD,CAAC,eAAe,CAAC;YACvE,IAAI,EAAE,YAAY;YAClB,MAAM;SACP,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC;YAC1C,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,2BAA2B;YAClC,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;QAC/B,GAAuD,CAAC,eAAe,CAAC;YACvE,IAAI,EAAE,mBAAmB;YACzB,MAAM;SACP,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC;YAC1C,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,2BAA2B;YAClC,IAAI,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE;SACpC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;QAC/B,GAAuD,CAAC,eAAe,CAAC;YACvE,IAAI,EAAE,eAAe;YACrB,MAAM;SACP,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;QAC/B,GAAuD,CAAC,eAAe,CAAC;YACvE,IAAI,EAAE,cAAc;YACpB,MAAM;SACP,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from "vite";
2
+ export declare function decoratorLoweringPlugin(): Plugin;
3
+ //# sourceMappingURL=decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAiEnC,wBAAgB,uBAAuB,IAAI,MAAM,CA+BhD"}
@@ -0,0 +1,84 @@
1
+ import ts from "typescript";
2
+ const JS_LIKE_FILE = /\.[cm]?[jt]sx?(?:$|\?)/;
3
+ function cleanFileName(id) {
4
+ return id.split("?")[0];
5
+ }
6
+ function scriptKindFor(fileName) {
7
+ if (fileName.endsWith(".tsx"))
8
+ return ts.ScriptKind.TSX;
9
+ if (fileName.endsWith(".jsx"))
10
+ return ts.ScriptKind.JSX;
11
+ if (fileName.endsWith(".ts") || fileName.endsWith(".mts") || fileName.endsWith(".cts")) {
12
+ return ts.ScriptKind.TS;
13
+ }
14
+ return ts.ScriptKind.JS;
15
+ }
16
+ // The `code.includes("@")` pre-filter below also matches "@" in comments
17
+ // (JSDoc, eslint-disable directives), so this walks a real AST to confirm
18
+ // actual decorator syntax is present before reprocessing the file — some
19
+ // framework code (`@praxisjs/concurrent`'s `acceptsSignal`) relies on
20
+ // `fn.toString()` returning untouched source.
21
+ function hasRealDecorator(code, fileName) {
22
+ const source = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, false, scriptKindFor(fileName));
23
+ let found = false;
24
+ const visit = (node) => {
25
+ if (found)
26
+ return;
27
+ if (ts.canHaveDecorators(node) && (ts.getDecorators(node)?.length ?? 0) > 0) {
28
+ found = true;
29
+ return;
30
+ }
31
+ ts.forEachChild(node, visit);
32
+ };
33
+ visit(source);
34
+ return found;
35
+ }
36
+ function formatDiagnostics(diagnostics) {
37
+ return ts.formatDiagnostics(diagnostics, {
38
+ getCanonicalFileName: (fileName) => fileName,
39
+ getCurrentDirectory: () => "",
40
+ getNewLine: () => "\n",
41
+ });
42
+ }
43
+ // oxc doesn't lower TC39 (non-legacy) decorators yet — `@Decorator()`
44
+ // syntax passes through unchanged and no JS runtime executes it directly:
45
+ // https://github.com/oxc-project/oxc/issues/9170
46
+ //
47
+ // TypeScript's compiler lowers them instead (the same lowering `tsc` uses
48
+ // to build every package here). At ES2022 a decorated field stays a real
49
+ // class field rather than being rewritten into a constructor assignment,
50
+ // so its initializer runs after `super()` automatically — no separate
51
+ // ordering fix-up is needed. JSX is left untouched (`jsx: "preserve"`) for
52
+ // oxc's own JSX pipeline to handle.
53
+ export function decoratorLoweringPlugin() {
54
+ return {
55
+ name: "praxisjs:decorators",
56
+ transform(code, id) {
57
+ if (!JS_LIKE_FILE.test(id) || !code.includes("@"))
58
+ return null;
59
+ const fileName = cleanFileName(id);
60
+ if (!hasRealDecorator(code, fileName))
61
+ return null;
62
+ const { outputText, sourceMapText, diagnostics } = ts.transpileModule(code, {
63
+ fileName,
64
+ reportDiagnostics: true,
65
+ compilerOptions: {
66
+ target: ts.ScriptTarget.ES2022,
67
+ module: ts.ModuleKind.ESNext,
68
+ useDefineForClassFields: true,
69
+ jsx: ts.JsxEmit.Preserve,
70
+ sourceMap: true,
71
+ },
72
+ });
73
+ const errors = diagnostics?.filter((d) => d.category === ts.DiagnosticCategory.Error) ?? [];
74
+ if (errors.length > 0) {
75
+ throw new Error(`[praxisjs:decorators] ${fileName}:\n${formatDiagnostics(errors)}`);
76
+ }
77
+ return {
78
+ code: outputText,
79
+ map: sourceMapText ? JSON.parse(sourceMapText) : null,
80
+ };
81
+ },
82
+ };
83
+ }
84
+ //# sourceMappingURL=decorators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import { praxisjsCSS } from "./css.js";
2
- import type { Plugin } from "vite";
2
+ import { decoratorLoweringPlugin } from "./decorators.js";
3
+ import type { PluginOption } from "vite";
3
4
  export { praxisjsCSS };
5
+ export { decoratorLoweringPlugin };
4
6
  export interface PraxisJSVitePluginOptions {
5
7
  hmr?: boolean;
6
8
  autoImport?: boolean;
7
9
  }
8
- export declare function praxisjs(options?: PraxisJSVitePluginOptions): Plugin[];
10
+ export declare function praxisjs(options?: PraxisJSVitePluginOptions): PluginOption[];
9
11
  export default praxisjs;
10
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,CAAC;AAEvB,MAAM,WAAW,yBAAyB;IACxC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,QAAQ,CAAC,OAAO,GAAE,yBAA8B,GAAG,MAAM,EAAE,CA6C1E;AAED,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,MAAM,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,OAAO,EAAE,uBAAuB,EAAE,CAAC;AAEnC,MAAM,WAAW,yBAAyB;IACxC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,QAAQ,CAAC,OAAO,GAAE,yBAA8B,GAAG,YAAY,EAAE,CA8ChF;AAED,eAAe,QAAQ,CAAC"}
package/dist/index.js CHANGED
@@ -1,14 +1,17 @@
1
1
  import { praxisjsCSS } from "./css.js";
2
+ import { decoratorLoweringPlugin } from "./decorators.js";
2
3
  export { praxisjsCSS };
4
+ export { decoratorLoweringPlugin };
3
5
  export function praxisjs(options = {}) {
4
6
  const { hmr = true, autoImport = true } = options;
5
7
  return [
8
+ decoratorLoweringPlugin(),
6
9
  {
7
10
  name: "praxisjs:core",
8
11
  enforce: "pre",
9
12
  config() {
10
13
  return {
11
- esbuild: {
14
+ oxc: {
12
15
  target: "es2022",
13
16
  },
14
17
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAIvC,OAAO,EAAE,WAAW,EAAE,CAAC;AAOvB,MAAM,UAAU,QAAQ,CAAC,UAAqC,EAAE;IAC9D,MAAM,EAAE,GAAG,GAAG,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAElD,OAAO;QACL;YACE,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,KAAK;YACd,MAAM;gBACJ,OAAO;oBACL,OAAO,EAAE;wBACP,MAAM,EAAE,QAAQ;qBACjB;iBACF,CAAC;YACJ,CAAC;YACD,SAAS,CAAC,IAAI,EAAE,EAAE;gBAChB,IAAI,CAAC,UAAU;oBAAE,OAAO;gBACxB,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;oBAAE,OAAO;gBACzD,IAAI,IAAI,CAAC,QAAQ,CAAC,2BAA2B,CAAC;oBAAE,OAAO;gBACvD,OAAO,IAAI,CAAC;YACd,CAAC;SACF;QACD,GAAG,CAAC,GAAG;YACL,CAAC,CAAC;gBACE;oBACE,IAAI,EAAE,cAAc;oBACpB,OAAO,EAAE,MAAe;oBACxB,eAAe,CAAC,EACd,IAAI,EACJ,MAAM,GAIP;wBACC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;4BAAE,OAAO;wBAE5D,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC;4BACb,IAAI,EAAE,QAAQ;4BACd,KAAK,EAAE,2BAA2B;4BAClC,IAAI,EAAE,EAAE,IAAI,EAAE;yBACf,CAAC,CAAC;oBACL,CAAC;iBACF;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC;AAED,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAI1D,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,OAAO,EAAE,uBAAuB,EAAE,CAAC;AAOnC,MAAM,UAAU,QAAQ,CAAC,UAAqC,EAAE;IAC9D,MAAM,EAAE,GAAG,GAAG,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAElD,OAAO;QACL,uBAAuB,EAAE;QACzB;YACE,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,KAAK;YACd,MAAM;gBACJ,OAAO;oBACL,GAAG,EAAE;wBACH,MAAM,EAAE,QAAQ;qBACjB;iBACF,CAAC;YACJ,CAAC;YACD,SAAS,CAAC,IAAI,EAAE,EAAE;gBAChB,IAAI,CAAC,UAAU;oBAAE,OAAO;gBACxB,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;oBAAE,OAAO;gBACzD,IAAI,IAAI,CAAC,QAAQ,CAAC,2BAA2B,CAAC;oBAAE,OAAO;gBACvD,OAAO,IAAI,CAAC;YACd,CAAC;SACF;QACD,GAAG,CAAC,GAAG;YACL,CAAC,CAAC;gBACE;oBACE,IAAI,EAAE,cAAc;oBACpB,OAAO,EAAE,MAAe;oBACxB,eAAe,CAAC,EACd,IAAI,EACJ,MAAM,GAIP;wBACC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;4BAAE,OAAO;wBAE5D,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC;4BACb,IAAI,EAAE,QAAQ;4BACd,KAAK,EAAE,2BAA2B;4BAClC,IAAI,EAAE,EAAE,IAAI,EAAE;yBACf,CAAC,CAAC;oBACL,CAAC;iBACF;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC;AAED,eAAe,QAAQ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisjs/vite-plugin",
3
- "version": "3.0.0",
3
+ "version": "4.0.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -11,13 +11,16 @@
11
11
  }
12
12
  },
13
13
  "peerDependencies": {
14
+ "vite": ">=8.0.0",
14
15
  "@praxisjs/css": "0.3.0"
15
16
  },
17
+ "dependencies": {
18
+ "esbuild": "^0.28.1",
19
+ "typescript": "^6.0.3"
20
+ },
16
21
  "devDependencies": {
17
22
  "@types/node": "^25.9.1",
18
- "esbuild": "^0.25.12",
19
- "typescript": "^5.9.3",
20
- "vite": "^7.3.1",
23
+ "vite": "^8.1.4",
21
24
  "@praxisjs/css": "0.3.0"
22
25
  },
23
26
  "scripts": {
@@ -1,90 +1,299 @@
1
+ import ts from "typescript";
1
2
  import { describe, it, expect, vi } from "vitest";
2
3
 
3
- import { praxisjs } from "../index";
4
+ import { praxisjs, decoratorLoweringPlugin } from "../index";
5
+
6
+ // `ts.transpileModule`'s own types mark `diagnostics` and `sourceMapText` as
7
+ // optional regardless of the options passed in — wrapping it lets a couple
8
+ // of tests below simulate that contract even though it never happens with
9
+ // the fixed options `decoratorLoweringPlugin()` always calls it with.
10
+ vi.mock("typescript", async (importOriginal) => {
11
+ const actual = (await importOriginal()) as typeof import("typescript") & {
12
+ default: typeof import("typescript");
13
+ };
14
+ return {
15
+ ...actual,
16
+ default: {
17
+ ...actual.default,
18
+ transpileModule: vi.fn(actual.default.transpileModule),
19
+ },
20
+ };
21
+ });
22
+
23
+ interface TransformResult {
24
+ code: string;
25
+ map?: unknown;
26
+ }
27
+
28
+ interface NamedPlugin {
29
+ name?: string;
30
+ enforce?: string;
31
+ config?: () => unknown;
32
+ transform?: (code: string, id: string) => string | TransformResult | null | undefined;
33
+ handleHotUpdate?: (ctx: { file: string; server: unknown }) => void;
34
+ }
35
+
36
+ function transformedCode(result: string | TransformResult | null | undefined): string | null | undefined {
37
+ if (result == null || typeof result === "string") return result;
38
+ return result.code;
39
+ }
40
+
41
+ function findPlugin(plugins: ReturnType<typeof praxisjs>, name: string): NamedPlugin {
42
+ const plugin = (plugins as NamedPlugin[]).find((p) => p.name === name);
43
+ if (!plugin) throw new Error(`plugin "${name}" not found`);
44
+ return plugin;
45
+ }
4
46
 
5
47
  describe("praxisjs() plugin factory", () => {
6
- it("returns two plugins by default (core + hmr)", () => {
48
+ it("returns three plugins by default (decorators + core + hmr)", () => {
7
49
  const plugins = praxisjs();
8
- expect(plugins).toHaveLength(2);
50
+ expect(plugins).toHaveLength(3);
9
51
  });
10
52
 
11
- it("returns one plugin when hmr is disabled", () => {
53
+ it("returns two plugins when hmr is disabled", () => {
12
54
  const plugins = praxisjs({ hmr: false });
13
- expect(plugins).toHaveLength(1);
55
+ expect(plugins).toHaveLength(2);
14
56
  });
15
57
 
16
58
  it("core plugin has the correct name", () => {
17
- const [core] = praxisjs();
59
+ const core = findPlugin(praxisjs(), "praxisjs:core");
18
60
  expect(core.name).toBe("praxisjs:core");
19
61
  });
20
62
 
21
63
  it("core plugin enforces 'pre'", () => {
22
- const [core] = praxisjs();
64
+ const core = findPlugin(praxisjs(), "praxisjs:core");
23
65
  expect(core.enforce).toBe("pre");
24
66
  });
25
67
 
26
68
  it("hmr plugin has the correct name", () => {
27
- const [, hmr] = praxisjs();
69
+ const hmr = findPlugin(praxisjs(), "praxisjs:hmr");
28
70
  expect(hmr.name).toBe("praxisjs:hmr");
29
71
  });
30
72
 
31
73
  it("hmr plugin enforces 'post'", () => {
32
- const [, hmr] = praxisjs();
74
+ const hmr = findPlugin(praxisjs(), "praxisjs:hmr");
33
75
  expect(hmr.enforce).toBe("post");
34
76
  });
77
+
78
+ it("omits the hmr plugin when hmr is disabled", () => {
79
+ const plugins = praxisjs({ hmr: false }) as NamedPlugin[];
80
+ expect(plugins.find((p) => p.name === "praxisjs:hmr")).toBeUndefined();
81
+ });
82
+
83
+ it("includes the decorator-lowering plugin", () => {
84
+ const plugins = praxisjs();
85
+ expect(findPlugin(plugins, "praxisjs:decorators")).toBeDefined();
86
+ });
87
+ });
88
+
89
+ describe("decoratorLoweringPlugin() standalone export", () => {
90
+ it("is exported so it can be reused outside of praxisjs(), e.g. in vitest.config.ts", () => {
91
+ const plugin = decoratorLoweringPlugin() as NamedPlugin;
92
+ expect(plugin.name).toBe("praxisjs:decorators");
93
+ });
94
+
95
+ it("lowers decorators the same way whether reused standalone or via praxisjs()", () => {
96
+ const code = `
97
+ function Dec() { return function (v, ctx) {}; }
98
+ class Foo {
99
+ @Dec()
100
+ accent = "red";
101
+ }
102
+ `;
103
+ const standalone = decoratorLoweringPlugin() as NamedPlugin;
104
+ const result = transformedCode(standalone.transform?.(code, "component.ts"));
105
+ expect(result).toBeTruthy();
106
+ expect(result).not.toContain("@Dec()");
107
+ });
108
+ });
109
+
110
+ describe("praxisjs:decorators transform()", () => {
111
+ it("lowers a real decorator into runtime-executable code", () => {
112
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
113
+ const code = `
114
+ function Dec() { return function (v, ctx) {}; }
115
+ class Foo {
116
+ @Dec()
117
+ accent = "red";
118
+ }
119
+ `;
120
+ const result = transformedCode(decorators.transform?.(code, "component.ts"));
121
+ expect(result).toBeTruthy();
122
+ expect(result).not.toContain("@Dec()");
123
+ });
124
+
125
+ it("returns null for a file with '@' only in a comment (no real decorator)", () => {
126
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
127
+ const code = `
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
+ export function identity(signal) { return signal; }
130
+ `;
131
+ const result = decorators.transform?.(code, "utils.ts");
132
+ expect(result).toBeNull();
133
+ });
134
+
135
+ it("returns null for non-JS/TS files", () => {
136
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
137
+ const result = decorators.transform?.("@import 'x.css';", "styles.css");
138
+ expect(result).toBeNull();
139
+ });
140
+
141
+ it("lowers a real decorator in a .tsx file, leaving JSX untouched", () => {
142
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
143
+ const code = `
144
+ function Dec() { return function (v, ctx) {}; }
145
+ class Foo {
146
+ @Dec()
147
+ accent = "red";
148
+ render() { return <div>{this.accent}</div>; }
149
+ }
150
+ `;
151
+ const result = transformedCode(decorators.transform?.(code, "component.tsx"));
152
+ expect(result).toBeTruthy();
153
+ expect(result).not.toContain("@Dec()");
154
+ expect(result).toContain("<div>{this.accent}</div>");
155
+ });
156
+
157
+ it("lowers a real decorator in a .jsx file, leaving JSX untouched", () => {
158
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
159
+ const code = `
160
+ function Dec() { return function (v, ctx) {}; }
161
+ class Foo {
162
+ @Dec()
163
+ accent = "red";
164
+ render() { return <div>{this.accent}</div>; }
165
+ }
166
+ `;
167
+ const result = transformedCode(decorators.transform?.(code, "component.jsx"));
168
+ expect(result).toBeTruthy();
169
+ expect(result).not.toContain("@Dec()");
170
+ expect(result).toContain("<div>{this.accent}</div>");
171
+ });
172
+
173
+ it("lowers a real decorator in a plain .js file", () => {
174
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
175
+ const code = `
176
+ function Dec() { return function (v, ctx) {}; }
177
+ class Foo {
178
+ @Dec()
179
+ accent = "red";
180
+ }
181
+ `;
182
+ const result = transformedCode(decorators.transform?.(code, "component.js"));
183
+ expect(result).toBeTruthy();
184
+ expect(result).not.toContain("@Dec()");
185
+ });
186
+
187
+ it("keeps a field decorator's addInitializer running after super() in a derived class", () => {
188
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
189
+ const code = `
190
+ function Dec() {
191
+ return function (value, ctx) {
192
+ ctx.addInitializer(function () { this.order.push("field:" + ctx.name); });
193
+ };
194
+ }
195
+ class Base {
196
+ constructor(order) { this.order = order; this.order.push("super"); }
197
+ }
198
+ class Comp extends Base {
199
+ @Dec()
200
+ accent = "red";
201
+ constructor(order) {
202
+ super(order);
203
+ order.push("after-super");
204
+ }
205
+ }
206
+ `;
207
+ const result = transformedCode(decorators.transform?.(code, "component.ts"));
208
+ expect(result).toBeTruthy();
209
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
210
+ const factory = new Function(`${result as string}\nreturn Comp;`);
211
+ const Comp = factory() as new (order: string[]) => unknown;
212
+ const order: string[] = [];
213
+ new Comp(order);
214
+ expect(order).toEqual(["super", "field:accent", "after-super"]);
215
+ });
216
+
217
+ it("throws on invalid decorator syntax instead of silently emitting broken code", () => {
218
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
219
+ const code = `
220
+ class Foo {
221
+ @ accent = "red";
222
+ }
223
+ `;
224
+ // An absolute id (as Vite always passes) makes TypeScript canonicalize
225
+ // the file name while formatting the diagnostic message.
226
+ expect(() => decorators.transform?.(code, "/src/component.ts")).toThrow();
227
+ });
228
+
229
+ it("returns a null map when ts.transpileModule omits sourceMapText", () => {
230
+ const transpileModule = ts.transpileModule as unknown as ReturnType<typeof vi.fn>;
231
+ transpileModule.mockReturnValueOnce({
232
+ outputText: "class Foo {}",
233
+ diagnostics: [],
234
+ sourceMapText: undefined,
235
+ });
236
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
237
+ const result = decorators.transform?.("class Foo { @Dec() x = 1; }", "component.ts");
238
+ expect(result).toEqual({ code: "class Foo {}", map: null });
239
+ });
240
+
241
+ it("does not throw when ts.transpileModule omits diagnostics", () => {
242
+ const transpileModule = ts.transpileModule as unknown as ReturnType<typeof vi.fn>;
243
+ transpileModule.mockReturnValueOnce({
244
+ outputText: "class Foo {}",
245
+ diagnostics: undefined,
246
+ sourceMapText: undefined,
247
+ });
248
+ const decorators = findPlugin(praxisjs(), "praxisjs:decorators");
249
+ expect(() =>
250
+ decorators.transform?.("class Foo { @Dec() x = 1; }", "component.ts"),
251
+ ).not.toThrow();
252
+ });
35
253
  });
36
254
 
37
255
  describe("praxisjs:core config()", () => {
38
- it("sets esbuild target to es2022", () => {
39
- const [core] = praxisjs();
40
- const config = (core as unknown as { config: () => unknown }).config();
41
- expect(config).toEqual({ esbuild: { target: "es2022" } });
256
+ it("sets oxc target to es2022", () => {
257
+ const core = findPlugin(praxisjs(), "praxisjs:core");
258
+ const config = core.config?.();
259
+ expect(config).toEqual({ oxc: { target: "es2022" } });
42
260
  });
43
261
  });
44
262
 
45
263
  describe("praxisjs:core transform()", () => {
46
- type TransformFn = (code: string, id: string) => null | undefined;
47
-
48
264
  it("returns null for .tsx files when autoImport is enabled", () => {
49
- const [core] = praxisjs({ autoImport: true });
50
- const transform = (core as { transform: TransformFn }).transform;
51
- const result = transform("const x = 1;", "component.tsx");
265
+ const core = findPlugin(praxisjs({ autoImport: true }), "praxisjs:core");
266
+ const result = core.transform?.("const x = 1;", "component.tsx");
52
267
  expect(result).toBeNull();
53
268
  });
54
269
 
55
270
  it("returns null for .jsx files when autoImport is enabled", () => {
56
- const [core] = praxisjs({ autoImport: true });
57
- const transform = (core as { transform: TransformFn }).transform;
58
- const result = transform("const x = 1;", "component.jsx");
271
+ const core = findPlugin(praxisjs({ autoImport: true }), "praxisjs:core");
272
+ const result = core.transform?.("const x = 1;", "component.jsx");
59
273
  expect(result).toBeNull();
60
274
  });
61
275
 
62
276
  it("returns undefined (no-op) for non-.tsx/.jsx files", () => {
63
- const [core] = praxisjs({ autoImport: true });
64
- const transform = (core as { transform: TransformFn }).transform;
65
- const result = transform("const x = 1;", "module.ts");
277
+ const core = findPlugin(praxisjs({ autoImport: true }), "praxisjs:core");
278
+ const result = core.transform?.("const x = 1;", "module.ts");
66
279
  expect(result).toBeUndefined();
67
280
  });
68
281
 
69
282
  it("returns undefined when autoImport is false", () => {
70
- const [core] = praxisjs({ autoImport: false });
71
- const transform = (core as { transform: TransformFn }).transform;
72
- const result = transform("const x = 1;", "component.tsx");
283
+ const core = findPlugin(praxisjs({ autoImport: false }), "praxisjs:core");
284
+ const result = core.transform?.("const x = 1;", "component.tsx");
73
285
  expect(result).toBeUndefined();
74
286
  });
75
287
 
76
288
  it("returns undefined when code already imports @praxisjs/jsx/jsx-runtime", () => {
77
- const [core] = praxisjs({ autoImport: true });
78
- const transform = (core as { transform: TransformFn }).transform;
289
+ const core = findPlugin(praxisjs({ autoImport: true }), "praxisjs:core");
79
290
  const code = 'import "@praxisjs/jsx/jsx-runtime"; const x = 1;';
80
- const result = transform(code, "component.tsx");
291
+ const result = core.transform?.(code, "component.tsx");
81
292
  expect(result).toBeUndefined();
82
293
  });
83
294
  });
84
295
 
85
296
  describe("praxisjs:hmr handleHotUpdate()", () => {
86
- type HandleHotUpdate = (ctx: { file: string; server: unknown }) => void;
87
-
88
297
  function makeMockServer() {
89
298
  return {
90
299
  ws: {
@@ -94,12 +303,9 @@ describe("praxisjs:hmr handleHotUpdate()", () => {
94
303
  }
95
304
 
96
305
  it("sends custom event for .ts files", () => {
97
- const [, hmr] = praxisjs({ hmr: true });
306
+ const hmr = findPlugin(praxisjs({ hmr: true }), "praxisjs:hmr");
98
307
  const server = makeMockServer();
99
- (hmr as unknown as { handleHotUpdate: HandleHotUpdate }).handleHotUpdate({
100
- file: "src/app.ts",
101
- server,
102
- });
308
+ hmr.handleHotUpdate?.({ file: "src/app.ts", server });
103
309
  expect(server.ws.send).toHaveBeenCalledWith({
104
310
  type: "custom",
105
311
  event: "praxisjs:component-update",
@@ -108,12 +314,9 @@ describe("praxisjs:hmr handleHotUpdate()", () => {
108
314
  });
109
315
 
110
316
  it("sends custom event for .tsx files", () => {
111
- const [, hmr] = praxisjs({ hmr: true });
317
+ const hmr = findPlugin(praxisjs({ hmr: true }), "praxisjs:hmr");
112
318
  const server = makeMockServer();
113
- (hmr as unknown as { handleHotUpdate: HandleHotUpdate }).handleHotUpdate({
114
- file: "src/component.tsx",
115
- server,
116
- });
319
+ hmr.handleHotUpdate?.({ file: "src/component.tsx", server });
117
320
  expect(server.ws.send).toHaveBeenCalledWith({
118
321
  type: "custom",
119
322
  event: "praxisjs:component-update",
@@ -122,22 +325,16 @@ describe("praxisjs:hmr handleHotUpdate()", () => {
122
325
  });
123
326
 
124
327
  it("does not send event for non-.ts/.tsx files", () => {
125
- const [, hmr] = praxisjs({ hmr: true });
328
+ const hmr = findPlugin(praxisjs({ hmr: true }), "praxisjs:hmr");
126
329
  const server = makeMockServer();
127
- (hmr as unknown as { handleHotUpdate: HandleHotUpdate }).handleHotUpdate({
128
- file: "src/style.css",
129
- server,
130
- });
330
+ hmr.handleHotUpdate?.({ file: "src/style.css", server });
131
331
  expect(server.ws.send).not.toHaveBeenCalled();
132
332
  });
133
333
 
134
334
  it("does not send event for .js files", () => {
135
- const [, hmr] = praxisjs({ hmr: true });
335
+ const hmr = findPlugin(praxisjs({ hmr: true }), "praxisjs:hmr");
136
336
  const server = makeMockServer();
137
- (hmr as unknown as { handleHotUpdate: HandleHotUpdate }).handleHotUpdate({
138
- file: "src/utils.js",
139
- server,
140
- });
337
+ hmr.handleHotUpdate?.({ file: "src/utils.js", server });
141
338
  expect(server.ws.send).not.toHaveBeenCalled();
142
339
  });
143
340
  });
@@ -0,0 +1,99 @@
1
+ import ts from "typescript";
2
+
3
+ import type { Plugin } from "vite";
4
+
5
+ const JS_LIKE_FILE = /\.[cm]?[jt]sx?(?:$|\?)/;
6
+
7
+ function cleanFileName(id: string): string {
8
+ return id.split("?")[0];
9
+ }
10
+
11
+ function scriptKindFor(fileName: string): ts.ScriptKind {
12
+ if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
13
+ if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
14
+ if (fileName.endsWith(".ts") || fileName.endsWith(".mts") || fileName.endsWith(".cts")) {
15
+ return ts.ScriptKind.TS;
16
+ }
17
+ return ts.ScriptKind.JS;
18
+ }
19
+
20
+ // The `code.includes("@")` pre-filter below also matches "@" in comments
21
+ // (JSDoc, eslint-disable directives), so this walks a real AST to confirm
22
+ // actual decorator syntax is present before reprocessing the file — some
23
+ // framework code (`@praxisjs/concurrent`'s `acceptsSignal`) relies on
24
+ // `fn.toString()` returning untouched source.
25
+ function hasRealDecorator(code: string, fileName: string): boolean {
26
+ const source = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, false, scriptKindFor(fileName));
27
+ let found = false;
28
+ const visit = (node: ts.Node): void => {
29
+ if (found) return;
30
+ if (ts.canHaveDecorators(node) && (ts.getDecorators(node)?.length ?? 0) > 0) {
31
+ found = true;
32
+ return;
33
+ }
34
+ ts.forEachChild(node, visit);
35
+ };
36
+ visit(source);
37
+ return found;
38
+ }
39
+
40
+ interface RawSourceMap {
41
+ version: number;
42
+ file?: string;
43
+ sourceRoot?: string;
44
+ sources: string[];
45
+ sourcesContent?: Array<string | null>;
46
+ names: string[];
47
+ mappings: string;
48
+ }
49
+
50
+ function formatDiagnostics(diagnostics: readonly ts.Diagnostic[]): string {
51
+ return ts.formatDiagnostics(diagnostics, {
52
+ getCanonicalFileName: (fileName) => fileName,
53
+ getCurrentDirectory: () => "",
54
+ getNewLine: () => "\n",
55
+ });
56
+ }
57
+
58
+ // oxc doesn't lower TC39 (non-legacy) decorators yet — `@Decorator()`
59
+ // syntax passes through unchanged and no JS runtime executes it directly:
60
+ // https://github.com/oxc-project/oxc/issues/9170
61
+ //
62
+ // TypeScript's compiler lowers them instead (the same lowering `tsc` uses
63
+ // to build every package here). At ES2022 a decorated field stays a real
64
+ // class field rather than being rewritten into a constructor assignment,
65
+ // so its initializer runs after `super()` automatically — no separate
66
+ // ordering fix-up is needed. JSX is left untouched (`jsx: "preserve"`) for
67
+ // oxc's own JSX pipeline to handle.
68
+ export function decoratorLoweringPlugin(): Plugin {
69
+ return {
70
+ name: "praxisjs:decorators",
71
+ transform(code, id) {
72
+ if (!JS_LIKE_FILE.test(id) || !code.includes("@")) return null;
73
+ const fileName = cleanFileName(id);
74
+ if (!hasRealDecorator(code, fileName)) return null;
75
+
76
+ const { outputText, sourceMapText, diagnostics } = ts.transpileModule(code, {
77
+ fileName,
78
+ reportDiagnostics: true,
79
+ compilerOptions: {
80
+ target: ts.ScriptTarget.ES2022,
81
+ module: ts.ModuleKind.ESNext,
82
+ useDefineForClassFields: true,
83
+ jsx: ts.JsxEmit.Preserve,
84
+ sourceMap: true,
85
+ },
86
+ });
87
+
88
+ const errors = diagnostics?.filter((d) => d.category === ts.DiagnosticCategory.Error) ?? [];
89
+ if (errors.length > 0) {
90
+ throw new Error(`[praxisjs:decorators] ${fileName}:\n${formatDiagnostics(errors)}`);
91
+ }
92
+
93
+ return {
94
+ code: outputText,
95
+ map: sourceMapText ? (JSON.parse(sourceMapText) as RawSourceMap) : null,
96
+ };
97
+ },
98
+ };
99
+ }
package/src/index.ts CHANGED
@@ -1,24 +1,27 @@
1
1
  import { praxisjsCSS } from "./css.js";
2
+ import { decoratorLoweringPlugin } from "./decorators.js";
2
3
 
3
- import type { Plugin, ViteDevServer } from "vite";
4
+ import type { PluginOption, ViteDevServer } from "vite";
4
5
 
5
6
  export { praxisjsCSS };
7
+ export { decoratorLoweringPlugin };
6
8
 
7
9
  export interface PraxisJSVitePluginOptions {
8
10
  hmr?: boolean;
9
11
  autoImport?: boolean;
10
12
  }
11
13
 
12
- export function praxisjs(options: PraxisJSVitePluginOptions = {}): Plugin[] {
14
+ export function praxisjs(options: PraxisJSVitePluginOptions = {}): PluginOption[] {
13
15
  const { hmr = true, autoImport = true } = options;
14
16
 
15
17
  return [
18
+ decoratorLoweringPlugin(),
16
19
  {
17
20
  name: "praxisjs:core",
18
21
  enforce: "pre",
19
22
  config() {
20
23
  return {
21
- esbuild: {
24
+ oxc: {
22
25
  target: "es2022",
23
26
  },
24
27
  };