📦 Bundle Node.js code into single executable binary
Muhammad Bin Zafar
Posted on August 20, 2022
Node.js 🐢, the asynchronous event-driven JavaScript runtime, has unparalleled support for file-system access, among other things - opening up the door to endless possiblities! However, Node.js often loses out to other runtimes/languages in cases where being able to package a single, executable application simplifies distribution and management of what needs to be delivered.
While there are components/approaches for doing this, they need to be better documented and evangelized so that this is not seen as a barrier for using Node.js in these situations. This is important to support the expansion of where/when Node.js is used in building solutions.
This article addresses 2 major concerns in the Node.js ecosystem: bundling and packaging. Let's talk about them briefly.
Bundling is the concept of merging the code, and all its dependencies into a single file. This is commonly seen for frontend development.
However, using the ESM packaging format has one advantage than CJS: tree-shaking. Tree-shaking is the concept of removing unused code from a dependency. Tools: esbuild, parcel, webpack, rollup, terser.
Packaging in Node.js is concept of creating a single executable binary, which includes the source code and the Node.js runtime. This way, Node.js will not be needed to be installed on end-user's machine.
During the process, the tool parses the source code, detects calls to require()
, traverses the dependencies, and includes them into executable. Usually the source code is compiled into bytecode using the V8
engine. Tools: pkg, ncc, nexe.
esbuild
to bundle
- An extremely fast JavaScript and CSS bundler and minifier
- Most convenient
- Fastest in comparison
- Support for TypeScript syntax, ESM, and CJS
- Supports tree-shaking for ESM
- Supports minification and source maps
# Output CommonJS bundle
$ npx esbuild index.js --bundle --outfile=build.cjs \
--format=cjs --platform=node
# Output ESM bundle
# Note that, you may not need the --banner flag.
# But, in some cases, require() and __dirname are needed.
$ npx esbuild index.js --bundle --outfile=build.mjs \
--format=esm --platform=node --banner:js="
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));"
pkg
to package
- Package your Node.js project into an executable
- Instantly make executables for Windows, Mac, Linux, etc
- No need to install Node.js, or hundreds of dependencies
# Packaging tools work best with CJS.
# These tools don't go well with ESM.
# To package into executable, just take the file outputted
# by `esbuild`, and pass it to `pkg`, and we're done!
$ npx pkg build.cjs
This command will output 3 binary exectuable files build-linux
, build-macos
, and build-win.exe
. You might want to run the executable file for your platform. Now you can simply distribute these files to your end-users or deploy in production - without installing Node.js or any dependencies or anything - just this one file!
Thanks for reading! Found it interesting? Give it a ❤️ or 🦄! Any topic you'd want to me cover? Let me know in the comments.
Have a great day!
Posted on August 20, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.