Write Bash scripts with Zx: Build project launcher P1

By XiaoXin
A Bit Randomly

The @keyframes at-rule in CSS is used to control the intermediate steps in an animation sequence. It defines styles for keyframes, or specific points in the animation sequence, allowing you to have more control over t... Read What Is @keyframes In CSS

Main Contents

Write Bash scripts with Zx: Build project launcher P1

Now that we've learned the basics of shell scripting with Google's zx, we're going to use it to build a tool. This tool will automate a usually time-consuming process: bootstrapping the configuration of a new Node.js project.

We will create an interactive shell script that prompts the user for input. It will also use zx's built-in chalklibrary to highlight the output in different colors and provide a friendly user experience. Our shell script will also install the npm packages needed for the new project, so it's ready for us to start developing right away.

Ready to start

Start by creating a bootstrap-tool.mjsnew file called, and adding the shebang line. We'll also zximport the functions and modules we'll use from the package, as well as the Node.js core pathmodules:

#! /usr/bin/env node
// bootstrap-tool.mjs
import { $, argv, cd, chalk, fs, question } from "zx";
import path from "path";

Like the script we created earlier, we want to make our new script executable:

chmod u+x bootstrap-tool.mjs

We'll also define a helper function that prints an error message in red text and exits the Node.js process with an error exit code of 1:

function exitWithError(errorMessage) {

We'll use this helper function in various places throughout our shell scripts when we need to handle an error.

Check dependencies

The tool we're going to create needs to use three different programs to run commands: git, nodeand npx. We can use the which library to help us check if these programs are installed and ready to use.

First, we need to install which:

npm install --save-dev which

and import it:

import which from "which";

Then create a checkRequiredProgramsExistfunction that uses it:

async function checkRequiredProgramsExist(programs) {
  try {
    for (let program of programs) {
      await which(program);
  } catch (error) {
    exitWithError(`Error: Required command ${error.message}`);

The above function accepts an array of program names. It loops through the array, calling the whichfunction for each program. If whichthe program's path is found, it will return the program. Otherwise, if the program cannot find it, it will throw an error. If any programs cannot be found, we call the exitWithErrorhelper function to display an error message and stop running the script.

We can now add a checkRequiredProgramsExistcall to check if the program our tool depends on is available:

await checkRequiredProgramsExist(["git", "node", "npx"]);

Add target directory option

Since the tools we're building will help us start new Node.js projects, we want to run any commands we add in the project's directory. We are now going to add a --directorycommand.

zxThe minimist the package is built in, which is able to parse any command line arguments passed to the script. These parsed command-line arguments are provided by the zxpackage as argv:

Let directory's add a check for the command line argument named:

let targetDirectory = argv.directory;
if (!targetDirectory) {
  exitWithError("Error: You must specify the --directory argument");

If directoryan argument is passed to our script, we check to see if it is the path to an existing directory. We'll use fs-extrathe provided fs.pathExistsmethod:

targetDirectory = path.resolve(targetDirectory);
if (!(await fs.pathExists(targetDirectory))) {
  exitWithError(`Error: Target directory '${targetDirectory}' does not exist`);

If the target path exists, we will use zxthe provided cdfunction to switch the current working directory:


If we run the script now without --directoryarguments, we should get an error:

$ ./bootstrap-tool.mjs
Error: You must specify the --directory argument

Check global Git settings

Later, we'll initialize a new Git repository under the project directory, but first we'll check that Git has the configuration it needs. We want to make sure that commits are properly categorized by code hosting services like GitHub.

In order to do this, create a getGlobalGitSettingValuefunction here. It will run git configthe command to retrieve the value of the Git configuration setting:

async function getGlobalGitSettingValue(settingName) {
  $.verbose = false;
  let settingValue = "";
  try {
    settingValue = (
      await $`git config --global --get ${settingName}`
  } catch (error) {
    // Ignore process output
  $.verbose = true;
  return settingValue;

You'll notice that we're turning off the mode that zx defaults to verbose. This means that when we run a git configcommand, neither the command nor anything it sends to standard output is displayed. We turn the mode back on at the end of the function verboseso we don't affect any other commands we add later in the script.

Now we add checkGlobalGitSettingsthe function that takes an array of Git setting names. It will loop through each setting name and pass it to a getGlobalGitSettingValuefunction to retrieve its value. If the setting has no value, a warning message will be displayed:

async function checkGlobalGitSettings(settingsToCheck) {
  for (let settingName of settingsToCheck) {
    const settingValue = await getGlobalGitSettingValue(settingName);
    if (!settingValue) {
        chalk.yellow(`Warning: Global git setting '${settingName}' is not set.`)

Let's checkGlobalGitSettingsadd a call to check if user.namethe user.emailGit settings have been set:

await checkGlobalGitSettings(["user.name", "user.email"]);


Please Share This Article Thank You!

Parts of Tutorial write Bash Scripts with ZX Library
Write Bash scripts with Zx: Build project launcher P2

Initialize the Git repository We can initialize a new Git repository in the project directory by adding the following command: await $`git init`; Generate package.json Every Node.js project needs package.jsonfiles.&nb...

Write Bash scripts with Zx: Import TypeScript

If we want to write shell scripts using zx in TypeScript, there are a few minor differences that we need to account for. Note: The TypeScript compiler provides a large number of configuration options that allow us to tweak...