Bundle your Node app to a single executable for Windows, Linux and OsX
Tags: node, compile, bundle
Published: 2018-09-16 19:15:49

---
title: Bundle your Node app to a single executable for Windows, Linux and OsX
published: true
description: A question I get asked so many times by so many people is how to compile a Node app to a single executable. I am surprised because this is actually pretty simple.
tags: node, compile, bundle
cover_image: https://thepracticaldev.s3.amazonaws.com/i/ge7gwar0323m15pggz7i.jpeg
---


A question I get asked so many times by so many people is how to compile a Node app to a single executable. I am surprised because this is actually pretty simple.

## Reasons to ask
- *protect source code from being altered or copied* - You can't open executable files in a simple text editor.
- *hide API credentials* - Same difference as protecting source code.
- *ship to systems without Node or NPM* - No need to NPM install dependencies, bundle everything in a single executable.
- *dictate Node version* - Force a certain version of Node to guarantee feature support.
- *prevent commercial application from being nulled* - It is not as easy anymore as commenting out, replacing or removing the license validation function.
- *increase performance* - This is not a valid reason. The bundled executable does not perform better and because it includes a full Node it is a whole lot bigger (22MB) than just the 13kb JavaScript.
- *show off to friends* - We all do this at times.
- *learn in general* - People with a general interest in how things work under the hood. My favorite reason.
- *see proof that I can* - Well, here it is. 

There are a few tools out there that do pretty much the same thing. In this post I will focus on using [pkg](https://www.npmjs.com/package/pkg) because it is free (open source) and in my experience so far the most pleasant to work with. 

## PKG
PKG is a command line tool that simplifies the build process of your app. Install it globally by running _npm i pkg -g_ You can also use it programmatically but we will come to that.

## Example Node app 'prettyprint.exe' 
We will create a Node app that opens a .json input file, add indentation (tabs, spaces) and console log the beautified much more readable JSON. I will extensively describe the process and create a git of these files.

### NPM init / package.json
An easy way to create a new Node application with a _package.json_ is to run _npm init_ in an empty directory.

```json
{
  "name": "prettyprint",
  "version": "0.0.1",
  "description": "Pretty print a JSON file.",
  "main": "main.js",
  "author": "anybody",
  "license": "MIT"
}
```

### Module that exports our function
For the sake of absolute simplicity let's say _main.js_ contains a single function that looks like this:

```js 
/* You might want to check first if the file exists and stuff but this is an example. */
const fs = require('fs')
module.exports = function(filePath) {
    let data = fs.readFileSync(filePath).toString() /* open the file as string */
    let object = JSON.parse(data) /* parse the string to object */
    return JSON.stringify(object, false, 3) /* use 3 spaces of indentation */
}
```

> Yes, [@joelnet](https://dev.to/joelnet). We all know that you prefer to write it like this. Thank you.

```js 
module.exports = filePath => JSON.stringify(JSON.parse(require('fs').readFileSync(filePath).toString()), false, 3)
```

Create a _bin.js_ file.

```js
const prettyprint = require('.') /* the current working directory so that means main.js because of package.json */
let theFile = process.argv[2] /* what the user enters as first argument */

console.log(
    prettyprint(theFile)
)
```

> Yes, [@joelnet](https://dev.to/joelnet). It is shorter like this:

```js
console.log(require('.')(process.argv[2]))
```

### A dummy JSON file to test if everything works 
Or use your own JSON file.

```json
{"user":{"name":"jochem","email":"jochemstoel@gmail.com"}}
```

### Test if you copy/pasted properly
If you run _node bin.js file.json_ you are expected to see this:
```json
{
   "user": {
      "name": "jochem",
      "email": "jochemstoel@gmail.com"
   }
}
```

### Add one property to package.json
Simply add a property _"bin"_ with value _"bin.js"_ to your package json like so:

```json
{
  "name": "prettyprint",
  "version": "0.0.1",
  "description": "Pretty print a JSON file.",
  "main": "main.js",
  "bin": "bin.js", 
  "author": "anybody",
  "license": "MIT"
}
```

### Run pkg
Run _pkg ._ from your app directory to build an executable.
By not providing a target it will build for all three platforms. Windows, Linux and OSX.

```bash
pkg .
> pkg@4.3.4
> Targets not specified. Assuming:
  node10-linux-x64, node10-macos-x64, node10-win-x64
```

### Done!
Voila. 3 new files will have been created. 
```text
prettyprint-win.exe
prettyprint-linux
prettyprint-macos
``` 

To see your application in action, run _prettyprint-win.exe file.json_. On Linux, chmod your binary _a+x_ to make it executable and then run _./prettyprint-linux file.json_. Don't know about MacOS.


### Extra 
Relevant things I could not squeeze in anywhere. 

#### Simple way to build for current platform and version
From your app folder, run _pkg -t host ._. The _-t_ means target platform and the value _host_ means whatever your system is. The _._ means current directory. 
Obviously, you can run _pkg --help_ for a complete list of arguments.

#### In package.json, "main" and "bin" need not be different
Although you generally want to separate them, _main_ and _bin_ can both have the same value and do not necessarily need to be two separate files. 

#### Dependencies need to be in package.json 
If you NPM install after you created your app, it will automatically add the dependency to package.json for you.

#### Native modules and assets
To include asset files/directories in your executable and/or to build a node app that depends on native Node modules, read the [documentation](https://www.npmjs.com/package/pkg).

#### ...
Everything we did in this tutorial is not absolutely neccessary. You don't need the whole package.json with a "bin" property and all that stuff. That is just common practive and helps you learn. You can also just build a single JavaScript file.  

##### PKG API
In this example I use the PKG API to build a single JavaScript file without the need of a whole working directory or package.json

```js
/* js2exe.js */
const { exec } = require('pkg')
exec([ process.argv[2], '--target', 'host', '--output', 'app.exe' ]).then(function() {
    console.log('Done!')
}).catch(function(error) {
    console.error(error)
})
``` 

> Yes, [@joelnet](https://dev.to/joelnet). It is shorter like this:

```js
require('pkg').exec([ process.argv[2], '--target', 'host', '--output', 'app.exe' ]).then(console.log).catch(console.error)
```

##### Run
```bash
node js2exe.js "file.js"
``` 

##### Make your own standalone compiler executable
You can even let it build itself, resulting in a single executable that can build itself and any other JavaScript on its own. A standalone compiler.

```bash
node js2exe.js js2exe.js
``` 

Now you can use your output executable _app.exe_ as a standalone compiler that does not require Node or NPM anymore.
```bash
app.exe myfile.js
```