Node.js is popularly though of as "JavaScript on the backend", but that's a very limited and inaccurate description.

Node.js is much more than that; it is a general purpose programming platform which can be used for developing all sorts of programs - from web servers, to CMSes, to microcontroller programs, to HTTP agents, to git implementations, to binary compilers, to web proxies, to package managers, to torrent clients, to desktop apps, to mobile app scaffolders, and so on.

You will notice that many of them are are command-line tools - they are available as a command on your command line.

In the technological old days, command-line tools were most commonly written in compiled-languages like C, C++, Java etc. You could also make your shell and Python programs to work like command-line tools but it involved many tedious steps to manage them. However, there was no way you could use JavaScript to develop command-line tools.

Along came Node.js in 2009, which changed everything. Now you can use JavaScript for programming your command-line tools, and installing/uninstalling the "binary" is just a single npm command away.

Ever wanted to create your own command-line tool? You have come to the right place!

Let's start from scratch.

Create a directory named "show" and a file inside it named "index.js" with the following content:

index.js console.log(process);

cd to the "show" directory and execute "index.js":

$ node index.js

This will print out a lot of meta information about the Node version, setup, and the environment. It is already a command-line tool, albeit it is executed using the node command.

An ideal command-line tool should be a standalone executable, which can be run using its own command.

In this tutorial, we will create a new command called show , which will show numerous interesting information.

First step: initialize the directory as a npm package directory.

$ npm init --yes

This will create a file named "package.json" with the following content:

{ "name": "show", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }

This file is required for installing the dependencies of the package and for configuring it as a command-line tool.

We will use the minimist package for parsing our tool's command-line options. We could create our own command-line options parser, but it would add to the noise; so let's keep it simple and just use minimist .

Install minimist :

$ npm i minimist

minimist is one of the many command-line options parsers available. It is my preferred parser for small projects, for bigger ones I use is one of the many command-line options parsers available. It is my preferred parser for small projects, for bigger ones I use commander . Then, there is yargs too, which is also pretty popular.

Before we proceed any further, let's decide the interface and functionality of our tool.

It will be named show , and it will have the following options:

-u, --user - to display information about the current user

- to display information about the current user -o, --os - to display information about the operating system

- to display information about the operating system -n, --node - to display information about the installed Node.js

- to display information about the installed Node.js -V, --version - to show the version of the tool

- to show the version of the tool -h, --help - to show the usage message

If none of them are specified, the usage message will be shown.

That makes the signature of our command:

show [(-uon | V | h)]

[] and <> are conventions used to denote optional and mandatory prameters of a command. Prameters within [] are optional and those with <> are mandatory. () and | are used for creating mutually exclusive parameters. For more details refer to andare conventions used to denote optional and mandatory prameters of a command. Prameters withinare optional and those withare mandatory.andare used for creating mutually exclusive parameters. For more details refer to http://docopt.org/ We used -V instead of the expected -v , because -v is commonly used as the short notation for --verbose .

And here is the complete implementation of our tool:

index.js const argv = require('minimist')(process.argv.slice(2)); const os = require('os'); const pkg = require('./package.json'); const u = argv.user || argv.u; const o = argv.os || argv.o; const n = argv.node || argv.n; const V = argv.version || argv.V; const h = argv.help || argv.h; const usage = ` Usage: show [(-uon | V | h)] -u, --user User details -o, --os OS details -n, --node Node details -V, --version show version -h, --help Show this screen `; if (h || (!u && !o && !n && !V)) { console.log(usage); } else if (V) { console.log(` ${pkg.name} version ${pkg.version}`); } else { if (u) { const user = os.userInfo(); const userInfo = ` Username: ${user.username} Home directory: ${user.homedir} UID: ${user.uid} GID: ${user.gid} `; console.log(userInfo); } if (o) { const osInfo = ` Architecture: ${os.arch()} Platform: ${os.platform()} CPU model: ${os.cpus()[0].model} CPU count: ${os.cpus().length} Total RAM: ${os.totalmem()} bytes Free RAM: ${os.freemem()} bytes Uptime: ${os.uptime()} seconds `; console.log(osInfo); } if (n) { const nodeInfo = ` Version: ${process.versions.node} Executable: ${process.execPath} `; console.log(nodeInfo); } }

Execute the file and check out the options in action.

$ node index.js Usage: show [(-uon | V | h)] -u, --user User details -o, --os OS details -n, --node Node details -V, --version show version -h, --help Show this screen

$ node index.js --os Architecture: x64 Platform: darwin CPU model: Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz CPU count: 4 Total RAM: 8589934592 bytes Free RAM: 184418304 bytes Uptime: 129198 seconds

$ node index.js -nou Username: yaapa Home directory: /Users/yaapa UID: 501 GID: 20 Architecture: x64 Platform: darwin CPU model: Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz CPU count: 4 Total RAM: 8589934592 bytes Free RAM: 404602880 bytes Uptime: 129304 seconds Version: 12.4.0 Executable: /Users/yaapa/.nvm/versions/node/v12.4.0/bin/node

Ok, the implementation looks pretty neat, BUT we need a standalone command, not something which should be executed by node .

Alrighty then!

Add the following line at the top of the "index.js" file, right at the very beginning:

#!/usr/bin/env node

With this, we are telling the operating system (OS) to use node for executing the file. Instead of us typing node , the OS will do it for us henceforth.

Now add this property to the object in the "package.json" file:

"bin": { "show": "./index.js" }

The show property is the name of the command we are creating. Essentially, we are saying "Create a new command called show , its implementation is in the ./index.js file". You can give the command any name you want, but don't give it something which conflicts with an existing command (for your own good).

And, by the way, you can add as many commands you like under the bin property. A single package can create more than one command.

The "package.json" file should look like this now:

package.json { "name": "show", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "bin": { "show": "./index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "minimist": "^1.2.0" } }

And the final step: link the package 💫

$ npm link

If you are a yarn user, the equivalent command is yarn link .

You should see an output which looks something like this:

npm WARN show@1.0.0 No description npm WARN show@1.0.0 No repository field. audited 1 package in 0.738s found 0 vulnerabilities /Users/yaapa/.nvm/versions/node/v12.4.0/bin/show -> /Users/yaapa/.nvm/versions/node/v12.4.0/lib/node_modules/show/index.js /Users/yaapa/.nvm/versions/node/v12.4.0/lib/node_modules/show -> /Users/yaapa/projects/hacksparrow.com/hacks/show

This is the magical step.

It does all the things (setting file permissions, creating symlinks) that are required to make a JavaScript file work like a compiled binary located in one of the directories of the PATH environment variable.

You have created a command-line tool using Node.js. Check it out:

$ show --help Usage: info [(-uon | V | h)] -u, --user User details -o, --os OS details -n, --node Node details -V, --version info version -h, --help Show this screen

Dayum! Isn't that neat?

Anytime you want to uninstall this tool, just unlink it:

$ npm unlink removed 1 package in 0.063s

What if you want others to use this amazing tool you have created? They can't be cloning your repository and doing npm link , right?

If you want to become a global command-line tool celebrity, publish your tool on npm. Once published, others can install your command-line interface (CLI) tool globally or use it using npx.

Now that you are an expert in creating command-line tools using Node, here are some packages you may find useful in your next project:

commander - command-line options parser (my choice)

yargs - another command-line options parser

chalk - add colors to printed text

prompt - prompter

inquirer - another prompter 🇺🇸

enquirer - another prompter 🇬🇧

ora - spinner

listr - another spinner

With Node's rich API there are many incredible things you can do. I hope to be using one of your tools some day. All the best!