vite-plugin-sinwan 0.2.5 → 0.2.7
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/README.md +11 -0
- package/__tests__/plugin.test.ts +328 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +9 -1
- package/package.json +6 -4
- package/src/index.ts +20 -1
package/README.md
CHANGED
|
@@ -8,6 +8,14 @@ Vite plugin for [Sinwan](https://sinwanjs.com) — JSX transformation with templ
|
|
|
8
8
|
bun add -d vite-plugin-sinwan
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Compiler updates
|
|
12
|
+
|
|
13
|
+
The plugin depends on `sinwan-compiler` with `>=0.2.5 <1.0.0` and does not bundle it. Publishing a new 0.x compiler does **not** require a plugin release.
|
|
14
|
+
|
|
15
|
+
- New installs resolve the newest compatible compiler automatically.
|
|
16
|
+
- Existing apps keep a lockfile pin until they run `bun update sinwan-compiler`.
|
|
17
|
+
- Republish this plugin only when the plugin API itself changes, or when `sinwan-compiler` reaches 1.0.
|
|
18
|
+
|
|
11
19
|
## Usage
|
|
12
20
|
|
|
13
21
|
```ts
|
|
@@ -26,6 +34,8 @@ export default defineConfig({
|
|
|
26
34
|
sinwan({
|
|
27
35
|
// Enable template hoisting (default: true)
|
|
28
36
|
hoist: true,
|
|
37
|
+
// Warn when hoisting is skipped (default: Vite mode !== "production")
|
|
38
|
+
dev: true,
|
|
29
39
|
// Emit explicit binding descriptors (default: false)
|
|
30
40
|
explicitBindings: false,
|
|
31
41
|
// Path to reactive-props metadata from `sinwan analyze`
|
|
@@ -45,6 +55,7 @@ sinwan({
|
|
|
45
55
|
| Option | Type | Default | Description |
|
|
46
56
|
| ------------------ | ------------------------------- | ----------- | ----------------------------------------------------- |
|
|
47
57
|
| `hoist` | `boolean` | `true` | Hoist static DOM to module-level templates |
|
|
58
|
+
| `dev` | `boolean` | Vite `mode !== "production"` | Warn when template hoisting is skipped |
|
|
48
59
|
| `explicitBindings` | `boolean` | `false` | Emit compiler-driven binding descriptors |
|
|
49
60
|
| `analyze` | `string` | `undefined` | Path to reactive-props metadata from `sinwan analyze` |
|
|
50
61
|
| `cache` | `boolean \| SinwanCacheOptions` | `false` | Enable incremental in-memory cross-file analysis |
|
package/__tests__/plugin.test.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
2
4
|
import { sinwan } from "../src/index";
|
|
3
5
|
|
|
4
6
|
describe("sinwan vite plugin", () => {
|
|
@@ -30,3 +32,329 @@ describe("sinwan vite plugin", () => {
|
|
|
30
32
|
expect(plugin.name).toBe("sinwan");
|
|
31
33
|
});
|
|
32
34
|
});
|
|
35
|
+
|
|
36
|
+
const SPREAD_JSX = "const Card = (props) => <div {...props}><p>Hello</p></div>;";
|
|
37
|
+
|
|
38
|
+
function collectWarns(run: () => void): string[] {
|
|
39
|
+
const warns: string[] = [];
|
|
40
|
+
const originalWarn = console.warn;
|
|
41
|
+
console.warn = (...args: unknown[]) => {
|
|
42
|
+
warns.push(String(args[0]));
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
run();
|
|
46
|
+
return warns;
|
|
47
|
+
} finally {
|
|
48
|
+
console.warn = originalWarn;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
describe("compiler dev warnings", () => {
|
|
53
|
+
it("does not warn about skipped hoists when dev is false", () => {
|
|
54
|
+
const plugin = sinwan({ cache: false, dev: false });
|
|
55
|
+
const warns = collectWarns(() => {
|
|
56
|
+
plugin.transform(SPREAD_JSX, "Card.tsx");
|
|
57
|
+
});
|
|
58
|
+
expect(warns.some((line) => line.includes("template hoisting skipped"))).toBe(
|
|
59
|
+
false,
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("warns about skipped hoists when dev is true", () => {
|
|
64
|
+
const plugin = sinwan({ cache: false, dev: true });
|
|
65
|
+
const warns = collectWarns(() => {
|
|
66
|
+
plugin.transform(SPREAD_JSX, "Card.tsx");
|
|
67
|
+
});
|
|
68
|
+
expect(warns.some((line) => line.includes("template hoisting skipped"))).toBe(
|
|
69
|
+
true,
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("uses Vite production mode after configResolved unless dev is set", () => {
|
|
74
|
+
const plugin = sinwan({ cache: false });
|
|
75
|
+
plugin.configResolved({ command: "build", mode: "production", root: "/app" });
|
|
76
|
+
const warns = collectWarns(() => {
|
|
77
|
+
plugin.transform(SPREAD_JSX, "Card.tsx");
|
|
78
|
+
});
|
|
79
|
+
expect(warns.some((line) => line.includes("template hoisting skipped"))).toBe(
|
|
80
|
+
false,
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("uses Vite development mode after configResolved unless dev is set", () => {
|
|
85
|
+
const plugin = sinwan({ cache: false });
|
|
86
|
+
plugin.configResolved({ command: "serve", mode: "development", root: "/app" });
|
|
87
|
+
const warns = collectWarns(() => {
|
|
88
|
+
plugin.transform(SPREAD_JSX, "Card.tsx");
|
|
89
|
+
});
|
|
90
|
+
expect(warns.some((line) => line.includes("template hoisting skipped"))).toBe(
|
|
91
|
+
true,
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("keeps an explicit dev option after configResolved production mode", () => {
|
|
96
|
+
const plugin = sinwan({ cache: false, dev: true });
|
|
97
|
+
plugin.configResolved({ command: "build", mode: "production", root: "/app" });
|
|
98
|
+
const warns = collectWarns(() => {
|
|
99
|
+
plugin.transform(SPREAD_JSX, "Card.tsx");
|
|
100
|
+
});
|
|
101
|
+
expect(warns.some((line) => line.includes("template hoisting skipped"))).toBe(
|
|
102
|
+
true,
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("defaults to quiet when NODE_ENV is production before configResolved", () => {
|
|
107
|
+
const previous = process.env.NODE_ENV;
|
|
108
|
+
process.env.NODE_ENV = "production";
|
|
109
|
+
try {
|
|
110
|
+
const plugin = sinwan({ cache: false });
|
|
111
|
+
const warns = collectWarns(() => {
|
|
112
|
+
plugin.transform(SPREAD_JSX, "Card.tsx");
|
|
113
|
+
});
|
|
114
|
+
expect(
|
|
115
|
+
warns.some((line) => line.includes("template hoisting skipped")),
|
|
116
|
+
).toBe(false);
|
|
117
|
+
} finally {
|
|
118
|
+
if (previous === undefined) {
|
|
119
|
+
delete process.env.NODE_ENV;
|
|
120
|
+
} else {
|
|
121
|
+
process.env.NODE_ENV = previous;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("does not override captured env when config.mode is not a string", () => {
|
|
127
|
+
const previous = process.env.NODE_ENV;
|
|
128
|
+
process.env.NODE_ENV = "production";
|
|
129
|
+
try {
|
|
130
|
+
const plugin = sinwan({ cache: false });
|
|
131
|
+
plugin.configResolved({ command: "serve", root: "/app" });
|
|
132
|
+
const warns = collectWarns(() => {
|
|
133
|
+
plugin.transform(SPREAD_JSX, "Card.tsx");
|
|
134
|
+
});
|
|
135
|
+
expect(
|
|
136
|
+
warns.some((line) => line.includes("template hoisting skipped")),
|
|
137
|
+
).toBe(false);
|
|
138
|
+
} finally {
|
|
139
|
+
if (previous === undefined) {
|
|
140
|
+
delete process.env.NODE_ENV;
|
|
141
|
+
} else {
|
|
142
|
+
process.env.NODE_ENV = previous;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe("vite transform, cache, and Fast Refresh", () => {
|
|
149
|
+
it("returns null for non-jsx files", () => {
|
|
150
|
+
const plugin = sinwan({ cache: false, fastRefresh: false });
|
|
151
|
+
expect(plugin.transform("const x = 1;", "file.ts")).toBeNull();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("transforms jsx and updates cache when bunfig exists", () => {
|
|
155
|
+
const tmpDir = fs.mkdtempSync(path.join(import.meta.dir, ".tmp-"));
|
|
156
|
+
const bunfig = path.join(tmpDir, "bunfig.toml");
|
|
157
|
+
fs.writeFileSync(bunfig, "[install]\n");
|
|
158
|
+
try {
|
|
159
|
+
const plugin = sinwan({
|
|
160
|
+
cache: {
|
|
161
|
+
root: tmpDir,
|
|
162
|
+
tsConfigPath: path.join(tmpDir, "tsconfig.json"),
|
|
163
|
+
cachePath: path.join(tmpDir, "cache.json"),
|
|
164
|
+
bunfigPath: bunfig,
|
|
165
|
+
workspaces: tmpDir,
|
|
166
|
+
},
|
|
167
|
+
fastRefresh: false,
|
|
168
|
+
dev: false,
|
|
169
|
+
});
|
|
170
|
+
plugin.configResolved({
|
|
171
|
+
command: "build",
|
|
172
|
+
mode: "production",
|
|
173
|
+
root: tmpDir,
|
|
174
|
+
});
|
|
175
|
+
const result = plugin.transform(
|
|
176
|
+
"export function Hello() { return <div>hi</div>; }",
|
|
177
|
+
path.join(tmpDir, "Hello.tsx"),
|
|
178
|
+
);
|
|
179
|
+
expect(result?.code).toContain("_$createTemplate");
|
|
180
|
+
} finally {
|
|
181
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("uses projectRoot when cache is a boolean", () => {
|
|
186
|
+
const plugin = sinwan({ cache: true, fastRefresh: false, dev: false });
|
|
187
|
+
plugin.configResolved({
|
|
188
|
+
command: "build",
|
|
189
|
+
mode: "production",
|
|
190
|
+
root: process.cwd(),
|
|
191
|
+
});
|
|
192
|
+
const result = plugin.transform("const App = () => <div>ok</div>;", "App.tsx");
|
|
193
|
+
expect(result?.code).toBeDefined();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("falls back to cwd when cache is a boolean and root is omitted", () => {
|
|
197
|
+
const plugin = sinwan({ cache: true, fastRefresh: false, dev: false });
|
|
198
|
+
plugin.configResolved({ command: "build", mode: "production" });
|
|
199
|
+
const result = plugin.transform("const App = () => <div>ok</div>;", "App.tsx");
|
|
200
|
+
expect(result?.code).toBeDefined();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("uses cache object defaults without root or bunfigPath", () => {
|
|
204
|
+
const plugin = sinwan({
|
|
205
|
+
cache: {},
|
|
206
|
+
fastRefresh: false,
|
|
207
|
+
dev: false,
|
|
208
|
+
});
|
|
209
|
+
plugin.configResolved({ command: "build", mode: "production", root: "/app" });
|
|
210
|
+
const result = plugin.transform("const App = () => <div>ok</div>;", "App.jsx");
|
|
211
|
+
expect(result?.code).toBeDefined();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("uses cwd when a cache object has no root and config has no root", () => {
|
|
215
|
+
const plugin = sinwan({
|
|
216
|
+
cache: {},
|
|
217
|
+
fastRefresh: false,
|
|
218
|
+
dev: false,
|
|
219
|
+
});
|
|
220
|
+
plugin.configResolved({ command: "build", mode: "production" });
|
|
221
|
+
const result = plugin.transform("const App = () => <div>ok</div>;", "App.tsx");
|
|
222
|
+
expect(result?.code).toBeDefined();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("injects Fast Refresh in serve for an exported component", () => {
|
|
226
|
+
const plugin = sinwan({ cache: false, fastRefresh: true, dev: false });
|
|
227
|
+
plugin.configResolved({
|
|
228
|
+
command: "serve",
|
|
229
|
+
mode: "development",
|
|
230
|
+
root: "/app",
|
|
231
|
+
});
|
|
232
|
+
const result = plugin.transform(
|
|
233
|
+
"export function Hello() { return <div>hi</div>; }",
|
|
234
|
+
"/app/src/Hello.tsx",
|
|
235
|
+
);
|
|
236
|
+
expect(result?.code).toContain("$$sinwanReplace");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("injects Fast Refresh using original source when the id has a query", () => {
|
|
240
|
+
const plugin = sinwan({ cache: false, fastRefresh: true, dev: false });
|
|
241
|
+
plugin.configResolved({
|
|
242
|
+
command: "serve",
|
|
243
|
+
mode: "development",
|
|
244
|
+
root: "/app",
|
|
245
|
+
});
|
|
246
|
+
const result = plugin.transform(
|
|
247
|
+
"export function Hello() { return <div>hi</div>; }",
|
|
248
|
+
"/app/src/Hello.tsx?t=1",
|
|
249
|
+
);
|
|
250
|
+
expect(result?.code).toContain("$$sinwanReplace");
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("skips Fast Refresh for node_modules", () => {
|
|
254
|
+
const plugin = sinwan({ cache: false, fastRefresh: true, dev: false });
|
|
255
|
+
plugin.configResolved({
|
|
256
|
+
command: "serve",
|
|
257
|
+
mode: "development",
|
|
258
|
+
root: "/app",
|
|
259
|
+
});
|
|
260
|
+
const result = plugin.transform(
|
|
261
|
+
"export function Hello() { return <div>hi</div>; }",
|
|
262
|
+
"/app/node_modules/lib/Hello.tsx",
|
|
263
|
+
);
|
|
264
|
+
expect(result?.code ?? "").not.toContain("$$sinwanReplace");
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("skips Fast Refresh for virtual modules", () => {
|
|
268
|
+
const plugin = sinwan({ cache: false, fastRefresh: true, dev: false });
|
|
269
|
+
plugin.configResolved({
|
|
270
|
+
command: "serve",
|
|
271
|
+
mode: "development",
|
|
272
|
+
root: "/app",
|
|
273
|
+
});
|
|
274
|
+
const result = plugin.transform(
|
|
275
|
+
"export function Hello() { return <div>hi</div>; }",
|
|
276
|
+
"\0virtual/Hello.tsx",
|
|
277
|
+
);
|
|
278
|
+
expect(result?.code ?? "").not.toContain("$$sinwanReplace");
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("skips Fast Refresh when the module exports no component", () => {
|
|
282
|
+
const plugin = sinwan({ cache: false, fastRefresh: true, dev: false });
|
|
283
|
+
plugin.configResolved({
|
|
284
|
+
command: "serve",
|
|
285
|
+
mode: "development",
|
|
286
|
+
root: "/app",
|
|
287
|
+
});
|
|
288
|
+
const result = plugin.transform("const x = 1;", "/app/src/util.tsx");
|
|
289
|
+
expect(result?.code ?? "").not.toContain("$$sinwanReplace");
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("does not inject Fast Refresh during build", () => {
|
|
293
|
+
const plugin = sinwan({ cache: false, fastRefresh: true, dev: false });
|
|
294
|
+
plugin.configResolved({
|
|
295
|
+
command: "build",
|
|
296
|
+
mode: "production",
|
|
297
|
+
root: "/app",
|
|
298
|
+
});
|
|
299
|
+
const result = plugin.transform(
|
|
300
|
+
"export function Hello() { return <div>hi</div>; }",
|
|
301
|
+
"/app/src/Hello.tsx",
|
|
302
|
+
);
|
|
303
|
+
expect(result?.code ?? "").not.toContain("$$sinwanReplace");
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("does not inject Fast Refresh when disabled", () => {
|
|
307
|
+
const plugin = sinwan({ cache: false, fastRefresh: false, dev: false });
|
|
308
|
+
plugin.configResolved({
|
|
309
|
+
command: "serve",
|
|
310
|
+
mode: "development",
|
|
311
|
+
root: "/app",
|
|
312
|
+
});
|
|
313
|
+
const result = plugin.transform(
|
|
314
|
+
"export function Hello() { return <div>hi</div>; }",
|
|
315
|
+
"/app/src/Hello.tsx",
|
|
316
|
+
);
|
|
317
|
+
expect(result?.code ?? "").not.toContain("$$sinwanReplace");
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("does not double-inject Fast Refresh", () => {
|
|
321
|
+
const plugin = sinwan({ cache: false, fastRefresh: true, dev: false });
|
|
322
|
+
plugin.configResolved({
|
|
323
|
+
command: "serve",
|
|
324
|
+
mode: "development",
|
|
325
|
+
root: "/app",
|
|
326
|
+
});
|
|
327
|
+
const alreadyWired =
|
|
328
|
+
"export function Hello() { return <div>hi</div>; }\nconst $$sinwanReplace = 1;";
|
|
329
|
+
const result = plugin.transform(alreadyWired, "/app/src/Hello.tsx");
|
|
330
|
+
expect(result?.code).not.toContain("import.meta.hot.accept");
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("skips Fast Refresh when the refresh helper is already present", () => {
|
|
334
|
+
const plugin = sinwan({ cache: false, fastRefresh: true, dev: false });
|
|
335
|
+
plugin.configResolved({
|
|
336
|
+
command: "serve",
|
|
337
|
+
mode: "development",
|
|
338
|
+
root: "/app",
|
|
339
|
+
});
|
|
340
|
+
const alreadyWired =
|
|
341
|
+
"export function Hello() { return <div>hi</div>; }\nconst __$sinwanRefresh = 1;";
|
|
342
|
+
const result = plugin.transform(alreadyWired, "/app/src/Hello.tsx");
|
|
343
|
+
expect(result?.code).not.toContain("import.meta.hot.accept");
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const COMPILER_RANGE = ">=0.2.5 <1.0.0";
|
|
348
|
+
|
|
349
|
+
describe("sinwan-compiler dependency range", () => {
|
|
350
|
+
it("accepts any 0.x compiler without a plugin republish", () => {
|
|
351
|
+
const pkg = JSON.parse(
|
|
352
|
+
fs.readFileSync(path.join(import.meta.dir, "..", "package.json"), "utf8"),
|
|
353
|
+
) as {
|
|
354
|
+
dependencies: Record<string, string>;
|
|
355
|
+
peerDependencies: Record<string, string>;
|
|
356
|
+
};
|
|
357
|
+
expect(pkg.dependencies["sinwan-compiler"]).toBe(COMPILER_RANGE);
|
|
358
|
+
expect(pkg.peerDependencies["sinwan-compiler"]).toBe(COMPILER_RANGE);
|
|
359
|
+
});
|
|
360
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,12 @@ export interface SinwanOptions {
|
|
|
31
31
|
* Only active in `vite serve`; never affects production builds.
|
|
32
32
|
*/
|
|
33
33
|
fastRefresh?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Warn when template hoisting is skipped (spreads, enhancer tags, …).
|
|
36
|
+
* Default: Vite `config.mode !== "production"` after `configResolved`,
|
|
37
|
+
* otherwise `process.env.NODE_ENV !== "production"` when `sinwan()` runs.
|
|
38
|
+
*/
|
|
39
|
+
dev?: boolean;
|
|
34
40
|
}
|
|
35
41
|
/**
|
|
36
42
|
* Unified Sinwan Vite plugin.
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,9 @@ ${replaces}
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
import * as fs from "fs";
|
|
32
32
|
import * as path from "path";
|
|
33
|
+
function defaultCompilerDev() {
|
|
34
|
+
return process.env["NODE_ENV"] !== "production";
|
|
35
|
+
}
|
|
33
36
|
var DEFAULT_SINWAN_OPTIONS = {
|
|
34
37
|
hoist: true,
|
|
35
38
|
explicitBindings: false,
|
|
@@ -39,6 +42,8 @@ var DEFAULT_SINWAN_OPTIONS = {
|
|
|
39
42
|
};
|
|
40
43
|
function sinwan(options = {}) {
|
|
41
44
|
const opts = { ...DEFAULT_SINWAN_OPTIONS, ...options };
|
|
45
|
+
const explicitDev = options.dev;
|
|
46
|
+
let compilerDev = explicitDev ?? defaultCompilerDev();
|
|
42
47
|
let isServe = false;
|
|
43
48
|
let projectRoot;
|
|
44
49
|
let cache = null;
|
|
@@ -55,6 +60,9 @@ function sinwan(options = {}) {
|
|
|
55
60
|
configResolved(config) {
|
|
56
61
|
isServe = config?.command === "serve";
|
|
57
62
|
projectRoot = config?.root;
|
|
63
|
+
if (explicitDev === undefined && typeof config?.mode === "string") {
|
|
64
|
+
compilerDev = config.mode !== "production";
|
|
65
|
+
}
|
|
58
66
|
if (opts.cache) {
|
|
59
67
|
const root = typeof opts.cache === "object" ? opts.cache.root ?? projectRoot ?? process.cwd() : projectRoot ?? process.cwd();
|
|
60
68
|
const tsConfigPath = typeof opts.cache === "object" ? opts.cache.tsConfigPath : undefined;
|
|
@@ -79,7 +87,7 @@ function sinwan(options = {}) {
|
|
|
79
87
|
result = transformJSX(code, id, {
|
|
80
88
|
hoist: opts.hoist,
|
|
81
89
|
explicitBindings: opts.explicitBindings,
|
|
82
|
-
dev:
|
|
90
|
+
dev: compilerDev,
|
|
83
91
|
analyze: opts.analyze,
|
|
84
92
|
analyzeMetadata: cache ? cache.reactiveProps : undefined,
|
|
85
93
|
resolveImport: cache ? cache.resolve : undefined
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-sinwan",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Vite plugin for sinwan",
|
|
5
5
|
"author": "Mohammed Ben Cheikh",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,17 +29,19 @@
|
|
|
29
29
|
"sinwan": "./dist/cli.js"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
|
-
"build": "bun run build.ts"
|
|
32
|
+
"build": "bun run build.ts",
|
|
33
|
+
"test": "bun test --coverage __tests__/plugin.test.ts"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@types/bun": "latest",
|
|
36
37
|
"typescript": "^5"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"sinwan-compiler": "
|
|
40
|
+
"sinwan-compiler": ">=0.2.5 <1.0.0"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
42
43
|
"typescript": "^5",
|
|
43
|
-
"vite": "^5"
|
|
44
|
+
"vite": "^5",
|
|
45
|
+
"sinwan-compiler": ">=0.2.5 <1.0.0"
|
|
44
46
|
}
|
|
45
47
|
}
|
package/src/index.ts
CHANGED
|
@@ -38,6 +38,20 @@ export interface SinwanOptions {
|
|
|
38
38
|
* Only active in `vite serve`; never affects production builds.
|
|
39
39
|
*/
|
|
40
40
|
fastRefresh?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Warn when template hoisting is skipped (spreads, enhancer tags, …).
|
|
43
|
+
* Default: Vite `config.mode !== "production"` after `configResolved`,
|
|
44
|
+
* otherwise `process.env.NODE_ENV !== "production"` when `sinwan()` runs.
|
|
45
|
+
*/
|
|
46
|
+
dev?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Bracket access so Bun.build cannot fold `process.env.NODE_ENV` into `true`
|
|
51
|
+
* when this plugin itself is bundled.
|
|
52
|
+
*/
|
|
53
|
+
function defaultCompilerDev(): boolean {
|
|
54
|
+
return process.env["NODE_ENV"] !== "production";
|
|
41
55
|
}
|
|
42
56
|
|
|
43
57
|
const DEFAULT_SINWAN_OPTIONS: Required<
|
|
@@ -66,6 +80,8 @@ const DEFAULT_SINWAN_OPTIONS: Required<
|
|
|
66
80
|
*/
|
|
67
81
|
export function sinwan(options: SinwanOptions = {}) {
|
|
68
82
|
const opts = { ...DEFAULT_SINWAN_OPTIONS, ...options };
|
|
83
|
+
const explicitDev = options.dev;
|
|
84
|
+
let compilerDev = explicitDev ?? defaultCompilerDev();
|
|
69
85
|
|
|
70
86
|
// Whether we are running the dev server (`vite serve`). Fast Refresh
|
|
71
87
|
// injection only happens here; production builds are never touched.
|
|
@@ -94,6 +110,9 @@ export function sinwan(options: SinwanOptions = {}) {
|
|
|
94
110
|
configResolved(config: any) {
|
|
95
111
|
isServe = config?.command === "serve";
|
|
96
112
|
projectRoot = config?.root;
|
|
113
|
+
if (explicitDev === undefined && typeof config?.mode === "string") {
|
|
114
|
+
compilerDev = config.mode !== "production";
|
|
115
|
+
}
|
|
97
116
|
|
|
98
117
|
if (opts.cache) {
|
|
99
118
|
const root =
|
|
@@ -129,7 +148,7 @@ export function sinwan(options: SinwanOptions = {}) {
|
|
|
129
148
|
result = transformJSX(code, id, {
|
|
130
149
|
hoist: opts.hoist,
|
|
131
150
|
explicitBindings: opts.explicitBindings,
|
|
132
|
-
dev:
|
|
151
|
+
dev: compilerDev,
|
|
133
152
|
analyze: opts.analyze,
|
|
134
153
|
analyzeMetadata: cache ? cache.reactiveProps : undefined,
|
|
135
154
|
resolveImport: cache ? cache.resolve : undefined,
|