preface

In our usual project development, there are often many similar code files, and we often copy and paste them when we use them. I’ve written an article about how to make development more efficient, but to be honest, it doesn’t work very well.

See today’s popular front-end frameworks, all have their own CLI tools, such as Vue CLI, creat-React-app, etc., it is very convenient to build projects. So I am also thinking, or to implement a CLI tool, not necessarily as lofty as the previous several, but as long as it can improve the work efficiency, it is worth a try.

Initialize the project

First, we need to open the CLI interface and initialize a project with NPM:

npm init
Copy the code

package.json

{
  "name": "mycli"."version": "1.0.0"."description": "A cli-demo"."main": "index.js"."author": "xmanlin"."license": "MIT"
}
Copy the code

Some of it has been removed.

Custom commands

I believe that many friends in the use of NPM initialization project, or in the Vue CLI construction project, they will find a common point — have their own personalized command, feel a bit cool. So how can we implement our own command? Simply add bin to package.json:

{
  "name": "mycli"."version": "1.0.0"."description": "A cli-demo"."main": "index.js"."bin": {
    "mycli": "./index.js"
  },
  "author": "xmanlin"."license": "MIT"
}

Copy the code

The purpose of bin in package.json is to make mycli an executable command, and the file to execute the command is the index.js file behind it, as you wish. Then we will continue to modify index.js:

index.js

#! /usr/bin/env node

console.log("Executed successfully")
Copy the code

The first line here is important, stating that index.js is the Node executable.

Once setup is complete, we can install our CLI tool globally:

npm install -g
Copy the code
+ [email protected]
added 1 package from 1 contributor in 0.12s
Copy the code

We can see that the global installation is successful. Finally, let’s try our command:

mycli
Copy the code

Output:

Execute successfullyCopy the code

Interactive command line

Just friends in the command line, use the command of different options, such as NPM init, NPM install – g, which contains some interactive, such as in the initial project, NPM init provide some input q&a form of command. Next, let’s add these similar commands to our OWN CLI tools.

We can rely on two libraries for our development: commander. Js and Inquirer

  • Commander.js: The complete Node.js command-line solution. We can use it to quickly write our command line and customize operations.
  • Inquirer. Js: a collection of general interactive command line user interfaces that provide Node.js with an easy-to-embed, beautiful command line interface. We can use it to quickly write interactive commands.

The specific usage of these two libraries, here is too much introduction, small friends can click on the above name link to familiarize with, it does not take too much time, the actual use is not difficult, the back there is no Chinese Readme, but it does not prevent you will search!

Define the options

Let’s first implement similar command options like NPM -v and node -v.

First install commander.js:

npm install commander
Copy the code

Then introduce commander. Js and modify index.js

#! /usr/bin/env node

const { program } = require('commander');

// A string split into arrays
function strToArr(value, preValue){
    return value.split(', ')}/ / cli version
program.version(require('./package').version, '-v, --version'.'Latest version of CLI');
// Set the options
program
    .option('-d, --debug'.'Debug it')
    .option('-l, --list <value>'.'Split string into array', strToArr)
    .action((options, command) = > {
        // perform logical processing
        if(options.debug) {
            console.log("Debug successful")}if(options.list ! = =undefined) {
            console.log(options.list)
        }
    });

// Process arguments entered on the command line
program.parse(process.argv);
Copy the code

Let’s try the command options we just set:

mycli -d
Copy the code

Output:

Debugging successCopy the code

Input:

Mycli -l 1, 2, 3Copy the code

Output:

['1', '2', '3']Copy the code

The –help option has been defined for us in commander. Js:

mycli -h
Copy the code

Output:

Usage: index [options] Options: -v, --version Latest CLI version -d, --debug Debug -l, --list <value> split string into array -h, --help display help for commandCopy the code

With the –help option, you can get a good idea of how many commands you already have in myCLI.

Set subcommand

In project development, we sometimes use commands like NPM run XXX, where run is the subcommand of NPM. Here we can also set a similar subcommand for mycli:

const { program } = require('commander'); .// Create file command line
program
    .command('create <filename>')
    .description('Create a file')
    .action((filename) = > {
        console.log(filename)
    })
    
...
// Process arguments entered on the command line
program.parse(process.argv);
Copy the code

Command (‘create

‘) creates a mycli create subcommand followed by a required parameter.

Input:

mycli create file
Copy the code

The output

file
Copy the code

The subcommand is successfully created.

Create project files using the command line

We can now define options and set commands. Now we can actually do something, using the command line to create a file for the project.

Simply design a flow:

Creating a template File

Let’s start by creating a templates folder, and then write a few common templates in it. Here’s a template file that uses template strings, structured like this:

reactClass.js

module.exports = function (className) {
    return `
import * as React from 'react';

export class ${className} extends React.Component{
    constructor(props){
        super(props);

        this.state = {}
    }

    componentDidMount(){

    }

    render() {
        return (
            <div></div>
        )
    }
}
    ` 
}
Copy the code

vueTemplate.js

module.exports = function () {
    return ` 
       
}
Copy the code

index.js

const reactClass = require('./reactClass');
const vueTemplate = require('./vueTemplate');

module.exports = [
    { name: 'reactClass'.src: reactClass },
    { name: 'vueTemplate'.src: vueTemplate }
]
Copy the code

Once the template file is created, let’s put it aside for later use.

Create interactive command lines and call templates

When we enter the mycli create file command, we want to get the image in the following image, we can manually select up and down, which can be called interactive command.

This uses another library we mentioned above, Inquirer. Js

You definitely need to install it first

npm install inquirer
Copy the code

Import and modify index.js in our root directory:

#! /usr/bin/env node

const { program } = require('commander');
const inquirer = require('inquirer');

// Import the template file
const templates = require('./templates/index');

// Command line selection list
let prompList = [
    {
        type:'list'.name: 'template'.message: 'Please select the template you want to generate? '.choices: templates,
        default: templates[0]}]...// Create file command line
program
    .command('create <filename>')
    .description('Create a file')
    .action(async (filename) => {
        const res = await inquirer.prompt(prompList)
        console.log(res)
    })

// Process arguments entered on the command line
program.parse(process.argv);
Copy the code

Next we will type on the command line:

mycli create file
Copy the code

You get the effect shown above

Then select the first one and press Enter:

You can see the output of the name of the template we selected. The next step is to create the actual file.

Creating project files

To create the file, call node.js’s Fs-related API and modify index.js:

// Process files
const fs = require("fs"); .// Create file command line
program
    .command('create <filename>')
    .description('Create a file')
    .action(async (filename) => {
        const res = await inquirer.prompt(prompList)
        if(res.template === 'reactClass') {
            templates.forEach((item) = > {
                if(item.name === 'reactClass') {
                    fs.writeFile(`. /${filename}.jsx`, item.src(filename), function(err) {
                        if(err) {
                            console.log('Failed to create:', err)
                        } else {
                            console.log('File created successfully!${filename}.jsx`); }})}})}if(res.template === 'vueTemplate') {
            templates.forEach((item) = > {
                if(item.name === 'vueTemplate') {
                    fs.writeFile(`. /${filename}.vue`, item.src(), function(err) {
                        if(err) {
                            console.log('Failed to create:', err)
                        } else {
                            console.log('File created successfully!${filename}`); }})}})}})...Copy the code

Again, type mycli Create File on the command line and select a template.

Output:

File created successfully! file.jsxCopy the code

At the same time, we can see that a file named file.jsx has been added to the project root directory:

If you open file.jsx, you can see that the class name of the file is also filled in accordingly. Not only that, but because our mycli is installed globally, we can say that anywhere on the computer, if we type mycli Create file, we can get the file file.jsx in the current directory. That means we don’t have to copy and paste, cut and change when we develop projects.

A single line of command will do it

More functions

If you can create files, can you create folders? The answer is yes, just keep adding commands:

.// Create folder command line
program
    .command('create-f <folder>')
    .description('Create a folder')
    .action((folder) = > {
        if(fs.existsSync(folder)) {
            console.log('Folder already exists')}else {
            fs.mkdirSync(folder);
            console.log('Folder created successfully')}}); .Copy the code

To create the desired folder, type mycli create-f XXX on the command line.

The last

By now our myCLI has been able to create files, create folders and so on. Follow along, there will be a harvest. Later, we can write template files according to the actual situation of the project, customize the command we want, and carry out necessary expansion. This is left to everyone to play ~

Please click here to vote for me. Thank you very much ~ 🙏🙏🙏

Interested partners can pay attention to my public number – front-end station, new articles will be the first time in the public number.