Using vue-CLI as a prototype as a reference, we wrote a CLI by hand

specifications

  • Realize human-computer interaction and select configuration as required
  • Github template resources automatically download and automatically install
  • Implement dependency automatically start template repository after installation
  • Implement reduced-form routing: When A page A is added, the page A is automatically added in route configuration and TAB

Depend on the installation

$ yarn add commander inquirer download-git-repo ora handlebars figlet clear chalk open watch -D
Copy the code
  • Dependency description:
    • Commander Nodejs command configuration tool
    • Inquirer A set of commonly used interactive command-line user interfaces.
    • Download-git-repo A tool for downloading github files
    • Ora is an elegant terminal loader
    • Handlebars is a simple template language
    • Figlet tool for printing good-looking custom prompts in terminals
    • clearThe terminal screen clearing tool functions as Linux commandsclearAnd the Windowscls
    • Chalk is similar to the terminal’s brush tool and can give logs hints with different color patterns
    • Open Automatically opens the browser
    • Watch is used to listen for file changes

Initialize the

  • New folder:ddb
  • Go to the folder and usenpmInitialization:npm init -y
  • New related file:
    • new/bin/ddb.js
    • new/lib/init.js
    • new/lib/download.js
    • newlib/refresh.js
  • configurationpackage.json
"name": "ddb"."bin": {
    "ddb": "./bin/ddb.js"
  },
Copy the code
  • Binding soft connection:npm linkIf there is an error here, it could be"./bin/ddb"without.jsThe suffix

Now that the configuration is complete, it’s time to start writing code

Hello,World

So step one, let’s say Hello World

  • in/bin/ddb.jsThe code is as follows
#! /usr/bin/env node
'use strict';

const program = require('commander')

program
    .version('0.0.1')
    .parse(process.argv);
Copy the code
  • Enter the command DDB -v on the terminal to check whether the corresponding version number is displayed. If yes, it indicates that the previous configuration is successful

  • Write /lib/init.js as follows:

const figlet = promisify(require('figlet'))
const clear = require('clear')


module.exports = async name => {
    clear()
    
    figlet('Hello World!! '.function(err, data) {
        if (err) {
            console.log('Something went wrong... ');
            console.dir(err);
            return;
          }
          console.log(data)
      })
}
Copy the code
  • Modify the/bin/ddb.jscode
#! /usr/bin/env node
'use strict';


const program = require('commander')

program
    .version('0.0.1')
    .parse(process.argv);


program
  .command("init <name>")
  .description("init project")
  .action(require('.. /lib/init'))

  program.parse(process.argv)
Copy the code
  • Test, enter command in terminalddb init testWe can see that the terminal output is very largeHello, worldWith the words

ddb create

Next, we start writing the DDB create command

  • Before you start, you might want to read about inquirer, click I read
  • To prepare/lib/quesiton.jsfile
const question = [
  {
       name:'conf'./* key */
       type:'confirm'.Confirm * / / *
       message:'Do you want to create a new project? ' / * hint * /}, {name:'name'.message:'Please enter a project name? '.when: res= > Boolean(res.conf) /* Whether to perform */}, {name:'author'.message:'Please enter author? '.when: res= > Boolean(res.conf)
   },{
       type: 'list'./* Select box */
       message: 'Please select Public Administration status? '.name: 'state'.choices: ['mobx'.'redux']./ * option * /
       filter: function(val) {    Filter / * * /
         return val.toLowerCase()
       },
       when: res= > Boolean(res.conf)
   }
]

module.exports = question
Copy the code
  • write/bin/init.jsfile
#! /usr/bin/env node
'use strict';


const program = require('commander')
const inquirer = require('inquirer')
const chalk = require('chalk')
const question = require('.. /lib/question')

program
    .version('0.0.1')
    .parse(process.argv);

program
  .command("create <name>")
  .description("create a project")
  .action(() = > {
    chalk.green("-_ -... Welcome to use DDB-CLI to easily build vuE-CLI applications.")
    	// Human-computer interaction, see question file for details
		inquirer.prompt(question).then((answer) = > {
			if (answer.conf) {
        	    console.log('answer', answer);
			}
		})
  })

  program.parse(process.argv)
Copy the code
  • Execute commands to test:ddb create testaaThe next step is to enter the interactive selection configuration interface, and finally, all the configurations will be printed out

There are two ways to implement template files. One is to put the template in the CLI and copy it directly during installation to modify some configuration information. The second option is to put the template in a remote repository, download it remotely each time you create a project, and write some configuration information. Here, we take the second option

  • Next, we start writing the create method, which includes the following features

    • Download the selected template
    • Edit the template information in the file, as shown inpackage.json
    • Install dependencies for this template
    • Automatically open browser preview after installing dependencies
  • Download the selected template and compile the /lib/download.js file

module.exports.clone = async function (repo, desc) {
  const { promisify } = require('util')
  const download = promisify(require('download-git-repo')); 
  const ora = require('ora')
  const process = ora('Downloading...${repo}`)
  process.start()
  await download(repo, desc)
  process.succeed()
}
Copy the code
  • Modify the/lib/create.jsfile

const { clone: download } = require('./download')
const chalk = require('chalk')
const log = content= > console.log(chalk.green(content))

module.exports = async function (answer = {}) {
    
  if (!Object.keys(answer)) return

  const { name = 'test', author = 'Tom', template = 'vue-template'} = answer
  const templateMap = {
    'vue-template': 'github:su37josephxia/vue-template'.'react-template': 'github:https:NLRX-WJC/react-antd-admin-template'
  }
  const downloadUrl = templateMap[template]

  log('Create project:${name}`)
  await download(downloadUrl, name)
}

Copy the code
  • The next step is to install a dependency on the downloaded file template, which is still edited/lib/create.js

const { clone: download } = require('./download')
const chalk = require('chalk')

const log = content= > console.log(chalk.green(content))

module.exports = async function (answer = {}) {
  if (!Object.keys(answer)) return
  const { name = 'test', author = 'Tom', template = 'vue-template'} = answer
  const templateMap = {
    'vue-template': 'github:su37josephxia/vue-template'.'react-template': 'github:https:NLRX-WJC/react-antd-admin-template'
  }
  const downloadUrl = templateMap[template]

  log('Create project:${name}`)
  await download(downloadUrl, name)
  InstallDev(name)
}


// Start the child process for dependency installation
const spawn = async(... args) => {const { spawn } = require('child_process')
  return new Promise(resolve= > {
    constproc = spawn(... args) proc.stdout.pipe(process.stdout) proc.stderr.pipe(process.stderr) proc.on('close'.() = > {
      resolve()
    })
  })
}

const InstallDev = async (name) => {
  // Automatically install dependencies - use child processes to do so
  log('===== Installation dependency =====')
  await spawn('yarn'['install'] and {cwd: `. /${name}`})

  log('===== Installation completed ===== ===== Startup mode ===== CD${name}

  npm run serve

  or

  yarn serve
  
  
  `)
Copy the code
  • Test, executeddb create, we found that after downloading template, it automatically entered the folder for dependency installation
  • Next, we implement automatic open browser and start project, edit/lib/create.js
const open = require('open')

// ...
const openAndStart = async (name) => {
  // Automatically open the browser
  open('http://localhost:8080')

  / / start
  await spawn('yarn'['serve'] and {cwd: `. /${name}`})}Copy the code

Now that we have implemented the above requirements, let’s implement the routing contract configuration

The reduced-form route is configured

In the vUE development process, we have one operation that must be repeated, we can use the command to implement:

  • Add a page
  • Configuring Routing Information
  • Other pages add this new page

Let’s take a simple example

  • new/lib/refresh.js, the code is:
const fs = require('fs')
// For template compilation
const handlebars = require('handlebars')

module.exports = async() = > {// Get the list
  const list = fs.readdirSync('/src/views')
            .filter(v= >v ! = ='Home.vue')
            .map(item= > ({
              name: item.replace('.vue'.' ').toLowerCase(),
              file: item
            }))
  // Generate a route definition
  compile({list}, '/router.js'.'/template/router.js.hbs')

  // Generate a menu
  compile({list}, '/src/App.vue'.'/template/App.vue.hbs')

  /** * Template compilation *@param {*} Meta Data definition *@param {*} FilePath Target file *@param {*} TemplatePath template file */
  function compile(meta, filePath, templatePath) {
    if (fs.existsSync(templatePath)) {
      const content = fs.readFileSync(templatePath).toString()
      const result = handlebars.compile(content)(meta)
      fs.writeFileSync(filePath, result)
      console.log(`${filePath}Create success ')}}}Copy the code
  • in/lib/create.jsjoin
program
  .command("refresh")
  .description("refresh routers and menu")
  .action(require('.. /lib/refresh'))
Copy the code
  • Testing:

    • inTemplate file/SRC /views/Creating a DirectoryTest.vue, the content of
    <template>
      <div class="test">
        <h1>This is an Test page</h1>
      </div>
    </template>
    Copy the code
    • performddb refreshAfter, you can see the success prompt, viewTemplate file/SRC/Phones.jsTemplate file/SRC/app.vue, you can see the newTestAssociated routes and tabs
    • interfacelocalhost:8080You can also see the corresponding effect

At this point, the CLI is done by hand

conclusion

  • We mainly usecommanderDefine the nodeJS terminal command, and then useinquirerRealize interaction with command interface
  • Then use the beautification toolfigletWrite ASCII style welcome interface field, usechalkTools to beautify command prompts
  • Then use thepromisifyWrapping asynchronous operations returns a promise, useddownload-git-repoCode download
  • Use the built-in NodeJSchild_processChild process to perform installation dependency tasks, usingopenOpen up our template project
  • And, usingfsRead and write modules, combinedhandlebarsTool to achieve automatic configuration of files, so as to achieve the effect of reduced-form routing

Above, our handwritten CLI learning comes to an end, more information, please read the following resources, write more practice, you will be able to write a better use of the CLI ~

Code: DDB – cli

References:

  • How-to-build-a-cli-with-node-js recommended!!
  • command-line-app-with-nodejs
  • Node.js command-line program development tutorial – Ruan Yifeng
  • How to develop a Node.js command-line (CLI) tool recommendation from scratch
  • Create CLI recommendations using Node.js
  • Open the class teacher Xia – CLI code information
  • Develop a Node command-line tool from scratch
  • Write command line tools -JavaScript,NodeJs
  • Customize CLI tools
  • This is a combination of crawler and Node command line tools, awesome!
  • Video tutorial – Complete a CLI tool with Node from 0 to 1
  • terminal-node-todo