Antler by Autoflux

Architecture

The dev/build split: native ESM dev server + esbuild dependency pre-bundling, Rollup production build, and the plugin API that spans both.

Path: architecture

Third-party documentation. This is independently authored analysis of the public Vite codebase — not the official docs, and not reviewed or endorsed by the Vite team.

Architecture

Vite runs two entirely different engines: a native-ESM dev server backed by esbuild, and a Rollup production build. Both are coordinated by a single config and a plugin API that can hook into either phase. Understanding the split explains almost every Vite behavior.

The dev server

  1. The browser requests the entry /index.html; Vite rewrites bare imports like import React from 'react' into URLs it can serve (/node_modules/.vite/deps/react.js).
  2. Dependencies (code in node_modules) are pre-bundled once with esbuild into a cached set under node_modules/.vite, converted to ESM, and served as a single optimized file. This makes them load fast and avoids per-file transform overhead. The cache is invalidated when a dependency's version or the lockfile changes.
  3. Application source files are served as-is (native ESM), transformed lazily per request: TS/JSX goes through esbuild, Vue/Svelte files through their compiler plugins, and CSS is inlined or served as a module.
  4. HMR works over a WebSocket: when a module changes, Vite invalidates just that module and its importers, and framework plugins push a hot update to the browser — component state is preserved where the framework supports Fast Refresh.

The production build

  1. Vite hands the module graph to Rollup, which performs a full static analysis pass: tree-shaking unused exports, resolving dynamic imports into separate chunks, minifying, and applying CSS/asset post-processing.
  2. Output lands in dist/ (configurable via build.outDir), with hashed filenames for long-term caching and an index.html that references the entry bundles.
  3. Because dev and build share the plugin pipeline (resolveId, load, transform, and Rollup's output hooks), most plugins work identically in both modes — Vite adds dev-only hooks like configureServer and handleHotUpdate on top.

Why the split exists

Pre-bundling dependencies once with esbuild makes cold starts fast and independent of app size, but esbuild's output isn't designed for optimal production caching — that's Rollup's job. Using the right tool for each phase means developers get an instant dev loop without sacrificing the quality of the shipped bundle.

System Diagram

User
Frontend
Lending
Vaults
Trading
Liquidation
Core
Oracle
Data / Storage

Interface

Interface
text
DEV PROD
┌────────────────────────────────────┐ ┌────────────────────────────────────┐
│ Browser (native ESM imports) │ │ Rollup (full bundle) │
│ ▲ │ │ │ ▲ │
│ │ import │ on-demand │ │ │ bundle + tree-shake + minify │
│ │ │ transform │ │ │ │
│ ┌─────┴─────┐ ┌─────▼─────┐ │ │ ┌────────────┐ ┌────────────┐ │
│ │ Vite dev │ │ esbuild │ │ │ │ Rollup │ │ plugins │ │
│ │ server │ │ dep opt │ │ │ │ (bundler) │ │ (Rollup- │ │
│ │ (HMR via │ │ + TS/JSX │ │ │ │ │ │ compatible│ │
│ │ WebSocket)│ │ transform │ │ │ └────────────┘ └────────────┘ │
│ └───────────┘ └───────────┘ │ └────────────────────────────────────┘
│ node_modules/.vite (dep cache) │ dist/ (optimized static output)
└────────────────────────────────────┘
 
STATUSinterface