Vite's ?raw gives you uncooked source. This plugin gives you the cooked version — compiled, bundled, and tree-shaken.
// ?raw → uncooked: types, JSX, and imports stay as-is. Can't run in a browser.
import raw from './worker.ts?raw'
// ?to=js → cooked: compiled to JS, all deps bundled in, tree-shaken. Ready to execute.
import cooked from './worker.ts?to=js'npm install -D vite-plugin-cooked// vite.config.ts
import { defineConfig } from 'vite'
import cooked from 'vite-plugin-cooked'
export default defineConfig({
plugins: [cooked()],
})import code from './worker.ts?to=js' // bundle + compile to JS
import min from './worker.ts?minify&to=js' // + minify
import iife from './worker.ts?format=iife&minify&to=js' // IIFE for Workers / <script>
import lite from './widget.tsx?external=react,react-dom&to=js' // keep react as import
import all from './lib.ts?external=*&to=js' // keep all bare imports
import raw from './utils.ts?nobundle&to=js' // single-file transpile onlyTypeScript tip: Put
to=js/to=tslast in the query. The type declarations use*to=jspatterns —?minify&to=jsmatches,?to=js&minifydoes not.
| Param | Values | Description |
|---|---|---|
to |
js, ts |
Target format. js strips types and compiles JSX. |
minify |
flag | Minify the output. |
target |
e.g. es2020 |
Syntax downleveling target. |
banner |
string | Text prepended to output (URL-encode special chars). |
format |
es, iife |
Output format. Default es. iife for Workers / scripts. |
external |
pkg names or * |
Deps to exclude from bundle, comma-separated. * = all bare imports. |
nobundle |
flag | Skip bundling — transpile only, imports preserved. |
cooked({
defaultTarget: 'es2020',
defaultMinify: false,
defaultFormat: 'es',
defaultExternal: ['react', 'react-dom'],
}){
"compilerOptions": {
"types": ["vite-plugin-cooked/client"]
}
}| Bundle (default) | Nobundle (&nobundle) |
|
|---|---|---|
| Imports | Resolved, inlined, tree-shaken | Preserved as-is |
| Output | Self-contained | Single-file transpile |
| Speed | Slower (full build) | Fast |
How is this different from ?worker?
?worker returns a Worker constructor from a separate file. cooked returns a code string — you control where it runs: Worker, iframe, <script>, sandbox.
Isn't ?raw enough?
?raw returns uncompiled source. TypeScript types, JSX, and bare imports can't execute in a browser. cooked compiles and bundles first.
Can I cook .vue / .svelte files?
Not yet. The internal build uses configFile: false and doesn't load framework plugins.
- Dep changes don't trigger HMR — only the entry file is watched. Re-save entry or restart dev server.
- CSS imports are ignored — cooked outputs a string, CSS has nowhere to inject. A warning is logged.
- No source maps — output is a string constant. Use
&nobundlefor easier debugging. - Dynamic imports break — no separate chunks exist after bundling. Use static imports only.
import.meta.urlchanges — points to blob/injection URL, not the original file.?to=tsrequires&nobundle— bundle mode always compiles to JS.&external=*+&format=iife— not supported. IIFE needs explicit globals mapping.- Build perf — each cooked import runs a full
vite.build(). Results are cached and concurrency is capped at 3.
| Vite | Behavior |
|---|---|
| 4 – 7 | transformWithEsbuild for nobundle transforms |
| 8+ | transformWithOxc when available, esbuild fallback for minification |