A NodeJS CLI With Commander

Written by Basile Samel.

Published Oct 16, 2024. Last updated Oct 16, 2024.

Creating a simple CLI command

Our main entrypoint:

cli.js

#!/usr/bin/env node

import { Command } from "commander"

import TestCommand from "./src/commands/test.js"

const program = new Command();

program
.addCommand(TestCommand)

program.parse();

We create a file for each command to make it easier to grow and maintain.

src/commands/test.js

import { Command } from "commander"

const program = new Command('test');

program
.argument('<string>', 'prompt')
.description('')
.action(async (str) => {     
    console.log(str)
});

export default program

A bit like in a Model-View-Controller architecture, we should try to keep the command thin and put most of the business logic in the model layer. In this case, if the command was a little more meaty, I would create services and models folders. models would hold class and schema definitions, and services would interact with the database or external libraries.

Adding options to the CLI

src/commands/test.js

import { Command } from "commander"

const program = new Command('test');

program
.argument('<string>', 'prompt')
.description('')
.option('-f, --file <string>', 'input file for context')
.option('-b, --boolean', 'input file for context')
.action(async (str, options) => { 
    console.log(str)

    if(options.file) {
        console.log(options.file)
    }

    if(options.boolean){
        console.log('boolean option')
    }
});

export default program

Adding a loading spinner

src/commands/test.js

import yoctoSpinner from 'yocto-spinner';

   
const spinner = yoctoSpinner({text: `starting`}).start();

// do things

spinner.success('Success!')

Running the CLI

All we have to do is to add an entry in our package.json:

package.json

"bin": {
    "cli": "./cli.js"
},

We can then use the CLI like so:

npx cli test ok
# output: "ok" 

npx cli test ok -f ./file.js
# output: "ok"
# "./file.js"

npx cli test ok -b
# output: "ok"
# "boolean option"

npx cli test ok -f ./file.js -b
# output: "ok"
# "./file.js"
# "boolean option"

And voilĂ 

Susbcribe to the newsletter