In this guide will we make a small pizza CLI in TypeScript with Node.js. After following all the steps you will have a completely working CLI, get an idea of how you set up one, and maybe create a custom one for yourself.
First, we going to initialize a package.json with npm init
. You can choose for yourself a name, author, version, description, keywords, and license.
We need to install all our dependencies:
npm i clear figlet [email protected] commander path --save
Followed by installing our devDependencies:
npm i @types/node nodemon ts-node typescript --save-dev
In our package.json
we need to set the entry point of our app (main and bin). This will be our compiled index.js
file in the lib
folder: ./lib/index.js
.
The word pizza
is the command which you use to eventually call your CLI.
"main": "./lib/index.js",
"bin": {
"pizza": "./lib/index.js"
}
Now we need some scripts to make it easy for ourselves. We have five scripts:
npm start
— you can watch your CLI right awaynpm run create
— runs our build
and test
script together.npm run build
—compiles our TypeScriptindex.ts
file to index.js
and index.d.ts
npm run local
—Installing our CLI globally with sudo npm i -g
and followed by firing our pizza
CLI command.npm run refresh
—removes the node modules, package-lock.json and runs npm install
.Paste the following into the package.json
:
"scripts": {
"start": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/index.ts",
"start:windows": "nodemon --watch 'src/**/*.ts' --exec \"npx ts-node\" src/index.ts",
"create": "npm run build && npm run test",
"build": "tsc -p .",
"local": "sudo npm i -g && pizza",
"refresh": "rm -rf ./node_modules ./package-lock.json && npm install"
},
For our CLI we have some TypesSript configurations set in a file named tsconfig.json
, create this file in the root and copy the following configurations into it:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es6", "es2015", "dom"],
"declaration": true,
"outDir": "lib",
"rootDir": "src",
"strict": true,
"types": ["node"],
"esModuleInterop": true,
"resolveJsonModule": true
}
}
Create a file named index.ts
in thesrc
folder. At the top of our index.ts
file we have:
#!/usr/bin/env node
“This is an instance of a shebang line: the very first line in an executable plain-text file on Unix-like platforms that tells the system what interpreter to pass that file to for execution, via the command line following the magic #!
prefix (called shebang).” — Stack Overflow
Then we need some imports to make use of our dependencies:
const chalk = require('chalk');
const clear = require('clear');
const figlet = require('figlet');
const path = require('path');
const program = require('commander');
Next, we call clear
(to clear our command line every time we call our pizza
command). Then we want to console log a big banner: a red-colored text (pizza-cli’), by using of figlet and chalk.
clear();
console.log(
chalk.red(
figlet.textSync('pizza-cli', { horizontalLayout: 'full' })
)
);
This will be looking like this:
_ __ (_) ____ ____ __ _ ___ | | (_)
| '_ \ | | |_ / |_ / / _` | _____ / __| | | | |
| |_) | | | / / / / | (_| | |_____| | (__ | | | |
| .__/ |_| /___| /___| \__,_| \___| |_| |_|
|_|
Now we came to the part where we can make our CLI interactive. We make use of program
. We can set here our CLI version, description, and various options and parse the result. The options contain a short and a long variant, example: for adding peppers we can use pizza -p
or pizza --peppers
.
program
.version('0.0.1')
.description("An example CLI for ordering pizza's")
.option('-p, --peppers', 'Add peppers')
.option('-P, --pineapple', 'Add pineapple')
.option('-b, --bbq', 'Add bbq sauce')
.option('-c, --cheese <type>', 'Add the specified type of cheese [marble]')
.option('-C, --no-cheese', 'You do not want any cheese')
.parse(process.argv);</type>
To see what we currently have, run npm run build
followed by npm start
, you will see this:
_ __ (_) ____ ____ __ _ ___ | | (_)
| '_ \ | | |_ / |_ / / _` | _____ / __| | | | |
| |_) | | | / / / / | (_| | |_____| | (__ | | | |
| .__/ |_| /___| /___| \__,_| \___| |_| |_|
|_|
Usage: pizza [options]
An example CLI for ordering pizza's
Options:
-V, --version output the version number
-p, --peppers Add peppers
-P, --pineapple Add pineapple
-b, --bbq Add bbq sauce
-c, --cheese <type> Add the specified type of cheese [marble]
-C, --no-cheese You do not want any cheese</type>
We want the users to see what they have ordered, and see their options be updated after they have made different choices.
console.log('you ordered a pizza with:');
if (program.peppers) console.log(' - peppers');
if (program.pineapple) console.log(' - pineapple');
if (program.bbq) console.log(' - bbq');
const cheese: string = true === program.cheese ? 'marble' : program.cheese || 'no';
console.log(' - %s cheese', cheese);
With this code users can use pizza -h
to get information about our options.
if (!process.argv.slice(2).length) {
program.outputHelp();
}
After we got all our code in the index.ts
we can run npm
run create
to test our CLI in the command line. You will see our end result:
_ _ _
_ __ (_) ____ ____ __ _ ___ | | (_)
| '_ \ | | |_ / |_ / / _` | _____ / __| | | | |
| |_) | | | / / / / | (_| | |_____| | (__ | | | |
| .__/ |_| /___| /___| \__,_| \___| |_| |_|
|_|
you ordered a pizza with:
- marble cheese
Usage: pizza [options]
An example CLI for ordering pizza's
Options:
-V, --version output the version number
-p, --peppers Add peppers
-P, --pineapple Add pineapple
-b, --bbq Add bbq sauce
-c, --cheese <type> Add the specified type of cheese [marble]
-C, --no-cheese You do not want any cheese
-h, --help output usage information</type>
You can choose to publish your CLI to npm, I’ve chosen to call the name of the project ‘pizza-cli’ in package.json
. If I would run npm publish
, it will be published to the npm registry (but I didn’t, but you could). Other people could install my project globally by running npm i pizza-cli -g
, and then use the pizza
command to get the CLI up and running!
A cool feature to add by yourself would be by using: Inquirer. With this, you can ask questions to users to fill in the information, the same way as the command: npm init
for example.
Find out if MentorCruise is a good fit for you – fast, free, and no pressure.
Tell us about your goals
See how mentorship compares to other options
Preview your first month