Programmatic API
Use Hakobu programmatically from Node.js scripts.
Hakobu exposes a programmatic API so you can drive packaging from build scripts, CI pipelines, or custom tooling without spawning a child process.
Installation
npm install @hakobu/hakobuexec()
The primary export is the exec() function. It accepts an array of CLI argument strings -- the same arguments you would pass on the command line, minus the hakobu command itself.
import { exec } from '@hakobu/hakobu';
// Equivalent to: hakobu ./my-app --target node24-linux-x64 --output ./dist/app
await exec([
'./my-app',
'--target', 'node24-linux-x64',
'--output', './dist/app',
]);Signature
export async function exec(argv: string[]): Promise<void>;The function returns a Promise<void> that resolves when packaging completes successfully, or rejects on error.
Arguments
Pass CLI flags as individual array elements. The full set of recognized flags:
| Flag | Type | Description |
|---|---|---|
(positional) | string | Entry file or directory (e.g. ., ./app.js) |
-t, --target | string | Comma-separated targets (e.g. node24-linux-x64,node24-win-x64) |
-o, --output | string | Output file path or directory |
--out-path, --outdir, --out-dir | string | Output directory path |
-c, --config | string | Path to a config JSON file |
-d, --debug | boolean | Enable verbose packaging diagnostics |
-b, --build | boolean | Force local build of base binary instead of downloading |
-C, --compress | string | Compression algorithm: None, Brotli, or GZip |
--bytecode | boolean | Enable V8 bytecode compilation (default: true) |
--no-bytecode | boolean | Disable bytecode, include source as plain JS |
--public | boolean | Mark top-level project sources as public |
--public-packages | string | Comma-separated list of packages to treat as public |
--no-dict | string | Comma-separated packages to ignore dictionaries for |
--options | string | Comma-separated V8 flags to bake into the executable |
--signature | boolean | Sign macOS executables (default: true) |
Examples
Package for the current host platform:
await exec(['.']);Package with compression:
await exec([
'./my-app',
'--target', 'node24-linux-x64',
'--output', './dist/app',
'--compress', 'Brotli',
]);Multi-target build:
await exec([
'.',
'--target', 'node24-linux-x64,node24-win-x64,node24-macos-arm64',
'--output', './dist/',
]);Build with debug output and V8 options:
await exec([
'./server.js',
'--debug',
'--options', 'max-heap-size=4096',
'--output', './dist/server',
]);packageMultiple()
For multi-target builds with structured input and output, the packageMultiple() function provides a higher-level interface.
import { packageMultiple } from '@hakobu/hakobu';
const results = await packageMultiple({
projectRoot: '/path/to/project',
targets: ['node24-linux-x64', 'node24-win-x64', 'node24-macos-arm64'],
outputDir: './dist',
});
for (const r of results) {
console.log(`${r.target.platform}-${r.target.arch}: ${r.outputPath} (${r.status})`);
}PackageMultipleOptions
interface PackageMultipleOptions {
projectRoot: string;
targets: string[];
outputDir?: string; // Directory for output files (default: cwd)
entry?: string;
assets?: string[];
externals?: string[];
bundle?: boolean | string;
bundleExternal?: string[];
}PackageMultipleResult
interface PackageMultipleResult {
target: { platform: string; arch: string; nodeRange: string };
status: 'success' | 'failed';
outputPath: string; // Full path to the produced executable
fileCount: number;
error?: string; // Error message if status === 'failed'
}packageMultiple() internally runs analysis and bundling once, then produces each target binary in sequence. This is more efficient than calling exec() in a loop for each target.
TypeScript Types
Hakobu exports several TypeScript types you can use in your build scripts:
import type { NodeTarget, Target, SymLinks } from '@hakobu/hakobu';NodeTarget
interface NodeTarget {
nodeRange: string; // e.g. "node24"
arch: string; // e.g. "x64", "arm64"
platform: 'macos' | 'win' | 'linux';
forceBuild?: boolean;
}Target
Extends NodeTarget with resolved binary paths:
interface Target extends NodeTarget {
binaryPath: string;
output: string;
fabricator: Target;
}Error Handling
Both exec() and packageMultiple() throw on fatal errors. Wrap calls in try/catch for graceful handling:
import { exec } from '@hakobu/hakobu';
try {
await exec(['.', '--target', 'node24-linux-x64', '--output', './dist/app']);
console.log('Packaging succeeded');
} catch (error) {
console.error('Packaging failed:', error.message);
process.exit(1);
}When using packageMultiple(), individual target failures do not throw. Instead, check the status field on each result. The function only throws for setup-level errors (e.g. invalid project root).