记录-开发一个脚手架
功能:
- 类似vue-cli,通过一个命令, 比如
vue create hello-world
,然后根据一步一步的提示,最终拉取到对应的内容
开发原因:
- 前端组内部有符合自己业务逻辑的模板项目,为了方便组员快速拉取,需要一个脚手架。
- 脚手架搭起来后,适应不同需求的模板也可以往脚手架里面加
- 不同的模板可以用git管理起来,很方便
使用效果:
依然用到了工具平台:内部前端工具平台搭建, 安装:npm i -g @myCompany/feTools
终端输入: fe cli (tips:启动cli)
打印:一个提示:? 请输入您想创建的文件名 (会创建在当前目录下): (cli-project) (tips:此时可以输入 想创建的文件名,或者用默认的名字cli-project)
按回车往下走:一个提示:? 请选择您想拉取的模板: (Use arrow keys) (tips:此时可以上下选择想要拉取的模板)
❯ vue2
test
按回车结束(文件已经拉到了当前目录)
开发原理解析:
一些第三方工具包的组合
- commander: 识别命令,例如 fe cli -h
- inquirer: 终端交互,可以输入文件名,选择模板
- download-git-repo: 拉取gitlab的库。类似git clone
- 其他,比如执行npm install
源码:
entry.js : 入口文件
#! /usr/bin/env node
// 上面是配置当前的环境变量地址: mac上是固定的
// 引入依赖
const commander = require('commander') // 完整的 node.js 命令行解决方案: https://www.npmjs.com/package/commander
const createProject = require('./create-project') // 下一步要看的
const inquirer = require('inquirer') // 命令行交互工具: https://www.npmjs.com/package/inquirer
const pkgJson = require('../../package.json')
// 定义版本号以及命令选项
commander
.version(pkgJson.version, '-v -V --version') // 不写后面这个的话,只能用大写的 -V
.option('-i --init [name]', 'init a project')
.parse(process.argv) // let commander can get process.argv
const promptList = [{
type: 'input',
message: '请输入您想创建的文件名 (会创建在当前目录下):',
name: 'name',
default: 'cli-project' // 默认值
}]
let projectName = ''
if (commander.init) { // 如果 cli -i xxName, 就走这里
projectName = commander.init
createProject(projectName)
} else { // 直接 cli 走这里
inquirer.prompt(promptList).then(answers => {
projectName = answers.name ? answers.name : 'cli-project'
createProject(projectName) // 拿到文件名
})
}
create-project.js : 配置用户的选项交互
const chalk = require('chalk') // 彩色console.log
const path = require('path') // 获取路径
const inquirer = require('inquirer')
const GitClone = require('./git-clone.js') // 下一步要看的
const npmInstall = require('./npm-install.js') // 扩展项, 命令行执行lnpm i, 不影响主功能
const promptList = [{
type: 'list',
message: '请选择您想拉取的模板:',
name: 'key',
choices: [],
filter: function (val) { // filter result
return val
}
}]
GitClone.tplObj.map(e => {
promptList[0].choices.push(e.key) // 把git模板放入 promptList 中, 最后由inquirer执行
})
module.exports = function createProject (name) {
inquirer.prompt(promptList).then(answers => {
// 获取将要构建的项目根目录
const projectPath = path.resolve(name) // return absolute path
console.log(`Start to init a project in ${chalk.green(projectPath)}`)
const key = answers.key
// 执行git clone
GitClone.gitCloneFn(key, name)
.then(msg => {
console.log(chalk.green(msg))
// 由于大一点的模板 npm install后, 编辑器很容易卡死, 编辑器执行可以注释掉此处 npm install的功能
npmInstall(projectPath)
})
.catch(err => {
if (err.includes('128')) {
console.log(chalk.red(`失败! ${name}已经存在`))
} else {
console.log(chalk.red(err))
}
})
})
}
git-clone.js : 利用git clone把项目拉下来,拉取的模板可以在这里配置
const download = require('download-git-repo') // 执行git clone拉代码: https://www.npmjs.com/package/download-git-repo
const tplObj = [
{
key: 'vue2',
git: 'https://gitlab.xxx.cn/xxxx/xxxx.git#vue2' // #vue2表示vue2分支
},
{
key: 'test',
git: 'https://gitlab.xxx.cn/xxxx/test.git'
}
]
const gitCloneFn = (key, tplName) => {
return new Promise((resolve, reject) => {
const obj = tplObj.find(e => e.key === key)
download('direct:' + obj.git, tplName, { clone: true }, function (err) {
const msg = `拉取最新${obj.git}: `
if (err) {
reject(`${msg}: Error: ${err}`)
} else {
resolve(`${msg}: Success`)
}
})
})
}
module.exports = {
tplObj,
gitCloneFn
}
npm-install.js : 执行 npm install
// 引入依赖
const which = require('which') // 当前环境(电脑环境)是否存在 某 全局变量: 用处: 找是否存在lnpm, 如果不存在则用npm
const chalk = require('chalk')
const { spawn } = require('child_process') // 执行命令行 如: lnpm i
// 开启子进程来执行npm install命令
function runCommand (command, args, fn) {
args = args || []
const runner = spawn(command, args, {
stdio: 'inherit' // (继承父进程的stdio输出) 输出 npm install 的信息
})
runner.on('close', function (code) {
if (fn) {
fn(code)
}
})
}
// 查找系统中用于安装依赖包的命令
function findNpm () {
const npms = ['lnpm', 'cnpm', 'npm']
for (let i = 0; i < npms.length; i++) {
try {
// 查找环境变量下指定的可执行文件的第一个实例
which.sync(npms[i])
console.log('use npm: ' + npms[i])
return npms[i]
} catch (e) {
}
}
throw new Error(chalk.red('please install lnpm'))
}
module.exports = function npmInstall (projectPath) {
console.log('Installing packages...')
// 将node工作目录更改成构建的项目根目录下
process.chdir(projectPath)
setTimeout(() => {
const npm = findNpm()
runCommand(which.sync(npm), ['install'], function () {
console.log(npm + ' install end')
})
}, 1000)
}
码字不易,点点小赞鼓励