Create scripts in JavaScript with zx


If you use JavaScript and need to automate your work, you’re missing out if you haven’t heard of zx.

Automate what can be automated

Let’s do this tedious thing manually, said no one ever. We all hate doing mind-numbing and repetitive work. Heck, even if we could do what needs to be done manually faster, we still look into automating it to avoid the repetitive work at all cost.

We don’t only automate things that are routine. Automation is a great way to eliminate human error. Unsurprisingly, many developers and dev leads advocate for having a fully integrated and automated test and deployment processes. It helps dev teams eliminate human-errors and focus on what truly matters.

When automating work, you typically write a script - a series of commands that leads to a desired outcome. And there are a few options how you could do it.

Bash

Probably the most common option for writing scripts is Bash. Bash has been around for a very long time and is available as the default shell on macOS and Linux. These days, you can also use it on Windows.

While it’s designed for speed, in practice it’s not trivial to use. It treats all input and output as a string, and requires solid understanding of arcane commands like awk or grep to process it. Bash has a basic concept of arrays but using them and building robust scripts is not easy. If you work with JSON you will need other tools like jq to query the data and turn it into data (strings) that can be processed by Bash.

Microsoft PowerShell

Another option is PowerShell - a shell built by Microsoft. Originally released as Windows PowerShell, nowadays it’s available on any platform as Microsoft PowerShell.

Where Bash treats everything as a string, for PowerShell everything’s an object. Each command returns objects which you can query and loop through. An you can create collections of objects with properties yourself too. It’s a programming language!

Another big difference between PowerShell and Bash is that PowerShell is designed with clarity in mind. Rather than using obscure commands like awk or grep, it uses commands (called cmdlets, pronounce commandlets) that follow a standard naming convention of verb-noun, for example Get-ChildItem. PowerShell cmdlets have a common way of parsing parameters, showing help and handling errors, so if you learn how PowerShell works, you can easily switch between the different sets of cmdlets and they’ll work exactly as you expect them to.

This strength is also PowerShell’s shortcoming. PowerShell is great if all you use is PowerShell. As soon as you will try to use a regular executable, like CLI for Microsoft 365, Azure CLI or any other CLI, in a PowerShell script, you will struggle with handling errors and processing CLIs’ output. What you also need to keep in mind, is that PowerShell is a separate language with its own syntax and concepts which you will need to learn to truly benefit from it.

Google zx

Recently I noticed yet another solution that grabbed my attention. While it’s not a shell like Bash or PowerShell, Google’s zx is a Node.js-based executable that simplifies running scripts built using JavaScript. Since I use JavaScript literally everywhere these days, I had to take a look at zx and boy, it’s so cool!

The idea of writing scripts in Node.js is not new and you could do it already today without zx. Node.js scripts are convenient, because you get to use a language you already know - JavaScript. Combined with access to many npm packages that supercharge your scripts, there is very little you can’t do. But the convenience ends as soon as you need to call an executable. Specifying the arguments, retrieving the response and handling errors is not trivial and quickly adds to your scripts. And this is exactly where zx shines.

zx solves the most important issues when writing scripts using JavaScript. It makes it easy to call executables and get their output, handle errors and it also offers shortcut functions for things like coloring output (chalk), prompting the user (question) or accessing the file system.

To see how it would work in practice, I wrote a short script that uses CLI for Microsoft 365 to retrieve all sites and for each one shows its lists:

#!/usr/bin/env zx
$.verbose = false;

console.log('Retrieving sites...');
const sites = JSON.parse(await $`m365 spo site list -o json`);
for (let i = 0; i < sites.length; i++) {
  const site = sites[i];
  console.log(`(${i + 1}/${sites.length}) Retrieving lists for ${site.Url}...`);

  try {
    const lists = JSON.parse(await $`m365 spo list list -u ${site.Url} -o json`);
    lists.forEach(list => console.log(`  ${list.Title} ${list.Url}`));
  }
  catch (err) {
    console.error(`  ${chalk.red(err.stderr)}`);
  }
}

What you immediately see, is that it’s just JavaScript. There is literally nothing you need to learn to write a zx script. Notice how simple it is to run an executable using the await $ syntax. See how the output from stdout is returned inline and can be further processed? And if you need to handle errors, you wrap await $ in a try..catch clause, and get the error details from stderr from the caught error. That’s it! Notice how easily you get to work with objects, how easy the script is to follow and little overhead handling errors requires!

With the shebang (#!/usr/bin/env zx) in place, you can mark the script as executable (chmod +x ./myscript.mjs) and run it just as any other Bash script.

Summary

If you need to write robust automation scripts and you use JavaScript in your daily work, you should consider using zx. It’ll let you use your existing skills and let you focus on the job at hand. Check out Google zx and I’m looking forward to hearing what you think.

Others found also helpful: