For a recent project I was leveraging the awesome Commander.js framework to build out a CLI with a ton of commands and subcommands.
Example: Fast-food CLI
Let's say you're building a CLI to order food from a fast food restaurant with the following interface:
Program overview:$ mcdOrder some food:$ mcd orderOrder a burger menu:$ mcd order burgerOrder some dessert:$ mcd order dessertPay a meal:$ mcd pay
File structure
With Commander.js, you can easily implement above interface using the following file structure:
cli/mcdmcd-ordermcd-order-burgermcd-order-dessertmcd-pay
😍 Note how neatly self-documenting this file structure is.
Each of these files is a standalone executable and could be used on its own (don't forget to run chmod +x cli/*
).
Code
The most top-level program (mcd
) will declare its subcommand order
.
cli/mcd
#!/usr/bin/env nodeconst { program } = require('commander')// mcd-orderprogram.command('order', 'Order some food')program.action(() => {program.help()})program.parse(process.argv)
Important: program.command
must be invoked with the second argument (description),
as this will hint to Commander that the subcommand lives in its own executable.
The subcommand mcd-order
will declare its own subcommands using
the same syntax:
cli/mcd-order
#!/usr/bin/env nodeconst { program } = require('commander')program// mcd-order-burger.command('burger', 'Order a burger menu')// mcd-order-dessert.command('dessert', 'Order some dessert')program.action(() => {program.help()})program.parse(process.argv)
And finally, at the end of the chain, mcd-order-burger
does not declare
any more subcommands:
cli/mcd-order-burger
#!/usr/bin/env nodeconst { program } = require('commander')program.action(() => {console.log('Thank you for ordering some burgers!')})program.parse(process.argv)
And that's how you can deeply nest subcommands for your Node.js CLI using Commander.js!
For a complete reproducible example, check out the repository on GitHub.