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
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 chalk
library 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.mjs
new file called, and adding the shebang line. We'll also zx
import the functions and modules we'll use from the package, as well as the Node.js core path
modules:
#! /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) {
console.error(chalk.red(errorMessage));
process.exit(1);
}
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
, node
and 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 checkRequiredProgramsExist
function 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 which
function for each program. If which
the 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 exitWithError
helper function to display an error message and stop running the script.
We can now add a checkRequiredProgramsExist
call 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 --directory
command.
zx
The 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 zx
package 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 directory
an argument is passed to our script, we check to see if it is the path to an existing directory. We'll use fs-extra
the provided fs.pathExists
method:
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 zx
the provided cd
function to switch the current working directory:
cd(targetDirectory);
If we run the script now without --directory
arguments, 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 getGlobalGitSettingValue
function here. It will run git config
the 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}`
).stdout.trim();
} 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 config
command, neither the command nor anything it sends to standard output is displayed. We turn the mode back on at the end of the function verbose
so we don't affect any other commands we add later in the script.
Now we add checkGlobalGitSettings
the function that takes an array of Git setting names. It will loop through each setting name and pass it to a getGlobalGitSettingValue
function 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) {
console.warn(
chalk.yellow(`Warning: Global git setting '${settingName}' is not set.`)
);
}
}
}
Let's checkGlobalGitSettings
add a call to check if user.name
the user.email
Git settings have been set:
await checkGlobalGitSettings(["user.name", "user.email"]);
Continuing...
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...
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...