DISCOVERY

May 26th, 2018

A Simple Node.js Command Line Application

Node.js

JavaScript

Command Line Application

Recently while working on my website I made a command line tool in Node.js. The tool took in an HTML file and tokenized its contents into a JSON file. Writing command line tools with Node.js sounds complicated at first but is actually incredibly simple! This discovery post introduces a simple command line tool for generating random numbers - written in Node.js.

One of the great things about the Node.js and the npm ecosystem is all the community built npm modules. For command line tools there are many different modules to choose from. In this discovery post I use commander.js. I chose this module over all the others because it has no dependencies! Less dependencies generally means more reliable code and less unused code.

The first step for building the application is initializing the projects package.json file and setting up a bin field.

yarn init
{ "name": "5-26-NodeJS-Command-Line", "version": "1.0.0", "description": "Node.JS Command Line Prototype Application", "main": "index.js", "author": "Andrew Jarombek", "license": "MIT", "bin": { "random": "./index.js" } }

bin is an object of key value pairs. The key is the name of a command and the value is the file that executes when the command is called. Npm takes the key value pairs in bin and adds the executable files (the values) to the systems PATH variable1. In Unix systems PATH declares a list of directories for executable programs2. In order to install this executable globally the following command is run:

npm install -g

Now the executable is referenced globally throughout the filesystem by running random in Bash. Finally the commander.js dependency is added. Now JavaScript development can start!

yarn add commander

Commander.js introduces a really nice API for building command line apps. The API uses chained methods to declare the applications functionality. Let's go through the Node.js application piece by piece before displaying the entire application in full.

The first line of the script must be a shebang line, otherwise the command line application won't work.

#!/usr/bin/env node --harmony

Shebang Line

A shebang line is written to determine which interpreter is used on an executable file3. The syntax consists of the #! symbols followed by a path to an interpreter script. The shebang line is used on Unix operating systems, but in different OS environments other methods are used. For example, on Windows the interpreter is simply implied by the file name extension.

Shebang lines are commonly used with the format #!/usr/bin/env followed by the interpreter name. This sequence uses the Unix env command to look for the interpreter name in the systems PATH variable4.

While the shebang line is only needed for Unix systems, its always included for cross compatibility. Windows OS simply ignores it5. Also note the shebang line isn't valid JavaScript - # is not a comment in JavaScript. The JavaScript interpreter actually ignores this line when executing the program.

I also used the --harmony flag when specifying the node interpreter. This flag enables the use of ES6+ features.

Now comes the main body of the application. I import the commander.js module and declare the command line program:

const program = require('commander'); // Help message to be displayed when using the --help option const helpMessage = ` Examples: "Return a random floating point number between 0 and 1" random 0 1 -t float "Return a random integer number between 1 and 10" random 1 10 -t int `; // Declare the command line program program .version('1.0') .arguments('<start> <end>') .option('-t, --type <type>', 'Type of Number') .action((start, end) => { ... }) .on('--help', () => { console.log(helpMessage) }) .parse(process.argv);

The application is broken down into pieces - each piece being a chained method call. The first method call is to version(), which specifies a version number that is displayed when using the random --version command.

The seconds method call is to arguments(), which specifies mandatory arguments users must enter when writing a command. Two arguments are specified here - <start> and <end>. Remember that the command line app is for generating random numbers. The start argument specifies a lower bound the random number, and end specifies an upper bound. Later in the method chain I accessed these two arguments with the start and end variables respectively.

Unlike arguments() which specifies mandatory parameters, option() specifies optional flags for the command. Optional flags provides additional functionality for an application. The code above specifies a flag declared with either -t or --type followed by a <type> argument. Its used to specify whether the random number is an integer or a floating point number.

Optional flags are also accessed as variables in the action() method. The <type> argument is stored in the program.type variable. I will go over action() in just a bit.

The methods on() and parse() specify text to display when calling random --help and to explicitly bind the programs arguments to the command line arguments (process.argv).

action() contains logic to perform based on the command line arguments. This is the meat of the application.

.action((start, end) => { // Log out the arguments and optional options console.log(`Start: ${start}`); console.log(`End: ${end}`); console.log(`Type: ${program.type}`); // Generate the random number const range = end - start; const random = Math.random(); const randomInRange = random * range; const randomInRangeFromStart = randomInRange + start; // Round the random number if the int option is specified if (program.type && (program.type.toLowerCase() === 'int' || program.type.toLowerCase() === 'integer')) { console.log(Math.round(randomInRangeFromStart)); } else { console.log(randomInRangeFromStart); } })

The code here is straightforward. Return a random integer or floating point number within the bounds specified in the command line arguments. That is all!

The program is executed like so:

random --help random 0 1 # Start: 0 # End: 1 # Type: undefined # 0.407353929118857040 random 1 10 -t int # Start: 0 # End: 10 # Type: int # 4

Easy! The command line application I built for my website has a bit more complexity, but even that is simple and easy to understand. You can check out the code for my app on GitHub.

There are many different languages command line applications can be built in. JavaScript running in the Node.js environment is one of them! Here is the full code for the application:

#!/usr/bin/env node --harmony const program = require('commander'); // Help message to be displayed when using the --help option const helpMessage = ` Examples: "Return a random floating point number between 0 and 1" random 0 1 -t float "Return a random integer number between 1 and 10" random 1 10 -t int `; // Declare the command line program program .version('1.0') .arguments('<start> <end>') .option('-t, --type <type>', 'Type of Number') .action((start, end) => { // Log out the arguments and optional options console.log(`Start: ${start}`); console.log(`End: ${end}`); console.log(`Type: ${program.type}`); // Generate the random number const range = end - start; const random = Math.random(); const randomInRange = random * range; const randomInRangeFromStart = randomInRange + start; // Round the random number if the int option is specified if (program.type && (program.type.toLowerCase() === 'int' || program.type.toLowerCase() === 'integer')) { console.log(Math.round(randomInRangeFromStart)); } else { console.log(randomInRangeFromStart); } }) .on('--help', () => { console.log(helpMessage) }) .parse(process.argv);

[1] "bin", https://docs.npmjs.com/files/package.json#bin

[2] "PATH (variable)", https://en.wikipedia.org/wiki/PATH_(variable)

[3] "Shebang (Unix)", https://en.wikipedia.org/wiki/Shebang_(Unix)

[4] "env", https://en.wikipedia.org/wiki/Env

[5] "What exactly does “/usr/bin/env node” do at the beginning of node files?", https://stackoverflow.com/a/33510581