从0手写一个cli 脚手架!!!

857 阅读4分钟

01 前言

只是你从事前端开发,无论是什么什么级别的程序员都使用过vue或者是react的脚手架。今天这篇文章就让我们一起从零开始搭建一个自己的脚手架。

脚手架是干什么的?

拿vue举例,当我们每一次使用脚手架初始化一个项目的时候,里面就已经有了很多的代码。这就是脚手架为我们初始化的标准项目。也就是说,脚手架会为我们做一些重复性,标准化的事情。

既然已经有了官方的脚手架,我们为什么还要搭建自己的脚手架?

1、脚手架会为我们定制一个标准化的项目,那么我们在实际的业务线里面也许就会有自己的定制化需求。如果偶尔一个项目定制化没什么,如果公司的一些项目都采用该标准,那么这个定制化的标准项目,在你们内部就可以理解为标准化了。既然是标准化了,那么他就有必要做一个标准(脚手架)。 2、学习。了解敌人的最好方式,就是加入敌人。我们学习使用一门技术的最好方式,就是将这个技术实现一遍。

话不多说,开干!!!

02 功能介绍

知己知彼,方能百战不殆。我们实现一个脚手架,先要知道他能有哪些哪些功能,才能去尝试做这些功能:

1、识别控制台的输入

2、控制台交互

3、将我们的模版项目下载下来

识别控制台的输入

类似于我们在控制台输入 --help,控制台要理解我们的输入内容

控制台交互

我们使用 vue create name 创建项目时候,会让我们选择默认创建,还是配置版创建。如果选择配置版创建,又会让我们选择是否配置vuex,vue-router,eslint这些内容。

将我们的模版项目下载下来

当我们选择完毕之后,会进行下载,下载失败的判断,断网重连的操作等等一系列。

03 项目结构

创建一个目录 cuteXuan-cli

然后 npm init -y 目录下创建 bin, lib 文件夹

在bin 下创建文件 xuan,写如下内容

#! /usr/bin/env node 
console.log("hello cuteXuan-cli")

这个时候可以修改我们的 package.json 文件,我们为它添加了bin属性

"name": "cuteXuan-cli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "bin": {
    "cuteXuan-cli": "./bin/xuan",
    "xuan": "./bin/xuan"
  },

这个时候我们的最基本结构搭建起来了,是时候执行最重要的一步了: 打开我们的控制台,或者终端。执行下面的命令

npm link
// 如果你之前做过类似的操作,也许需要强制这一次的操作
npm link --force

如果你的控制台,和我的一样(类似),那么恭喜你,你完成了最最重要的一步。 我们已经将我们自己开发的包(脚手架)放到了全局的npm中。也就是说,这个时候,你已经可以像使用vue,react脚手架一样,全局任何位置操作它的命令了。 注:命令就是我们之前配置在package.json 文件中 bin 属性。cuteXuan-cli和xuan 都可以。

如果你走到这,恭喜你,你的脚手架已经完成了一半了。

04 利用Commander解析输入参数

npm i commander

我们平时使用 vue create xxx

vue --version vue --help 这一类的命令,如果想要执行后续的操作,就要解析用户的输入参数,当然了,-v ,-h 这一类的简写也要考虑。当然了,这些我们都可以借助Commader 来实现。

#! /usr/bin/env node const program = require('commander')
​
​
// 重名,需要强制创建的模式
program
    .command('create <app-name')
    .description('create a new project')
    .option('-f, --force','overwrite target directory if it exists')
    .action((name,cmd) => {
      console.log(anme,cmd)
    })
    
// xuan -v     
program
    .version(`cuteXuan-cli @${require('../package.json').version}`)
    .usage(`<command> [option]`)
​
​
// 解析用户执行命令传入的参数
program.parse(process.argv)

简单说明一下, command 里面输入的是我们控制台执行的命令模版 xuan create xxx description 一些描述文字 option 一些参数配置及简写形式 action 输入正确命令之后的操作

action 中name 是命令,cmd 是参数,但是它不仅仅🈶️我们的参数,还有各种默认的参数。这就需要进行参数清洗:

const clearArgs = (cmd) => {
    const args = {}
    cmd.options.forEach(element => {
        const key = element.long.slice(2)
        if (cmd[key]) {
            args[key] =cmd[key]
        }
    });
    return args
}

这个时候,我们就可以在我们的action 中拿到正确的name和cmd了。

当然了,一个脚手架会有很多这类型的命令,其他的基本上就是苦力活了。而我们这里仅仅写了最重要的 xuan create name 和 xuan --version

05 利用Inquirer 与用户做交互

npm i Inquirer 我们在实际的项目中,会遇到下载不同的版本。就比如我们会使用不同的node版本一样。这个时候就需要进行这样的配置。又比如我们需要为我们的新项目提前一些库,比如vuex,vue-router。

当然了,就像前面说的,我们还需要进行版本的选择,这个时候就需要去我们的代码管理地址,获取我们的版本库列表。 github举例子: api.github.com/users/haimi…]

async getTags() {
  let tags = await fetchTagList() 
  if (!tags) { return }
  tags = tags.map(item => item.name)
  let {tag} = await Inquirer.prompt({
      name: "tag",
      type: 'list',
      choices: tags,
      message: "please choose a tag to create project"
  })
  return tag
}    

上述代码中type: 'list',就会将我们的tags以列表的形式展示。并返回你选择的tags。

当然了,的配置(vuex,vue-router)列表也是同样的意思,只不过它变成了多选,考虑自己试一试吧!!!

06 使用download-git-repo下载配置模版 到了这一步,我们选择好了项目版本,同时也选择好了项目配置。接下来,就可以将选择变现了——下载到本地。 npm install download-git-repo 值得一提的是,download-git-repo 本身是不支持promise。

const downloadGitRepo = require('download-git-repo')
const util = require('util')
const newDownloadGitRepo util.promisify(downloadGitRepo)

通过上述方法,可以让 download-git-repo 支持promise形式。


/**
 * 
 * @param {*} repo  具体模版
 * @param {*} tag   模版的版本号
 */
async download(repo,tag) {
      let requestUrl = `zhu-cli/${repo}${tag?'#'+tag:''}`
      await this.downloadGitRepo(requestUrl,path.resolve(process.cwd),`${repo}@{tag}`)
      return this.target
}

到了这个时候,简易版的脚手架就搭建完毕了,发布在npm上,就可以供公司内以及公司外的其他人使用了。当然了,真正的脚手架远比这复杂,我们这仅仅是实现了一个最简单不过的脚手架而已。

07 总结

开始功能介绍的时候,也许你自己还有些疑惑,读到了这里。让我们一起回顾一下cli。 初始化项目,并利用 npm link 将我们开发的脚手架与全局的npm联系起 使用 Commander 解析用户的输入,这样就可以对用户的输入作出反应 使用 Inquirer 与用户进行交互。如果没有交互,我们也没有做脚手架的必要了,直接给一个链接它不方便吗? 使用 download-git-repo 将用户的选择的内容 download 到本地

如果感觉这篇文章对你有帮助,点个赞👍,关注一下吧!!!