4

从0-1实现脚手架开发、发布及执行过程探索

 1 year ago
source link: https://jelly.jd.com/article/64199a6fdba0b50060b3ba5f
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
日常开发中,尤其是初始化项目的时候,用的最多的就是脚手架了,它可以帮我们一键创建项目,省去了大量的复制粘贴工作,本文我们就探索一下脚手架是怎样的实现的,它的工作原理是怎样的。

目前我们在开发新项目的时候,大都用vue-cli、create-react-app等脚手架一键创建,但是,每次创建一个新项目之后,都需要我们再配置大量的业务相关公司相关的配置,如果,我们把这些比较通用一些的配置集成到自己的脚手架里,那么将会省去大量的重复工作量,目前公司内部已有一些,今天我们就来探索一下,如何搭建一个自己的脚手架。

首先具体了解一下脚手架的优点:

  • 减少重复性工作,不再需要复制其他项目再删除无关代码,或者从零创建一个项目和文件
  • 根据交互动态生成项目结构和配置文件等
  • 多人协作更为方便,不需要把文件传来传去

二、脚手架功能

脚手架都有哪些功能,首先,我们以 vue-cli 脚手架为例,它有以下功能:

  • vue init
  • vue list
  • vue build
  • vue create
  • vue help

我们今天先实现这些功能,但脚手架肯定不止这些功能,我们只是用这些功能做个例子

三、实现思路

vue 官方脚手架执行过程:vue init webpack my-app => webpack 模版 => 模板引擎 => 本地项目

实现过程:

  • 项目模版放在github上
  • 用户通过命令交互的方式下载不同的模板
  • 经过模板引擎渲染订制项目模板
  • 模板变动,只需要更新模板即可,不需要用户更新脚手架

四、涉及知识点及模块

  • NodeJS
  • 基于Node.js开发命令行工具
  • npm 发包
  • npm包的发布及更新流程
  • commander.js
  • 可以自动解析命令和参数,用于处理用户输入的命令
  • download-git-repo
  • 下载并提取git仓库,用于下载项目模板
  • Inquirer.js
  • 通用的命令行用户界面集合,用于和用户进行交互
  • handlebars.js
  • 模板引擎,将用户提交的信息动态填充到文件中
  • 下载过程久的话,可以用于显示下载中的动画效果
  • chalk
  • 可以给终端的字体加上颜色
  • log-symbols
  • 可以再终端上显示√或×等图标

五、创建脚手架

a. 初始化

mkdir jdl-cli
cd jdl-cli
npm init -y

新建 index.js 并写入以下内容

#!/usr/bin/env node
console.log('hello jdl')

#!/usr/bin/env node 这行代码不是注释,是用来告诉脚本工具(bash/zsh),下面的内容是要在node环境中运行的代码,一定不能省略!

配置 package.json 中的 bin 字段

"bin": {
"jdl": "index.js"
}

bin属性是用于配置指令,指令由key、value组成,key是指令名,value为运行指令时运行的文件

在安装时,npm 会将文件符号链接到 prefix/bin 以进行全局安装

或 ./node_modules/.bin/ 本地安装,这样就可以全局使用了

执行 npm link

npm link 

执行npm link 之后,会将当前项目链接到node 全局 node_modules 中,作为一个库文件,并解析 bin 配置 创建可执行文件

  • 初始化完成

执行完以上操作之后,在控制台执行:jdl 命令,就会看到:hello jdl 输出

b. 交互能力

以下所有涉及到得工具包api详细使用方法可以自行查询文档,这里不做赘述

vue官方脚手架创建项目时,会有一些交互,如下图:

11d36b920f7b79f2.png

通过vue create命令创建vue项目,首先,第一步先要让vue create命令被识别,这里我们可以通过原生的nodejs实现,由于过于麻烦,这里我们使用现成的工具包

创建命令 - commander

  • commander

commander 对命令行的参数获取以及自定义命令及相关信息展示进行了封装,具体使用:

#!/usr/bin/env node 

const { program } = require('commander');
program.version('1.0.0')
program.parse(process.argv)

通过对index.js进行以上改造之后,将会得到下面效果

11d36b920f7b79f2.png

commander内部封装了-V和-h命令,可以直接使用

接下来 我们可以通过command方法自定义一些命令:

program.command('create <name>')
.description('创建项目')
.option('-t, --type <type>', '创建什么类型的项目')
.action((name, opts) => {
console.log(name, opts)
})
11d36b920f7b79f2.png

自定义命令的action里是我们要对命令做的操作,我们的脚手架在这里的操作是将对应的模版拉取到对应的位置,接下来就是拉取模版,这里我们通过 download-git-repo 包进行下载github上的代码

拉取代码 - download-git-repo

在 index.js 增加以下方法,从github上拉取tpl-a模版

const downloadGit = require('download-git-repo')
...
downloadGit('xuyoo/tpl-a', name, async (err) => {
if (err) {
console.error(JSON.stringify(err))
console.error('下载失败')
} else {
console.log('下载成功')
}
})
执行jdl create demo -t a命令
11d36b920f7b79f2.png

当前目录下便生成个demo文件夹,并拉取了github上得tpl-a模板

11d36b920f7b79f2.png

以上,我们便创建了个简易的脚手架,但是,这还远远不够,下面我们做些简单的完善

交互 - inquirer、handlebars

前面脚手架创建的项目过于死板,现实生活中,我们需要一些定制化,比如,package.json里面,我们需要定制化我们项目的name、description、author等,这些信息,我们需要用户告诉我们,然后我们做替换,这里我们用到inquirer去跟用户交互获取相关信息,handlebars将相关信息替换

在index.js里加入以下代码

const answer = await inquirer.prompt([
{
name: 'description',
message: '项目描述'
},{
name: 'author',
message: '作者名字'
}
])
answer.name = name
const packagePath = ${name}/package.json
const packageContent = fs.readFileSync(packagePath, 'utf-8') // utf-8读字符串,否则是二进制数据
const packageResult = handlebars.compile(packageContent)(answer) // 编译 & 替换
console.log(packageResult)
fs.writeFileSync(packagePath, packageResult)

这里遇到个坑:npm install inquirer之后,执行报错

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/xuyuanyuan52/mine/jdl-cli/node_modules/inquirer/lib/inquirer.js
require() of ES modules is not supported.

因为Inquirer V9以上用esm modules了,也就意味着我们不能用require('inquirer')引用了,这里我卸载了刚装的Inquirer包,重新装了个8.0.0版的,问题解决

npm install --save inquirer@^8.0.0 
11d36b920f7b79f2.png

tpl-a模版的package.json的内容是这样的

11d36b920f7b79f2.png

现在,我们完成了基本功能,但是,交互有点不是很友好,接下来我们做个简单的交互体验优化

优化 - ora、chalk、child_process

我们可以在下载模版的时候加个loading,给一些成功失败的提示

const spinner = ora('正在下载模板...')
spinner.fail('下载失败')
spinner.succeed('下载成功')

可以加一些颜色,更友好一些

chalk.green('初始化项目成功!') 

还可以项目下载成功之后自动 npm i 安装依赖

child_process.spawnSync('npm install', [], {
cwd: ${process.cwd()}/${proName},
shell: true
})

等等功能及优化待探索

到目前位置,我们写的脚手架都只能在我们本地运行,怎样才能让别人在别人的电脑上也运行,那就需要我们把项目发布到npm上

首先登录一下npm

npm login 

输入你npm的个人账号和密码

再执行发布命令

npm pulish 

别人就可以用npm install在自己电脑上安装你的脚手架并使用了

七、脚手架执行过程

  • npm install jdl-cli(我没发布,所以安装不了)
  1. 把安装的包 jdl-cli 下载到 node 当前版本的下面的 lib/node_modules 下面
  2. 解析 package.json 文件内容的 bin 内容
  3. 在 node 安装版本的 bin 目录下面创建一个软连接,这个软连接的名字就是上一步 bin 下面的 key,软连接的地址就是 bin 的 value 地址
  • jdl create demo
  1. which jdl寻找 jdl 命令,在环境变量中找到 jdl 命令
  2. 找到后发现是一个软连接,根据软连接再找到真是的文件地址
  3. 终端用 node 执行 index.js
  4. index.js 解析出 command 和 options
  • 软连接 - 创建一个软连接,来指向本地的某个js文件
  • 找到所有的环境变量 echo $PATH
  • 找到 node 的 bin 目录,并进入目录
  • 创建一个软连接: ln -s /Users/xuyuanyuan52/mine/test.js test
  • 执行 test

至此,我们完成了脚手架所需要的基本功能:初始化项目,定制化,这里只是个简单的例子,我们在开发的时候可以不局限于一种脚手架,我们可以创建多种项目模版,让用户选择其中一种并定制化,另外,我们还可以做的更多,如集成单元测试、集成eslint、集成stylelint、项目打包发布等等,更多符合业务特点的脚手架还需要根据实际情况去开发。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK