When we use VUE to develop projects, we usually generate project templates directly from the command line, eg:

$ yarn create vite-app <project-name>
$ cd <project-name>
$ yarn
$ yarn dev
Copy the code
$ yarn create vite-app <project-name>

/ / is equivalent to
$ yarn global add create-vite-app
$ create-vite-app <project-name>
Copy the code

What if we wanted to create a CLI tool to manage our own project templates? Write a simple CLI tool here to pull defined templates from the command line.


At the beginning of construction projects

Create a directory and initialize it:

mkdir test-cli // Create directory

cd test-cli // Enter the directory
 
npm init -y // Initialize NPM
Copy the code

Then install any NPM packages you need:

npm install commander download-git-repo ora handlebars figlet clear chalk -s
Copy the code

Register global commands

We need to register a global command on the system environment.

New bin/index.js file:

mkdir bin
cd bin
touch index.js

// bin/index.js
#!/usr/bin/env node
// Specify the file interpreter
// Tell the operating system to call the node interpreter in the system environment when executing this file

console.log('test 111 ... ')
Copy the code

Add bin command to package.json:

{
  "bin": {
    "tcli": "./bin/index.js"
  }
  / /...
}
Copy the code

/bin/index.js file./bin/index.js file./bin/index.js file

NPM link is a temporary global installation of the local package. It is equivalent to NPM install -g XXX, which causes global pollution and may cause global installation failure. Administrator permission is required.

Create node command

#! /usr/bin/env node
// Introduce a custom command component
const program = require('commander')

// Set the version number
program.version(require('.. /package').version)

// Set the init command with name as an argument
program.command('init <name>')
      .description('init project') // Sets the description of the init command
      .option('-s, --session'.'session param') // Set parameters
      .action((name, params) = > { // Execute logic
        console.log('params => ', name)
        console.log('params.session => ', params.session)
      })

// Command execution is actually initiated by commander parsing the process.argv argument
program.parse(process.argv);
Copy the code

Download the template

Modify the program.action method:

program.command('init <name>')
    .description('init project')
    .option('-s, --session'.'session param')
    .action(require('.. /lib/init'))
Copy the code

New lib/init. Js

// lib/init.js
const { promisify } = require('util') // Convert the function to a promise format
const figlet = promisify(require('figlet')) / / font package

const clear = require('clear') // Console clear screen
const chalk = require('chalk') // Modify the style of strings in the console
const log = ctx= > console.log(chalk.green(ctx)) // Encapsulate log output

module.exports = async name => {
  clear()
  const data = await figlet('Hello ' + name)
  log(data)
}
Copy the code

Create a new lib/download.js file to handle the template download logic:

// lib/download.js
const { promisify } = require('util')

/** * repo download path * desc download destination path */
module.exports.clone = async function(repo, desc) {
  const download = promisify(require('download-git-repo')) // Download the component
  const ora = require('ora')
  const process = ora(` ⏳ download...${repo}`) // Display the loading effect, similar to the loading effect of a page
  process.start()
  await download(repo, desc)
  process.succeed()
}
Copy the code

Modify lib/init.js, encapsulate spawn command, add download template in init initialization, install dependency

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

// Encapsulate the spawn command
// The child output stream imports the main output stream
const spawn = async(... args) => {const { spawn } = require('child_process')

  const options = args[args.length - 1]
  // If the operating system is Windows, change the shell to true
  if (process.platform === 'win32') {
    // Set the shell to true to implicitly call CMD
    options.shell = true
  }

  return new Promise(resolve= > {
    constproc = spawn(... args)// proc(child) -> process(master)
    proc.stdout.pipe(process.stdout) // Standard output
    proc.stderr.pipe(process.stderr) // Standard error output
    proc.on('close', resolve)
  })
}

module.exports = async name => {
  // Print the welcome page
  clear()
  const data = await figlet('Hello ' + name)
  log(data)

  log('🚀 Create project ' + name)
  await clone('github:554246839/project-template', name)

  // Install dependencies
  log('💣 Installation depends on...... ')
  await spawn('yarn'['install'] and {cwd: `. /${name}` })
  log('👌 Installation completed To get start ====================== CD${name}
      yarn dev
    ======================
  `)}Copy the code

I’m almost done here, so let’s see what it looks like.

Create a new directory somewhere else and perform download install:

If you want to download different templates, there are two ways:

Inquirer components

Inquirer is a tool for users to interact with the command line. New lib/confirm. Js:

// lib/confirm.js
const inquirer = require("inquirer")

module.exports.prompt = () = > {
  return inquirer.prompt([
    {
      type: 'confirm'.name: 'simple'.message: 'use simple'.default: true})}// lib/init.js
/ / new
const { prompt } = require('./confirm')

/ / modify
module.exports = async (name, params) => {
  // Print the welcome page
  clear()
  const data = await figlet('Hello ' + name)
  log(data)

  log('🚀 Create project ' + name)
  const ans = await prompt()
  console.log(ans, 'ans')
  await clone('github:554246839/project-template', name)
  // xxx
Copy the code

The options parameter

Create template.js template management file:

// lib/template.js
module.exports.templates = {
  project1: 'github:554246839/project-template'.project2: 'github:554246839/project-template'.project3: 'github:554246839/project-template'
}

// lib/init.js
/ / new
const { templates } = require('./template')

function getParam(params) {
  let p = Object.keys(params)
  for (let i = 0; i < p.length; i++) {
    if (params[p[i]]) {
      return p[i]
    }
  }
}
/ / modify
module.exports = async (name, params) => {
  // Print the welcome page
  clear()
  const data = await figlet('Hello ' + name)
  log(data)

  log('🚀 Create project ' + name)
  await clone(templates[getParam(params) || 'project1'], name) / / modify

  // Install dependencies
  log('💣 Installation depends on...... ')
  await spawn('yarn'['install'] and {cwd: `. /${name}`})
  log('👌 Installation completed To get start ====================== CD${name}
      yarn dev
    ======================
  `)}Copy the code

GitHub:github.com/554246839/t… The master branch