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 |
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"
}
}
}Only one .d.ts file is generated per build (for the first build variant).
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)