# Behavior traces — vitejs/vite

> Canonical page: https://codevetter.com/unpack/vite/behavior-traces

# Behavior traces — vitejs/vite

Scanned commit 1544bb1b7ac92f775e9d0dea97954c99cc6cbd43 (2026-09-22). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Four flows are worth tracing: dev-server startup, a module request, a file-change HMR round trip, and a production build. Startup is notable for deferring `buildStart` and optimizer init until the HTTP `listen` call is intercepted. The module request path is a long but linear middleware chain. HMR distinguishes config/env changes (full server restart) from module changes (graph propagation). The build path is a per-environment loop over Rolldown.
- Dev startup: `bin/vite.js` → `dist/node/cli.js` → the default cac action → `createServer()` → `_createServer` → `disableCache()` → `resolveConfig(inlineConfig,'serve')` → `resolveHttpServer` → `createWebSocketServer` → chokidar watcher → per-environment `createEnvironment`/`init` → middleware registration → `server.listen()`. — [packages/vite/src/node/cli.ts#L219-243](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/cli.ts#L219-L243) [packages/vite/src/node/server/index.ts#L512-620](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L512-L620)
- Startup defers expensive work by monkey-patching the HTTP server: `httpServer.listen` is replaced with a wrapper that awaits `initServer(true)` first and re-emits any error as an `'error'` event, so `buildStart` and `environment.listen(server)` run exactly once before the socket binds. — [packages/vite/src/node/server/index.ts#L1095-1136](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L1095-L1136)
- In middleware mode there is no HTTP server at all — `httpServer` is `null`, `resolveHttpServer` is skipped, and `initServer(false)` is awaited inline at the end of `_createServer`. — [packages/vite/src/node/server/index.ts#L569-571](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L569-L571) [packages/vite/src/node/server/index.ts#L1128-1134](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L1128-L1134)
- The middleware chain is registered in a fixed order: `timeMiddleware` (only when `process.env.DEBUG`) → `rejectInvalidRequestMiddleware` → `corsMiddleware` → `hostValidationMiddleware` → user `configureServer` hooks → `cachedTransformMiddleware` → `proxyMiddleware` → `baseMiddleware` → `/__open-in-editor` → HMR ping → `servePublicMiddleware` → `transformMiddleware` → `serveRawFsMiddleware` → `serveStaticMiddleware` → `htmlFallbackMiddleware` → post hooks → `indexHtmlMiddleware` → `notFoundMiddleware` → `errorMiddleware`. — [packages/vite/src/node/server/index.ts#L979-1090](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L979-L1090)
- `configureServer` hooks run in two phases around the internal middlewares: the hook's synchronous body runs before `cachedTransformMiddleware` is added, while any function it *returns* is pushed to `postHooks` and invoked after the static/HTML-fallback middlewares but before `indexHtmlMiddleware`, which is what lets user middleware pre-empt `index.html`. — [packages/vite/src/node/server/index.ts#L1000-1010](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L1000-L1010) [packages/vite/src/node/server/index.ts#L1071-1082](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L1071-L1082)
- A module request runs `transformMiddleware` → `environment.transformRequest(url)` → `transformRequest` (pending-request dedupe) → `doTransform` (`pluginContainer.resolveId`) → `loadAndTransform` (`pluginContainer.load`, then `pluginContainer.transform`) → optional `ssrTransform` → `send()` with the etag. — [packages/vite/src/node/server/transformRequest.ts#L76-430](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/transformRequest.ts#L76-L430) [packages/vite/src/node/server/environment.ts#L300-303](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/environment.ts#L300-L303) [packages/vite/src/node/server/send.ts](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/send.ts)
- `EnvironmentPluginContainer.resolveId` lazily triggers `this.buildStart()` and awaits `_buildStartPromise` on the first resolution, so for non-client environments the `buildStart` hook fires on first request rather than at server boot. — [packages/vite/src/node/server/pluginContainer.ts#L320-375](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/pluginContainer.ts#L320-L375) [packages/vite/src/node/server/index.ts#L1100-1106](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L1100-L1106)
- File change → `watcher.on('change')` → `onFileChange` → `handleHMRUpdate(type, file, server)`, which first checks whether the file is `config.configFile`, a member of `config.configFileDependencies`, or an env file for the current mode — any of those triggers `restartServerWithUrls(server)` instead of an HMR update. — [packages/vite/src/node/server/index.ts#L960-968](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L960-L968) [packages/vite/src/node/server/hmr.ts#L411-452](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/hmr.ts#L411-L452)
- `handleHMRUpdate` snapshots `server.environments` and defines `isStale()` comparing identity, because a `hotUpdate`/`handleHotUpdate` plugin hook may restart the server mid-transaction and invalidate the whole HMR pass. — [packages/vite/src/node/server/hmr.ts#L418-425](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/hmr.ts#L418-L425)
- Edits to Vite's own client directory can't be hot-updated: if the changed file starts with `normalizedClientDir`, every environment immediately gets `hot.send({ type:'full-reload', path:'*' })` and the function returns. — [packages/vite/src/node/server/hmr.ts#L454-466](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/hmr.ts#L454-L466)
- `updateModules` runs `propagateUpdate(mod, traversedModules, boundaries)` per changed module, invalidates via `moduleGraph.invalidateModule`, and escalates to full reload on any dead end, on an empty `modules` array, on `circular import invalidate`, or when a client-environment `.html` file changes and none of its modules are of type `js`. — [packages/vite/src/node/server/hmr.ts#L683-775](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/hmr.ts#L683-L775)
- Production build: `cli.ts` build action → `createBuilder(inlineConfig, null)` → `builder.buildApp()` → sorted `buildApp` plugin hooks with `configBuilder.buildApp(builder)` interleaved before `order:'post'` hooks → fallback loop calling `builder.build(environment)` for any environment still `!isBuilt` → `buildEnvironment` → `rolldown()` → `bundle.write()` → `builder.runDevTools()`. — [packages/vite/src/node/cli.ts#L340-370](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/cli.ts#L340-L370) [packages/vite/src/node/build.ts#L1855-1900](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/build.ts#L1855-L1900) [packages/vite/src/node/build.ts#L860-930](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/build.ts#L860-L930)
- Server shutdown runs `closeServer(reason)` which `Promise.allSettled`s watcher close, ws close, every `environment.close()`, `closeHttpServer()` and `_ssrCompatModuleRunner?.close()`, and only *afterwards* invokes the sorted `closeServer` plugin hooks with `{ reason }`. — [packages/vite/src/node/server/index.ts#L628-660](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L628-L660)
- Browser reconnection is handled separately from HMR: `client.ts` opens a second WebSocket with the `vite-ping` subprotocol, which the server's `shouldHandle` allows from any origin without a token and which `handleUpgrade` closes immediately with code 1000 so the pinging client is never added to `wss.clients`. — [packages/vite/src/client/client.ts#L478-490](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/client/client.ts#L478-L490) [packages/vite/src/node/server/ws.ts#L169-212](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/ws.ts#L169-L212)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
