Distributing executable binaries in npm package
Let’s say you need to distribute binary in npm. I’m not talking about node extensions (node-gyp, napi, etc.). I’m talking about standalone executables, like CLI applications.
We have options. How to distribute binary:
- host executables on some server, for example, GitHub releases, and download appropriate (e.g. for given OS and architecture) binary on installation
- put all executables (for all OSs and architectures) in one npm package
- create sub-packages with binary for each OS and architecture and one “root” npm package which would list all sub-packages as optional dependencies
How to run the binary:
- Use JS wrapper which would call actual executable with
exec,spawn, etc. - On installation figure out where Node puts binaries and overwrite it with your binary from the package
- Use JS script to call node binding (
*.node) or WASM
Tools and blog posts:
| name | distribution | execution |
|---|---|---|
| binary-install | 1 | 1 (spawnSync) |
| binwrap | 1 | 1 (spawn) |
| bin-wrapper | 1 | 1 (spawn, …) |
| prebuild-install | 1 | 3 |
| blog.xendit.enginee | 2 | 2 |
| hspak.dev | 2 | 2 |
| prebuildify | 2 | 3 |
| blog.orhun.dev | 3 | 1 (spawnSync) |
| napi-rs | 3 | 3 |
Example projects:
| name | distribution | execution |
|---|---|---|
| odiff | 2 | 2 |
| @astrojs/compiler | 2 (WASM) | 3 |
| esbuild | 3 | 1 (execFileSync) |
| biome | 3 | 1 (spawnSync) |
| Turborepo | 3 | 1 (execFileSync) |
| lightningcss | 3 | 3 |
Read more: Distributing CSS in npm package, Server-side UI Components