How to create a Node.js interactive Command Line Interface (CLI)
January 31, 2021Tested with Ubuntu Desktop 18.04 LTS and Node.js v14.15.4 LTS
Usually I write shell scripts and CLI using Bash and / or Python. But well, I mainly spend my days writing JavaScript to create Web applications and on my workstation Node.js is the king (alongside with Python). So, why do not use JavaScript even for shell?
In this post, we are going to familiarize with the basic concepts, specially how to read the user input, without using third-party libraries. Just Node.js!
At the end we will have a script that allows the user to display the environment variables, the CLI help and to quit the CLI.
Let is begin:
-
create a folder, e.g.
node-cli-example
$ mkdir node-cli-example
-
create a file called
node-cli-example.run
$ touch node-cli-example.run
-
set executable permissions
$ chmod u+x node-cli-example.run
- open the file with our favorite text editor
-
the first line of the file must be
#!/usr/bin/env node
this set the interpreter to be used. In this case
node
-
we are going to create an interactive CLI, so we need something that prints a prompt and waits for user input. Node.js provides a module called
readline
and it is perfect for our purposes.const readline = require("readline"); // instantiate a readline const cli = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: "\n> ", }); // prompts the user and waits for input cli.prompt(); // listen to user input cli.on("line", (line) => { console.log(line); cli.prompt(); });
-
from the console we can run the script typing
$ ./node-cli-example.run
At the prompt, type
Hello, World!
and press Enter. The script should print our input and should show a new prompt ready for the next input. To exit press Ctrl + C. -
Nice, now we have the scafolding. Let add the possibility to quit without the need to press Ctrl + C. Let assume that if the user type
quit
, the CLI must quit. The code within the listener becomes:cli.on("line", (line) => { const input = line.trim(); switch (input) { case "quit": { console.log("Bye!"); process.exit(0); } default: { console.log(line); } } cli.prompt(); });
-
Well, the CLI is going to grow with the
env
command, so we need anhelp
to remeber all the available commands.
Let add to theswitch
theenv
case.
Let add thehelp
case that is even thedefault
. This means that if we typehelp
or just press Enter the CLI will display the help.
The code within the listener becomes:cli.on("line", (line) => { const input = line.trim(); switch (input) { case "env": { console.log(line); break; } case "quit": { console.log("Bye!"); process.exit(0); } case "help": default: { console.log( "\nCommands available\n\ --------------------------------------\n\n\ env display environment variables\n\n\ quit quit the CLI\ " ); } } cli.prompt(); });
-
And now let implement the
env
command. Thecase
statement forenv
becomes:case "env": { for (const variable in process.env) { console.log(`${variable}: ${process.env[variable]}`); } break; }
That is it! We created our interactive CLI! Of course, it is possible extends this small example to manage more complex scenarios, like commands with arguments.