How to Build an Efficient CLI with NodeJS?

How to Build an Efficient CLI with NodeJS?

Command-Line Interfaces or also known as CLIs (build in Node.js), gives developers a chance to automate all mundane tasks, and that too while leveraging the humongous Node.js ecosystem. The credit even goes to the package managers like yarn and npm, which helps CLI gets distributed and be used on different platforms seamlessly.

Why Create CLI with Node.js? 

Hire nodejs Developers

One of the most prominent reasons to use Node.js to create CLIs is its rich package ecosystem with more than 1.3 million packages in the npm registry. (Source) Just imagine how gigantic is the ecosystem that developers are stepping into with Node.js. 

This article is all about why a software developer would want to write a CLI, use Node.js for it, and distribute it without interruption. 

Prerequisites 

There are certain prerequisites to create CLI, so make sure you have them before you begin:

  • The latest version of Node.JS installed
  • A text editor

Recommended Read: How To Build CMS Using NodeJS

Let’s Set Up the Project Now

Nodejs Development Services

Here, we will be doing a setup of a basic Node.js project. Follow these steps for the same:

  1. Open Terminal 
  2. Create a folder for the project

~$mkdir termTranslate

  1. Navigate to it. 

~$cd termTranslate 

  1. Now, initialize a Node.js project in this newly created folder. 

~$npm init

  1. Fill in the prompt, and your project is all set up now. 

Now, Let’s Build the Basic CLI

Once the last step is done, and we have actually created the node project, it is time now to move to make the CLI. What you need to do is right here:

  1. Create a folder named ‘bin’ in the project’s root directory created in the step above. 
  2. Now, inside the bin folder, you need to create an index.js file that will act as the entry point of your new CLI. 
  3. In this step, you will have to open the package. json file to further modify the main part to bin/index.js. 
  4. Lastly, you will have to add another entity manually into the package.json file named bin. Set its key to tran and the value to ./bin/index.js. Here is what it must look like now:

"bin": {  

    "tran": "./bin/index.js"  

  }

So, the key, tran that we set in the step above, will act as the keyword for calling the CLI.  People will type this keyword in the terminal using the CLI. Besides, you can always choose the name for your choice; however, keeping it simple, short, and semantic is important to type and remember it easily. Also, you can change this name anytime you want. 

Know about the Growing Nodejs Development Trends here.

Now, after following all the steps, here is how your package.json file will look like:

{  

    "name": "termtran",  

    "version": "1.0.0",  

    "description": "A CLI to translate between languages in the terminal",  

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

    "scripts": {  

 "test": "echo "Error: no test specified" && exit 1"  

    },  

    "keywords": [  

 "cli"  

    ],  

    "bin": {  

 "tran": "./bin/index.js"  

    },  

    "author": "Your Name",  

    "license": "ISC"  

}

Now, open the index.js file in the bin folder and use the following code in it:

#! /usr/bin/env node

console.log("Hello World!");

In this code, the #! Is known as the shebang line, which specifies the absolute path to the interpreter, which will help run the code. 

After that, we will install the CLI and test it as well. For people to call CLI anywhere, we need to install it globally. Navigate to the root directory and then run the following code:

~$npm install -g 

The -g flag will signal npm to install it in the system globally, and CLI can be tested by typing a keyword in the terminal. 

~$tran

So, if you have been following everything correctly, you will see a message that we console.logged in the index.js file. 

Command Line Arguments Handling 

We have prepared our CLI, and we will add other functionalities in the same. Also, the basic task for any CLI is to handle the command-line arguments. So, we will receive the language name in our CLI and the sentence that we need to translate. After that, we will parse it. 

We will use an npm package ‘yargs’ which is specially made for CLI creation. It will simplify the entire process of parsing the arguments and will help us manage and organize the command line flags. 

  1. Start by installing yargs

~$npm i yargs

  1. Include the module in index.js

~$const yargs = require("yargs");

  1. Now we will create the options object that includes all your command line flags

const usage = "\nUsage: tran <lang_name> sentence to be translated";const options = yargs  

      .usage(usage)  

      .option("l", {alias:"languages", describe: "List all supported languages.", type: "boolean",

demandOption

: false })                                                                                                    

      .help(true)  

      .argv;

In the code given above, we defined an option -I which will print every supported languages when passed by the API. Yargs helps us with --version and --help flags by default. 

Nodejs Development Services

Now, We Will Add Utility Functions

~$tran lang_name the sentence to be translated

Now, we will need to parse the arguments. So, here’s what we need to do:

  1. Create another file named utils.js in bin folder
  2. Include this file in your index.js

const utils = require('./utils.js')

  1. Here, we create the function to parse the sentence. We will write the function in util.js before finally exporting it. 

module.exports = { parseSentence: parseSentence };function parseSentence(words) {  var sentence = ""; 

for(var i = 1; i < words.length; i++) {  

sentence = sentence + words[i] + " ";  

 }

Now, call it in index.js

var sentence = utils.parseSentence(yargs.argv._);

Read our other post on Nodejs Advantages.

  1. In this step, we will create a function to help when we don’t pass any argument. The function will  be created in utils.js. 

module.exports = { showHelp: showHelp, parseSentence: parseSentence };const usage = "\nUsage: tran <lang_name

> sentence to be translated";

function showHelp() {                                                            

    console.log(usage);  

    console.log('\nOptions:\r')  

    console.log('\t--version\t      ' + 'Show version number.' + '\t\t' + '[boolean]\r')  

    console.log('    -l, --languages\t' + '      ' + 'List all languages.' + '\t\t' + '[boolean]\r')  

    console.log('\t--help\t\t      ' + 'Show help.' + '\t\t\t' + '[boolean]\n')  

}

Now, call it in index.js

if(yargs.argv._[0] == null){  

    utils.showHelp();  

    return;  

}

  1. Once that is done, you will see all supported languages right on the screen. 

module.exports = { showAll: showAll, showHelp: showHelp, parseSentence: parseSentence};  

function showAll(){  

    console.log(chalk.magenta.bold("\nLanguage Name\t\tISO-639-1 Code\n"))  

    for(let [key, value] of languages) {  

 console.log(key + "\\t\\t" + value + "\\n")  

    }  

}

let languages = new Map();

languages.set('afrikaans',      'af')  

languages.set('albanian', 'sq')  

languages.set('amharic', 'am')  

languages.set('arabic',         'ar')  

languages.set('armenian', 'hy')  

languages.set('azerbaijani', 'az')  

languages.set('basque',         'eu')  

languages.set('belarusian', 'be')  

languages.set('bengali', 'bn')  

languages.set('bosnian', 'bs')  

languages.set('bulgarian', 'bg')  

languages.set('catalan', 'ca')  

languages.set('cebuano', 'ceb')   

languages.set('chinese',        'zh')   

languages.set('corsican', 'co')  

languages.set('croatian', 'hr')  

languages.set('czech',         'cs')  

languages.set('danish',         'da')  

languages.set('dutch',         'nl')  

languages.set('english', 'en')  

languages.set('esperanto', 'eo')  

languages.set('estonian', 'et')  

languages.set('finnish', 'fi')  

languages.set('french',         'fr')  

languages.set('frisian', 'fy')  

languages.set('galician', 'gl')  

languages.set('georgian', 'ka')  

languages.set('german',         'de')  

languages.set('greek',         'el')  

languages.set('gujarati', 'gu')  

languages.set('haitian creole', 'ht')  

languages.set('hausa',         'ha')  

languages.set('hawaiian', 'haw') // (iso-639-2)  

languages.set('hebrew',         'he') //or iw  

languages.set('hindi',         'hi')  

languages.set('hmong',         'hmn') //(iso-639-2)  

languages.set('hungarian', 'hu')  

languages.set('icelandic', 'is')  

languages.set('igbo',         'ig')  

languages.set('indonesian', 'id')  

languages.set('irish',         'ga')  

languages.set('italian', 'it')  

languages.set('japanese', 'ja')  

languages.set('javanese', 'jv')  

languages.set('kannada', 'kn')  

languages.set('kazakh',         'kk')  

languages.set('khmer',         'km')  

languages.set('kinyarwanda', 'rw')  

languages.set('korean',         'ko')  

languages.set('kurdish', 'ku')  

languages.set('kyrgyz',         'ky')  

languages.set('lao',         'lo')  

languages.set('latin',         'la')  

languages.set('latvian', 'lv')  

languages.set('lithuanian', 'lt')  

languages.set('luxembourgish', 'lb')  

languages.set('macedonian', 'mk')  

languages.set('malagasy', 'mg')  

languages.set('malay',         'ms')  

languages.set('malayalam', 'ml')  

languages.set('maltese', 'mt')  

languages.set('maori',         'mi')  

languages.set('marathi', 'mr')  

languages.set('mongolian', 'mn')  

languages.set('burmese', 'my')  

languages.set('nepali',         'ne')  

languages.set('norwegian', 'no')  

languages.set('nyanja',         'ny')  

languages.set('odia',         'or')  

languages.set('pashto',         'ps')  

languages.set('persian', 'fa')  

languages.set('polish',         'pl')  

languages.set('portuguese', 'pt')  

languages.set('punjabi', 'pa')  

languages.set('romanian', 'ro')  

languages.set('russian', 'ru')  

languages.set('samoan',         'sm')  

languages.set('scots',          'gd')//gd gaelic  

languages.set('serbian', 'sr')  

languages.set('sesotho', 'st')  

languages.set('shona',         'sn')  

languages.set('sindhi',         'sd')  

languages.set('sinhalese', 'si')  

languages.set('slovak',         'sk')  

languages.set('slovenian', 'sl')  

languages.set('somali',         'so')  

languages.set('spanish', 'es')  

languages.set('sundanese', 'su')  

languages.set('swahili', 'sw')  

languages.set('swedish', 'sv')  

languages.set('tagalog', 'tl')  

languages.set('tajik',         'tg')  

languages.set('tamil',         'ta')  

languages.set('tatar',         'tt')  

languages.set('telugu',         'te')  

languages.set('thai',         'th')  

languages.set('turkish', 'tr')  

languages.set('turkmen', 'tk')  

languages.set('ukrainian', 'uk')  

languages.set('urdu',         'ur')  

languages.set('uyghur',         'ug')  

languages.set('uzbek',         'uz')  

languages.set('vietnamese', 'vi')  

languages.set('welsh',         'cy')  

languages.set('xhosa',         'xh')  

languages.set('yiddish',        'yi')  

languages.set('yoruba',         'yo')  

languages.set('zulu',    'zu')

So, right there, we have created a hash map for all the languages to their code. There are two reasons behind doing that - one it will help in displaying all the languages as and when needed. And, secondly, we must know that API only takes language code into consideration, which means it will display the language even if a wrong language name is entered. 

Now we will have to call the showAll() function in your index.js file if the languages flag is true. 

if(yargs.argv.l == true || yargs.argv.languages == true){  

    utils.showAll();  

    return;  

}

  1. Now, we will write the function. 

module.exports = { parseLanguage: parseLanguage, showAll: showAll, showHelp: showHelp, parseSentence: parseSent

ence };

function parseLanguage (language) {                                                                    

    if(language.length == 2){  

 return language;  

    }  

    if(languages.has(language)){  

 return languages.get(language)  

    }  

    else {  

 console.error("Language not supported!")  

 return; //returning null if the language is unsupported.  

    }  

};

After that, we will convert the language to lower case before calling the function in index.js. 

if(yargs.argv._[0])  

var language = yargs.argv._[0].toLowerCase(); // stores the language.

//parsing the language specified to the ISO-639-1 code.                                                                                              

language = utils.parseLanguage(language);

  1. Include the API at top of your index.js and check if sentence is empty. If it not empty then we will send it to the API. 

const translate = require('[@vitalets/google-translate-api](http://twitter.com/vitalets/google-translate-api)')

;if(sentence == ""){                                                                                          

    console.error("\nThe entered sentence is like John Cena, I can't see it!\n")  

    console.log("Enter tran --help to get started.\n")  

    return;

}translate(sentence, {to: language}).then(res => {console.log("\n" + "\n" + res.text + "\n" + "\n";}).catch

(err => {                                                                                                     

     console.error(err);  

 });

We have completed the CLI now. Finally, if you want you can also use chalk and boxen to decorate or enhance the errors and output with colors and boxes. 

Conclusion 

Hope this guide helped you learn how to create CLI in Node.js. This was a fully portable CLI and you can easily prepare it using these steps mentioned above. So, go ahead and try it yourself now! Happy coding!! 

At Your Team In India, we have a pool of certified Node.js engineers. Need help setting up a dedicated team of developers in India? Connect with us our business head now and get a free consultation.

Mangesh Gothankar

Mangesh Gothankar

Seasoned technology professional with over 19 years of experience leading technology innovation and execution with startups and MNCs. Experience in hiring and creating multiple world-class technology teams. Results-oriented with a passion for technology, recognized for successfully planning and executing major initiatives.