This package provides a zero-configuration CLI tool for compiling Node.js modules into single files along with all their dependencies, similar to how gcc works for compiled languages.
—
The @vercel/ncc programmatic API provides a Node.js interface for integrating ncc into build processes and tools. It offers all CLI features plus additional programmatic options for custom webpack configurations and asset handling.
The core ncc function that bundles Node.js modules programmatically.
/**
* Compiles a Node.js module into a single file with all dependencies bundled
* @param entry - Path to the input file to compile
* @param options - Configuration object with build options
* @returns Promise resolving to NccResult or NccWatcher when watch: true
*/
function ncc(entry: string, options?: NccOptions): Promise<NccResult> | NccWatcher;
interface NccOptions {
/** Custom cache path or disable caching */
cache?: string | boolean;
/** Custom asset emission function */
customEmit?: (path: string) => boolean | void;
/** Enable ES module output */
esm?: boolean;
/** External modules to exclude from bundling */
externals?: string[] | { [key: string]: string };
/** Output filename (default: 'index.js') */
filename?: string;
/** Enable minification */
minify?: boolean;
/** Generate source maps */
sourceMap?: boolean;
/** Include source-map-support */
sourceMapRegister?: boolean;
/** Source map base prefix */
sourceMapBasePrefix?: string;
/** Build nested JS assets recursively */
assetBuilds?: boolean;
/** Enable watch mode */
watch?: boolean;
/** Enable V8 compile cache */
v8cache?: boolean;
/** Directory filter for assets */
filterAssetBase?: string;
/** List of existing asset names */
existingAssetNames?: string[];
/** Disable build summaries */
quiet?: boolean;
/** Enable debug logging */
debugLog?: boolean;
/** Use TypeScript transpile-only mode */
transpileOnly?: boolean;
/** License file output */
license?: string;
/** ECMAScript target version */
target?: string;
/** Production mode */
production?: boolean;
/** Package.json main fields resolution order */
mainFields?: string[];
}
interface NccResult {
/** Compiled JavaScript code */
code: string;
/** Source map JSON string (if enabled) */
map?: string;
/** Asset files with their content and permissions */
assets: { [filename: string]: { source: Buffer | string; permissions: number } };
/** Symbolic links mapping */
symlinks: { [path: string]: string };
/** Webpack compilation statistics */
stats: any;
}
interface NccWatcher {
/** Set build completion handler */
handler(callback: (result: NccResult & { err?: any }) => void): void;
/** Set rebuild start handler */
rebuild(callback: () => void): void;
/** Close the watcher */
close(): void;
}Usage Examples:
const ncc = require('@vercel/ncc');
// Basic compilation
const { code, map, assets } = await ncc('/path/to/input.js', {
minify: false,
sourceMap: false
});
// Write the compiled code
require('fs').writeFileSync('dist/index.js', code);
// With external dependencies
const result = await ncc('/path/to/server.js', {
externals: ['aws-sdk', 'sharp'],
minify: true,
sourceMap: true
});
// TypeScript compilation
const tsResult = await ncc('/path/to/main.ts', {
transpileOnly: false,
target: 'es2020'
});When watch: true is specified, ncc returns a watcher object instead of a promise.
interface NccWatcher {
/**
* Set build completion handler
* Called whenever a build completes (initial or rebuild)
* @param callback - Handler function receiving build result or error
*/
handler(callback: (result: NccResult & { err?: any }) => void): void;
/**
* Set rebuild start handler
* Called when a file change triggers a rebuild
* @param callback - Handler function called before rebuild starts
*/
rebuild(callback: () => void): void;
/**
* Close the watcher
* Stops watching and cleans up resources
*/
close(): void;
}Watch Mode Examples:
const ncc = require('@vercel/ncc');
// Watch mode
const watcher = ncc('/path/to/input.js', {
watch: true,
minify: false
});
// Handle build completion
watcher.handler(({ err, code, assets }) => {
if (err) {
console.error('Build failed:', err);
return;
}
console.log('Build completed successfully');
require('fs').writeFileSync('dist/index.js', code);
// Write assets
for (const [filename, asset] of Object.entries(assets)) {
require('fs').writeFileSync(`dist/${filename}`, asset.source);
}
});
// Handle rebuild start
watcher.rebuild(() => {
console.log('Rebuilding...');
});
// Close watcher when done
process.on('SIGINT', () => {
watcher.close();
process.exit(0);
});// Disable caching
const result = await ncc('/path/to/input.js', { cache: false });
// Custom cache directory
const result = await ncc('/path/to/input.js', { cache: '/tmp/my-cache' });
// Default cache (uses system temp directory)
const result = await ncc('/path/to/input.js', { cache: true });// Array format
const result = await ncc('/path/to/input.js', {
externals: ['aws-sdk', 'sharp', 'canvas']
});
// Object format for aliasing
const result = await ncc('/path/to/input.js', {
externals: {
'aws-sdk': 'aws-sdk',
'node-gyp': false // exclude completely
}
});
// RegExp patterns (as strings in object format)
const result = await ncc('/path/to/input.js', {
externals: {
'/^@aws-sdk\\/.*$/': '@aws-sdk/$1' // RegExp as string
}
});// ES Module output
const result = await ncc('/path/to/input.js', {
esm: true,
filename: 'index.mjs'
});
// CommonJS output with custom filename
const result = await ncc('/path/to/input.js', {
esm: false,
filename: 'bundle.cjs'
});
// With source maps and minification
const result = await ncc('/path/to/input.js', {
minify: true,
sourceMap: true,
sourceMapBasePrefix: '../../'
});// Fast TypeScript compilation (no type checking)
const result = await ncc('/path/to/main.ts', {
transpileOnly: true,
target: 'es2018'
});
// Full TypeScript compilation with type checking
const result = await ncc('/path/to/main.ts', {
transpileOnly: false,
target: 'es2020'
});// Recursive asset building
const result = await ncc('/path/to/input.js', {
assetBuilds: true,
filterAssetBase: process.cwd(),
existingAssetNames: ['package.json']
});
// Custom asset emission
const result = await ncc('/path/to/input.js', {
customEmit: (assetPath) => {
// Return true to emit asset, false to skip
return !assetPath.includes('test');
}
});The ncc function throws errors for various failure conditions:
const ncc = require('@vercel/ncc');
try {
const result = await ncc('/path/to/input.js', {
target: 'invalid-target' // Will throw error
});
} catch (error) {
if (error.message.includes('Invalid "target" value')) {
console.error('Invalid ECMAScript target specified');
} else if (error.message.includes('Can\'t resolve')) {
console.error('Module resolution failed');
} else {
console.error('Build failed:', error.message);
}
}// Custom build script
const ncc = require('@vercel/ncc');
const { writeFileSync, mkdirSync } = require('fs');
const { join } = require('path');
async function buildProject(entry, outputDir) {
try {
const { code, assets, symlinks } = await ncc(entry, {
minify: true,
sourceMap: true,
externals: ['aws-sdk']
});
// Ensure output directory exists
mkdirSync(outputDir, { recursive: true });
// Write main file
writeFileSync(join(outputDir, 'index.js'), code);
// Write assets
for (const [filename, asset] of Object.entries(assets)) {
writeFileSync(join(outputDir, filename), asset.source);
}
// Create symlinks
for (const [linkPath, targetPath] of Object.entries(symlinks)) {
require('fs').symlinkSync(targetPath, join(outputDir, linkPath));
}
console.log('Build completed successfully');
return true;
} catch (error) {
console.error('Build failed:', error);
return false;
}
}// Development server with hot reloading
const ncc = require('@vercel/ncc');
const express = require('express');
function startDevServer(entry) {
const app = express();
let currentCode = '';
const watcher = ncc(entry, {
watch: true,
sourceMap: true,
quiet: true
});
watcher.handler(({ err, code }) => {
if (err) {
console.error('Build error:', err);
return;
}
currentCode = code;
console.log('Code updated');
});
app.get('/bundle.js', (req, res) => {
res.type('application/javascript');
res.send(currentCode);
});
const server = app.listen(3000, () => {
console.log('Dev server running on port 3000');
});
process.on('SIGINT', () => {
watcher.close();
server.close();
process.exit(0);
});
}