loading...
Cover image for Building your first interactive Node JS CLI

Building your first interactive Node JS CLI

hugodias profile image Hugo Dias Updated on ・3 min read

Originally posted in my blog

NodeJS can be very useful when it comes to building Command-line Interfaces also known as CLI's.

In this post, I'll teach you how to build a CLI that asks some questions and creates a file, based on the answers.

Getting started

Let's start by creating a brand new npm package

mkdir my-script
cd my-script
npm init

NPM will ask some questions. After that, we need to install some packages.

npm install --save chalk figlet inquirer shelljs

What these packages do:

  • chalk - Terminal string styling done right
  • figlet - Figlet is a program for making large letters out of ordinary text
  • inquirer - A collection of common interactive command line user interfaces
  • shelljs - Portable Unix shell commands for Node.js

index.js file

Now create a index.js file with the following content:

#!/usr/bin/env node

const inquirer = require("inquirer");
const chalk = require("chalk");
const figlet = require("figlet");
const shell = require("shelljs");

Planning the CLI

it's always good to plan what a CLI needs to do before writing any code.

This CLI will do just one thing: Create a file.

It should ask a couple of questions and after that, show a success message with the created file path.

The questions are: what is the filename and what is the extension.

// index.js

const run = async () => {
  // show script introduction
  // ask questions
  // create the file
  // show success message
};

run();

The first function is the script introduction. Let's use chalk and figlet to get the job done.


const init = () => {
  console.log(
    chalk.green(
      figlet.textSync("Node f*cking JS", {
        font: "Ghost",
        horizontalLayout: "default",
        verticalLayout: "default"
      })
    )
  );
}

const run = async () => {
  // show script introduction
  init();

  // ask questions
  // create the file
  // show success message
};

run();

Now it's time to write a function that asks questions.

const askQuestions = () => {
  const questions = [
    {
      name: "FILENAME",
      type: "input",
      message: "What is the name of the file without extension?"
    },
    {
      type: "list",
      name: "EXTENSION",
      message: "What is the file extension?",
      choices: [".rb", ".js", ".php", ".css"],
      filter: function(val) {
        return val.split(".")[1];
      }
    }
  ];
  return inquirer.prompt(questions);
};

// ...

const run = async () => {
  // show script introduction
  init();

  // ask questions
  const answers = await askQuestions();
  const { FILENAME, EXTENSION } = answers;

  // create the file
  // show success message
};

Notice the constants FILENAME and EXTENSIONS that came from inquirer.

The next step is to create the file.

const createFile = (filename, extension) => {
  const filePath = `${process.cwd()}/${filename}.${extension}`
  shell.touch(filePath);
  return filePath;
};

// ...

const run = async () => {
  // show script introduction
  init();

  // ask questions
  const answers = await askQuestions();
  const { FILENAME, EXTENSION } = answers;

  // create the file
  const filePath = createFile(FILENAME, EXTENSION);

  // show success message
};

And last but not least, show the success message along with the file path.

const success = (filepath) => {
  console.log(
    chalk.white.bgGreen.bold(`Done! File created at ${filepath}`)
  );
};

// ...

const run = async () => {
  // show script introduction
  init();

  // ask questions
  const answers = await askQuestions();
  const { FILENAME, EXTENSION } = answers;

  // create the file
  const filePath = createFile(FILENAME, EXTENSION);

  // show success message
  success(filePath);
};

Let's test the script by running node index.js.

Screenshot

Yay! And here is the final code:

Final code

#!/usr/bin/env node

const inquirer = require("inquirer");
const chalk = require("chalk");
const figlet = require("figlet");
const shell = require("shelljs");

const init = () => {
  console.log(
    chalk.green(
      figlet.textSync("Node f*cking JS", {
        font: "Ghost",
        horizontalLayout: "default",
        verticalLayout: "default"
      })
    )
  );
};

const askQuestions = () => {
  const questions = [
    {
      name: "FILENAME",
      type: "input",
      message: "What is the name of the file without extension?"
    },
    {
      type: "list",
      name: "EXTENSION",
      message: "What is the file extension?",
      choices: [".rb", ".js", ".php", ".css"],
      filter: function(val) {
        return val.split(".")[1];
      }
    }
  ];
  return inquirer.prompt(questions);
};

const createFile = (filename, extension) => {
  const filePath = `${process.cwd()}/${filename}.${extension}`
  shell.touch(filePath);
  return filePath;
};

const success = filepath => {
  console.log(
    chalk.white.bgGreen.bold(`Done! File created at ${filepath}`)
  );
};

const run = async () => {
  // show script introduction
  init();

  // ask questions
  const answers = await askQuestions();
  const { FILENAME, EXTENSION } = answers;

  // create the file
  const filePath = createFile(FILENAME, EXTENSION);

  // show success message
  success(filePath);
};

run();

To execute this script anywhere add a bin section in your package.json file and run npm link

{
  "name": "creator",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "chalk": "^2.4.1",
    "figlet": "^1.2.0",
    "inquirer": "^6.0.0",
    "shelljs": "^0.8.2"
  },
  "bin": {
    "creator": "./index.js"
  }
}

$ npm link
$ creator

Hope it helps :)


Photo by Alex Knight on Unsplash

Posted on by:

Discussion

markdown guide
 

Great tutorial Hugo. There are also many people writing CLI tools using Go, have you ever built any CLI tool using Go? Any thoughts?

 

Thanks Lucas.

Yes, Go is also amazing on building CLIs! Never tried before but it should be really fast and easy to work with files.

Maybe in a near future!

Any tips?

 

Great tutorial!

Another nice thing to do is using argv (npmjs.org/package/argv) in order to allow non-interactive or silence execution by providing the answers to the questions through command line arguments

 

This is super awesome dude!

I wanted it to stay alive and all I had to do was make the run function recurse.

This is great work, keep it up