开发脚手架工具包

const { program } = require('commander')

program.version('1.0.0')

const { program } = require('commander')

program.version('1.0.0')

//创建新项目
program
  .command('create <app-name>')
  .description('创建一个新项目')
  .option('-t, --template <template-name>', '选择一个模板下载')
  .action((appName, options) => {
    console.log(appName, options.template)
  })

program.parse(process.argv)

// 执行命令 node index.js create my-app -t react

// 打印出: my-app react
const figlet = require('figlet')

figlet('Hello World!!', function (err, data) {
  if (err) {
    console.log('Something went wrong...')
    console.dir(err)
    return
  }
  console.log(data)
})

// 打印出:
 _   _      _ _         __        __         _     _ _
| | | | ___| | | ___    \ \      / /__  _ __| | __| | |
| |_| |/ _ \ | |/ _ \    \ \ /\ / / _ \| '__| |/ _` | |
|  _  |  __/ | | (_) |    \ V  V / (_) | |  | | (_| |_|
|_| |_|\___|_|_|\___/      \_/\_/ \___/|_|  |_|\__,_(_)

const download = require('download-git-repo')

download('github:vuejs/vue', 'test/tmp', function (err) {
  console.log(err ? 'Error' : 'Success')
})

// 打印出: Success
const ora = require('ora')
const spinner = ora('Loading unicorns').start()

setTimeout(() => {
  spinner.color = 'yellow'
  spinner.text = 'Loading rainbows'
}, 1000)

setTimeout(() => {
  spinner.stop()
}, 3000)

// 打印出: Loading unicorns
// 1s 后打印出: Loading rainbows
// 3s 后打印出: Loading rainbows
const chalk = require('chalk')
console.log(chalk.blue('Hello world!'))

// 打印出: Hello world!
const shell = require('shelljs')
// git 命令是否存在
if (!shell.which('git')) {
  shell.echo('Sorry, this script requires git')
  shell.exit(1)
}
const inquirer = require('inquirer')

const questions = [
  {
    type: 'input',
    name: 'name',
    message: '请输入项目名称',
  },
  {
    type: 'input',
    name: 'author',
    message: '请输入作者名称',
  },
  {
    type: 'list',
    name: 'template',
    message: '请选择模板',
    choices: ['react', 'vue'],
  },
]

inquirer.prompt(questions).then((answers) => {
  console.log(answers)
})

// 打印出:
// { name: 'my-app', author: 'col0ring', template: 'react' }

一个简单的脚手架

  • 新建目录

    
    mkdir my-cli && cd my-cli
    # 初始package.json
    npm init -y
    
  • 安装依赖

    npm i commander figlet download-git-repo ora chalk shelljs inquirer
    
  • package.json 文件如下:

    {
      "name": "xmllein-cli",
      "version": "1.0.3",
      "main": "index.js",
      "author": "lein",
      "license": "MIT",
      "bin": {
        "xmllein": "./bin/index.js"
      },
      "dependencies": {
        "chalk": "^4.1.2",
        "commander": "^11.0.0",
        "download-git-repo": "^3.0.2",
        "figlet": "^1.6.0",
        "inquirer": "^8.2.0",
        "ora": "^5.1.0",
        "shelljs": "^0.8.5"
      }
    }
    
  • 新建文件 入口文件

    mkdir bin && cd bin
    
    touch index.js
    
    • 目录如下:

      .
      ├── README.md
      ├── bin
      │   ├── index.js
      │   └── init.js
      ├── package.json
      └── yarn.lock
      
  • index.js 文件代码如下:

    #!/usr/bin/env node
    const params = require('commander')
    const { promisify } = require('util')
    const asyncFiglet = promisify(require('figlet'))
    const chalk = require('chalk')
    const inquirer = require('inquirer')
    const init = require('./init')
    const figlet = require('figlet')
    
    // 打印日志
    const log = (content) => {
      console.log(chalk.green(content))
    }
    
    // 版本
    params.version('1.0.0')
    // name 参数
    params.option('-n, --name <name>', 'output name')
    
    params.on('--help', function () {
      console.log(
        '\r\n' +
          figlet.textSync('xmllein-cli', {
            font: '3D-ASCII',
            horizontalLayout: 'default',
            verticalLayout: 'default',
            width: 120,
            whitespaceBreak: true,
          })
      )
    })
    
    // 打印 logo
    const printLogo = async () => {
      let data = await asyncFiglet('xmllein-cli')
      log(data)
    }
    
    // 创建项目命令
    params
      .command('create <projectName>')
      .description('创建项目')
      .action(async (projectName) => {
        // 打印 logo
        await printLogo()
        log('准备创建项目....')
        // 命令行交互
        let answer = await inquirer.prompt([
          {
            name: 'language',
            type: 'list',
            message: '请选择开发语言',
            choices: ['JavaScript', 'TypeScript'],
          },
        ])
    
        if (answer.language === 'JavaScript') {
          log('创建JavaScript项目')
          init(projectName)
        } else {
          log('创建TypeScript项目')
        }
      })
    
    // 解析参数
    params.parse(process.argv)
    
  • init.js 文件代码如下:

    #!/usr/bin/env node
    const { promisify } = require('util')
    const ora = require('ora')
    const download = promisify(require('download-git-repo'))
    const chalk = require('chalk')
    const shell = require('shelljs')
    const inquirer = require('inquirer')
    
    /**
     *  封装下载的方法
     * @param {string} path
     * @param {string} name
     */
    function downloadRepositorie(path, name) {
      return new Promise((resolve, reject) => {
        const spinner = ora('下载模板中....')
        spinner.start()
        download(path, name, { clone: true }, (err) => {
          if (err) {
            // 下载完成
            spinner.fail('下载失败')
            reject(err)
          }
    
          // 下载完成
          spinner.succeed('下载完成')
          resolve()
        })
      })
    }
    // 打印日志
    const log = (content) => {
      console.log(chalk.green(content))
    }
    
    module.exports = async (projectName) => {
      log(`创建项目:${projectName}`)
    
      // 如果目录存在,提示用户是否覆盖
      if (shell.test('-d', projectName)) {
        let answer = await inquirer.prompt([
          {
            name: 'cover',
            type: 'confirm',
            message: '目录已存在,是否覆盖?',
          },
        ])
    
        if (answer.cover) {
          // 删除目录
          shell.rm('-rf', projectName)
        } else {
          return
        }
      }
      // 下载模板
      await downloadRepositorie(
        'direct:http://github.com/xmllein/react18.x_mobile_template#main',
        projectName
      )
    
      // 安装依赖
      log(`
      下载完成,请执行下面命令启动任务:
      =================================
      cd ${projectName}
      yarn install or npm install
      yarn dev or npm run dev
      `)
    }
    
  • 本地执行命令

    node bin/index.js create test
    
  • 打包发布

    # 要通过nrm 切换到 npm 源
    npm login
    npm publish
    
    • 发布成功后,可以通过 npm i xmllein-cli -g 全局安装,然后执行 xmllein create test 创建项目

参考资料