Node.js Bundling
How to create an executable (.exe) file from JavaScript code (using Node.js)
In this lesson, we are going to learn how to create a .exe binary executable file from JavaScript code. This is possible with the help of Node.js runtime and some third-party tools.

It’s harder to digest the title of this article. How can we possibly create a binary executable .exe file from a JavaScript program if JavaScript is an interpreted language? JavaScript doesn’t even have a compiler so how do we even fathom the idea of generating executables for JavaScript?
A .exe file is generally called a binary executable file. Unlike a normal program file or simply any file with data of a particular format (such as a .png file with image data), a .exe file contains various different things such as actual program code, program data, static resources, dependencies among others. It is meant to execute a task on the system instead of simply being a data carrier.
So a binary executable file is more like a compressed file with different data segments that play different roles to execute the program contained in it. This program can be run when the user simply double-clicks on the file. The native operating system understands the .exe file extension and how to process the file and finally execute the program in it.
The .exe extension in my opinion is a little misunderstood since it is overly generalized. The .exe extension is specific to the Windows operating system. When Windows OS sees the .exe file extension, it knows that the file is a portable binary executable file (written in PE format) and the operating system will execute the file natively when the user double-clicks on it (instead of trying to open the file in an installed application).
Other operating systems may use different extensions. When it comes to macOS, a binary executable file doesn’t have an extension. The OS determines if a file is a binary executable by looking at the first few bytes of the file which we call Magic Numbers. You can see the entire list of valid magic bytes here. The macOS uses CF FA ED FE leading bytes of a file to determine if a file is a 64-bit binary executable. Such files are shown with a terminal icon in the finder as shown below and have Unix executable kind.


The macOS operation system uses .app file extension to determine a native application file. Unlike a binary executable file, a .app file is more like a system directory that contains proper folders for different segments of the applications. When the user double-clicks on it, the OS finds Info.plist file inside this file (since its a directory) and runs a suitable program (located inside the same directory) indicated by the Info.plist.
💡 Read this QUORA answer to understand how macOS treats executable and
.appfiles. The same principles are generalized in this answer.
Now that we have some idea of what executable files are and how they work, we can proceed to understand how JavaScript executables can be created.
Since an executable file is more like a compressed file with different data segments your actual program (contained in it) depends on, you can cram absolutely anything inside it such as images, program files, dependencies, or even the entire interpreter that your program would need in order to run.
Since we can’t compile a JavaScript program to machine language (such that it can run natively on the machine), we need to ship the JavaScript program in its true form (source code) along with the JavaScript runtime such as Node which contains the JavaScript interpreter and OS-level dependencies.
Therefore, the final executable contains the JavaScript source code (along with dependencies such as .js files or NPM packages), static resources (such as .json files, images files, etc. if needed), and most importantly the Node.js runtime (executable) to run these JavaScript programs. This executable contains a subroutine (program) that knows how to run included JavaScript programs using the included JavaScript runtime when the user double-clicks on it. And that is how a JavaScript executable works.
But we can’t make such an executable on our own since neither Node.js nor NPM provides any tools to do the same. But there are some popular third-party tools we can use such as pkg and nexe.
But before we have a look at them, let’s first create a sample JavaScript (Node) project that we want to provide to our clients as a binary executable so that they do not need to install Node.js or NPM modules on their own. This is quite helpful as it eliminates the need of having an installation guide which can be frustrating for you and your clients especially to those who do not possess technical knowledge.
💡 However, including sources, dependencies, and a runtime environment inside an executable file makes its size bigger than an executable file compiled from a compiled language such as C, C++ or Go. At times, this file size could be so big that it’s not even feasible to transfer over the network. That’s why you should prefer a compiled language for things like this.
JavaScipt Project
We are going to build a small web application for this demo. When the user double-clicks on the executable file, our executable file will start a web server using express.js and open the root server endpoint (simply /) in the default browser of the system.
This default endpoint returns an HTML page (with external .js and .css files) that render some images of formula one drivers on the screen. To get the list of drivers, the .js file makes an AJAX request to the server using fetch API of the browser.
To make this application, we need some .html, .js, .css and .png files as static assets. We are going to use a simple .json file to provide a standard response for the AJAX request. You should be able to find the source code of this demo in the repository below.
First of all, we need to initialize package.json in the project directory and install all NPM dependencies needed for this application to work.
$ npm init -y
$ npm install -S express get-port openThe express package is what will create a web-server and process incoming requests. The get-port package provides us the available port on the system so that we can start the web-server on that port without any issues. The open package opens an URL in the default browser of the system.
node-js-executable
├── build/
├── package.json
├── server.js
└── src
├── static
| ├── images
| | ├── alexander_albon.png
| | ├── ...
| | └── valtteri_bottas.png
| └── jsons
| └── images.json
└── www
├── index.html
├── main.js
└── style.cssOur project structure looks like above. The src directory contains the source code and assets of the web application. The www directory contains the static assets of the web application. When the web application starts (in the browser), the express server is going to serve the index.html by default which imports main.js file to dynamically render images and style.css for styles.
The static directory contains some static assets such as images directory to serve the images for the web application and the images.json file which contains an array of image objects with titles and relative image URLs.










