记录-开发一个脚手架
功能:
- 类似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:此时可以上下选择想要拉取的模板)❯ vue2test按回车结束(文件已经拉到了当前目录)
开发原理解析:
一些第三方工具包的组合
- 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/commanderconst createProject = require('./create-project') // 下一步要看的const inquirer = require('inquirer') // 命令行交互工具: https://www.npmjs.com/package/inquirerconst 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.argvconst promptList = [{type: 'input',message: '请输入您想创建的文件名 (会创建在当前目录下):',name: 'name',default: 'cli-project' // 默认值}]let projectName = ''if (commander.init) { // 如果 cli -i xxName, 就走这里projectName = commander.initcreateProject(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.logconst 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 resultreturn 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 pathconsole.log(`Start to init a project in ${chalk.green(projectPath)}`)const key = answers.key// 执行git cloneGitClone.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-repoconst 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, 如果不存在则用npmconst 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)}
码字不易,点点小赞鼓励
