Production Builds
Understand how Lunx compiles, optimizes, and secures your application for production deployment. Learn about SWC compilation, LightningCSS, code splitting, and the security pipeline.
Build pipeline overview
When you run lunx build, the Rust compiler orchestrates these stages in order:
- Security scan — secrets, CVEs, SBOM, SRI (aborts on violations)
- Module resolution — resolves all imports, applies path aliases and externals
- SWC compilation — TypeScript → JavaScript, JSX transform, decorator support
- LightningCSS — CSS transforms, nesting, vendor prefixes, minification
- Rollup bundling — tree-shaking, code splitting, chunk hashing
- Asset processing — images, fonts, SVGs inlined or hashed and copied
- HTML injection — SRI hashes, preload links, and manifest injected into index.html
bash
npm run build ⚡ lunx v1.0.0 adapter: react mode: production 🔍 Security pipeline... [1/4] Secret scan: ✓ 0 secrets in 84 files [2/4] CVE check: ✓ 0 critical, 0 high in 312 packages [3/4] SBOM: ✓ dist/bom.json (CycloneDX 1.5) [4/4] SRI hashes: ✓ injected into index.html 📦 Bundling... dist/assets/main-4f2c1a.js 92.4 kB │ gzip: 29.8 kB dist/assets/vendor-9a3d2b.js 48.2 kB │ gzip: 16.1 kB dist/assets/style-8d3f20.css 12.4 kB │ gzip: 3.6 kB dist/assets/logo-3b1e9c.svg 2.1 kB dist/index.html 1.1 kB dist/bom.json 8.4 kB ✓ built in 533ms
Code splitting
Lunx performs automatic code splitting. Every dynamic import() creates a separate chunk that is loaded on demand. Shared vendor dependencies are split into a stable chunk that is cached across deployments.
typescript
// Automatic code splitting with React.lazy import { lazy, Suspense } from 'react' const AdminPanel = lazy(() => import('@/pages/AdminPanel')) const Analytics = lazy(() => import('@/pages/Analytics')) function App() { return ( <Suspense fallback={<div>Loading...</div>}> <Routes> <Route path="/admin" element={<AdminPanel />} /> <Route path="/analytics" element={<Analytics />} /> </Routes> </Suspense> ) }
Manual chunks
Use build.rollupOptions.output.manualChunks to control which modules land in which chunk. This is useful for ensuring large libraries are cached separately from application code.
typescriptlunx.config.ts
export default defineConfig({ framework: 'react', build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules/react')) return 'react-vendor' if (id.includes('node_modules/@radix-ui')) return 'ui-vendor' if (id.includes('node_modules/chart.js')) return 'charts' } } } } })
Asset handling
Files imported in your source code are processed automatically:
- Files under 4KB are inlined as base64 data URIs (configurable via
build.assetsInlineLimit) - Files over 4KB are copied to
dist/assets/with a content hash in the filename - SVG files can be imported as React components via the
@lunx/plugin-svgplugin
typescript
// Static URL import (content-hashed) import logoUrl from '@/assets/logo.png' // '/assets/logo-3b1e9c.png' // SVG as React component (with @lunx/plugin-svg) import { ReactComponent as Logo } from '@/assets/logo.svg' // JSON files are imported and tree-shaken import config from '@/config/defaults.json'
Environment variables in builds
bash.env.production
# Only LUNX_PUBLIC_ variables are bundled into client code LUNX_PUBLIC_API_URL=https://api.example.com LUNX_PUBLIC_SENTRY_DSN=https://...@sentry.io/123 # These remain server-side only (never in bundle) DATABASE_URL=postgres://... STRIPE_SECRET_KEY=sk_live_...
Analyzing bundle size
bash
# Generate an interactive bundle size report lunx build --analyze # Opens a treemap visualization at http://localhost:8888 # showing every module and its contribution to bundle size
Keep vendor chunks stable
Use
manualChunks to separate vendor libraries from your app code. Vendor chunks rarely change, so users benefit from long-term browser caching across deploys. Only the app chunk gets a new hash when you ship a feature.