The npm blog has been discontinued.
Updates from the npm team are now published on the GitHub Blog and the GitHub Changelog.
Adding subcommands to your command line tool
This is the second post in a tutorial on command line tools.
- Building a simple command line tool with npm
- Adding subcommands to your command line tool
- Making your command line tool configurable
In the last post, we made a simple command line tool. In this post, we’ll add subcommands to that tool.
You might not have heard of subcommands, but you’ve certainly used them before. Examples of subcommands are install in npm install or push in git push. Subcommands make it possible for you to group related command line functionality under one root command.
Before we start
This builds on the last post. To get started, you can either read that, or just download the code.
At the end of the post, we had a version 1.0.0 of our module. We’re going to be making breaking changes to the code. For example, in the 1.0.0 version of the module we had commands like github-pages-push. We’re going to change this to be github-pages push, which means anyone who is using the original command in their code is going to need to update it.
In semantic versioning, when you make a breaking change, you increment the first number. So our new version will be 2.0.0, but since we’re still working on it we might make some breaking changes before it’s really ready for 2.0.0. To indicate that to our consumers, we’ll use 2.0.0-alpha.0.
npm version 2.0.0-alpha.0
Step 1: Add the root command
The root command is the command that comes before the subcommand, like npm or git.
Add
yargsas a dependencynpm install --save yargsWe’ll be using
yargsto handle the subcommands.Create an executable for the root command
Create a file named
bin/github-pages.jswith the following code:
#! /usr/bin/env node
var yargs = require("yargs");
var argv = yargs.usage("$0 command")
.command("commit", "commit changes to the repo")
.command("push", "push changes up to GitHub")
.command("deploy", "commit and push changes in one step")
.demand(1, "must provide a valid command")
.help("h")
.alias("h", "help")
.argv
This code defines three subcommands, commit, push, and deploy and provides help messages for each. It also adds a demand. If none of these commands are given when the root command is run, then the demand will not be met and the output will say “must provide a valid command”.
In addition, it adds options for showing help. Running the root command without any subcommands will display this help.
Add the root command
In `package.json`, we defined three separate commands in the `bin` key. We now want to replace those separate commands with a single root command.
"bin": {
"github-pages": "bin/github-pages.js"
}
Run npm link to pick up the change. Then run github-pages to test the command. You should see a help message.
Step 2: Make the subcommands work
The subcommands will run the code that we previously had in bin/commit.js, bin/push.js, and bin/deploy.js.
Add a handler for the subcommand
We need to tell yargs what should happen when someone passes in a subcommand, so we"ll pass in a function to handle the subcommand.
.command("commit", "commit changes to the repo", function (yargs) { console.log("committed") })Now when you run
github-pages commit, it should logcommitted.Require
shelljsYou will need to require
shelljslike you did in the previous post.var shell = require("shelljs");Fill in the functionality for the subcommands
.command("commit", "commit changes to the repo", function (yargs) { shell.exec("git add -A . && git commit -a -m 'gh-pages update'"); }) .command("push", "push changes up to GitHub", function (yargs) { shell.exec("git push origin master --force"); }) .command("deploy", "commit and push changes in one step", function (yargs) { shell.exec("github-pages commit && github-pages push"); })
Step 3: Update the module on npm
Set a stable version number
npm version patchPublish the module to npm
If you’re publishing this as a private module, you can just run
npm publish. If you’re publishing it as a public scoped module and this is your first time running the publish command, you will need to use the access option:--access=public.