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

### 01 前言

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

**脚手架是干什么的?**

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

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

1、脚手架会为我们定制一个标准化的项目,那么我们在实际的业务线里面也许就会有自己的定制化需求。如果偶尔一个项目定制化没什么,如果公司的一些项目都采用该标准,那么这个定制化的标准项目,在你们内部就可以理解为标准化了。既然是标准化了,那么他就有必要做一个标准(脚手架)。

2、学习。了解敌人的最好方式,就是加入敌人。我们学习使用一门技术的最好方式,就是将这个技术实现一遍。

话不多说,开干!!!

### 02 功能介绍

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

1、识别控制台的输入

2、控制台交互

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

识别控制台的输入

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

控制台交互

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

![在这里插入图片描述](https://img-blog.csdnimg.cn/2021011002262899.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjM4NTk5,size_16,color_FFFFFF,t_70#pic_center)

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

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

### 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

```

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210110022654935.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjM4NTk5,size_16,color_FFFFFF,t_70#pic_center)

如果你的控制台,和我的一样(类似),那么恭喜你,你完成了最最重要的一步。

我们已经将我们自己开发的包(脚手架)放到了全局的npm中。也就是说,这个时候,你已经可以像使用vue,react脚手架一样,全局任何位置操作它的命令了。

注:命令就是我们之前配置在package.json 文件中 bin 属性。cuteXuan-cli和xuan 都可以。

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210110022715625.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjM4NTk5,size_16,color_FFFFFF,t_70#pic_center)

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

### 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。

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210110022818418.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjM4NTk5,size_16,color_FFFFFF,t_70#pic_center)

当然了,就像前面说的,我们还需要进行版本的选择,这个时候就需要去我们的代码管理地址,获取我们的版本库列表。

github举例子:

https://api.github.com/users/haimingyue/projectName/tags]

```

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 到本地

![](https://img-blog.csdnimg.cn/img_convert/e1ca573518207f85eccff5a1eed845c1.bmp)

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容