eserstack Product-Candidate — build pipeline for laroux · eser/stack Install:
pnpm add jsr:@eserstack/laroux-bundler
Build system and bundler for laroux.js - A React Server Components framework for Deno.
This package provides the complete build system for laroux.js, including:
- Runtime Bundler - On-demand bundling during development with HMR
- Prebuilt Bundler - Production build optimization with code splitting
- Deno Bundler - Deno-native bundling with TypeScript support
- Transform Pipeline - JSX, TypeScript, and RSC transformation
- Chunk Manifest - Smart code splitting and lazy loading
- Module Map - Client component discovery and mapping
- HMR Client - Hot Module Replacement for instant updates
- Error Overlay - Beautiful development-mode error display
- CSS Processing - CSS bundling and optimization
- Import Rewriting - Automatic import path resolution
pnpm add @eserstack/laroux-bundlerThe runtime bundler provides on-demand bundling with Hot Module Replacement:
import { RuntimeBundler } from "@eserstack/laroux-bundler/runtime";
const bundler = new RuntimeBundler({
srcDir: "./src",
distDir: "./dist",
enableHMR: true,
logLevel: "info",
});
// Build on demand
const result = await bundler.buildClientComponent("./src/app/counter.tsx");
// Get client entry
const entry = await bundler.getClientEntry();
// Get module map for RSC
const moduleMap = bundler.getModuleMap();The prebuilt bundler optimizes for production with code splitting:
import { PrebuiltBundler } from "@eserstack/laroux-bundler/prebuilt";
const bundler = new PrebuiltBundler({
srcDir: "./src",
distDir: "./dist",
optimize: true,
});
// Build all components upfront
await bundler.buildAll();
// Get optimized assets
const assets = bundler.getAssets();
const moduleMap = bundler.getModuleMap();Direct access to Deno's bundling capabilities:
import { DenoBundler } from "@eserstack/laroux-bundler/deno";
const bundler = new DenoBundler({
srcDir: "./src",
distDir: "./dist",
});
// Bundle a single file
const result = await bundler.bundle("./src/app.tsx");
// Bundle with dependencies
const { code, map, dependencies } = await bundler.bundleWithDeps(
"./src/app.tsx",
);On-demand bundler for development with HMR support.
class RuntimeBundler {
constructor(config: BundlerConfig);
// Build a client component on-demand
buildClientComponent(path: string): Promise<BuildResult>;
// Get the client entry point (includes HMR client)
getClientEntry(): Promise<string>;
// Get module map for RSC rendering
getModuleMap(): ModuleMap;
// Watch for file changes and trigger HMR
watch(): AsyncIterator<HMRUpdate>;
}Features:
- Lazy bundling - only bundles requested files
- Hot Module Replacement with smart refresh
- Fast rebuild times with incremental compilation
- Source maps for debugging
Production bundler with optimization and code splitting.
class PrebuiltBundler {
constructor(config: BundlerConfig);
// Build all components upfront
buildAll(): Promise<void>;
// Get all bundled assets
getAssets(): Map<string, Asset>;
// Get module map
getModuleMap(): ModuleMap;
// Get chunk manifest for lazy loading
getChunkManifest(): ChunkManifest;
}Features:
- Automatic code splitting by route
- Tree shaking and minification
- Asset optimization (CSS, images)
- Content hashing for cache busting
- Chunk manifest for lazy loading
Low-level bundler using Deno's native capabilities.
class DenoBundler {
constructor(config: BundlerConfig);
// Bundle a single file
bundle(path: string): Promise<string>;
// Bundle with dependency tracking
bundleWithDeps(path: string): Promise<{
code: string;
map: string;
dependencies: string[];
}>;
}Transform TypeScript and JSX code for the browser.
import { transform } from "@eserstack/laroux-bundler/transform";
const result = await transform({
code: `export function Counter() { return <div>Count</div> }`,
filename: "counter.tsx",
target: "client", // or "server"
jsx: "react",
});
console.log(result.code); // Transformed JavaScript
console.log(result.map); // Source mapTransformation Features:
- TypeScript to JavaScript
- JSX to React.createElement (or custom pragma)
- Client/Server component boundary handling
- Import path rewriting for browser compatibility
- Source map generation
Analyze modules to find client components and dependencies.
import { analyze } from "@eserstack/laroux-bundler/analyze";
const info = await analyze("./src/app/counter.tsx");
console.log(info.isClientComponent); // true if "use client"
console.log(info.isServerAction); // true if "use server"
console.log(info.imports); // All import statements
console.log(info.exports); // All exportsManage code splitting and lazy loading.
import { ChunkManifest } from "@eserstack/laroux-bundler/chunk-manifest";
const manifest = new ChunkManifest();
// Register chunks
manifest.addChunk("counter", {
path: "/chunks/counter-abc123.js",
dependencies: ["react", "shared"],
size: 4096,
});
// Get chunk info
const chunk = manifest.getChunk("counter");
const deps = manifest.getDependencies("counter");
// Serialize for client
const json = manifest.toJSON();Map server component references to client component chunks.
import { createModuleMap } from "@eserstack/laroux-bundler/module-map";
const moduleMap = createModuleMap({
"file:///src/app/counter.tsx": {
default: {
id: "/chunks/counter-abc123.js",
chunks: ["/chunks/counter-abc123.js"],
name: "default",
},
},
});
// Used by RSC renderer to inject client component referencesRewrite import paths for browser compatibility.
import { rewriteImports } from "@eserstack/laroux-bundler/rewrite-imports";
const code = `
import React from "react";
import { Counter } from "./counter.tsx";
`;
const rewritten = rewriteImports(code, {
baseUrl: "/src",
externals: {
"react": "https://esm.sh/react@18",
},
});
// Imports now use browser-compatible pathsProcess and bundle CSS files.
import { processCSS } from "@eserstack/laroux-bundler/css-processor";
const result = await processCSS({
code: `
.button {
background: blue;
&:hover { background: darkblue; }
}
`,
filename: "button.css",
minify: true,
});
console.log(result.code); // Processed and minified CSSCSS Features:
- PostCSS processing
- Nested selectors
- Auto-prefixing
- Minification
- CSS Modules support
Hot Module Replacement client for development.
import "@eserstack/laroux-bundler/client/hmr";
// Automatically connects to dev server via WebSocket
// Handles module updates without full page reload
// Preserves component state when possibleDevelopment-mode error display.
import { ErrorOverlay } from "@eserstack/laroux-bundler/client/error-overlay";
// Add to your client entry during development
export function ClientRoot() {
return (
<>
<App />
{import.meta.env.DEV && <ErrorOverlay />}
</>
);
}Features:
- Full-screen error display with stack traces
- Source-mapped error locations
- Dismissible overlays
- Auto-clear on fix
Lazy load components with automatic code splitting.
import { lazy } from "@eserstack/laroux-bundler/client/lazy-loader";
// Lazy load a component
const Counter = lazy(() => import("./counter.tsx"));
// Use with Suspense
<Suspense fallback={<div>Loading...</div>}>
<Counter />
</Suspense>;Intelligent component refresh during HMR.
import { SmartRefresh } from "@eserstack/laroux-bundler/client/smart-refresh";
// Wraps your app to enable smart refresh
export function ClientRoot() {
return (
<SmartRefresh>
<App />
</SmartRefresh>
);
}Features:
- Preserves component state when safe
- Full reload when needed (context changes, etc.)
- Error boundary integration
interface BundlerConfig {
// Source directory
srcDir: string;
// Output directory
distDir: string;
// Enable Hot Module Replacement
enableHMR?: boolean;
// Minify output
minify?: boolean;
// Generate source maps
sourceMaps?: boolean;
// External dependencies (not bundled)
externals?: Record<string, string>;
// Target environment
target?: "browser" | "deno";
// Logging level
logLevel?: "trace" | "debug" | "info" | "warn" | "error";
}- Runtime bundling (on-demand)
- Hot Module Replacement
- Source maps enabled
- Fast rebuild times
- Error overlay
- Unminified code
laroux dev # Uses RuntimeBundler- Prebuilt bundling (all files upfront)
- Code splitting by route
- Tree shaking and minification
- Content hashing
- Optimized chunks
- No source maps (unless enabled)
laroux build # Uses PrebuiltBundlerThe bundler scans your source directory for client components (files with
"use client").
Each file goes through:
- TypeScript → JavaScript
- JSX → React calls
- Import path rewriting
- Client/Server boundary injection
Components are split into chunks based on:
- Route boundaries
- Dynamic imports
- Size thresholds
The bundler creates a module map that RSC uses to:
- Reference client components from server
- Inject chunk URLs into the stream
- Enable lazy loading
File changes trigger:
- Incremental rebuild of affected modules
- WebSocket notification to client
- Smart component refresh (preserve state when safe)
- @eserstack/cli - Main CLI tool
(
eser larouxcommands) - @eserstack/laroux-core - Core runtime and utilities
Apache-2.0 © Eser Ozvataf