npm Blog (Archive)

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.

  1. Building a simple command line tool with npm
  2. Adding subcommands to your command line tool
  3. Making your command line tool configurable

Get the code.

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.

  1. Add yargs as a dependency

    npm install --save yargs

    We’ll be using yargs to handle the subcommands.

  2. Create an executable for the root command

    Create a file named bin/github-pages.js with 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.

    1. 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 log committed.

    2. Require shelljs

      You will need to require shelljs like you did in the previous post.

      var shell = require("shelljs");
          
    3. 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

    1. Set a stable version number

      npm version patch
    2. Publish 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.