We often use scaffolding tools to quickly prototype projects, such as VUe-CLI of Vue and create-react-app of React. But because these scaffolds are generic, it is necessary to re-wrap them, such as configuring CSS, configuring network request AXIos, configuring UI libraries, and so on. Since this is the case, it is necessary to customize their own scaffolding according to their own needs.

Effect of the project

Project core dependencies

  • Commander Command line tool
  • Download -git-repo Git repository code download
  • The chalk command line output is styled
  • Inquirer. Js command line interaction
  • Ora command line loading effect
  • Cross-spawn executes the dependency installation command

Please go to github repository to check the usage of related dependencies. We will only use the most basic usage, and take a look to know how to use it.

The project structure

├─ ├─ WWW // Exercises ├─ WWW // Exercises ├─ / / generated file └ ─ ─ the SRC ├ ─ ─ index. The ts / / the main entry documents ├ ─ ─ init. Ts / / scaffold initialization └ ─ ─ utils ├ ─ ─ the ts / / define constants ├ ─ ─ the ts / / install depend on ├ ─ ─ └─ downloadgitrepo.ts // Download ├─ downloadgitrepo.d.ts // Type declaration ├─.babelrc // Babel Config file ├─ tsconfig.json //typescript config file ├ ─ └─ package.jsonCopy the code

Initialize the project

Create an empty project (gong-CLI) and initialize it with NPM init-y.

Gong – the cli command

Node.js has built-in support for command-line operations, and the bin field in package.json defines command names and associated execution files. Add the bin field to package.json

package.json

{
    "name": "gong-cli"."version": "1.0.0"."description": "a simple cli"."main": "dist/index.js"."files": [// Upload file to NPM"bin"."dist/**/*.js"]."bin": {
      "gong-cli": "./bin/www"
    },
    "scripts": {
      "compile": "babel src -d dist --extensions '.ts' "."watch": "npm run compile -- --watch"}},Copy the code

www

#! /usr/bin/env node
require('.. /dist/index.js'); // Use Babel to compile ts to js and run itCopy the code

Run NPM link in the root directory of the current Gong-CLI to link the gong-cli command to the global environment. This allows gong-CLI related commands to be run in a development environment.

Entrance to the file

./src/index.ts

import commander from 'commander'
import { VERSION } from './utils/constants'
const program = new commander.Command()

program
  .command('init <projectName>')
  .description('gong-cli init')
  .action(() => {
    require('./init') (... process.argv.slice(3)) }) program.version(VERSION,'-v --version')

program.parse(process.argv)
Copy the code

In the entry file we define an init command, where projectName is entered by the user and cannot be empty; Such as:

gong-cli init demo
Copy the code

When the user enters this command, the initialization function init starts to load, passing in the string demo.

Template initialization

./src/init.ts

import { download } from './utils/downloadGitRepo'
import { install } from './utils/install'
import { promisify } from 'util'
import ora from 'ora'
import inquirer from 'inquirer'
import fs from 'fs'
import chalk from 'chalk'
import path from 'path'const exist = promisify(fs.stat) const init = async (projectName: String) => {const projectExist = await exist(projectName). Catch (err => {// Handle any error other than the file already existsif(err.code ! = ='ENOENT') {console.log(talk.redbright.bold (err))}}) // The file already existsif (projectExist) {
    console.log(chalk.redBright.bold('The file has exited! '))
    return} // Receive user command inquirer. Prompt ([{name:'description',
        message: 'Please enter the project description',
      },
      {
        name: 'author',
        message: 'Please enter the project author'}, {type: 'list',
        name: 'language',
        message: 'select the develop language',
        choices: ['javaScript'.'typeScript'],}, {type: 'list',
        name: 'package',
        message: 'select the package management',
        choices: ['npm'.'yarn'],},]). Then (async answer => {// Download template configuration informationlet loading = ora('downloading template... ')
      loading.start()
      loading.color = 'yellow'
      download(projectName, answer.language).then(
        async () => {
          loading.succeed()
          const fileName = `${projectName}/package.json`
          if (await exist(fileName)) {
            const data = fs.readFileSync(fileName).toString()
            let json = JSON.parse(data)
            json.name = projectName
            json.author = answer.author
            json.description = answer.description
            fs.writeFileSync(
              fileName,
              JSON.stringify(json, null, '\t'),
              'utf-8',
            )
            console.log(chalk.green('Project initialization finished! '))
            console.log()
            console.log(chalk.yellowBright('start install dependencies... '{CWD: path.join(process.cwd(), projectName), package: answer.package, }).then(() => { console.log() console.log('We suggest that you begin by typing:')
              console.log()
              console.log(chalk.cyan(' cd'), projectName)
              console.log(`  ${chalk.cyan(`${answer.package} start`)}`)
            })
          }
        },
        () => {
          loading.fail()
        },
      )
    })
}

module.exports = init

Copy the code

After entering the init initialization function, first check whether there is a folder with the same name as demo, if there is an error to ask the user to re-enter; Then, inquirer is used to obtain the information entered by the user in the terminal. Here, we only obtain the four fields of user-defined project description, project author, project development language and project package management. Use download to download the template. After downloading the template, use install to install dependencies. That’s the end of the whole process. It’s a little bit easier.

Pull the remote template

./src/utils/downloadGitRepo.ts

import downloadGit from 'download-git-repo'

exportConst download = async (projectName: string, language: string) => {//let api = 'microsoft/'
  language === 'javaScript'
    ? (api = api + 'vscode-react-sample')
    : (api = api + 'TypeScript-React-Starter')
  return new Promise((resolve, reject) => {
    downloadGit(api, projectName, (err: any) => {
      if (err) {
        reject(err)
      }
      resolve()
    })
  })
}

Copy the code

Install dependencies

./src/utils/install.ts

const spawn = require('cross-spawn') interface installProps {CWD: string // Project path Package: string // package manager yarn or NPM}export const install = async (options: installProps) => {
  const cwd = options.cwd
  return new Promise((resolve, reject) => {
    const command = options.package
    const args = ['install'.'--save'.'--save-exact'.'--loglevel'.'error']
    const child = spawn(command, args, {
      cwd,
      stdio: ['pipe', process.stdout, process.stderr],
    })

    child.once('close', (code: number) => {
      if(code ! == 0) { reject({command: `${command} ${args.join(' ')}`,})return
      }
      resolve()
    })
    child.once('error', reject)
  })
}

Copy the code

Start the project

yarn watch

Package and publish to NPM

Run the yarn compile command. The generated file is stored in the dist folder. Then run NPM adduser to log in to your NPM account, and finally run NPM publish to publish the contents of bin and dist folders to NPM. Other users can install scaffolding using the gong-cli command after NPM install gong-cli -g global installation.

Full source code address