1.0.0 • Published 4 years ago

txm-cli-demo v1.0.0

Weekly downloads
1
License
MIT
Repository
-
Last release
4 years ago

脚手架实现过程

初始化

初始化一个项目目录并初始化package.json

mkdir txm-cli
cd txm-cli
yarn init -y

新建bin目录,在bin目录下创建入口文件cli.js

cli应该必须要在入口文件声明

#!/usr/bin/env node

如果是 Linux 或者 macOS 系统下还需要修改此文件的读写权限为 755 chmod 755 cli.js

我们现在cli.js文件中输出一段话

#!/usr/bin/env node
console.log('cli init')

然后配置package.json文件中的bin字段

{
  "name": "txm-cli",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "bin": {
    "txm": "bin/cli.js"
  }
}

键代表我们要执行的cli命令,值是入口文件的位置

执行yarn link将命令连接到全局

执行bin中配置的命令测试,例如:

txm

输出如下:

cli init

说明我们的基本配置就完成了,接下来就是完成具体的功能了

命令行参数设计

txm-cli -h|--help 查看使用帮助
txm-cli -v|--version 查看版本号
txm-cli list 列出所有可用模板
txm-cli init <template name> <project name> 基于指定的模板进行项目初始化

使用 commander 模块处理命令行

我们要做的就是

  1. 获取用户输入的参数
  2. 根据不同的参数生成不同的模板

nodejs中获取用户输入的命令可以使用process.argv来获取,它拿到的是一个数组,然后数组里面的值是用户在命令行输入的参数,如果我们自己来判断这个参数还是比较麻烦的,所以这里借助 commander这个模块来帮助我们处理参数

首先去安装commander

yarn add commander

commander的使用也是比较简单的,使用教程

下面我们就来使用一下这个包

#!/usr/bin/env node

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

program
  .version('0.1.0')

program.parse(process.argv);

然后我们在命令行中输入

txm -V

输出结果:

0.1.0

这就说明这个包可以正常使用了,接下来就来配置一下我们自己的命令

因为help命令它自己就有,所以我们不用配置,我们先来配置一下init命令

// 配置init命令
program
  .command('init <template> <project>') // 尖括号代表必输项,第一个参数是模板名称,第二个参数是项目名称
  .description('init project...') // 执行命令时的描述信息
  .action(function (templateName, projectName) { // 接收到用户输入的参数后要做的事情
    // 根据模板名称下载对应的模板到本地并起名为 projectName
    console.log(templateName, projectName)
  })

执行一下

txm init webpack my-project

输出结果:

webpack my-project

init命令配置好了之后我们先去配置一下list命令,具体的实现我们等下在去做

// 配置 list 命令
program
  .command('list')
  .description('list all templates')
  .action(() => {
    console.log(`sample     sample模板`)
    console.log(`webpack    webpack模板`)
    console.log(`vue        vue模板`)
    console.log(`react      react模板`)
  })

我们简单的配置一下,具体的一会儿再来实现

执行

txm list

输出结果:

sample     sample模板
webpack    webpack模板
vue        vue模板
react      react模板

到这里我们的命令就配置好了

准备模板

github上创建三个仓库,一个仓库里面放vue的基础模板,一个仓库放react的基础模板,一个就准备一个简单的项目结构,然后我们修改一下我们的list命令

三个模板仓库的地址

https://github.com/tangxinming0310/txm-vue-tempalte
https://github.com/tangxinming0310/txm-react-template
https://github.com/tangxinming0310/txm-sample-tempalte

我们在cli.js中去定义我们的模板仓库

const templates = {
  'sample': {
    url: 'https://github.com/tangxinming0310/txm-sample-template',
    desc: 'sample-template'
  },
  'react': {
    url: 'https://github.com/tangxinming0310/txm-react-template',
    desc: 'react-template'
  },
  'vue': {
    url: 'https://github.com/tangxinming0310/txm-vue-tempalte',
    desc: 'vue-template'
  }
}

然后在list命令中去修改一下

// 配置 list 命令
program
  .command('list')
  .description('list all templates')
  .action(() => {
    Object.keys(templates).forEach(key => {
      console.log(`${key}      ${templates[key].desc}`)
    })
  })

执行:

txm list

输出结果:

sample      sample-template
react      react-template
vue      vue-template

下载模板

模板准好了之后,我们就需要根据用户的输入然后去获取模板地址并下载对应的模板

下载模板我们需要借助一个 download-git-repo的库

先安装这个库

yarn add download-git-repo

使用方式:

// 第一参数是下载地址 规则为 => [github]:[账户名]/[仓库名]
// 第二参数是项目的名称
download(downloadUrl, projectName, { clone: true }, (err) => {
    if (err) {
        console.log(`download ${templateName} template  failed`)
    } else {
        console.log('download success')
    }
})

所以这里我们对templates添加一个下载地址

const templates = {
  'sample': {
    url: 'https://github.com/tangxinming0310/txm-sample-template',
    downloadUrl: 'https://github.com:tangxinming0310/txm-sample-template',
    desc: 'sample-template'
  },
  'react': {
    url: 'https://github.com/tangxinming0310/txm-react-template',
    downloadUrl: 'https://github.com:tangxinming0310/txm-react-template',
    desc: 'react-template'
  },
  'vue': {
    url: 'https://github.com/tangxinming0310/txm-vue-tempalte',
    downloadUrl: 'https://github.com:tangxinming0310/txm-vue-tempalte',
    desc: 'vue-template'
  }
}

然后去修改我们的init命令

// 配置 init 命令
program
  .command('init <template> <project>') // 尖括号代表必输项,第一个参数是模板名称,第二个参数是项目名称
  .description('init project...') // 执行命令时的描述信息
  .action(function (templateName, projectName) { // 接收到用户输入的参数后要做的事情
    // 根据模板名称下载对应的模板到本地并起名为 projectName
    // 根据用户输入获取对应的模板地址
    const { downloadUrl } = templates[templateName]
    // 下载模板
    download(downloadUrl, projectName, { clone: true }, (err) => {
      if (err) {
        console.log(`download ${templateName} template  failed`)
      } else {
        console.log('download success')
      }
    })
  })

在命令行执行:

txm vue vue-demo

结果:可以看到在当前目录下生成了一个 vue-demo 的文件夹,里面就是我们仓库里面的模板文件

命令行交互

模板下载好了之后我们在增加一下命令行交互来增强用户体验,比如说package.json文件中的一些信息我们可以让用户自己来填写,例如author,name

这里我们就需要使用到模板引擎了

我们先对仓库中的package.json文件做一些改写,我们把nameauthordescription这个三个字段改写成模板引擎的方式

  "name": "<%= name %>",
  "author": "<%= author %>",
  "description": "<%= description %>"

然后我们使用ejs作为模板引擎

yarn add ejs

命令行交互这里需要用到inquirer库,先安装

yarn add inquirer

然后我们去引入几个模块

const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')
const ejs = require('ejs')

因为我们需要读取package.json文件,所以需要用到fspath两个库

然后我们在项目模板下载完成后

  1. 向用户发起询问
  2. 根据用户的回答使用模板引擎把用户的值解析到 package.json
  3. 解析完成后,把解析之后结果重新写入 package.json

根据这个步骤我们去修改一下我们的init命令

// 配置 init 命令
program
  .command('init <template> <project>') // 尖括号代表必输项,第一个参数是模板名称,第二个参数是项目名称
  .description('init project...') // 执行命令时的描述信息
  .action(function (templateName, projectName) { // 接收到用户输入的参数后要做的事情
    // 根据模板名称下载对应的模板到本地并起名为 projectName
    // 根据用户输入获取对应的模板地址
    const { downloadUrl } = templates[templateName]
    download(downloadUrl, projectName, { clone: true }, (err) => {
      if (err) {
        return console.log(`download ${templateName} template  failed`)
      }
      // 发起命令行交互
      inquirer.prompt([
        {
          type: 'input',
          name: 'name',
          message: 'Project name?',
          default: projectName
        },
        {
          type: 'input',
          name: 'description',
          message: 'Project description?'
        },
        {
          type: 'input',
          name: 'author',
          message: 'Project author?'
        }
      ])
      .then(answers => {
        // package.json 文件的路径
        const packagePath = path.join(projectName, 'package.json');
        // 使用模板引擎把用户的值解析到 package.json 中
        ejs.renderFile(packagePath, answers, (err, result) => {
          if (err) throw err
          // 解析完成后,把解析之后结果重新写入 package.json
          fs.writeFileSync(packagePath, result)
          console.log(`${templateName} template init success`)
        })
      })
    })
  })

然后我们去测试一下

txm vue vue-demo

然后可以看到命令行会发出一些问题交互,我们输入信息后,打开vue-demo文件夹,查看package.json文件,可以看到其中的表达式已经全部被替换成我们输入的信息了

增加下载动画

现在我们的cli工具有了,但是在下载模板的时候是感知不到的,这一点体验不是很好,我们去增加一个下载的loading动画,借助ora这个库来实现

安装

yarn add ora

基本的使用方式

const ora = require('ora')
const spinner = ora('downloading...')
spinner.start()
spinner.fail() // 下载失败
spinner.succeed() // 下载成功

增加文字颜色和小图标

然后我们可以通过chalk这个库为打印的信息加上样式,比如成功为绿色,失败为红色,可以使得我们的终端效果更加友好

yarn add chalk

基本使用,它有很多种颜色,我们需要什么颜色就使用什么颜色就好了

console.log(chalk.red(err))
console.log(chalk.green('success'))

使用log-symbols来增加小图标

yarn add log-symbols

使用方式就是在输出的第一个位置直接使用他对应的图标就好了

console.log(logSymbols.success, chalk.green('success'))

完整的cli代码

请查看bin目录下cli.js文件