Build System
Learn how Loly Framework compiles your application for production, optimizing code and generating static assets.
Introduction
Loly Framework's build system compiles your application for production, optimizing code and generating static assets.
The build process handles server compilation, client bundling, static site generation, and asset optimization automatically.
Build Commands
Production Build
loly build
# or
pnpm buildThis command:
- Compiles server code
- Bundles client code
- Generates static pages (SSG)
- Creates route manifests
- Optimizes assets
Development
loly dev
# or
pnpm devStarts the development server with:
- Hot Module Replacement (HMR)
- Automatic route reloading
- Source maps
- Detailed logging
Build Process
1. Route Loading
The framework scans app/ and loads:
- Pages (
page.tsx) - API routes (
route.ts) - WebSocket routes (
events.ts) - Layouts
- Middlewares
- Loaders
2. Server Build
Compiles server code using esbuild:
- Bundling of ESM modules (ES Modules)
- Tree shaking
- Minification
- Output in
.loly/server/as.mjsfiles (ESM) - Source maps (
.mjs.map) generated for debugging - Server hooks (
*.server.hook.mjs) compiled separately when present - Path aliases resolved correctly
- Static files (JSON, txt, etc.) automatically copied maintaining structure
Note: Server code is compiled as ESM modules (.mjs), which allows using modern features like top-level await, dynamic imports, and import.meta.url.
3. Client Build
Bundles client code using Rspack:
- Automatic code splitting
- Import optimization
- Minification
- Output in
.loly/client/
4. SSG Generation
For pages with force-static:
- Executes
generateStaticParamsif it exists - Executes
getServerSidePropsfor each route - Generates static HTML
- Output in
.loly/ssg/
5. Manifest Generation
Creates manifests and client route files:
routes-manifest.json: Server routes, API routes, and patternsroutes-client.ts: Client-side route definitions with lazy loadingroute-chunks.json: Mapping of routes to their chunk namesasset-manifest.json: Assets with hashes for cache bustingrewrites-manifest.json: URL rewrite rulesboostrap.ts: Client bootstrap file that initializes the router
Output Structure
.loly/
├── server/ # Compiled server code (ESM .mjs)
│ ├── app/
│ │ └── docs/
│ │ └── build/
│ │ ├── page.mjs
│ │ └── page.mjs.map # Source maps
│ ├── api/
│ │ └── health/
│ │ ├── route.mjs
│ │ └── route.mjs.map
│ ├── init.server.mjs
│ ├── layout.mjs
│ ├── page.mjs
│ └── styles.css
├── client/ # Client code bundles
│ ├── client.js # Main client bundle (hashed)
│ ├── client.css # Styles (hashed)
│ ├── route-docs_build.js # Route-specific chunk
│ ├── route-docs_build.abc123.js # Route chunk with hash
│ ├── vendor-commons.js # Shared vendor code
│ └── favicon.png
├── ssg/ # Static pages (generated for force-static routes)
│ └── (empty if no static routes)
├── routes-manifest.json # Server route definitions
├── routes-client.ts # Client route loader definitions
├── route-chunks.json # Route to chunk mapping
├── asset-manifest.json # Asset hashes and entrypoints
├── rewrites-manifest.json # URL rewrite rules
└── boostrap.ts # Client bootstrap fileKey Points:
- Server code is compiled as ESM modules (
.mjs), enabling modern features like top-level await, dynamic imports, andimport.meta.url - Source maps (
.mjs.map) are generated alongside compiled files for debugging - Client chunks are automatically code-split by route for optimal loading
- Assets include content hashes for cache busting (e.g.,
client.abc123.js) - SSG directory only contains files for routes marked with
dynamic = "force-static"
Build Configuration
The framework uses native ESM by default. Server files are compiled as ESM modules (.mjs).
You can configure build options in loly.config.ts:
import type { FrameworkConfig } from "@lolyjs/core";
export default {
build: {
clientBundler: "rspack", // "rspack" | "webpack" | "vite"
serverBundler: "esbuild", // "esbuild" | "tsup" | "swc"
outputFormat: "esm", // "cjs" | "esm" (default: "esm")
},
} satisfies Partial<FrameworkConfig>;Code Splitting
Automatic
The framework automatically splits:
- Client bundle: Shared code
- Route chunks: Code per route
- Error chunk: Error page code
Lazy Loading
Chunks are loaded on demand:
<link rel="modulepreload" href="/static/chunk-route.js" as="script">Optimizations
Minification
- JavaScript minified
- CSS minified
- HTML optimized
Tree Shaking
Removes unused code:
- Unused imports
- Uncalled functions
- Complete unused modules
Asset Hashing
Assets include hashes for cache busting:
client.js → client-abc123.js
chunk-route.js → chunk-route-def456.jsEnvironment Variables
Build Time
LOLY_BUILD=1
NODE_ENV=productionRuntime
PORT=3000
HOST=0.0.0.0
PUBLIC_WS_BASE_URL=http://localhost:3000Hot Module Replacement (HMR)
In development:
- Automatic component reloading
- Preserves state when possible
- Reloads routes when files change
Source Maps
Source maps (.mjs.map) are generated for all server files during the build process. They enable:
- Better error stack traces pointing to original source files
- Debugging in production with original source code context
- Easier troubleshooting of runtime issues
Source maps are always generated and placed alongside their corresponding .mjs files in the .loly/server/ directory.
Troubleshooting
Build Fails
- Check TypeScript errors
- Review circular imports
- Check for missing dependencies
ESM Import Errors
If you encounter errors like "The requested module does not provide an export named..." or issues with ESM imports:
- Verify that
package.jsonincludes"type": "module" - Check that you're using ESM-compatible import syntax for packages that require it
- Some packages (like
react-syntax-highlighter) require default imports in ESM mode instead of named imports
{
"name": "my-app",
"version": "1.0.0",
"type": "module",
// ... rest of config
}Bundle Too Large
- Review unnecessary imports
- Use code splitting
- Optimize dependencies
SSG Not Generating Pages
- Verify that
dynamic = "force-static" - Verify that
generateStaticParamsreturns data - Check for errors in loaders