mirror of
https://github.com/zhom/banderole.git
synced 2026-08-17 14:57:12 +02:00
76 lines
3.6 KiB
Markdown
76 lines
3.6 KiB
Markdown
# Banderole
|
|
|
|
Create cross-platform single-executables for Node.js projects. Windows is not supported.
|
|
|
|
Banderole bundles your Node.js app, all dependencies, and a portable Node binary into a single native executable. On first launch, it unpacks to a cache directory for fast subsequent executions.
|
|
|
|
Unlike [Node.js SEA](https://nodejs.org/api/single-executable-applications.html) or [pkg](https://github.com/yao-pkg/pkg), banderole handles complex projects with dynamic imports and non-JavaScript files without requiring patches — it ships a stock Node binary and your real dependency tree rather than a patched runtime and a virtual filesystem.
|
|
|
|
## Performance
|
|
|
|
Measured on linux-x64, Node 22.17.1, against [`@yao-pkg/pkg`](https://github.com/yao-pkg/pkg) 6.22.0 building the same app. Startup is the median of 60 interleaved runs (targets rotate every round, so machine drift affects both equally).
|
|
|
|
| | trivial app | | app with deps<br>(express, lodash, chalk, dayjs) | |
|
|
|---|---|---|---|---|
|
|
| | **banderole** | pkg | **banderole** | pkg |
|
|
| Executable size | **33,416,408 B** | 74,306,703 B | **35,366,424 B** | 77,346,956 B |
|
|
| Startup (median) | **16.1 ms** | 25.7 ms | **53.1 ms** | 78.8 ms |
|
|
| Resident processes | **1** | 1 | **1** | 1 |
|
|
| Resident memory (RSS) | **43.8 MiB** | 49.4 MiB | **66.4 MiB** | 76.2 MiB |
|
|
|
|
pkg's size floor is the uncompressed Node binary it embeds, which its `--compress` option does not touch. banderole compresses the Node binary with zstd and ships only the executable itself, so a bundle is roughly half the size.
|
|
|
|
The trade-off is the **first** launch, which unpacks the payload into the cache directory: 117 ms versus pkg's 30 ms for the trivial app. Every launch after that takes the warm path above.
|
|
|
|
### Scaling with project size
|
|
|
|
First-launch extraction grows with the dependency tree; steady-state launch does not, because it is one file read followed by `exec`.
|
|
|
|
| `node_modules` | files | bundle time | first launch | **every later launch** |
|
|
|---|---|---|---|---|
|
|
| — | 0 | 16 s | 159 ms | **19.4 ms** |
|
|
| 14 MB | 5,500 | 18 s | 224 ms | **22.4 ms** |
|
|
| 68 MB (real npm tree) | 9,725 | 19 s | 316 ms | **71.2 ms** |
|
|
| 143 MB | 27,500 | 20 s | 348 ms | **23.3 ms** |
|
|
| 763 MB | 110,000 | 25 s | 862 ms | **22.9 ms** |
|
|
|
|
A 763 MB dependency tree still unpacks in under a second, and warm launch is flat across the whole range. (The 68 MB row is a real npm install; its higher warm figure is Node resolving express/lodash/dayjs at runtime, not launcher overhead. The other rows are generated trees, which compress better than real ones.)
|
|
|
|
## Requirements
|
|
|
|
Banderole requires a Rust toolchain (`cargo`, `rustc`, `rustup`) **and a working C linker** — `cc`/`gcc`/`clang` on Unix, MSVC on Windows — because it compiles a small launcher for each bundle.
|
|
|
|
## Installation
|
|
|
|
```sh
|
|
cargo install banderole
|
|
```
|
|
|
|
## Usage
|
|
|
|
```sh
|
|
# Bundle a project using the project name
|
|
banderole bundle /path/to/project
|
|
|
|
# Bundle with custom output path
|
|
banderole bundle /path/to/project --output /path/to/output/executable
|
|
|
|
# Bundle with custom name
|
|
banderole bundle /path/to/project --name my-app
|
|
|
|
# Bundle with both custom output and name
|
|
banderole bundle /path/to/project --output /path/to/my-app --name my-app
|
|
```
|
|
|
|
## Feature List
|
|
|
|
- [x] Support Linux, MacOS, and Windows for both x64 and arm64 architectures.
|
|
- [x] Support custom node.js version based on project's `.nvmrc` and `.node-version`
|
|
- [x] Support TypeScript projects with automatic detection of compiled output directories
|
|
- [x] Support workspaces (only pnpm workspaces tested)
|
|
- [ ] Only the executable has permissions to read and execute bundled files
|
|
|
|
## License
|
|
|
|
MIT
|