Using Node.js as your build tool comes with a few handy advantages. First, you can write your build scripts in the same language as the actual Node.js project you’re working on. In other words, you can just use JavaScript, no need to figure out complicated Bash commands! Writing in JavaScript also makes it a lot easier to perform complex actions that can be tricky to get right in something like Bash.
The rich library of packages provided by npm also means you have a plethora of things you can do that the typical setup may not be able to do at all. Best of all, just about anything you want to do can be done in a cross-platform manner using Node.js, unlike Bash scripts, for example, which don’t run on Windows without WSL, or PowerShell scripts, which don’t run outside Windows without PowerShell Core. Because you run the script through Node.js itself, you can be sure any developers using it will be able to run the build script regardless of what platform they’re working in, since they’ll need to have a Node.js environment set up already. No need to deal with extra installations and setup!
A Simple Example
Let’s say you have a simple process to deploy your code: all you want to do is package the code up into a .zip file.
We’ll assume your code is just in a src folder at the root of your project.
Create a new JavaScript file in your project and give it a fitting name for a build script, we’ll use _build.js as an example here. Set the contents to match what’s below:
While you could use a normal function, using an async function makes it easier to call any functions that return Promises with async/await, and can also be used to run tasks in parallel. The shebang line at the top just makes the file executable from the command line.
We’ll use the adm-zip package for zipping our files, and the fs-extra package for some handy features it adds to fs. Go ahead and install the packages with npm i adm-zip fs-extra --save-dev. We’re using the --save-dev flag here since this script will only run in a dev environment, and isn’t used in your program itself.
Now add the import for adm-zip, and add the code to the main function to create the dist folder where the zip folder will be created, then zip up your code files:
Now if you run your script with node _build.js it’ll zip up your files into a package.zip file inside a dist folder.
Not too bad, right? Right now this is a little more complex than a simple Bash script, but the advantage will become clearer as your requirements become more complex.
Adding Some Flexibility
You probably want to do more than one thing with your build scripts, so let’s add some more functionality. First, install the minimist package with npm i minimist --save-dev. This package will let you parse arguments passed to the command line, which can be used to perform different functions.
Next, import minimist and the exit function from process into _build.js:
Add two new functions to the file, build(), and clean(). Move the contents of the main() function into build(). At the end of build(), add the below line to have the script exit once the function finishes:
Now set up main() to have minimist process the arguments and call the two new functions based on them.
Now by passing -b, the script will build, while passing -c will clean the build output. Let’s fill out the clean() function as well. All we want it to do is remove the dist folder.
Now let’s test it. If the distfolder is present, delete it, then run node _build.js -b to generate the zip file. Now test the “clean” functionality by running node _build.js -c to remove the dist folder.
Simplifying the Process
You probably don’t want to have to pass in command line arguments every time you want to build or clean. As you probably know, you can use your package.json file to include short scripts. Let’s add some scripts to simplify the calls.
Add the below two script lines to the scripts section of package.json. You can create the section if it doesn’t exist:
Now instead of calling node _build.js with arguments, you can instead call npm run build and npm run clean to build and clean respectively. But you can simplify your process even further: let’s say you want to clean before building. You don’t have to call both commands each time; you can join them into a single command instead, like so:
Note that && is a way to join commands that generally works cross-platform. Now you can just run npm run cleanbuild to kick off the entire process of cleaning and building your packaged output.
A Few Things To Keep In Mind
While this can be a great way to simplify your build process, it may not be as helpful if you’re relying on platform-specific tools, or if you need to call specific tools that can only be called from the command line, though you might be able to work around this with the cross-spawn npm package, which lets you make command line calls in a cross-platform way.
Since the example discussed above isn’t very complex, you might not see the benefits of this method as clearly as you would in a more complicated example. In addition to the benefit of being cross-platform, the benefits of this approach become much more apparent as your build process becomes more complicated.
If you’d like to see a working example that handles a much more complicated build process, here’s an example of a project whose build process involves generating multiple Windows executables from both Node.js and Python code, and packaging them up with other required files. The build script can be found here, and the package.json file can be found here.