Tools Rolldown
Rolldown-based build tool for library packages — ESM/CJS/UMD output with DTS generation and platform globals.
@vitus-labs/tools-rolldown is the primary build tool for the vitus-labs ecosystem. It uses Rolldown (a Rust-based bundler) to produce optimized library bundles with automatic TypeScript declaration generation.
Installation
npm install @vitus-labs/tools-rolldownQuick Start
Add a build script to your package.json:
{
"scripts": {
"build": "vl_rolldown_build"
}
}Run it:
bun run buildThe tool reads your package.json fields (main, module, exports) and generates the appropriate output files in lib/.
How It Works
- Reads
package.jsonto determine output formats and entry points - Loads config from
vl-tools.config.mjs(viatools-core) - Generates a build pipeline based on
exports/main/modulefields - Bundles with Rolldown, generates DTS with
rolldown-plugin-dts - Injects platform globals (
__WEB__,__NATIVE__, etc.) - Outputs source maps, file size stats, and bundle visualization
CLI Commands
vl_rolldown_build
Production build — generates all output formats defined in package.json.
vl_rolldown_buildvl_rolldown_watch
Watch mode — rebuilds on file changes (development).
vl_rolldown_watchConfiguration
Configure via vl-tools.config.mjs under the build key:
// vl-tools.config.mjs
export default {
build: {
sourceDir: 'src', // Input directory (default: 'src')
outputDir: 'lib', // Output directory (default: 'lib')
replaceGlobals: true, // Inject platform globals (default: true)
typescript: true, // Generate .d.ts files (default: true)
filesize: true, // Show file sizes in console (default: true)
visualise: { // Bundle visualization
template: 'network', // 'network' | 'treemap' | 'sunburst'
gzipSize: true,
outputDir: 'analysis',
},
external: ['react/jsx-runtime'], // Additional external packages
exclude: [ // Patterns to exclude from bundle
'lib', 'node_modules/**',
'**/__tests__/**', '**/__stories__/**',
'*.test.*', '*.stories.*',
],
},
}Output Format Detection
The tool reads your package.json to determine what to build:
exports Field (Preferred)
{
"exports": {
".": {
"import": "./lib/index.js",
"require": "./lib/index.cjs",
"types": "./lib/index.d.ts"
}
}
}| Condition | Format | Platform |
|---|---|---|
import | ESM | universal |
require | CJS | universal |
node | ESM | node |
default | ESM | universal |
Subpath Exports
Packages with multiple entry points are automatically detected and built:
{
"exports": {
".": {
"import": "./lib/index.js",
"types": "./lib/types/index.d.ts"
},
"./devtools": {
"import": "./lib/devtools.js",
"types": "./lib/types/devtools.d.ts"
},
"./validation/zod": {
"import": "./lib/validation/zod.js",
"types": "./lib/types/validation/zod.d.ts"
}
}
}Each subpath is resolved to a source file by convention:
"."→src/index.ts"./devtools"→src/devtools(resolves tosrc/devtools.tsorsrc/devtools/index.ts)"./validation/zod"→src/validation/zod
Separate .d.ts declarations are generated for each subpath with a types field.
Passthrough exports like "./package.json": "./package.json" are automatically skipped (not built).
main / module Fields (Fallback)
{
"main": "lib/index.cjs",
"module": "lib/index.js"
}main→ CJS (or ESM iftype: "module")module→ ESMreact-native→ ESM with native platform
Browser Field
If browser differs from main, separate builds are generated:
{
"main": "lib/index.js",
"browser": {
"./lib/index.js": "./lib/browser.js"
}
}Platform Globals
When replaceGlobals: true (default), these constants are injected at build time:
| Global | Type | Description |
|---|---|---|
__VERSION__ | string | Package version from package.json |
__WEB__ | boolean | true for web/node/browser platforms |
__NATIVE__ | boolean | true for React Native |
__BROWSER__ | boolean | true for browser-only |
__NODE__ | boolean | true for Node.js |
__CLIENT__ | boolean | true for browser + native |
Platform assignment depends on the build variant:
| Platform | __WEB__ | __NATIVE__ | __BROWSER__ | __NODE__ | __CLIENT__ |
|---|---|---|---|---|---|
| universal | — | — | — | — | — |
| browser | true | false | true | false | true |
| node | true | false | false | true | false |
| native | false | true | false | false | true |
| web | true | false | false | false | false |
Type Generation
When typescript: true, the first build in the pipeline generates a rolled-up .d.ts file using rolldown-plugin-dts. The output path is read from package.json:
{
"exports": {
".": {
"types": "./lib/index.d.ts"
}
}
}For packages with subpath exports, a separate .d.ts file is generated for each subpath that has a types field.
External Dependencies
All dependencies and peerDependencies from package.json are automatically marked as external (not bundled). Additional externals can be added via config:
export default {
build: {
external: ['react/jsx-runtime', 'some-other-package'],
},
}Bundle Visualization
When visualise is configured, an interactive HTML visualization is generated:
lib/analysis/index.htmlTemplates: 'network' (default), 'treemap', 'sunburst'
Advanced Build Options
For non-library builds such as Chrome extensions, CLI tools, serverless functions, or Electron apps, you can bypass the package.json-driven pipeline with explicit configuration:
// vl-tools.config.mjs
export default {
build: {
// Explicit entries — skips package.json field detection
entries: [
{ input: 'src/background.ts', file: 'dist/background.js', format: 'iife', env: 'production' },
{ input: 'src/content.ts', file: 'dist/content.js', format: 'iife' },
{ input: 'src/popup.ts', file: 'dist/popup.js', format: 'es' },
],
// Bundle all dependencies (no externals)
bundleAll: true,
// Copy static assets after build
copyFiles: [
{ from: 'src/manifest.json', to: 'dist/manifest.json' },
{ from: 'src/popup.html', to: 'dist/popup.html' },
],
// Inject text at top/bottom of output
banner: '#!/usr/bin/env node',
footer: '// Built with @vitus-labs/tools-rolldown',
// Resolve aliases
alias: {
'@': './src',
},
// Custom rolldown plugins (appended to built-in ones)
plugins: [],
// Disable declarations for non-library builds
typescript: false,
},
}| Option | Type | Default | Description |
|---|---|---|---|
entries | Array<{ input, file, format?, env?, platform? }> | undefined | Explicit entry points (bypasses package.json detection) |
bundleAll | boolean | false | Bundle all dependencies instead of externalizing |
copyFiles | Array<{ from, to }> | undefined | Copy static files/directories after build |
banner | string | undefined | Text injected at the top of each output file |
footer | string | undefined | Text injected at the bottom of each output file |
alias | Record<string, string> | undefined | Resolve aliases for import remapping |
plugins | RolldownPlugin[] | [] | Custom plugins appended to built-in ones |
Entry Format Options
Each entry in the entries array supports:
| Field | Required | Default | Values |
|---|---|---|---|
input | yes | — | Source file path (e.g., 'src/index.ts') |
file | yes | — | Output file path (e.g., 'dist/index.js') |
format | no | 'es' | 'es', 'cjs', 'umd', 'iife' |
env | no | 'development' | 'development', 'production' |
platform | no | 'universal' | 'universal', 'browser', 'node', 'native' |
Use Cases
- Chrome extensions — multiple IIFE entries,
bundleAll: true,copyFilesfor manifest/HTML - CLI tools — single entry,
banner: '#!/usr/bin/env node',bundleAll: true - AWS Lambda — single entry,
bundleAll: true,env: 'production' - Electron — separate entries for main (node) and renderer (browser)
- Web Workers — IIFE or ES entry alongside the main app
Build Pipeline
The tool generates multiple builds sequentially. A typical library with ESM + CJS + types produces:
[1/2] Creating build: lib/index.js (ES Module)
[2/2] Creating build: lib/index.cjs (CommonJS)Each build:
- Resolves node modules with platform-specific extensions
- Replaces platform globals
- Generates source maps
- Reports file sizes
- Produces visualization (first build only)