npm2exe
v0.4.0
Published
Pack a JavaScript local project to a Portable Executable
Readme
NPM to EXE
Pack a JavaScript local project to a portable executable.
Usage
package.json
{
"name": "my-app",
"version": "1.0.0",
"bin": "dist/index.js",
"devDependencies": {
"typescript": "^6",
"npm2exe": "^0.4"
},
"scripts": {
"build": "tsc",
"bundle": "npm run build && npm2exe ."
}
}Command
cd path/to/project
npm install
npm run bundlemore options
--arch <x86|ia32|x64|arm|arm64>target runtime architecture (ia32maps tox86,armmaps toarmv7l)--platform <linux|darwin|win|win32>target platform (default current platform)--node-version <version>specific runtime version (for examplev22.18.0)--output <name>output bundle file name (default package name; Windows appends.exe)--compression-level <0-9>compression level (default0, no compression; higher levels trade speed for a smaller archive)--verboseor-Venable Verbose output
npm2exe reads your package.json#bin, installs only production dependencies into a temporary staging directory, downloads a matching stock Node.js runtime, then wraps both into a self-extracting package.
Feature comparison
| Feature | npm2exe | Node SEA | pkg | nexe | caxa |
| ---------------- | ------------------------------------------------------------------------------ | ------------------------------------ | --------------------------------------------------------- | ----------------------------------------- | ------------------------------------------- |
| OS / CPU | ✅ linux / darwin / win; x64 / arm64 / armv7l; Windows x86 | ✅ stock Node target | ✅ Linux / macOS / Windows / Alpine / static Linux | ✅ target triple | ✅ Windows / macOS / Linux |
| Node version | ✅ build >=22; runtime auto-resolved or overridden | ✅ Node SEA in official Node | ✅ build >=22; node22 / node24 / latest | ✅ runtime version in target string | ✅ build >=22.15.0 |
| Entry model | ✅ package.json#bin, one or many launchers | ⚠️ one main only | ⚠️ one package entry | ⚠️ one input or stdin bundle | ✅ command array; multi-target output |
| Monorepo | ✅ workspace: staging | ❓ no dedicated flow documented | ❓ no dedicated flow documented | ❓ no dedicated flow documented | ❓ no dedicated flow documented |
| Installed layout | ✅ real app tree in home/profile | ⚠️ in-binary VFS | ⚠️ snapshot FS + cache extraction | ⚠️ single executable VFS | ✅ extracted app tree in temp/cache |
| Native addons | ✅ normal on-disk loading | ⚠️ must extract first | ⚠️ supported, but extracted to cache | ❌ ship beside binary | ✅ extracted before run |
| Config | ✅ low (npm2exe configuration) | ⚠️ medium | ⚠️ medium / high | ⚠️ medium / high | ⚠️ low / medium |
| Wrapper form | ✅ 7z SFX / makeself | ⚠️ injected stock Node binary | ⚠️ patched runtime or SEA | ⚠️ compiled single executable | ⚠️ Rust self-extractor |
In short: SEA, pkg, and nexe lean toward a tighter single-binary image, while caxa and npm2exe lean toward extracting and running a real app tree. That makes npm2exe especially friendly to regular Node.js install behavior and workspace: monorepos.
GitHub Actions release workflow
cd path/to/your/project
npx git-utility download https://github.com/idea2app/npm2exe main .github/workflows/ .github/workflows/Notes:
- The downloaded workflow example keeps a single packaging step, so no per-OS
shellswitching is needed. - Build on each native runner instead of cross-packaging from one host, because
npm2execurrently uses different wrapping backends for Windows and POSIX targets. - Linux/macOS outputs are self-extracting shell archives; Windows output is a self-extracting
.exe. - The release asset is the installer wrapper. The final launcher gets installed into the user home/profile when the asset is executed.
- Code signing, notarization, and checksum publishing are intentionally left to the application repository.

