@mgz-app/viteforge 1.0.0
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/LICENSE +21 -0
- package/README.md +738 -0
- package/dist/index.cjs +3359 -0
- package/dist/index.d.ts +319 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +3319 -0
- package/dist/plugins/base64-assets.d.ts +3 -0
- package/dist/plugins/base64-assets.d.ts.map +1 -0
- package/dist/plugins/calculate-built-size.d.ts +6 -0
- package/dist/plugins/calculate-built-size.d.ts.map +1 -0
- package/dist/plugins/dev-reload.d.ts +31 -0
- package/dist/plugins/dev-reload.d.ts.map +1 -0
- package/dist/plugins/glsl/index.d.ts +5 -0
- package/dist/plugins/glsl/index.d.ts.map +1 -0
- package/dist/plugins/glsl/load-shader.d.ts +4 -0
- package/dist/plugins/glsl/load-shader.d.ts.map +1 -0
- package/dist/plugins/glsl/types.d.ts +21 -0
- package/dist/plugins/glsl/types.d.ts.map +1 -0
- package/dist/plugins/glsl-import.d.ts +4 -0
- package/dist/plugins/glsl-import.d.ts.map +1 -0
- package/dist/plugins/index.cjs +3038 -0
- package/dist/plugins/index.d.ts +11 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.mjs +3017 -0
- package/dist/plugins/inject-nonce.d.ts +6 -0
- package/dist/plugins/inject-nonce.d.ts.map +1 -0
- package/dist/plugins/post-processing.d.ts +46 -0
- package/dist/plugins/post-processing.d.ts.map +1 -0
- package/dist/plugins/restart-on-rebuild.d.ts +36 -0
- package/dist/plugins/restart-on-rebuild.d.ts.map +1 -0
- package/dist/plugins/text-file.d.ts +17 -0
- package/dist/plugins/text-file.d.ts.map +1 -0
- package/dist/plugins/txt-loader.d.ts +3 -0
- package/dist/plugins/txt-loader.d.ts.map +1 -0
- package/dist/plugins/worker.d.ts +7 -0
- package/dist/plugins/worker.d.ts.map +1 -0
- package/package.json +75 -0
package/README.md
ADDED
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
# @mgz-app/viteforge
|
|
2
|
+
|
|
3
|
+
Build and serve utilities for Vite. Pre-configured presets for apps, packages, and libraries, plus a collection of plugins for asset embedding, shader loading, HTML compression, dev reload, and more.
|
|
4
|
+
|
|
5
|
+
I built this for game jams. The goal: write a game, run one command, and get a single self-contained HTML file. No server required and no dependencies at runtime. The HTML file works like a binary: I can just send it to someone, they double-click it, and it runs (or, I host it anywhere and it just works). Everything (JavaScript, CSS, textures, models, shaders, audio) is inlined and compressed into one file that decompresses and boots itself in the browser.
|
|
6
|
+
|
|
7
|
+
Install this package and Vite. That's it.
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"vite": "^7.0.0",
|
|
13
|
+
"@mgz-app/viteforge": "^1.0.0"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Table of Contents
|
|
21
|
+
|
|
22
|
+
- [Quick Start](#quick-start)
|
|
23
|
+
- [Configuration Presets](#configuration-presets)
|
|
24
|
+
- [defineAppConfig](#defineappconfigoptions) - Single-page applications
|
|
25
|
+
- [definePackageConfig](#definepackageconfigoptions) - Reusable packages
|
|
26
|
+
- [defineLibraryConfig](#definelibraryconfigoptions) - npm libraries
|
|
27
|
+
- [defineViteConfig](#defineviteconfigoptions) - Base configuration
|
|
28
|
+
- [Library Presets](#library-presets)
|
|
29
|
+
- [Plugins](#plugins)
|
|
30
|
+
- [base64AssetPlugin](#base64assetplugin) - Embed assets as base64
|
|
31
|
+
- [txtLoaderPlugin](#txtloaderplugin) - Load text files as strings
|
|
32
|
+
- [textFilePlugin](#textfileplugin) - Virtual modules from files
|
|
33
|
+
- [injectNoncePlugin](#injectnonceplugin) - CSP nonce injection
|
|
34
|
+
- [calculateBuiltSizePlugin](#calculatebuiltsizeplugin) - Build size reporting
|
|
35
|
+
- [restartOnRebuildPlugin](#restartonrebuildplugin) - Auto-restart servers
|
|
36
|
+
- [getGLSLPlugin](#getglslplugin) - GLSL/WGSL shader imports
|
|
37
|
+
- [postProcessingPlugin](#postprocessingplugin) - HTML compression
|
|
38
|
+
- [workerPlugin](#workerplugin) - Web Worker bundling
|
|
39
|
+
- [devReloadPlugin](#devreloadplugin) - Browser reload on server rebuild
|
|
40
|
+
- [Dev Reload (Server + Client)](#dev-reload-server--client)
|
|
41
|
+
- [Building a Universal Library](#building-a-universal-library)
|
|
42
|
+
- [Monorepo Usage](#monorepo-usage)
|
|
43
|
+
- [Configuration Reference](#configuration-reference)
|
|
44
|
+
- [Re-exported Utilities](#re-exported-utilities)
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
### Single-page application
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// vite.config.ts
|
|
54
|
+
import { defineAppConfig, defineConfig } from "@mgz-app/viteforge";
|
|
55
|
+
|
|
56
|
+
export default defineConfig(defineAppConfig({ port: 3000 }));
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"scripts": {
|
|
62
|
+
"dev": "vite",
|
|
63
|
+
"build": "vite build",
|
|
64
|
+
"preview": "vite preview"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### npm library
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
// vite.config.ts
|
|
73
|
+
import { defineConfig, defineLibraryConfig } from "@mgz-app/viteforge";
|
|
74
|
+
|
|
75
|
+
export default defineConfig(
|
|
76
|
+
defineLibraryConfig({
|
|
77
|
+
name: "my-lib",
|
|
78
|
+
preset: "npm-full"
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"scripts": {
|
|
86
|
+
"build": "vite build",
|
|
87
|
+
"dev": "vite build --watch"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Node.js server with auto-restart
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
// vite.config.ts
|
|
96
|
+
import { defineConfig, defineLibraryConfig, restartOnRebuildPlugin } from "@mgz-app/viteforge";
|
|
97
|
+
|
|
98
|
+
export default defineConfig(
|
|
99
|
+
defineLibraryConfig({
|
|
100
|
+
name: "my-server",
|
|
101
|
+
preset: "npm-esm",
|
|
102
|
+
additionalPlugins: [
|
|
103
|
+
restartOnRebuildPlugin({ startCommand: "node ./dist/index.mjs" })
|
|
104
|
+
]
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"scripts": {
|
|
112
|
+
"dev": "vite build --watch",
|
|
113
|
+
"build": "vite build",
|
|
114
|
+
"start": "node ./dist/index.mjs"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Configuration Presets
|
|
122
|
+
|
|
123
|
+
### `defineAppConfig(options)`
|
|
124
|
+
|
|
125
|
+
For single-page applications. Enables all the bells and whistles by default.
|
|
126
|
+
|
|
127
|
+
What you get:
|
|
128
|
+
- Single-file output in build mode (all JS/CSS inlined into one HTML file)
|
|
129
|
+
- HTML minification in build mode
|
|
130
|
+
- Base64 asset inlining
|
|
131
|
+
- CSP nonce injection
|
|
132
|
+
- Build size reporting
|
|
133
|
+
- Terser minification (drops console/debugger, strips comments)
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { defineAppConfig, defineConfig } from "@mgz-app/viteforge";
|
|
137
|
+
|
|
138
|
+
export default defineConfig(
|
|
139
|
+
defineAppConfig({
|
|
140
|
+
port: 8080,
|
|
141
|
+
previewPort: 8081,
|
|
142
|
+
isDevMode: process.env.DEV_MODE === "true"
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
| Feature | Dev Mode | Build Mode |
|
|
148
|
+
|---------|----------|------------|
|
|
149
|
+
| Single file output | No | Yes |
|
|
150
|
+
| HTML minification | No | Yes |
|
|
151
|
+
| JS minification | No | Yes (terser) |
|
|
152
|
+
| Base64 assets | Yes | Yes |
|
|
153
|
+
| CSP nonce | Yes | Yes |
|
|
154
|
+
| Build size report | No | Yes |
|
|
155
|
+
|
|
156
|
+
### `definePackageConfig(options)`
|
|
157
|
+
|
|
158
|
+
Same as `defineAppConfig`, but with CSP nonce injection disabled. Use for packages that export components but don't have their own HTML.
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import { defineConfig, definePackageConfig } from "@mgz-app/viteforge";
|
|
162
|
+
|
|
163
|
+
export default defineConfig(definePackageConfig({ port: 8090 }));
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### `defineLibraryConfig(options)`
|
|
167
|
+
|
|
168
|
+
For npm packages and libraries. Generates multiple output formats, TypeScript declarations, and includes a Vitest configuration.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { defineConfig, defineLibraryConfig } from "@mgz-app/viteforge";
|
|
172
|
+
|
|
173
|
+
export default defineConfig(
|
|
174
|
+
defineLibraryConfig({
|
|
175
|
+
name: "@my-org/my-lib",
|
|
176
|
+
preset: "npm-full",
|
|
177
|
+
external: ["three"],
|
|
178
|
+
globals: { three: "THREE" }
|
|
179
|
+
})
|
|
180
|
+
);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
See [Library Presets](#library-presets) for all available presets and [Building a Universal Library](#building-a-universal-library) for a complete walkthrough.
|
|
184
|
+
|
|
185
|
+
#### Options
|
|
186
|
+
|
|
187
|
+
| Option | Type | Default | Description |
|
|
188
|
+
|--------|------|---------|-------------|
|
|
189
|
+
| `name` | `string` | **(required)** | Package name |
|
|
190
|
+
| `preset` | `LibraryPreset` | `"npm-full"` | Output format preset |
|
|
191
|
+
| `entry` | `string` | `"src/index.ts"` | Entry point |
|
|
192
|
+
| `external` | `(string \| RegExp)[]` | `[]` | Dependencies to exclude from bundle |
|
|
193
|
+
| `globals` | `Record<string, string>` | `{}` | UMD/IIFE global variable mappings |
|
|
194
|
+
| `globalName` | `string` | auto-generated | Global name for UMD/IIFE builds |
|
|
195
|
+
| `buildMode` | `"mode-aware" \| "static"` | `"mode-aware"` | `mode-aware`: sourcemaps in dev, minify in prod. `static`: always sourcemaps, never minify |
|
|
196
|
+
| `useBase64Assets` | `boolean` | `false` | Enable `?base64` asset imports |
|
|
197
|
+
| `outDir` | `string` | `"./dist"` | Output directory |
|
|
198
|
+
| `formats` | `string[]` | from preset | Override preset formats |
|
|
199
|
+
| `rollupTypes` | `boolean` | `true` | Roll up `.d.ts` into a single file |
|
|
200
|
+
| `alwaysEmptyOutDir` | `boolean` | `true` | Empty output dir before each build |
|
|
201
|
+
| `preserveModules` | `boolean` | `false` | Emit one file per source module for fine-grained tree-shaking |
|
|
202
|
+
| `testEnvironment` | `"node" \| "jsdom"` | `"node"` | Vitest environment |
|
|
203
|
+
| `setupFiles` | `string[]` | - | Vitest setup files |
|
|
204
|
+
| `testTimeout` | `number` | - | Vitest timeout (ms) |
|
|
205
|
+
| `passWithNoTests` | `boolean` | `false` | Pass if no test files found |
|
|
206
|
+
| `additionalPlugins` | `Plugin[]` | `[]` | Additional Vite plugins |
|
|
207
|
+
| `minifiedBundles` | `boolean` | auto | Generate separate `.min.js` bundles |
|
|
208
|
+
|
|
209
|
+
### `defineViteConfig(options)`
|
|
210
|
+
|
|
211
|
+
The base configuration that all presets build on. Use it when you want full control.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import { defineConfig, defineViteConfig } from "@mgz-app/viteforge";
|
|
215
|
+
|
|
216
|
+
export default defineConfig(
|
|
217
|
+
defineViteConfig({
|
|
218
|
+
port: 3000,
|
|
219
|
+
useBase64Assets: true,
|
|
220
|
+
useNonceInjection: false,
|
|
221
|
+
singleFile: false,
|
|
222
|
+
minify: "esbuild"
|
|
223
|
+
})
|
|
224
|
+
);
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### Options
|
|
228
|
+
|
|
229
|
+
| Option | Type | Default | Description |
|
|
230
|
+
|--------|------|---------|-------------|
|
|
231
|
+
| `root` | `string` | `process.cwd()` | Project root directory |
|
|
232
|
+
| `port` | `number` | `4173` | Dev server port |
|
|
233
|
+
| `previewPort` | `number` | `4174` | Preview server port |
|
|
234
|
+
| `outDir` | `string` | `"./dist"` | Build output directory |
|
|
235
|
+
| `cacheDir` | `string` | `".vite"` | Vite cache directory |
|
|
236
|
+
| `singleFile` | `boolean` | build: `true`, dev: `false` | Bundle into single HTML file |
|
|
237
|
+
| `minifyHtml` | `boolean` | build: `true`, dev: `false` | Minify HTML output |
|
|
238
|
+
| `minify` | `boolean \| "terser" \| "esbuild"` | `"terser"` | JS minification strategy |
|
|
239
|
+
| `useBase64Assets` | `boolean` | `true` | Enable base64 asset plugin |
|
|
240
|
+
| `useNonceInjection` | `boolean` | `true` | Enable CSP nonce injection |
|
|
241
|
+
| `calculateBuiltSize` | `boolean` | `true` | Report build size |
|
|
242
|
+
| `isDevMode` | `boolean` | `false` | Dev mode (strips CSP meta tag) |
|
|
243
|
+
| `additionalPlugins` | `any[]` | `[]` | Additional Vite plugins |
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Library Presets
|
|
248
|
+
|
|
249
|
+
| Preset | Formats | Use Case |
|
|
250
|
+
|--------|---------|----------|
|
|
251
|
+
| `"internal"` | ES | Packages consumed only within your monorepo |
|
|
252
|
+
| `"npm-esm"` | ES | Modern ESM-only npm packages |
|
|
253
|
+
| `"npm-full"` | ES + CJS | Maximum npm compatibility (default) |
|
|
254
|
+
| `"cdn"` | UMD + IIFE | Script tags and CDN distribution |
|
|
255
|
+
| `"universal"` | ES + CJS + UMD + IIFE | Every distribution channel |
|
|
256
|
+
|
|
257
|
+
Output file extensions:
|
|
258
|
+
|
|
259
|
+
| Format | Extension | Loaded via |
|
|
260
|
+
|--------|-----------|------------|
|
|
261
|
+
| ES | `.mjs` | `import` |
|
|
262
|
+
| CJS | `.cjs` | `require()` |
|
|
263
|
+
| UMD | `.umd.js` | AMD, CommonJS, or `<script>` tag |
|
|
264
|
+
| IIFE | `.iife.js` | `<script>` tag |
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Plugins
|
|
269
|
+
|
|
270
|
+
All plugins can be imported from the package root or from the `/plugins` subpath:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import { base64AssetPlugin } from "@mgz-app/viteforge";
|
|
274
|
+
// or
|
|
275
|
+
import { base64AssetPlugin } from "@mgz-app/viteforge/plugins";
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### `base64AssetPlugin()`
|
|
279
|
+
|
|
280
|
+
Embeds assets as base64 data URIs directly in your JavaScript bundle.
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
// Import an asset — returns a data URI string
|
|
284
|
+
import model from "./model.glb";
|
|
285
|
+
// "data:model/gltf-binary;base64,..."
|
|
286
|
+
|
|
287
|
+
// Use ?base64 for raw base64 (no data URI prefix) — works with any file type
|
|
288
|
+
import wasmBase64 from "./physics.wasm?base64";
|
|
289
|
+
// "SGVsbG8gd29ybGQ="
|
|
290
|
+
|
|
291
|
+
// Or use the base64: prefix — same behavior, reads like intent
|
|
292
|
+
import wasmBase64 from "base64:./physics.wasm";
|
|
293
|
+
|
|
294
|
+
// Both syntaxes work with node_modules
|
|
295
|
+
import font from "some-package/font.ttf?base64";
|
|
296
|
+
// or
|
|
297
|
+
import font from "base64:some-package/font.ttf";
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Supported extensions (auto-detected, returned as data URIs):
|
|
301
|
+
`.glb`, `.gltf`, `.fbx`, `.obj`, `.png`, `.jpg`, `.jpeg`, `.gif`, `.webm`, `.mp4`, `.mp3`, `.ogg`, `.wav`, `.ttf`, `.otf`
|
|
302
|
+
|
|
303
|
+
SVGs get special treatment: they are optimized with svgo and returned as URL-encoded data URIs (smaller than base64 for SVGs).
|
|
304
|
+
|
|
305
|
+
The `?base64` query works with **any** file type and returns a raw base64 string.
|
|
306
|
+
|
|
307
|
+
### `txtLoaderPlugin()`
|
|
308
|
+
|
|
309
|
+
Loads text-based files as string exports. Always enabled in all presets.
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import readme from "./data.txt";
|
|
313
|
+
import lut from "./color-grading.cube";
|
|
314
|
+
import lut2 from "./lookup.3dl";
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Supported: `.txt`, `.cube` (LUT), `.3dl` (LUT)
|
|
318
|
+
|
|
319
|
+
### `textFilePlugin(virtualModuleId, filePath)`
|
|
320
|
+
|
|
321
|
+
Creates a virtual module that exports a file's contents as a string. Useful for embedding generated code.
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
// vite.config.ts
|
|
325
|
+
import { defineConfig, resolve, textFilePlugin } from "@mgz-app/viteforge";
|
|
326
|
+
|
|
327
|
+
export default defineConfig({
|
|
328
|
+
plugins: [
|
|
329
|
+
textFilePlugin("iframe-runner-code", resolve(__dirname, "./dist/iframe-runner.js"))
|
|
330
|
+
]
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// In your code
|
|
334
|
+
import iframeCode from "iframe-runner-code";
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### `injectNoncePlugin(options?)`
|
|
338
|
+
|
|
339
|
+
Injects a CSP nonce into all `<script>` tags and updates the CSP `<meta>` tag. A new random nonce is generated on each build.
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
injectNoncePlugin({ isDevMode: false })
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Your HTML must include a CSP meta tag:
|
|
346
|
+
|
|
347
|
+
```html
|
|
348
|
+
<meta http-equiv="Content-Security-Policy" content="script-src 'self';">
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Set `isDevMode: true` to remove the CSP meta tag entirely (for easier debugging in development).
|
|
352
|
+
|
|
353
|
+
### `calculateBuiltSizePlugin(options?)`
|
|
354
|
+
|
|
355
|
+
Logs the total build output size in bytes, SI (kB/MB), and IEC (KiB/MiB) after compilation. Only runs in build mode.
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
calculateBuiltSizePlugin({ outputDir: "./dist" })
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### `restartOnRebuildPlugin(options)`
|
|
362
|
+
|
|
363
|
+
Kills and restarts a Node.js process after each rebuild. Designed for `vite build --watch`.
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
restartOnRebuildPlugin({
|
|
367
|
+
startCommand: "node ./dist/index.mjs",
|
|
368
|
+
delay: 100 // ms to wait before starting (default: 100)
|
|
369
|
+
})
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### `getGLSLPlugin()`
|
|
373
|
+
|
|
374
|
+
Enables importing GLSL and WGSL shader files. This is an async function — use `await`.
|
|
375
|
+
|
|
376
|
+
```ts
|
|
377
|
+
// vite.config.ts
|
|
378
|
+
import { defineConfig, getGLSLPlugin } from "@mgz-app/viteforge";
|
|
379
|
+
|
|
380
|
+
export default defineConfig({
|
|
381
|
+
plugins: [await getGLSLPlugin()]
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// In your code
|
|
385
|
+
import vertexShader from "./shaders/vertex.vert";
|
|
386
|
+
import fragmentShader from "./shaders/fragment.frag";
|
|
387
|
+
import computeShader from "./shaders/compute.wgsl";
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Supported: `.glsl`, `.wgsl`, `.vert`, `.vs`, `.frag`, `.fs`
|
|
391
|
+
|
|
392
|
+
Shaders support `#include` directives for composing shaders from multiple files.
|
|
393
|
+
|
|
394
|
+
### `postProcessingPlugin(options?)`
|
|
395
|
+
|
|
396
|
+
Compresses the final HTML bundle using deflate. Extracts all inline JS/CSS, compresses into a binary blob, and wraps it in a self-extracting HTML page. The result is a single HTML file that decompresses and runs itself.
|
|
397
|
+
|
|
398
|
+
```ts
|
|
399
|
+
postProcessingPlugin({
|
|
400
|
+
enabled: true,
|
|
401
|
+
compressionLevel: 9, // 1-9 (default: 9)
|
|
402
|
+
logStats: true, // Log compression stats (default: true)
|
|
403
|
+
titleString: "My Game", // HTML <title> tag
|
|
404
|
+
iconString: '<svg>...</svg>', // SVG favicon (optimized with svgo)
|
|
405
|
+
usePako: false // false: native DecompressionStream (default)
|
|
406
|
+
// true: bundle pako for older browsers
|
|
407
|
+
})
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Output example:
|
|
411
|
+
```
|
|
412
|
+
[post-processing] Compression complete:
|
|
413
|
+
Original HTML: 1.14 MB
|
|
414
|
+
Script content: 1.14 MB
|
|
415
|
+
Compressed blob: 404.84 KB (34.8% of script)
|
|
416
|
+
Final HTML: 406.11 KB
|
|
417
|
+
Total savings: 758.74 KB (65.1% reduction)
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Set `usePako: true` if you need to support Firefox < 105 or Safari < 16.4 (browsers without `DecompressionStream`).
|
|
421
|
+
|
|
422
|
+
### `workerPlugin()`
|
|
423
|
+
|
|
424
|
+
Bundles `.worker` imports as separate IIFE bundles and returns blob URLs for `new Worker()` instantiation.
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
// vite.config.ts
|
|
428
|
+
export default defineConfig({
|
|
429
|
+
plugins: [workerPlugin()]
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// In your code
|
|
433
|
+
import workerUrl from "./physics.worker";
|
|
434
|
+
const worker = new Worker(workerUrl);
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
The worker file should be a `.ts` file — the plugin appends `.ts` when resolving. The worker is compiled as a minified IIFE with all dependencies bundled (no externals).
|
|
438
|
+
|
|
439
|
+
### `devReloadPlugin(options?)`
|
|
440
|
+
|
|
441
|
+
Starts a lightweight WebSocket server during `vite build --watch`. When a rebuild completes, all connected browser clients receive a reload signal.
|
|
442
|
+
|
|
443
|
+
See [Dev Reload](#dev-reload-server--client) for the full setup.
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
devReloadPlugin({ port: 21816 }) // default port
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Dev Reload (Server + Client)
|
|
452
|
+
|
|
453
|
+
When your Node.js server serves the client directly (no separate Vite dev server), you need a way to tell the browser "the server rebuilt, reload." This is a two-piece setup:
|
|
454
|
+
|
|
455
|
+
### Server side
|
|
456
|
+
|
|
457
|
+
Add `devReloadPlugin` to your **server's** vite.config.ts. It starts a WebSocket server inside the Vite build process and sends a `"change"` message to all connected browsers after every rebuild.
|
|
458
|
+
|
|
459
|
+
```ts
|
|
460
|
+
// server/vite.config.ts
|
|
461
|
+
import { defineConfig, defineLibraryConfig, devReloadPlugin } from "@mgz-app/viteforge";
|
|
462
|
+
|
|
463
|
+
export default defineConfig(
|
|
464
|
+
defineLibraryConfig({
|
|
465
|
+
name: "my-server",
|
|
466
|
+
preset: "npm-esm",
|
|
467
|
+
additionalPlugins: [
|
|
468
|
+
devReloadPlugin({ port: 21816 })
|
|
469
|
+
]
|
|
470
|
+
})
|
|
471
|
+
);
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Client side
|
|
475
|
+
|
|
476
|
+
Use `devReloadClientScript()` to get a JavaScript snippet that connects to the WebSocket server and calls `location.reload()` on any message. Inject it into your client HTML during development builds only.
|
|
477
|
+
|
|
478
|
+
```ts
|
|
479
|
+
// client/vite.config.ts
|
|
480
|
+
import { defineAppConfig, defineConfig, devReloadClientScript } from "@mgz-app/viteforge";
|
|
481
|
+
|
|
482
|
+
const isDev = process.env.DEV_MODE === "true";
|
|
483
|
+
|
|
484
|
+
export default defineConfig(
|
|
485
|
+
defineAppConfig({
|
|
486
|
+
additionalPlugins: isDev
|
|
487
|
+
? [
|
|
488
|
+
{
|
|
489
|
+
name: "inject-dev-reload",
|
|
490
|
+
transformIndexHtml(html) {
|
|
491
|
+
const script = devReloadClientScript({ port: 21816 });
|
|
492
|
+
return html.replace("</head>", `<script>${script}</script></head>`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
]
|
|
496
|
+
: []
|
|
497
|
+
})
|
|
498
|
+
);
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
The client snippet auto-reconnects (up to 5 times) when the connection drops — which happens when the server process restarts.
|
|
502
|
+
|
|
503
|
+
In production, you simply don't add either plugin. No dev code in your prod builds.
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## Building a Universal Library
|
|
508
|
+
|
|
509
|
+
A complete recipe for a library consumable via ESM, CJS, UMD (AMD/CDN), and IIFE (script tag).
|
|
510
|
+
|
|
511
|
+
### vite.config.ts
|
|
512
|
+
|
|
513
|
+
```ts
|
|
514
|
+
import { defineConfig, defineLibraryConfig } from "@mgz-app/viteforge";
|
|
515
|
+
|
|
516
|
+
export default defineConfig(
|
|
517
|
+
defineLibraryConfig({
|
|
518
|
+
name: "@my-org/my-lib",
|
|
519
|
+
preset: "universal",
|
|
520
|
+
globalName: "MyLib",
|
|
521
|
+
external: ["three"],
|
|
522
|
+
globals: { three: "THREE" }
|
|
523
|
+
})
|
|
524
|
+
);
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### package.json
|
|
528
|
+
|
|
529
|
+
```json
|
|
530
|
+
{
|
|
531
|
+
"name": "@my-org/my-lib",
|
|
532
|
+
"type": "module",
|
|
533
|
+
"main": "./dist/index.cjs",
|
|
534
|
+
"module": "./dist/index.mjs",
|
|
535
|
+
"browser": "./dist/index.umd.js",
|
|
536
|
+
"unpkg": "./dist/index.umd.js",
|
|
537
|
+
"jsdelivr": "./dist/index.umd.js",
|
|
538
|
+
"types": "./dist/index.d.ts",
|
|
539
|
+
"exports": {
|
|
540
|
+
".": {
|
|
541
|
+
"types": "./dist/index.d.ts",
|
|
542
|
+
"import": "./dist/index.mjs",
|
|
543
|
+
"require": "./dist/index.cjs"
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
"files": ["./dist"]
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
**Order matters in `exports`.** `types` must come first.
|
|
551
|
+
|
|
552
|
+
**Do NOT put `"browser"` inside the `exports` field** — it causes bundlers to pick UMD over ESM. The top-level `"browser"` field is fine for legacy tooling.
|
|
553
|
+
|
|
554
|
+
### tsconfig.lib.json
|
|
555
|
+
|
|
556
|
+
Required by the dts plugin:
|
|
557
|
+
|
|
558
|
+
```json
|
|
559
|
+
{
|
|
560
|
+
"extends": "./tsconfig.json",
|
|
561
|
+
"compilerOptions": {
|
|
562
|
+
"declaration": true,
|
|
563
|
+
"declarationMap": true,
|
|
564
|
+
"emitDeclarationOnly": true
|
|
565
|
+
},
|
|
566
|
+
"include": ["src/**/*"],
|
|
567
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### Output
|
|
572
|
+
|
|
573
|
+
```
|
|
574
|
+
dist/
|
|
575
|
+
index.mjs # ESM
|
|
576
|
+
index.cjs # CJS
|
|
577
|
+
index.umd.js # UMD
|
|
578
|
+
index.iife.js # IIFE
|
|
579
|
+
index.d.ts # TypeScript declarations
|
|
580
|
+
index.d.ts.map # Declaration source maps
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### How consumers use it
|
|
584
|
+
|
|
585
|
+
```ts
|
|
586
|
+
// ESM
|
|
587
|
+
import { MyClass } from "@my-org/my-lib";
|
|
588
|
+
|
|
589
|
+
// CJS
|
|
590
|
+
const { MyClass } = require("@my-org/my-lib");
|
|
591
|
+
|
|
592
|
+
// CDN
|
|
593
|
+
<script src="https://unpkg.com/@my-org/my-lib"></script>
|
|
594
|
+
<script>
|
|
595
|
+
const { MyClass } = window.MyLib;
|
|
596
|
+
</script>
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### Best practices
|
|
600
|
+
|
|
601
|
+
Use **named exports only**. Avoid default exports — they cause CJS consumers to need `.default`:
|
|
602
|
+
|
|
603
|
+
```ts
|
|
604
|
+
// Don't do this
|
|
605
|
+
export default MyClass;
|
|
606
|
+
|
|
607
|
+
// Do this
|
|
608
|
+
export { MyClass };
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## Monorepo Usage
|
|
614
|
+
|
|
615
|
+
The presets produce clean configs with no monorepo-specific settings. If you're in a monorepo with workspace packages, use `mergeConfig` to layer your monorepo defaults on top:
|
|
616
|
+
|
|
617
|
+
```ts
|
|
618
|
+
// shared/monorepo-config.ts
|
|
619
|
+
import { defineAppConfig, mergeConfig } from "@mgz-app/viteforge";
|
|
620
|
+
|
|
621
|
+
const monorepoDefaults = {
|
|
622
|
+
server: {
|
|
623
|
+
watch: { ignored: ["!**/node_modules/@my-org/**"] },
|
|
624
|
+
fs: { allow: ["../.."] }
|
|
625
|
+
},
|
|
626
|
+
optimizeDeps: {
|
|
627
|
+
exclude: ["@my-org/*"],
|
|
628
|
+
include: ["three"]
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
export const defineMonorepoAppConfig = (opts) =>
|
|
633
|
+
(env) => mergeConfig(defineAppConfig(opts)(env), monorepoDefaults);
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
```ts
|
|
637
|
+
// apps/my-app/vite.config.ts
|
|
638
|
+
import { defineConfig } from "@mgz-app/viteforge";
|
|
639
|
+
import { defineMonorepoAppConfig } from "../../shared/monorepo-config";
|
|
640
|
+
|
|
641
|
+
export default defineConfig(defineMonorepoAppConfig({ port: 3000 }));
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
## Configuration Reference
|
|
647
|
+
|
|
648
|
+
### Terser options
|
|
649
|
+
|
|
650
|
+
Production builds use aggressive terser settings by default:
|
|
651
|
+
|
|
652
|
+
- Mangles all names (toplevel, module, eval)
|
|
653
|
+
- Drops `console.*` and `debugger` statements
|
|
654
|
+
- Strips all comments
|
|
655
|
+
- 2 optimization passes
|
|
656
|
+
|
|
657
|
+
The `terserOptions` object is exported if you need to reference or extend it.
|
|
658
|
+
|
|
659
|
+
### Asset handling
|
|
660
|
+
|
|
661
|
+
All presets recognize these file types as assets:
|
|
662
|
+
`.glb`, `.gltf`, `.bin`, `.wasm`, `.png`, `.jpg`, `.svg`, `.jpeg`, `.gif`, `.mp4`, `.webm`, `.mp3`, `.wav`, `.ogg`, `.ttf`, `.otf`
|
|
663
|
+
|
|
664
|
+
In build mode, assets are inlined (the inline limit is set to 900MB — effectively everything).
|
|
665
|
+
|
|
666
|
+
---
|
|
667
|
+
|
|
668
|
+
## Re-exported Utilities
|
|
669
|
+
|
|
670
|
+
The package re-exports these for convenience so you don't need extra imports:
|
|
671
|
+
|
|
672
|
+
```ts
|
|
673
|
+
import { defineConfig, mergeConfig, resolve, dts } from "@mgz-app/viteforge";
|
|
674
|
+
import type { Plugin } from "@mgz-app/viteforge";
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
- `defineConfig` and `mergeConfig` from `vite`
|
|
678
|
+
- `dts` from `vite-plugin-dts`
|
|
679
|
+
- `resolve` from `path`
|
|
680
|
+
- `Plugin` type from `vite`
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
684
|
+
## Releasing
|
|
685
|
+
|
|
686
|
+
This package uses [Changesets](https://github.com/changesets/changesets) for versioning and publishing.
|
|
687
|
+
|
|
688
|
+
### When you make a change
|
|
689
|
+
|
|
690
|
+
After making your changes, create a changeset to describe what changed:
|
|
691
|
+
|
|
692
|
+
```sh
|
|
693
|
+
pnpm changeset
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
You'll be prompted to:
|
|
697
|
+
1. Select the package
|
|
698
|
+
2. Choose the bump type:
|
|
699
|
+
- **patch** — bug fixes, dependency updates, internal refactors
|
|
700
|
+
- **minor** — new plugins, new config options, new features
|
|
701
|
+
- **major** — breaking changes to existing presets, removed options, renamed exports
|
|
702
|
+
3. Write a summary of the change
|
|
703
|
+
|
|
704
|
+
Commit the changeset file with your code.
|
|
705
|
+
|
|
706
|
+
### How publishing works
|
|
707
|
+
|
|
708
|
+
When you push to `master` (or merge a PR):
|
|
709
|
+
|
|
710
|
+
1. The CI **release** workflow detects pending changeset files
|
|
711
|
+
2. It opens a "Version Package" PR that bumps the version in `package.json` and updates `CHANGELOG.md`
|
|
712
|
+
3. When you merge that PR, the workflow publishes to npm automatically via trusted publishing (OIDC)
|
|
713
|
+
|
|
714
|
+
No npm tokens to manage or rotate. GitHub Actions authenticates directly with npm.
|
|
715
|
+
|
|
716
|
+
### First-time setup
|
|
717
|
+
|
|
718
|
+
The very first publish must be done manually because trusted publishing requires the package to already exist on npm.
|
|
719
|
+
|
|
720
|
+
1. Use your existing npm token (or create a temporary 90-day granular one at Profile > Access Tokens)
|
|
721
|
+
2. Publish manually:
|
|
722
|
+
```sh
|
|
723
|
+
npm publish --access public --//registry.npmjs.org/:_authToken=$(cat ~/.npmtoken)
|
|
724
|
+
```
|
|
725
|
+
3. Configure trusted publishing on npm: go to `https://www.npmjs.com/package/@mgz-app/viteforge/access`, click **GitHub Actions**, and fill in:
|
|
726
|
+
- **Organization or user**: `marcogomez` (case-sensitive)
|
|
727
|
+
- **Repository**: `viteforge`
|
|
728
|
+
- **Workflow filename**: `release.yml`
|
|
729
|
+
- **Allowed actions**: select "npm publish"
|
|
730
|
+
4. Delete the temporary npm token
|
|
731
|
+
|
|
732
|
+
### Manual publishing (if needed)
|
|
733
|
+
|
|
734
|
+
```sh
|
|
735
|
+
pnpm changeset # create a changeset
|
|
736
|
+
pnpm run version # bump version + update CHANGELOG
|
|
737
|
+
pnpm run release # publish to npm
|
|
738
|
+
```
|