📦 Bundle Node.js code into single executable binary
Tags: javascript, programming, tutorial, node
Published: 2022-08-20 12:13:00

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](https://esbuild.github.io), [parcel](https://parceljs.org), [webpack](https://webpack.js.org/), [rollup](https://rollupjs.org/), [terser](https://terser.org/).

**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](https://www.npmjs.com/package/pkg), [ncc](https://www.npmjs.com/package/ncc), [nexe](https://www.npmjs.com/package/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

```sh
# Output CommonJS bundle
$ npx esbuild index.js  --bundle --outfile=build.cjs \
--format=cjs --platform=node
```

```sh
# 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

```sh
# 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!