Build (vext build)
vext build compiles server source code into deployable JavaScript products and, when frontend.enabled is true, bundles the browser client into dist/client/. The current version of vext build is designed as a production-target build: it statically injects production mode into process.env.NODE_ENV in user source code; the runtime config profile is selected independently with vext start --config <name> or VEXT_CONFIG=<name>. Based on esbuild implementation, the compilation phase is optimized for fast local and CI feedback.
Quick Start
Build pre-product refresh
When a TypeScript project executes vext build, it will first refresh the generated and manifest products required by the development tool chain, and then enter optional type checking and esbuild compilation:
vext typegenbasic refresh: write.vext/types/*.generated.d.ts,src/types/generated/index.d.tsand.vext/manifest/services.jsondoctor routes --refresh --write-manifest: Rescan routes and write.vext/manifest/routes.json- If
--typecheckis passed in, executetsc --noEmitat this time - esbuild outputs the server runtime into
dist/ - If
config.frontend.enabledis true, esbuild bundles the browser client intodist/client/
This ensures that new scaffolding or projects that have just cleaned .vext/ can also get the latest generated type in vext build --typecheck before entering TypeScript verification.
Compilation strategy
File-by-File Transform
vext build uses file-by-file compilation mode for server code instead of bundle mode - each source file is independently compiled into an output file, maintaining the directory structure mapping of src/. src/frontend/** is excluded from this server file-by-file step and is handled by the frontend bundler and SSR renderer build steps.
Why not Bundle?
Compile options
CLI parameters
Frontend build
When config.frontend.enabled is true, the browser pipeline uses esbuild in bundle mode:
The frontend build writes:
vext start fails fast when frontend is enabled but dist/client/index.html is missing. Run vext build before production start.
Browser pages, layouts, error pages, and locale entries are loaded through dynamic imports so page-level chunks are emitted by default. React and other shared packages are pulled into a Vext-managed vendor entry and split through esbuild. frontend.build.client.external can exclude a module from the browser bundle, but the browser must receive it through frontend.build.client.externalRuntime, which Vext writes into an import map.
When frontend.deploy.integrity=true, Vext injects build-time SRI into generated JS/CSS tags. deploy-manifest.json includes esbuild output and copied public/** files for vext deploy assets; it intentionally excludes index.html and source maps because HTML is rendered and cached by the Vext server path, while source maps should stay on the server debugging path unless explicitly published.
Output format
Optimization options
Automatic injection
The following definitions are automatically injected at compile time:
This will cause the process.env.NODE_ENV branch in the user source code after build to be statically collapsed according to production semantics.
This injection does not mean the runtime can only load config/production.ts. The runtime config profile is selected by vext start --config <name> or VEXT_CONFIG=<name>, for example:
Will try to load:
Therefore, the current recommended usage of vext build is:
- Use it to generate production-target artifact
- Use
vext start --config <profile>orVEXT_CONFIG=<profile>to select the config profile - Do not rely on the
process.env.NODE_ENVconditional branch in the user source code after build to do sit/uat/prod switching
File scanning rules
Included files
vext build scans the src/ directory for all files matching the following patterns:
Server file-by-file compilation ignores src/frontend/**; frontend files are handled by the browser bundle and SSR renderer build steps.
Excluded files
Compilation automatically excludes the following files (two-level exclusion rules):
Universal exclusions (shared with development mode)
Additional exclusions for production compilation
This means that development/test/local configuration files will not be included in dist/, preventing sensitive information from leaking into the production environment.
Source Map
External Source Map
vext build generates an external .js.map file (unlike vext dev’s inline source map):
Enable Source Map support
Enable source maps when running in Node.js so that error stacks show TypeScript line numbers:
Error stack when not enabled:
Error stack after enabling:
Source Map Purpose
When deploying to a production environment, the .js.map file can remain on the server (not exposed by HTTP), but do not deploy to a CDN or static file serving.
Comparison with DevCompiler
Shared configuration
The esbuild configuration shared by both includes:
platform: 'node'— Node.js runtimetarget: 'node20'— Minimum support for Node.js 20.19.0format: 'cjs'— CommonJS outputbundle: false— compile file by filetreeShaking: true— dead code eliminationkeepNames: true— keep function namescharset: 'utf8'— UTF-8 encodingloader—.ts/.js/.jsonand other file type mappings
Compilation results
After vext build is executed, a compilation report is output:
BuildResult structure
Run the compiled product
If the TypeScript project lacks dist/ or key build products, vext start will fail directly and prompt to execute vext build first. Please use vext dev to start the source code during the development period.
If frontend is enabled, vext start also checks dist/client/index.html and serves dist/client/ with SPA fallback outside API/documentation paths.
There is no fixed dist/index.js startup entry for universal scaffolding projects; the direct node dist/index.js is only suitable for advanced scenarios where you maintain the entry file yourself and explicitly call the framework startup logic.
VEXT_BUILT tag
When running a valid dist/ build via vext start, the CLI sets the VEXT_BUILT=1 environment variable. This affects:
- Path parsing:
src/routes/→dist/routes/ - Module loading: Load routes, services, plugins, middlewares from the
dist/directory - Model loading: MonSQLize loads the Model definition from
dist/models/
You don't need to set this variable manually, the framework handles it automatically when using vext start.
Deployment manifest
Recommended steps for deploying to production using vext build:
Docker multi-stage build
.gitignore
Make sure the dist/ directory is in .gitignore (the compiled product should not be committed to Git):
Troubleshooting
Compilation failed
Make sure the src/ directory exists and contains .ts or .js files.
Module not found during runtime
Possible reasons:
- Dependencies are not reinstalled after
vext build(npm ci) - Some files were skipped by exclusion rules (check whether they meet the exclusion rules)
- Used dynamic
import()to reference the.d.tsfile
Source Map does not take effect
Make sure to pass the --enable-source-maps parameter via NODE_OPTIONS when starting:
Next step
- Learn about the complete deployment guide at Deployment and Production
- See Hot Reload to learn about development mode compilation strategies
- Learn the full parameters of
vext buildin CLI Commands - Explore Cluster Multi-Processing to take full advantage of multi-core CPUs