How to Prototype a CLI Using CommanderJS and InquirerJS in Less Than 3 Hours
In this tutorial, I want to help you create a very quick and dirty prototype to collect feedback for the CLI concepts that you want to test out quickly, fail fast, and move on to the next solution.
In the past few months, I was tasked to redesign the CLI experience for one of our products at Canonical (Ubuntu), MAAS. Feel free to check out my MAAS CLI prototype.
Before we start, I am assuming that you have npm and node installed on your local machine.
My current environment:
node version: v12.16.1
npm version: 6.13.4
In this tutorial I will show you how to create a CLI application for a bubble tea shop called macha.
The 2 main commands that our users can do:
- List everything in the menu:
$> macha list
2. Order something from the menu
$> macha order
Functionalities:
A user should have a choice to add toppings, select sugar levels, serve hot or cold, and select ice level. The result will show the total price of your order.
Let’s get started!
First of all let’s create a workspace and install a few packages.
Initialization:
$> mkdir macha && cd macha
$> npm init -y
Your output should look like this:
{
"name": "macha",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
After you initialize the package, we need to install commanderJS, inquirerJS along with 2 npm modules: colors, to add colors to our CLI, and pad,to add padding to the outputs. These will come in handy afterwards.
$> npm install --save commander inquirer colors pad
Now that our project is initialized, we need to add a bin field into the package.json file. In the bin
field, we need to add macha
, because this is going to be our app name in the CLI.
{
"name": "macha",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"macha": "./bin/index.js"
},"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Add 2 folders to your working directory ( bin
and lib
):
You will need to create a bin
folder and a lib
folder in your working directory. Under bin is where index.js
lives, this is where you will put all your commands and how you want to structure them. Under lib is where you store all you commands such as list
and order
. This folder handles all the command outputs. You will need one additional file where you store all your values, let’s name it menu_items.js
.
|-macha
|-bin
|-index.js
|-lib
|-menu_items.js
|-list.js
|-order.js
|-node_modules
|-package.json
Create items in our menu menu_items.js
:
This step, we are going to put all the items in our menu into one place menu_items.js
. In this file, we need to export all the variables so we can use it in other modules.
Create your first command output list.js
:
In the list.js
file, you will import the tea
variable from menu_items.js
and customize how you want to show the output. I use pad
and colors
to make the space between outputs easily readable.
Create a list command in index.js:
In our index.js
file, we will define the list command so when the prompt calls a list command, it will call the list.js
file to show the output.
Now let’s try to run the command from your terminal.
In your macha
directory, run:
~/macha> node bin/index.js
Your output should look like this:
Create a proper sub-command list
:
Let’s create a proper list sub-command, the commanderJS way. We want to be able to run $> macha list
from our command prompt, so we need to create list
as a sub-command with CommanderJS.
The way it works is Commander will parse arguments in program.parse(arguments)
, which leaves any args not consumed by the program options in the program.args
array.
In the example above, we need to add the Shebang line on the first line (as we will make it global later). Then create a list
command with Commander. The convenient thing about this library is that, commanderJS will create a default help
argument for you. There are different options and flavors you can choose from in commanderJS specific options, where you can choose how you want to customize your command.
Finally, add process.agrv
in the last line, so Commander can parse user inputs.
Before we run, let’s install this CLI app globally so you can access this command from any prompt.
$> npm install -g ./
Now you can type macha list
from any prompt.
Using InquirerJS to create interactive prompt for the order command:
For the order
sub-command, I will show you a different approach. InquirerJS basically made it really easy for you to create an interactive command prompt, it accepts a list of questions where you can define its type: list
rawlist
expand
checkbox
confirm
input
number
and password
.
The structure of the answers are in a key/value hash. This will contain the answer you select in the prompt. There are also several plug-ins available for more fun stuff like inquirer-emoji, chalk-pipe, search-list, autocomplete, etc.
As a start, you need to create a set of questions.
In the example below, I created 2 questions sets. The first set of questions is the general question. However, If a user wants the tea cold, they need to answer one more question before seeing the total price.
In this example, the inquirer.prompt
takes these list of questions and returns asPromise
, which will be resolved when all questions are answered.
Because the answers
I got from the inquirer prompt are in type: String, I needed to cast the variable into Float so I can sum the total price for tea + topping + milk.
total = parseFloat(answers.tea.split("$")[1]);
Your index.js
file will now include the order
sub-command.
Let’s try out the order command:
$> macha order
That sums up our tutorial for CommanderJS and InquirerJS.
In case you want to clone this project and play around with it, here is the code for macha-cli : https://github.com/amylily1011/macha-cli
Learning points:
- When you apply colors to the output text and using PAD at the same time, they output may not align.
- Inquirer has a default color setting, this is harder to override. When testing this out, make sure you communicate this with the user.
- Although having colored output is a nice feature, but in reality, your users’ terminal might have a theme that does not support your color output. For instance, when you use normal blue on the text, it can be too dark and unreadable for certain terminal themes.
Bonus points for fancy add-ons:
If you want to create a CLI in style with spinners and progress bars, there are libraries like cli-progress, cli-table, cli-spinner, etc that can save you a lot of time creating cool stuff.
I hope this is helpful for UXers, Prototypers, and anyone who shares the love for prototyping. Feel free to reach out for any questions! Happy prototyping :).