436
Documentation

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 build

This command:

  1. Compiles server code
  2. Bundles client code
  3. Generates static pages (SSG)
  4. Creates route manifests
  5. Optimizes assets

Development

loly dev
# or
pnpm dev

Starts 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 .mjs files (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 generateStaticParams if it exists
  • Executes getServerSideProps for 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 patterns
  • routes-client.ts: Client-side route definitions with lazy loading
  • route-chunks.json: Mapping of routes to their chunk names
  • asset-manifest.json: Assets with hashes for cache busting
  • rewrites-manifest.json: URL rewrite rules
  • boostrap.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 file

Key Points:

  • Server code is compiled as ESM modules (.mjs), enabling modern features like top-level await, dynamic imports, and import.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:

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.js

Environment Variables

Build Time

LOLY_BUILD=1
NODE_ENV=production

Runtime

PORT=3000
HOST=0.0.0.0
PUBLIC_WS_BASE_URL=http://localhost:3000

Hot 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

  1. Check TypeScript errors
  2. Review circular imports
  3. 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:

  1. Verify that package.json includes "type": "module"
  2. Check that you're using ESM-compatible import syntax for packages that require it
  3. Some packages (like react-syntax-highlighter) require default imports in ESM mode instead of named imports
package.json
{
  "name": "my-app",
  "version": "1.0.0",
  "type": "module",
  // ... rest of config
}

Bundle Too Large

  1. Review unnecessary imports
  2. Use code splitting
  3. Optimize dependencies

SSG Not Generating Pages

  1. Verify that dynamic = "force-static"
  2. Verify that generateStaticParams returns data
  3. Check for errors in loaders