# Data flow — vitejs/vite

> Canonical page: https://codevetter.com/unpack/vite/data-flow

# Data flow — vitejs/vite

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

Three input boundaries feed the system: the config file (loaded by bundling, runner-import, or native import), `.env` files filtered by prefix, and HTTP/WS requests from the browser. Config flows into a single frozen `ResolvedConfig` that fans out into per-environment configs; each `DevEnvironment` then owns a `EnvironmentModuleGraph` as the authoritative in-memory state. A request's data path is URL → plugin container `resolveId`→`load`→`transform` → optional `ssrTransform` → cached `TransformResult` (code + map + etag + deps) stored on the module node and written back out through `send()`. The reverse direction — file change → HMR payload → WebSocket → browser — is the only push channel. In build mode the same plugin container is driven by Rolldown instead of by HTTP requests, and the terminal output boundary is `outDir` plus optional `manifest.json`/`ssr-manifest.json`.
- Config entry: `resolveConfig` merges inline config over the file config from `loadConfigFromFile`, then re-derives `mode`, filters plugins by `apply`, runs `config` hooks via `runConfigHook`, and force-injects `client` (and usually `ssr`) into `config.environments` before per-environment resolution. — [packages/vite/src/node/config.ts#L1489-1610](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/config.ts#L1489-L1610)
- `loadConfigFromFile` picks one of three loaders selected by `--configLoader`: `bundleAndLoadConfigFile` (default — Rolldown-bundles the config then evaluates it), `runnerImportConfigFile` (uses Vite's own module runner), or `nativeImportConfigFile` (raw `import()` of a `file://` URL with a `?t=` cache-buster, or `fresh-import`). — [packages/vite/src/node/config.ts#L2423-2535](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/config.ts#L2423-L2535)
- Env vars enter through `loadEnv(mode, envDir, prefixes = 'VITE_')`, which reads `.env`, `.env.local`, `.env.<mode>`, `.env.<mode>.local` (accepting FIFOs so 1Password-style pipes work), parses with `node:util` `parseEnv`, then expands with `dotenv-expand` against a *copy* of `process.env` so expansion doesn't mutate the global. — [packages/vite/src/node/env.ts#L12-80](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/env.ts#L12-L80)
- `transformRequest(environment, url, options)` dedupes concurrent work through `environment._pendingRequests` keyed by timestamp-stripped URL, and records a `monotonicDateNow()` timestamp up front so a transform that races an invalidation is discarded rather than cached stale. — [packages/vite/src/node/server/transformRequest.ts#L76-150](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/transformRequest.ts#L76-L150)
- Inside `loadAndTransform` the order is `pluginContainer.load(id)` → fall back to reading from disk → `pluginContainer.transform(code, id, ...)` → `ssrTransform(code, normalizedMap, url, originalCode, ...)` when the environment needs module-runner output. — [packages/vite/src/node/server/transformRequest.ts#L237-430](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/transformRequest.ts#L237-L430)
- The transform result carries `{ code, map, ssr?, etag?, deps?, dynamicDeps? }`; the etag is what `cachedTransformMiddleware` matches against `if-none-match` via `moduleGraph.getModuleByEtag` to short-circuit with a 304. — [packages/vite/src/node/server/transformRequest.ts#L48-56](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/transformRequest.ts#L48-L56) [packages/vite/src/node/server/middlewares/transform.ts#L74-106](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/middlewares/transform.ts#L74-L106)
- `importAnalysisPlugin` is the rewrite stage that converts every import specifier to a server-resolvable URL via its internal `normalizeUrl`, records importer/imported edges on the module graph, and computes `isSelfAccepting` from `import.meta.hot.accept` calls — the data the HMR propagation algorithm later consumes. — [packages/vite/src/node/plugins/importAnalysis.ts#L225-340](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/plugins/importAnalysis.ts#L225-L340) [packages/vite/src/node/plugins/importAnalysis.ts#L500-600](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/plugins/importAnalysis.ts#L500-L600) [packages/vite/src/node/plugins/importAnalysis.ts#L840-880](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/plugins/importAnalysis.ts#L840-L880)
- HMR output is one of two payload shapes computed in `updateModules`: a `{type:'full-reload', triggeredBy, path}` message, or a `{type:'update', updates:[...]}` array whose entries carry `path`, `acceptedPath`, `explicitImportRequired`, `isWithinCircularImport` and `firstInvalidatedBy`. — [packages/vite/src/node/server/hmr.ts#L683-790](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/hmr.ts#L683-L790)
- Client-side the payload arrives over a `normalizeModuleRunnerTransport`-wrapped WebSocket opened to `${protocol}://${socketHost}?token=${wsToken}` with subprotocol `vite-hmr`, and is dispatched by `handleMessage` into `hmrClient.queueUpdate(update)`. — [packages/vite/src/client/client.ts#L46-60](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/client/client.ts#L46-L60) [packages/vite/src/client/client.ts#L187-230](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/client/client.ts#L187-L230)
- Static assets bypass the transform pipeline entirely: `servePublicMiddleware` is registered *before* `transformMiddleware` specifically so `publicDir` files are served as-is, and `serveRawFsMiddleware` handles `/@fs/`-prefixed absolute paths by slicing `FS_PREFIX` off the pathname. — [packages/vite/src/node/server/index.ts#L1044-1059](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/index.ts#L1044-L1059) [packages/vite/src/node/server/middlewares/static.ts#L204-230](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/middlewares/static.ts#L204-L230)
- In bundled-dev mode the data path inverts: Rolldown produces output files that `BundledDev` stores as lazily-evaluated entries in `MemoryFiles` (including `BUNDLED_DEV_CLIENT_FILENAME`), and `memoryFilesMiddleware` serves from that map instead of from the module graph. — [packages/vite/src/node/server/bundledDev.ts#L28-130](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/bundledDev.ts#L28-L130) [packages/vite/src/node/server/bundledDev.ts#L371-390](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/bundledDev.ts#L371-L390) [packages/vite/src/node/server/middlewares/memoryFiles.ts](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/server/middlewares/memoryFiles.ts)
- Build-mode data flow is fanned out by environment: `createBuilder` re-runs `resolveConfigToBuild` once per environment name (unless `configBuilder.sharedConfigBuild`) with a `patchPlugins` pass that swaps in shared plugin instances for those marked `sharedDuringBuild`. — [packages/vite/src/node/build.ts#L1920-1975](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/build.ts#L1920-L1975)
- Module-runner data flow closes the loop server→runtime: `ModuleRunner.directRequest` calls `this.transport.invoke('fetchModule', [...])`, the server answers from `fetchModule`, and the result is evaluated and cached in `EvaluatedModules` keyed by both id and url. — [packages/vite/src/module-runner/runner.ts#L245-340](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/module-runner/runner.ts#L245-L340) [packages/vite/src/node/ssr/fetchModule.ts#L24-70](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/ssr/fetchModule.ts#L24-L70)
- The `ResolvedConfig` object is also the carrier for two derived security primitives computed once at resolve time: `webSocketToken` (72 random bits, base64url) and `fsDenyGlob` (a `picomatch` matcher built from `server.fs.deny` with `nocase` and `dot` enabled). — [packages/vite/src/node/config.ts#L2174-2212](https://github.com/vitejs/vite/blob/1544bb1b7ac92f775e9d0dea97954c99cc6cbd43/packages/vite/src/node/config.ts#L2174-L2212)

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)
