Bin and main

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 you CLI.

"main": "./lib/index.js",

"bin": {

"pizza": "./lib/index.js"

}

Scripts

Now we need some scripts to make it easy for ourselves. We have five scripts:

npm start — you can watch your CLI right away

— you can watch your CLI right away npm run create — runs our build and test script together.

— runs our and script together. npm run build —compiles our TypeScript index.ts file to index.js and index.d.ts

—compiles our TypeScript file to and npm run local —Installing our CLI globally with sudo npm i -g and followed by firing our pizza CLI command.

—Installing our CLI globally with and followed by firing our 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",

"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"

},

TSconfig

For our CLI we 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

}

}

Now let’s start creating the CLI

Environment

Create a file named index.ts in the src folder. At thetop 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

Imports

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');

A nice banner

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:

_ __ (_) ____ ____ __ _ ___ | | (_)

| '_ \ | | |_ / |_ / / _` | _____ / __| | | | |

| |_) | | | / / / / | (_| | |_____| | (__ | | | |

| .__/ |_| /___| /___| \__,_| \___| |_| |_|

|_|

Our CLI with options

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, 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);

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

The last part

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

Publish to NPM?

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!