How to build a CLI NPM package

Rodrigo
Level Up Coding
Published in
5 min readJun 1, 2019

--

Photo by Paul Esch-Laurent on Unsplash

Some packages you install to use a library in your code, other packages you install to get some command in the terminal like yarn or to format your code like prettier. In this article, we are going to build a package command line program that returns the sum of its two arguments.

Creating the basic package

Let’s start by creating the package

Create a file index.js with:

Now we have a package with a function that adds two numbers together, we could install it and use it like this:

Creating the CLI script

We need to create a new file cli.js with the code of our command. It is good practice to name this file “cli” or to put in a folder with “cli” in its name.

The first line of our script is called “shebang”. It is a mechanism to help UNIX based systems to run our script.

In systems based on UNIX, shell scripts don’t need to have an extension (the extension is what follows the dot, like js in index.js). So the system can’t use the name of the file to figure out in what the language the script was written. To solve this problem, we use the convention to start the script with a comment line with the path of the program that is able to run our script. In our case, Node is going to run our script.

Most languages for shell scripts in UNIX systems use the # character to indicate line comments. JavaScript actually uses // for line comments, but node, in order to be compatible with the shebang convention, ignores the first line if it starts with #.

We could have written the full path of the node executable file, but instead, we provide the path for the env program and pass node as an argument. This is commonly done because in UNIX systems sometimes programs are installed in different locations, but usually the program env is in that same path. You pass a program name to env, and it will return the right path for that program.

Next, we import our add function and call it with the arguments that come from the terminal. They always come as strings, so we have to convert them to numbers and the first two arguments are always the path of Node and the path of our script (cli.js in our case).

Now in package.json, we have to let NPM know about our CLI script.

We could also pass an object instead of the path of our CLI script. If we pass an object each key of the object would be a new command, and the value should be the path of the script to run that command. Since we didn’t pass an object, the name of our command is the name of our package add.

Trying our new package

Our package is basically done, let’s simulate someone installing our package to see if it works.

Here I am just going to a different folder, starting a new project and passing the path of our add package to npm install.

This is a simple way of running the CLI scripts locally without having to install it globally. We also have access to our CLI script in the package.json.

Using yargs to improve usability

Right now our CLI is very crude. For example:

We can improve our command line interface, using the package yargs.

In our cli.js file we can do:

The function coarseNumber converts a string to a number, but throws an error if the string is not a valid number.

The builder function describes the two arguments to yargs. We have two numbers and you pass them in order. They are converted to numbers using our previous function.

The handler function is called when all the arguments are ok.

The last line we are using what is called a default command in order to use the positional arguments, without it, we would have to pass our arguments like flags (-x 1). The second argument is the description of our command (we don’t have any), we pass our builder and handler function and then call parse to parse the arguments in the command line.

Now we can re-install the package and test it.

We get a nice description of our command line script and some default flags like version and help, not to mention the very useful check on the parameters with descriptive error messages.

Conclusion

I hope that you use this text to create some nice NPM packages, let me know in the comments what have you created.

Summary of the things learned

  1. Create a simple package
  2. Add a CLI script to a package
  3. Shebang
  4. Test a package
  5. Run packages CLI scripts locally
  6. Improve interface with yargs

--

--