Last week, I wrote an article about building a Node.js CLI using yargs. I introduced it saying we use cli tools everyday to simplify common tasks in our jobs. I made myself a couple of cli like gitmoji-changelog. It is a changelog generator for gitmoji commit convention.
I would like to share with you a few libraries I used on this project and while contributing to Gatsby. Gatsby is a good source of inspiration, consider contributing to it. I learned a lot while doing it (they give free swag to thank contributions 🤫).
yargs
It is a library that helps you defining your tool's interface. It also parses arguments for you. The icing on the cake is that yargs generates automatically an help menu.
Here is a simple example that displays a message "Hello [something]" a certain amount of times.
require('yargs')
.command('$0 [name]', 'say hello', (yargs) => {
yargs
.positional('name', {
describe: 'hello\'s target',
default: 'world'
})
.option('times', {
alias: 't',
type: 'number',
default: 1,
description: 'number of times to say hello'
})
}, (argv) => {
for (let i = 0;i < argv.times; i++) {
console.log(`Hello ${argv.name}!`)
}
})
.argv
Result:
prompts
A common use case in cli tools is asking user for information. Prompts is a lightweight library based on promises. It implements an exhautive list of question's types.
(async () => {
const prompts = require('prompts')
const response = await prompts({
type: 'confirm',
name: 'value',
message: 'Can you confirm?',
initial: true
})
console.log('Reponse: ', response.value)
})()
Result:
signale
Standard console
API provides only a few methods to display information. Signale comes with 19 built-in methods (logs are prefixed with emojies ❤️)! You can also implements custom loggers.
const signale = require('signale')
signale.success('CLI started');
const options = {
types: {
santa: {
badge: '👽',
color: 'magenta',
label: 'alien',
logLevel: 'info'
}
}
}
const custom = new signale.Signale(options);
custom.santa('E.T go home')
signale.complete('Call sent')
Result:
chalk
It is a pain in the neck to add style to a cli output. Chalk provides an easy-to-use API to colorize logs. It also supports template literals!
const chalk = require('chalk')
console.log(`${chalk.blue('Welcome')} in the activity monitor${chalk.red('!')}`)
console.log(chalk.green(`Your computer seems in ${chalk.underline('great')} shape.`))
console.log(`
envinfo:
CPU: ${chalk.red('90%')}
RAM: ${chalk.green('40%')}
DISK: ${chalk.yellow('70%')}
`)
Result:
progress
Another common use case is dealing with asynchronous operations. It is nice to give user a percentage of completion when your cli is doing a heavy computation. Progress is an highly customizable ascii progress bar. It comes with a bunch of options and standard information (percentage, total, estimated completion, ...) to display on the progress bar. You can also add your own information.
const ProgressBar = require('progress')
let ticks = 0
const bar = new ProgressBar(
'Rocket launch :bar in :counter',
{ total: 10, width: 50 },
)
const timer = setInterval(function () {
ticks++
bar.tick({ counter: 10 - ticks })
if (bar.complete) {
console.log('\n🚀')
clearInterval(timer)
}
}, 100)
Result:
configstore
Earlier we saw Prompts to ask user information. It is also nice to store its answer to avoid asking it again and again. Configstore is a library that persists data for you. It stores it in a json file on the user's disk. It handles well the dot notation!
const Configstore = require('configstore')
const packageJson = require('../package.json')
const config = new Configstore(packageJson.name)
config.set('answer', true);
console.log('answer:', config.get('answer'));
config.set('a.really.deep.config', true);
console.log('a.really.deep.config:', config.get('a.really.deep.config'));
config.delete('answer');
console.log('answer:', config.get('answer'));
Result:
envinfo
As frontend developer I use user-agent to get information about my user device. It helps a lot to reproduce bugs for example. As cli developer you don't have access to this kind of information. envinfo is a library that generates reports that users can provide when opening issues on your project.
(async () => {
const envinfo = require('envinfo')
const environment = await envinfo.run(
{
System: ['OS', 'Shell'],
Binaries: ['Node', 'Yarn', 'npm'],
Utilities: ['Git'],
},
{ markdown: true }
)
console.log(environment)
})()
Result:
Alternatives exist for these libraries but I used these ones and I enjoy working with them. In my opinion, they covers the majority of problems you might encounter while coding cli tools.
Hope it will help 🙌 Happy cli coding!
Feedback is appreciated 🙏 Please tweet me if you have any questions @YvonnickFrin!
Latest comments (28)
Thanks a lot
Commander.js seems to be another good option! github.com/tj/commander.js
ASCIIFIED banners can be a great addition to your CLI utilities bringing about stunning looks.
Check out node-banner
Awesome!
Nice write up! I didn't know about some of those packages.
If you are in the process of building NodeJS CLIs you definitely should check Oclif from Heroku, I wrote a post with an example some time ago here dev.to/fedekau/building-awesome-cl...
How about OCLIF? Have you thought of Oclif.io?
The gluegun framework is also incredible.
infinitered.github.io/gluegun
Didn't know this project, thank you for sharing 🙏
Author of Prompts here! Thank you for featuring the prompts library ❤️
Thank you for your awesome work 🙏
Commander is missing from the list 😝
Sorry, this is an opinionated list. I use yargs for this purpose 😉
Very nice article!
I am going to try all of this for sure, thank you for sharing.
I've used gluegun to create a CLI, it's quite nice and have a lot of functionality out of the box, worth checking it out!
There is also 🌈 React for interactive command-line apps, Ink :-)
Used it with pastel for several CLIs. Really nice :)