src/build/vite/ is Nitro's Vite-based build system using Vite 6+ multi-environment API. It integrates as a Vite plugin (nitro()) that manages server builds, service environments, dev server, and production output.
| File | Purpose |
|---|---|
plugin.ts |
Main plugin — 6 sub-plugins orchestrating the build |
env.ts |
Vite environment creation (nitro, services, env-runner) |
dev.ts |
Dev server integration, FetchableDevEnvironment, middleware |
prod.ts |
Production multi-env build, asset management, virtual setup module |
bundler.ts |
Rollup/Rolldown config generation |
build.ts |
CLI build entry for nitro build (viteBuild()) |
preview.ts |
Preview server plugin |
types.ts |
Type definitions (NitroPluginConfig, NitroPluginContext) |
nitro(config?) returns an array of 6 sub-plugins:
- Calls
setupNitroContext()on firstconfighook - Creates Nitro instance via
createNitro() - Detects Rolldown vs Rollup (
_isRolldown) - Resolves bundler config via
getBundlerConfig() - Initializes env-runner in dev mode
- Attaches rollup plugins for dev environments
- Registers Vite environments:
client,nitro, and user services - Auto-detects
entry-serverfor SSR - Configures per-environment build options (consumer type, externals, etc.)
- Sets app type to
"custom" - Configures resolve aliases, server port
buildApphook → callsbuildEnvironments()(production)generateBundlehook → tracks entry pointsconfigureServer→ callsconfigureViteDevServer()(dev)hotUpdate→ server-only module reload
- Cleans build directory before build starts
- Resolves
#nitro-vite-setupvirtual module - Provides production setup code for service environments
- Routes all preview requests through Nitro
- WebSocket upgrade support
- Merge plugin config with user config
- Load dotenv files
- Detect SSR entry (looks for
entry-server.{ts,js,tsx,jsx,mjs}) - Create Nitro instance (
createNitro()) - Resolve bundler config (
getBundlerConfig()) - Initialize env-runner for dev (
initEnvRunner())
Nitro uses Vite 6+ environments API for multi-bundle builds:
| Environment | Consumer | Purpose |
|---|---|---|
client |
"client" |
Browser HTML/CSS/JS |
nitro |
"server" |
Main server bundle |
ssr |
"server" |
Optional SSR service |
| Custom | "server" |
User-defined services |
- Consumer:
"server" - Uses bundler config (Rollup/Rolldown)
- Dev: creates
FetchableDevEnvironmentwith hot reload - Prod: standard environment with minify, sourcemap, commonJS options
- Resolve:
noExternaldiffers for dev vs prod - Special conditions:
"workerd"for miniflare, excludes"node"
- Uses
env-runnerpackage for worker management - Supports Node Worker or Miniflare runtime
- Auto-restarts on failure (max 3 retries)
- Custom evaluator for workerd (
AsyncFunctionnot allowed) - Routes module imports through Vite's transform pipeline
- Triggers full reload of the env-runner worker
- Overrides
fetchModule()for CJS/ESM resolution - For workerd: prevents externalization of bare imports
dispatchFetch()— routes requests to the dev server worker- Sends custom message on init with environment info
- Watches Nitro config file for changes (triggers full restart)
- WebSocket upgrade handling
- File watchers for route/API/middleware directories (debounced reload)
- RPC for
transformHTMLmessages
Two-stage request routing:
- Pre-processor — checks if request should go to Nitro:
- Skips Vite internal requests (
/@,/__) - Skips file extension requests (
.js,.css, etc.) - Uses
sec-fetch-destheader for browser detection - Routes to
NitroDevAppfirst (static/proxy/dev handlers)
- Skips Vite internal requests (
- Main handler — falls back to env-runner worker for server routes
Browser → Vite Dev Server
→ nitroDevMiddleware (pre-processor)
→ NitroDevApp (static assets, dev proxy, /_vfs)
→ env-runner worker (main server logic)
→ Vite static/HMR (if not handled)
Stage 1: Build non-Nitro environments
- Client environment (browser bundle)
- Service environments (SSR, API, custom)
- Detailed logging per environment
Stage 2: Renderer template processing
- If client input == renderer template, replaces SSR outlet
- Inlines
globalThis.__nitro_vite_envs__?.["ssr"]?.fetch($REQUEST) - Moves processed template to build dir
Stage 3: Asset management
- Calls
builder.writeAssetsManifest?.() - Registers asset dirs with
max-age=31536000, immutable
Stage 4: Build Nitro environment
prepare()→ clean output- Build main server bundle
- Close Nitro instance
- Fire
compiledhook - Write build info
Stage 5: Preview
- Start preview server, log success
Generates #nitro-vite-setup content:
// For each service environment
globalThis.__nitro_vite_envs__ = {
"ssr": { fetch: (...args) => import("entry").then(m => m.default.fetch(...args)) }
}getBundlerConfig() returns:
{
base: BaseBuildConfig,
rollupConfig?: RollupConfig, // if using Rollup
rolldownConfig?: RolldownConfig // if using Rolldown
}Common config: ESM output, tree-shaking, chunking, sourcemaps.
Rolldown-specific: Transform injection, library chunking, inlineDynamicImports/iife support.
Rollup-specific: @rollup/plugin-inject, @rollup/plugin-alias, manual chunk naming.
Server-only module reload:
hotUpdatehook detects file change- Determines if module is server-only or shared
- Server-only → sends
full-reloadto nitro environment - Shared → returns for normal Vite client HMR
- Optionally reloads browser (
experimental.vite.serverReload)
Directory watchers (debounced):
- Routes, API, middleware, plugins, modules dirs
- Add/delete → full routing rebuild + reload
| File | Purpose |
|---|---|
dev-entry.mjs |
Dev entry: polyfills, WebSocket adapter, schedule runner |
dev-worker.mjs |
Worker process: ViteEnvRunner class, RPC layer, env management |
ssr-renderer.mjs |
SSR service: calls fetchViteEnv("ssr", req) |
- Manages Vite
ModuleRunnerper environment - Loads environment entry via
runner.import() - Routes fetch requests to loaded entries
- Exposes
__VITE_ENVIRONMENT_RUNNER_IMPORT__for RSC
fetchViteEnv(name, input, init)— route fetch to named Vite environment- Accesses
globalThis.__nitro_vite_envs__registry
| Aspect | Dev | Production |
|---|---|---|
| Runner | env-runner (node-worker / miniflare) | Bundled ESM |
| HMR | Full reload on file change | N/A |
| Errors | Interactive error page (Youch) | JSON or minimal HTML |
| Services | Lazy-loaded via env-runner | Pre-bundled via prodSetup() |
| Template | Dynamic (vite-env route) | Static (inlined SSR outlet) |
| Sourcemaps | Enabled | Optional |
experimental.vite options:
assetsImport(default: true) —?assetsimports via@hiogawa/vite-plugin-fullstackserverReload(default: true) — reload on server-only module changesservices— register custom service environments
src/vite.ts— public export (nitroplugin)src/build/build.ts— dispatcher callsviteBuild()src/build/config.ts— base build configsrc/build/plugins.ts— base build plugins (virtual modules, auto-imports, etc.)src/build/virtual/— 14 virtual module templatessrc/dev/app.ts—NitroDevAppfor dev-only handlerssrc/dev/server.ts—NitroDevServerwithRunnerManagersrc/runtime/internal/vite/— runtime worker and entry points