This repository elaborates on on node's skimpy instructions by showing how you might bundle an app with multiple files and a dependency, and build it into an executable binary.
I wrote a guide here that walks you step by step through how you might end up with something similar to the code in this repository.
This repository's main addition to that article is a simple Makefile
that will build the executable binary into dist/sum
on either mac or linux.
- Building the code in this repository
- Stripping the binary
- Benchmarks
- Comparison with bun
- Comparison with deno
- Why use make?
- TODO
On a mac or linux computer:
- clone this repository and cd into it
- execute
make
You should end up with a sum
binary in the dist
folder, which will be an executable file that sums up all the numbers you pass to it.
For example:
$ make
npm i
up to date, audited 4 packages in 618ms
1 package is looking for funding
run `npm fund` for details
found 0 vulnerabilities
node --experimental-sea-config sea-config.json
Wrote single executable preparation blob to dist/sea-prep.blob
cp /Users/llimllib/.local/share/mise/installs/node/latest/bin/node dist/sum
codesign --remove-signature dist/sum
npx postject dist/sum NODE_SEA_BLOB dist/sea-prep.blob \
--sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
--macho-segment-name NODE_SEA
Start injection of NODE_SEA_BLOB in dist/sum...
💉 Injection done!
codesign --sign - dist/sum
$ dist/sum 1 2 3 4
10
# the file's not small, but at least it works!
$ ls -alh dist/sum
-rwxr-xr-x@ 1 llimllib staff 82M Jan 27 16:24 dist/sum*
Using strip
to remove debug symbols from the node binary results in a binary that is 66 megabytes instead of 82 on my local computer, with node v20.2.0.
In the container provided in this repo, it saves about 16mb on the binary.
I'm not sure this is safe to do in general, but it works in this case and saves some space so I've put it in the Makefile. If you see adverse effects with it, or you want to debug your binary with gdb
or lldb
, you may want to remove it.
make bench
will run the compilation processes for node
, bun
, and deno
and report build time, the reuslt of a benchmark, and file size.
On my system (Macbook Pro with M1 Max and 32gb ram), which I should emphasize is not set up to do proper benchmarking so take this whith a large pile of salt, I get:
interpreter | version | build (ms) | size (mb) | execution time |
---|---|---|---|---|
node | 20.11.1 | 3290 | 67 | 86.6 ms ± 168.0 ms |
bun | 1.0.29 | 690 | 77 | 77.1 ms ± 180.0 ms |
deno | 1.41.0 | 490 | 58 | 76.0 ms ± 147.4 ms |
You can build a binary with bun, if you have it installed, by running make dist/sum_bun
I tested with bun version 1.0.25
against a node binary build with node version 20.11.1
Pros
- faster to build
- much simpler build command
- executable is 46mb, vs 82mb for node
- the executable runs with low overhead
Cons
- bun is a rapidly evolving distribution and still has bugs in its node compatibility
- for example, a recent program I tried to build with
bun
hit this showstopper bug. Every program I've tried to build with bun so far has hit a bug somewhere or other with bun's node compatibility. - my recommendation would be to build with bun only if you intend to exclusively build with bun as a target; otherwise you're likely to suffer compatibility bugs like this one at unexpected and inconvenient times
- for example, a recent program I tried to build with
You can build a deno version of this binary with make dist/sum_deno
, which runs deno compile -o dist/sum_deno ./deno/index.js
Update: Deno version 1.40.2, which I had previously used, generated a very large binary that was extremely slow; it appears they have improved executable generation greatly in version 1.41.0, which generates a smaller and faster binary. I used 1.41.0 for the below
Pros
- much simpler compilation command
- the resulting binary is between
bun
and node SEA in size (58mb) - the executable runs with low overhead
Because it's still great at what it was meant to do: build binaries out of source files when they change.
- I would love to support windows! But I haven't used a windows computer in 20 years. Pull requests would be gladly accepted
- I'd also love ideas about how to make the binary any smaller than its current weight of 82 megabytes
- see: stripping the binary
- add a demo of bytecode compiling with bytenode