How to Write a Command Line Application in Node.js

What is a command line application? Usually it means a program that is ran via the command line. It can be a simple script that changes your working directory. Or it can be a complicated program that has a multitude of options and arguments. Examples of command line applications are mutt, irssi, htop.

You can write a command line application using most programming languages. Today, dynamically typed programming languagues are popular choices for writing such scripts. Reason being they tend to be shorter to write and easy to learn. A short startup time is also important. If a script only takes a fraction of a second to run, and startup time takes 5s, it makes the script feels slow. Examples are the JVM languages. Node.js fits the bill for all these cases. Let’s take a look to see how we can write a command line application using Node.js.

Library shopping

In Node.js, there are many libraries that can help you to write a command line application. Which to choose? There are a few main features that all these libraries provide:

  • Option parsing
  • Interactive prompt
  • Usage/help message helpers
  • Interface elements(progress bars, coloring, password input, spinners, etc)

Let us classify the libraries.

All in one

These libraries provides all the major features. CLI provides input parsing as well.

Specialist option parsers

These libraries only do option parsing

Interactive prompt

Interactive prompt is like a sub shell. You can run custom commands in it. Good examples are node interactive shell and irb.

Interface elements

Components of a command line application

We can break a command line application into separate components. Namely, the entry point, arguments and options, output, documentation, configuration and distribution. I’ll be using the twitter search app as an example for illustrating the components.

Entry point

Entry point is the executable script which starts the program. Typically there’re 3 ways to run a command line application.

One way is to indicate both the program loader and the path to the script. Assuming you have installed the example application and is now on the project root.

1
node bin/birdie

A shorter way is to indicate just the path to the script. Don’t forget to make the script executabe by changing the permissions. Another caveat is, if the path to the program loader is wrong, the program won’t run correctly.

1
bin/birdie

It is possible to do that because of the shebang) line present on the first line of the script. The shebang line hints to the operating system which program loader to use. You are encouraged to use it in your own scripts.

Here is an example of the shebang line used in the example application

1
#!/usr/local/bin/node

It can be even shorter if the script is placed in the PATH directories.

1
birdie

The operating system will find the script and run it. The only caveat is you have to install the script there.

If your program affects the whole system, install it in the PATH). A good example is NPM and Coffeescript.

By convention, scripts meant to be executed are placed in the bin directory of your project.

Arguments and options

A complex command line application usually takes in arguments and options. An option(or flag or switch) is used by the program to modify the operation. In Unix like systems, it is usually indicated by a hyphen-minus followed by a letter or word. Using node as an example.

1
node -v

node is the command and -v is the option.

For the example application, I chose Optimst because of its simplicity. Accessing the options is very easy as illustrated below

1
var resultTypeOpt = argv.r || argv.resulttype || 'mixed';

Optimist parses the option and makes it accessible via the argv object. For example when running the command

1
node bin/birdie -r foo bar

Optimist parses the arguments and options and returns argv in an object like this:

1
2
3
4
5
{
  _: [ 'bar' ],
  '$0': '/path/to/bin/birdie',
  r: 'foo'
}

The r option value is placed under the r property and the rest of the arguments are in the underscore array.

Output

If your command line application need to show output, you can do so using plain old console.log.

Another option is to use Winston. Winston is a logging libarary that can be used to log to STDOUT. It is almost the same as console.log.

1
winston.log('foo');

It comes with logging levels like info, warn and error which also comes in color. Applying them is as easy as

1
winston.info('bar');

There’re plenty of other features to Winston. Feel free to explore the library.

If you want to add colors to your text output, you can make use of Colors.js. Applying colors is very easy.

1
console.log("foo".green);

That will output the text in green. Colors.js does that by overriding the Javascript String prototype. So if you are also overriding the String prototype, be careful when using Colors.js.

Documentation

Typically a command line application comes with documentation in the form of man pages. In fact NPM does comes with man pages. NPM generates its man pages via the RonnJS package. It generates man pages from a Markdown file. You can also use the Ronn ruby gem directly. DailyJS did a good write up on generating man pages in Node.js.

Your application should also come with HTML documentation.

Configuration

If your command line application has a lot of options, a configuration file could help. Let’s take a look at Mocha. Mocha has a mocha.opts file which you can state the common options that you want included.

Adding a configuration file feature is as easy as reading from file. But do note that by convention, the option stated on the command line has higher precendence. Which means if I stated an option to print the output in green, but the option in the configuration file says it should be red, you should render it in green. Here’s the conventional order of precedence in overriding configurations.

  1. Command line options
  2. Configuration file options
  3. Application default options

Distribution

If you want to distribute your command line application, using NPM is the best option. NPM helps to install your executable script if you add the bin option in your package.json manifest.

If the user installs the package locally like this

1
npm install foo

The program’s executable script would be found in 2 places:

  1. ./node_modules/.bin
  2. ./node_modules/foo/bin

NPM adds a symbolic link from the .bin directory to your script.

If the user chose to install your package globally like this:

1
npm install -g foo

The package will install your executable script under the PATH directories. More information can be found in the global vs local npm blog article.

Conclusion

Despite the relative youth of the Node.js eco-system, all the pieces required for building a command line application are already there. Feel free to give the example application a run to see how easy it is to build one in Node.js.

Comments