现代前端开发中已经离不开各种脚手架的使用了,例如vue-cli和create-react-app等脚手架,而我们在日常开发中都会在这些脚手架的基础上做一些升级改造,例如集成一些常用的工具、网络请求封装、状态管理、特定布局等等,然后在后续的新项目中复用这些基础架构。为了方便管理和使用,我们可以将其抽离出来形成一个项目模版,通过类似vue-cli脚手架的方式来创建一个新项目,接下来本文就介绍一下如何动手创建一个自己的脚手架工具。
一、创建项目
新建一个项目文件夹,这里我将其命名为easy-cli
,然后在该目录下执行:npm init -y
生成package.json
文件。
为了能够使用全局命令,我们需要在package.json
里增加bin
字段。
json
{
"name": "easy-cli",
"version": "1.0.0",
// ...
"bin": {
"easy-cli": "./src/index.js"
}
}
增加bin
字段后我们就可以像下面这样全局使用命令:
bash
easy-cli xxx
现在我们还无法执行命令,因为我们还没创建bin
字段所指向的文件,接下来我们创建src/index.js
:
js
#! /usr/bin/env node
console.log('hello')
第一行代码
#! /usr/bin/env node
是必须的,它用来告诉系统使用node环境来执行该文件。
然后执行命令:npm link
将其链接到全局变量,接下来我们就可以使用easy-cli
命令了。
可以看到js文件被执行成功了。
二、定义版本
首先需要安装commander
库,commander
可以帮助我们处理命令行。
bash
npm i commander --save
commander
内部做了封装,使用命令--version
查看版本时会输出我们在version
方法里定义的值。
js
#! /usr/bin/env node
const program = require('commander')
const pkg = require('../package.json')
program.version(`v${pkg.version}`) // 定义当前版本
program.parse(process.argv) // 解析用户执行命令传入参数
三、命令行交互
我们希望在使用脚手架时通过如下步骤来创建一个新项目项目:
- 首先执行
easy-cli create
命令。 - 用户输入项目名称。
- 用户选择模板。
- 将用户选择的模板copy到本地。
在最后一步这里,一种选择是将项目模板放在脚手架里,然后将其对应的目录复制到本地。另一种选择是将项目模板放在git
仓库里,然后在用户选择后将其下载到本地。这里我们选择第二种方案,这样当项目模板有改动时不需要频繁的去脚手架里改动。
1、获取项目名称
这里我们需要先安装inquirer
,由于inquirer
9.0以上版本无法使用require
,所以安装时我们选择安装8版本的inquirer
。
bash
npm i inquirer@8 --save
js
#! /usr/bin/env node
const program = require('commander')
const inquirer = require('inquirer')
const pkg = require('../package.json')
program.version(`v${pkg.version}`) // 定义当前版本
program
.command('create')
.description('创建模版')
.action(async () => {
const { projectName } = await inquirer.prompt({
type: 'input',
name: 'projectName',
message: '请输入项目名称:'
})
console.log(projectName)
})
program.parse(process.argv) // 解析用户执行命令传入参数
项目名称我们需要用户手动输入,所以prompt
里的type
的值设置为input
,除此之外type
值还有list(列表选择)
、confirm(单选确认)
等,name
是设置结果返回值对应的key名称,设置为projectName则返回结果里通过projectName获取。
2、选择模板
js
const program = require('commander')
const inquirer = require('inquirer')
const pkg = require('../package.json')
program
.command('create')
.description('创建模版')
.action(async () => {
const { projectName, template } = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: '请输入项目名称:'
},
{
type: 'list',
name: 'template',
message: '请选择模版:',
choices: [
{
name: 'vue-admin',
value: 'https://github.com:PanJiaChen/vue-element-admin'
},
{
name: 'react-admin',
value: 'https://github.com:marmelab/react-admin'
}
]
}
])
console.log('projectName===', projectName)
console.log('template===', template)
})
prompt
也支持传入数组的形式,这里我们可以将输入项目和选择模板的操作依次传入,choices
接收的数组对象包含一个name和value,name用于显示,选择后会将对应的value值返回。
3、下载模版到本地
安装download-git-repo
:
bash
npm i download-git-repo --save
js
const program = require('commander')
const inquirer = require('inquirer')
const pkg = require('../package.json')
const downloadGitRepo = require('download-git-repo')
program
.command('create')
.description('创建模版')
.action(async () => {
const { projectName, template } = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: '请输入项目名称:'
},
{
type: 'list',
name: 'template',
message: '请选择模板:',
choices: [
{
name: 'vue-admin',
value: 'https://github.com:PanJiaChen/vue-element-admin'
},
{
name: 'react-admin',
value: 'https://github.com:marmelab/react-admin'
}
]
}
])
console.log('projectName===', projectName)
console.log('template===', template)
// 获取目标文件夹
const dest = path.join(process.cwd(), projectName)
downloadGitRepo(template, dest, (err) => {
if (err) {
console.log('下载模板失败' + err.message)
return
}
console.log('下载模板成功')
})
})
通过process.cwd
将项目模板下载到命令行当前目录下。
运行命令后可以看到项目模板已经成功下载下来了,一个自制的脚手架已经基本完成了。接下来我们做一些优化。
4、优化:判断项目是否已存在
将项目模版下载到本地前,我们可以做一下优化判断,如果用户输入的项目名称在本地目录中已经存在,则弹出选择让用户确认是否删除覆盖。
接下来我们安装第三方包fs-extra
方便进行文件操作:
bash
npm i fs-extra --save
js
const program = require('commander')
const inquirer = require('inquirer')
const pkg = require('../package.json')
const downloadGitRepo = require('download-git-repo')
const fs = require('fs-extra')
program
.command('create')
.description('创建模版')
.action(async () => {
const { projectName, template } = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: '请输入项目名称:'
},
{
type: 'list',
name: 'template',
message: '请选择模板:',
choices: [
{
name: 'vue-admin',
value: 'https://github.com:PanJiaChen/vue-element-admin'
},
{
name: 'react-admin',
value: 'https://github.com:marmelab/react-admin'
}
]
}
])
// 获取目标文件夹
const dest = path.join(process.cwd(), projectName)
if (fs.existsSync(dest)) {
const { isExist } = await inquirer.prompt({
type: 'confirm',
name: 'isExist',
message: '您输入的项目名称已存在,是否覆盖?',
})
// 如果覆盖就删除文件夹,否则退出
isExist ? fs.removeSync(dest) : process.exit(1)
}
downloadGitRepo(template, dest, (err) => {
if (err) {
console.log('下载模板失败' + err.message)
return
}
console.log('下载模板成功')
})
})
5、优化:增加loading
安装ora
:
bash
npm i ora@5 --save
js
const ora = require('ora')
const loading = ora('正在下载中...')
loading.start()
downloadGitRepo(template, dest, (err) => {
if (err) {
loading.fail('创建模版失败' + err.message)
return
}
loading.succeed('创建模版成功')
})
6、优化:修改package.json
我们下载下来的项目模板里,package.json
里的name
和version
记录的都是项目模板里的信息。
通常我们需要把name
改成我们新创建的的项目名称,version
从1.0.0开始,所以我们可以简单的借助node的文件操作能力进行修改。
js
downloadGitRepo(template, dest, (err) => {
if (err) {
loading.fail('创建模版失败' + err.message)
return
}
loading.succeed('创建模版成功')
// 修改package.json
const pkgPath = path.join(process.cwd(), projectName, 'package.json')
const pkgContent = fs.readFileSync(paths, 'utf-8')
const pkgJson = JSON.parse(pkgContent)
pkgJson.name = projectName
pkgJson.version = '1.0.0'
fs.writeFileSync(paths, JSON.stringify(pkgJson, null, 2))
})
四、发布npm
上面的工作完成后就可以选择发布到npm
了,发布npm
的步骤比较简单,这里就不多赘述了。
注册一个npm
账号,然后npm login
和npm publish
。
五、感谢
本次分享到这里就结束了,感谢您的阅读,如果本文对你有什么帮助的话,别忘了动动手指点个赞❤❤❤!