In daily front-end development, there are various KINDS of CLI, such as webpack for you with one command, VUe-CLI for you to generate vUE project template with one command, create-react-app for creating react project, and so on. These tools greatly facilitate our daily work, let the computer to do the tedious work, and we can save a lot of time for study, communication, development, visit steam.
However, there are some very special requirements that we cannot find suitable CLI tools to do. For example, if you have a large project and you want to add a new route to the project, you need to create a directory -> create a.vue file -> update the route list of vue-router. Even if you are familiar with the shortcut to create a directory file, it will not be faster than you can use a one-line command, especially if the route directory is deeply nested. .vue file initialization template complex.
So why not write a CLI for your own project? To do all the tedious work?
0x1 hello world
Nodejs CLI is basically a running node script, basically every front-end er will:
// index.js
console.log('hello world')
Copy the code
Then the command line call
> node index.js
## output:
> hello world
Copy the code
To make it more realistic, let’s add a script name to the scripts field in package.json:
{
"scripts": {"hello":"node index.js"}}Copy the code
Then the command line calls:
> npm run hello
Copy the code
However, you will definitely say that webpack and VUe-CLI are “named”! Vue -cli init app, webpack -p, NPM run hello, nodeindex. js, NPM run hello Bad review!!!!!
Hold your breath, gentlemen. Here’s how to name this Node script.
0 x2 name
For example, to name the cli hello-cli, we can type hello-cli on the command line, and it will print hello world, no node and no NPM.
- The top of the index.js file declares the execution environment:
// index.js #! /usr/bin/env node console.log('hello world') Copy the code
add
#! /usr/bin/env node
or#! /usr/bin/node
This tells the system that the following script is executed using NodeJS. Of course, this system does not include Windows, because Windows has a JScript legacy that will stop your scripts running.
#! /usr/bin/env node
Let the system find the node executable itself.
#! /usr/bin/node
Explicitly tells the system that node’s execution is in path/usr/bin/node
. - Add the bin field of package.json.
It can be executed in the current directory of index.jsnpm init
Create package.json and add a bin field to package.json:{ "name": "hello-test"."version": "1.0.0"."bin": {"hello-cli":"index.js"}}Copy the code
The bin field contains the name of the command line, i.e
hello-cli
, which tells NPM that the js script inside can be executed from the command line tohello-cli
Command call. Of course, you are free to write whatever you want on the command line name, such as: - In the current package.json directory, open a command line tool and execute
npm link
, leaving a shortcut for the current code in the NPM global directory.
NPM detects the presence of a bin field in package.json, which simultaneously generates an executable file in the global NPM package directory:
When we execute directly on the system command line
hello-cli
Is actually executing the script here.
Because when installation node, NPM will this directory configured for system variable environment, when you execute the command, the system will look for the system commands and system variables, and then to the variable environment to find the command name, and then find the directory, found that match the command of the executable file, then execute it directly. Vue-cli or webpack-CLI is done this way.
Now that your first CLI script has been successfully installed, you can type your CLI name directly from the command line and see the output.
In addition, if you only want your CLI script to be executed in your project, you need to create a new directory in your project and repeat the above operation, but in step 3, do not llink to the global directory, but use NPM I -d file:< your script CLI directory path >. Install it into node_modules as a dependency for your project. If it works, you’ll see an extra dependency in your project’s package.json. The value of this dependency is not the version number, but the path of your script. Then node_modules will have a.bin directory that will hold your executable files.
NPM I -d file: XXX is recommended for partial installation, so that it will leave a record in package.json for other users to see. Naturally, your scripts should also be placed in the project directory.
Of course, to install such a CLI script, you must declare the script command on the scripts field of the project’s package.json, and then execute it through NPM run.
Oh? NPM run Hello is the same as NPM run Hello.
Yes, but there are qualitative differences. Nodeindex. js is simple and flexible, but relies heavily on the script path. Whenever the directory structure changes, the commands written in scripts change once. However, with the NPM installation, the local CLI scripts are pulled into node_modules and are not affected by directory structure changes. If you want to publish your CLI script, it’s much better to have a nice and loud name than to have a documentation telling users how to find your script path and execute it using Node.
Here is an idea of the CLI development process:
- Initial development can pass
node index.js
Let’s see what happens. - I can pass the test
npm link
To test the installation. - release
0x3 Parameter read :process.argv
Now that we have a name, we have output, and we have a look at some of the most famous CLI tools. By the way, people can support different parameter options, and depending on the input, can produce different results.
Hello world: hello world: hello world: hello world: hello world
> hello-cli older
## output
> hello older
Copy the code
Although this function is simple, at least it can achieve the effect of “depending on the input, produce different results”.
Parameters on the command line can be obtained from the process variable, which is a global object rather than a package and does not need to be introduced by require. The process object provides information about the current script execution environment, including input from the command line, which is stored in the process.argv property. We can print it:
//index.js
console.log(process.argv);
Copy the code
Print result:
//index.js
console.log(`hello ${process.argv[2] | |'world'}`)
Copy the code
There are also some excellent command line argument parsing packages in the NPM community, such as Yargs, commander. Js for TJ, and so on
If you want to use more complex parameters or commands, it is recommended to use a third-party package, handwritten parsing is too expensive.
0 x4 child process
Now you are free to write your own CLI scripts. If you want to create a project that is automatically displayed on the git cli, or automatically pull the project startup template from the Git repository, you need to start the child process using node child_process.
//test.js
const child_process = require('child_process');
let subProcess=child_process.exec("git version".function(err,stdout){
if(err)console.log(err);
console.log(stdout);
subProcess.kill()
});
Copy the code
Not only git commands, but also system commands and other CLI commands can be executed here. Especially system commands, using system commands to operate on files and directories, efficiency is much higher than FS.
There are also some good bags on the community, such as shelljs recommended by Teacher Ruan Yifeng
0x5 Beautify output
If you don’t want your CLI to be “hardcore” and you want it to be more user-friendly, such as providing friendly input, hints, adding color to your output, writing simple progress bars, etc., then you need to beautify your output.
In addition to the color part, it is very complicated not to use a third-party package to implement, other functions, you can try to write their own. The colors section uses the third-party package colors, which is not demonstrated here. The rest is implemented by nodeJS’s own readline module.
//index.js
const readline = require('readline');
const unloadChar=The '-';
const loadedChar='=';
const rl=readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Who do you want to say hello to? ',answer=>{
let i = 0;
let time = setInterval((a)= >{
if(i>10){
clearInterval(time);
readline.cursorTo(process.stdout, 0.0);
readline.clearScreenDown(process.stdout);
console.log(`hello ${answer}`);
process.exit(0)
return
}
readline.cursorTo(process.stdout,0.1);
readline.clearScreenDown(process.stdout);
renderProgress('saying hello',i);
i++
},200);
});
function renderProgress(text,step){
const PERCENT = Math.round(step*10);
const COUNT = 2;
const unloadStr = new Array(COUNT*(10-step)).fill(unloadChar).join(' ');
const loadedStr = new Array(COUNT*(step)).fill(loadedChar).join(' ');
process.stdout.write(`${text}: 【${loadedStr}${unloadStr}|${PERCENT}%] `)}Copy the code
- First of all, through
readline.createInterface
Method to create ainterface
classThis class has a method underneath it.question
, using this method to raise a question on the command line and pass in a function on the second argument to listen. Once the user finishes typing and hits Enter, the callback function is triggered. - Then we write a timer inside the callback function and pretend we’re doing something.
- use
readline.cursorTo
This method changes the position of the cursor on the command line.
readline.cursorTo(process.stdout, 0, 0);
I’m going to go to column 1, row 1,
readline.cursorTo(process.stdout, 0, 1);
I’m going to go to column 1, row 2. - use
readline.clearScreenDown
In this method, the command line is cleared from the current line to the end of the last line. renderProgress
Is a method that encapsulates itself, throughprocess.stdout.write
Method outputs a string that looks like a progress bar to the command line.- So in the timer, when the count is less than 10, we move the cursor to the first line, and then clear all output, printing the progress bar string; When the count is greater than 10, we turn off the timer, clear the output, and print the result.
- Finally, don’t forget to close the process so it can be used
interface
This class of.close
Method to turn off the readline process, or directlyprocess.exit
To exit.
This is done in the same way as drawing animation on canvas, except that canvas clears the canvas, whereas on the command line readline.clearScreenDown clears the output.
This way, a simple, user-friendly command line CLI tool with a bit of progress bar animation is written, and you can use your imagination to write some more interesting effects.
After all, in our front end, we can animate with a browser, and we can animate without a browser.
0 x6 reference
- www.ruanyifeng.com/blog/2015/0…
- Nodejs. Cn/API/readlin…
- Github.com/Marak/color…
- Github.com/shelljs/she…