Architecture
Overview
evjs is a React fullstack framework with type-safe routing (TanStack Router), data fetching (TanStack Query), and server functions ("use server"). It uses a Hono-based API server and is designed to be bundler-agnostic.
Build-Time Architecture
┌─────────────────────────── Build Time ───────────────────────────┐
│ │
│ @evjs/cli ──► @evjs/bundler-webpack ──► @evjs/manifest │
│ ▲ (manifests) │
│ @evjs/build-tools ──┘ │
│ (bundler-agnostic) │
│ │
└──────────────────────────────┬───────────────────────────────────┘
│
┌────────────────┴────────────────┐
▼ ▼
┌──────── Client (Browser) ────────┐ ┌──────── Server (Node/Edge) ──────┐
│ │ │ │
│ TanStack Router │ │ Hono App (createApp) │
│ TanStack Query │ │ registerServerFn() + route() │
│ __fn_call() stubs │ │ createFetchHandler() │
│ ServerTransport ────────────────┼─┼──► POST /api/fn ──► registry │
│ │ │ │
└──────────────────────────────────┘ └───────────────────────────────────┘
Package Dependency Graph
@evjs/cli ──► @evjs/bundler-webpack ──► @evjs/build-tools ──► @swc/core
│
└──► webpack (Node API)
@evjs/shared ──► @evjs/manifest
@evjs/server ──► @evjs/shared, hono, @hono/node-server
@evjs/client ──► @evjs/shared, @tanstack/react-router, @tanstack/react-query
Configuration Flow
ev.config.ts ──► defineConfig({ entry, html, dev, server, plugins })
│
├── entry, html ──► webpack entry + HtmlPlugin
├── plugins ──► EvPlugin[] (setup → buildStart/bundler/buildEnd)
├── dev.port ──► WebpackDevServer port
├── server.endpoint ──► EvWebpackPlugin + proxy path
├── server.dev.port ──► API server port
└── server.dev.https ──► HTTPS for API server
│
▼
plugin.setup(ctx) → collect hooks
│
▼
hooks.buildStart() → hooks.bundler(config) → createWebpackConfig()
│
▼
webpack Node API → hooks.buildEnd(result)
Server Function Pipeline
The "use server" directive triggers two separate transforms during build:
┌── Client Build ──► import { __fn_call } from 'transport'
│ export function getUsers(...args) {
.server.ts ────┤ return __fn_call(fnId, args)
│ }
│
└── Server Build ──► import { registerServerFn } from 'server'
// original body preserved
registerServerFn(fnId, getUsers)
Dev Server Architecture
Browser ──(:3000)──► WebpackDevServer ──► HMR (static assets)
│
└── /api/* proxy ──► Node Server (:3001)
│
Hono App
│
POST /api/fn
│
registry.get(fnId)(...args)
ev dev uses the webpack Node API directly:
- Creates webpack compiler + WebpackDevServer in-process
- Polls for
dist/server/manifest.json - Writes a CJS bootstrap and runs it with
node --watch
Build Flow (ev build)
loadConfig(cwd)— loadsev.config.tsor returns defaultscreateWebpackConfig(config, cwd)— generates webpack config (no temp files)- Calls
webpack()Node API directly @evjs/bundler-webpackruns during compilation:- Discovers
*.server.tsvia glob - Applies SWC transforms (client + server variants)
- Runs child compiler for server bundle
- Emits
dist/server/manifest.jsonanddist/client/manifest.json
- Discovers
Deployment Adapters
Node.js server.entry.mjs ──► @hono/node-server
ECMA (Deno/Bun) server.entry.mjs ──► createFetchHandler(app)
Service Worker sw.entry.js ──► self.addEventListener('fetch', ...)