Developing a node Command Line Tool from scratch

  javascript

What is a command line tool?

Command line tools (Cmmand Line Interface) are simply called cli. As the name implies, they are tools used in command line terminals. We often usegitnpmvimAll of these are cli tools. For example, we can use thegit cloneSuch as command simply copy the remote code to the local.

Why use cli tools?

Compared with cli, graphical user interface (gui) is almost all gui tools in windows environment and cli tools in linux environment, because the two users are different, gui focuses on ease of use and cli focuses on efficiency. For programmers familiar with gui and integrated development environment (IDE), this seems difficult to understand. After all, isn’t it more convenient to drag with the mouse?

Unfortunately, the answer is no. Gui may be faster and more convenient for some simple operations. Such as moving files, reading mail, or writing word documents. But if you rely on the gui to do all the work, you will miss some of the capabilities of the environment, such as automating common tasks or using all the features of various tools. Also, you cannot combine tools to create custom macro tools. The advantage of gui isWhat you see is what you get.. The disadvantage isWhat you see is all you get..

As a pragmatic programmer, you constantly want to perform special operations (operations that gui may not support). When you want to quickly combine some commands to complete a query or some other task, cli is more appropriate. For example, check which js files were unchanged last week:

# cli:
 find . -name '*.js' -mtime +7 -print
 # gui:
 1. Click and go to "find file", click on the "file name" field, type "*.js", select the "modify date" tab;
 2. Then select "Between". Click "Start Date" and type in the project start date.
 3. Click "End Date", type the date 1 week ago (make sure you have a calendar at hand), and click "Start Search";

How to develop a cli tool?

Basically, cli tools can be developed using any mature language. As a front-end white, JavaScript is more convenient, so we choose node as the development language.

Create a project

# 1. Create a directory:
mkdir kid-cli && cd kid-cli
# 2. Because we will eventually release cli to npm, we need to initialize a package:
npm init
# 3. Create an index.js file
touch index.js
# 4. open editor (vscode)
code  .

InstallationcodeCommand, runVS codeAnd opens the command panel (⇧⌘P), and then entershell commandFind:Install 'code' command in PATHJust do it.

Open the index.js file and add a test code:

#!  /usr/bin/env node

console.log('hello world!  ’)

When the terminal runs the node program, it needs to input the node command first, for example

node index.js

Can be output correctlyhello world!The at the top of the code#! /usr/bin/env nodeIs to tell the terminal, this file to use node to execute.

Create a command

Generally, cli has a specific command, such asgit, just usedcodeWait, we also need to set a command, just callkid! How can the terminal recognize this command? Simply open the package.json file and add a fieldbinAnd declare a command key and the corresponding file to execute:

# package.json
 ......
 "bin": {
 "kid": "index.js"
 },
 ......

If you want to declare multiple commands, modify this field.

Then let’s test it and enter it in the terminalkid, will prompt:

zsh: command not found: kid

Why is this so? Recall that usually when we use a cli tool, we need to install it first, such as vue-cli. before using it, we need to install it globally:

npm i vue-cli -g

However, our kid-cli has not been released to npm, and of course it has not been installed, so the terminal does not know this command yet. Usually we want to test an npm package locally, we can use:npm linkThis command, install this package locally, let’s execute it:

npm link

Then execute it again.

kid

Command, see correct outputhello world!Here we go.

At this point, a simple command-line tool has been completed, but this tool is useless. Don’t worry, let’s enhance its function bit by bit.

View version information

The first is to view the version information of cli. I hope to view the version information through the following command:

kid -v

There are two problems here.

  1. How to get-vThis parameter?
  2. How do I get version information?

In the node program, throughprocess.argvYou can get the parameters of the command, return it as an array, modify index.js, and output this array:

console.log(process.argv)

Then enter any command, such as:

kid -v -h -lalala

The console will output

[ '/Users/shaolong/.nvm/versions/node/v8.9.0/bin/node',
'/Users/shaolong/.nvm/versions/node/v8.9.0/bin/kid',
'-v',
'-h',
'-lalala' ]

The third parameter of this array is what we want-v.

The second problem is that the version information is usually placed in the version field of the package.json file. it is good to have require come in. the modified index.js code is as follows:

#!  /usr/bin/env node
 const pkg = require('./package.json')
 const command = process.argv[2]
 
 switch (command) {
 case '-v':
 console.log(pkg.version)
 break
 default:
 break
 }

Then we execute kid -v again, and we can output the version number.

Initialize an item

Next, let’s implement one of the most common functions, using cli to initialize a project.

The whole process is like this:

  1. cdGo to a directory where you want to create new projects.
  2. carry outkid initCommand, according to the prompt to enter the name of the project;
  3. Cli pulls the template project code through git and copies it to the directory where the project name is located.

In order to realize this process, we need to solve the following problems:

Execute complex commands

In the above example, we obtained the parameters of the command through process.argv, but when a command has multiple parameters, or a command like a new project that requires the user to enter the name of the project (we call it “question and answer”), a simpleswith caseIt seems that we are short of money. Here we refer to a package that specifically handles command line interactions:commander.

npm i commander --save

Then transform index.js

#!  /usr/bin/env node
 
 const program = require('commander')
 
 program.version(require('./package.json').version)
 program.parse(process.argv)

run

kid -h

Will output

Usage: kid [options] [command]
 
 Options:
 
 -V, --version  output the version number
 -h, --help     output usage information

Commander has already created the help information for us and two parameters-VAnd-hProgram.version in the above code returns the version number, which is consistent with the previous function. program.parse passes command parameters into the commander pipeline and is generally executed last.

Add question and answer operation

Next we addkid initQ&A operations, there is a need to introduce a new package:inquirerThis package allows cli to support question-and-answer interaction through simple configuration.

npm i inquirer --save

index.js:

#!  /usr/bin/env node

const program = require('commander')
var inquirer = require('inquirer')

const initAction = () => {
inquirer.prompt([{
type: 'input',
Message:' Please enter project name:',
name: 'name'
}]).then(answers => {
Log ('project name:', answers.name)
Log ('Copying items, please wait')
})
}

program.version(require('./package.json').version)

program
.command('init')
. description ('create project')
.action(initAction)

program.parse(process.argv)

Com mand can define program.command, description adds a description, in--helpAs shown in, action specifies a callback function to execute the command. Inquirer.prompt can receive a group of question and answer objects. the type field indicates the type of question and answer. name specifies the key of the answer, and can get the user’s input through name in answers. there are many types of question and answer. here we use input to let the user input the name of the item.

Run kid init and you will be prompted to enter the project name, which will be printed.

Run shell script

Students familiar with git and linux can initiate a project in a few words:

git clone xxxxx.git --depth=1
mv xxxxx my-project
rm -rf ./my-project/.git
cd my-project
npm i

So how do you execute shell scripts in node? Only installation is requiredshelljsThis bag can be easily handled.

npm i shelljs --save

Suppose we want to clone the code of vue-admin-template on github, install dependencies automatically, transform index.js, and add the logic of executing shell scripts to the initAction function:

#!  /usr/bin/env node

const program = require('commander')
const inquirer = require('inquirer')
const shell = require('shelljs')

const initAction = () => {
inquirer.prompt([{
type: 'input',
Message:' Please enter project name:',
name: 'name'
}]).then(answers => {
Log ('project name:', answers.name)
Log ('Copying items, please wait')

const remote = 'https://github.com/PanJiaChen/vue-admin-template.git'
const curName = 'vue-admin-template'
const tarName = answers.name

shell.exec(`
git clone ${remote} --depth=1
mv ${curName} ${tarName}
rm -rf ./${tarName}/.git
cd ${tarName}
npm i
`, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`)
return
}
console.log(`${stdout}`)
console.log(`${stderr}`)
});
})
}

program.version(require('./package.json').version)

program
.command('init')
. description ('create project')
.action(initAction)

program.parse(process.argv)

Shell.exec can help us execute a script, and the callback function can output the result of script execution.

Test our initialization function:

cd   ..
 kid init
 # Enter a project name

As you can see, cli has automatically pulled vue-admin-template code from github, placed it in the specified directory, and helped us install dependencies automatically.

The end

Finally, don’t forget to release your cli tools to npm for more students to use.

npm publish

How about that? Does it feel that the seemingly mysterious command line development actually has no technical content? The above list is just the tip of the iceberg of cli development. In order to develop powerful cli tools, you need to be familiar with node and common toolkits, and more importantly, understand linux common commands and file systems. I hope that all students can be inspired to develop their own cli tools.

Amway time

There are many technical points at the front end, including abstract and obscure knowledge points, which cannot be expressed intuitively in words. Therefore, many developers have a correct understanding of these knowledge points. If we show them through pictures, it will be easy to understand. Therefore, the Diagram project hopes that developers can understand the knowledge points in the front-end technology field through this way.
We will update regularly every week, updating 5 technical diagrams or mind maps in each issue.
Review these diagrams every week. I believe that before long, you can become a great god at the front!
Project github address:https://github.com/Tnfe/TNFE-Diagram