我们都知道 vue-cli 可是这cli 是怎样实现的呢,下面我们来实现一个自己的 cli
处理痛点:
- 项目配置繁琐、耗时,重复无意义的工作
- 项目结构不统一、不规范
- 前台项目类型繁多,不同项目不同配置,管理成本高
- 脚手架也可以是一套命令集,不只用来创立项目
用到的 node 知识
process.argv 接收命令行参数、process.cwd()获取当前文件绝对地址
// 终端执行my-cli init projectNameprocess.argv/* [ 'D:\\Program Files\\nodejs\\node.exe', 'C:\\Users\\USER\\AppData\\Roaming\\npm\\node_modules\\s-cli\\bin\\cli', 'init', 'projectName'] */process.cwd()/* E:\study\node\template*/常用到的几个库
commander命令行界面
用来编写指令和解决命令行参数的
const program = require('commander')// 程序命令分析、解决program.version('1.0.0') .command('init <name>') .description('generate a new project') .action(name => { console.log(name) })program.parse(process.argv)yargs解决命令行参数
相似 commander,还有minimist库,都是解决命令行参数
var argv = require('yargs').argv;console.log('hello ', argv.name);// 测试s-cli --name=woniu // hello woniuinquirer
输入交互库,就是你在创立项目的时候,输入项目名,项目形容等等
const inquirer = require('inquirer')const promptList = [ { type: 'input', // type: 默认 input,还可以是:number, confirm, list, rawlist, expand, checkbox, password, editor name: 'name', default: 'vue2-m-template', message: 'project name:' }, { type: 'input', name: 'version', default: '1.0.0', message: 'version:' }, { type: 'input', name: 'description', message: 'project description:' }, { type: 'input', name: 'author', message: 'author:' }]inquirer.prompt(promptList).then(async answer => { console.log(answer) })参考
shelljs
-f:强制删除文件;
-i:删除之前先讯问客户;
-r:递归解决目录;
-v:显示解决过程;
echo:在控制台输出指定内容
exit(code):以退出码为code退出当前进程
const shell = require('shelljs')shell.rm('-rf','out/test');//强制递归删除out/Release目录shell.cp('-r','stuff/','out/test');//将`stuff/`中所有内容拷贝至`out/Release`目录shell.mv(`./ssr-with-${language}`, `./${option.appName}`) // 把文件移到文件参考
ora加载工具
显示loading动画
const ora = require('ora');const ora1 = ora('正在创立...')ora1.start()setTimeout(function(){ ora1.succeed('构建成功');}, 1000)chalk
用来修改控制台输出内容样式的,比方颜色
const chalk = require('chalk')const glog = data => console.log(chalk.green(data))const rlog = data => console.log(chalk.red(data))glog('green word')rlog('red word')git-clone
用来下载远程模板,支持 github、gitlab等。
const gitClone = require('git-clone')// 拉取模板const cloneRp = (answer) => { return new Promise((resolve, reject) => { gitClone(' woniu1112/vue2-m-template.git', path.resolve(process.cwd(), answer.name), null, () => { // glog('拉取完成') ora1.succeed(`创立完成,项目地址:${path.resolve(process.cwd(), answer.name)}`) resolve() }) })}扩展:
Github API 接口// RC 配置下载模板的地方,给 github 的 api 使用https://api.github.com/users/xxx/repos // https://api.github.com/${type}/${registry}/reposcurl -u name:secret https://api.github.com/user/reposhttps://api.github.com/users/xxx // xxx的客户信息,包含粉丝数https://api.github.com/users/xxx/keys // xxx的 ssh keyhttps://api.github.com/users/xxx/gpg_keys // xxx的gpghttps://api.github.com/users/xxx/followers?page=2 // xxx的粉丝https://api.github.com/users/xxx/following?page=2 // xxx关注谁https://api.github.com/orgs/bitcoin bitcoin项目的信息https://api.github.com/orgs/bitcoin/repos bitcoin项目的reposhttps://api.github.com/repos/bitcoin/bitcoinhttps://api.github.com/repos/bitcoin/bitcoin/contributors bitcoin的贡献者github API 相关
Handlebars模板引擎
const Handlebars = require('handlebars')var template = Handlebars.compile("Handlebars <b>{{doesWhat}}</b>");console.log(template({ doesWhat: "rocks!" })); // 进行编译并输出ini 格式转换
const fs = require('fs'), { decode, encode } = require('ini')const config = decode.parse(fs.readFileSync('./config.ini', 'utf-8'))/*{ '<!DOCTYPE html>': true, '<html lang': '"en">', '<head>': true, '<meta charset': '"UTF-8">', '<meta name': '"viewport" content="width=device-width, initial-scale=1.0">', '<title>Document</title>': true, '</head>': true, '<body>': true, '<p>ddddddddddd</p>': true, '</body>': true, '</html>': true}*/简单 的cli 示例
#!/usr/bin/env nodeconst program = require('commander')const chalk = require('chalk')const path = require('path')const inquirer = require('inquirer')const ora = require('ora')const gitClone = require('git-clone')const fs = require('fs')const shelljs = require('shelljs')// console.log(process.argv)const glog = data => console.log(chalk.green(data))const rlog = data => console.log(chalk.red(data))const ora1 = ora('正在创立...')const promptList = [ { type: 'input', // type: 默认 input,还可以是:number, confirm, list, rawlist, expand, checkbox, password, editor name: 'name', default: 'vue2-m-template', message: 'project name:' }, { type: 'input', name: 'version', default: '1.0.0', message: 'version:' }, { type: 'input', name: 'description', message: 'project description:' }, { type: 'input', name: 'author', message: 'author:' }]// 拉取模板const cloneRp = (answer) => { return new Promise((resolve, reject) => { gitClone(' woniu1112/vue2-m-template.git', path.resolve(process.cwd(), answer.name), null, () => { // glog('拉取完成') ora1.succeed(`创立完成,项目地址:${path.resolve(process.cwd(), answer.name)}`) resolve() }) })}// 读取package.json 文件并修改部分内容const readPackageFile = (answer) => { return new Promise((resolve, reject) => { fs.readFile(path.resolve(process.cwd(), `${answer.name}/package.json`),'utf-8', async (err, result) => { result = JSON.parse(result) result = Object.assign(result, answer) await writePackageFile(result) resolve() }) })}// 写入 package.json 文件const writePackageFile = (content) => { return new Promise((resolve, reject) => { fs.writeFile(path.resolve(process.cwd(), `${content.name}/package.json`), JSON.stringify(content, null , 4), 'utf-8', (err, result) => { rmGitFile(content.name) glog(`you can do=========================cd ${content.name}npm installnpm run dev========================= `) resolve() }) })}// 删除 .git 文件const rmGitFile = function (projectName) { shelljs.rm('-rf', path.resolve(process.cwd(), `${projectName}/.git`))}// 程序命令分析、解决program.version('1.0.0') .command('init <name>') .description('generate a new project') .action(name => { promptList[0].default = name inquirer.prompt(promptList).then(async answer => { console.log(answer) ora1.start() await cloneRp(answer) await readPackageFile(answer) }) })program.parse(process.argv)扩展
扩展:npm link把my-cli注册到全局,而后链接到工程目录,可以直接去执行(把当前目录放到当前环境变量)
// package.json bin{ "name": "syl-cli", "version": "1.0.7", "description": "", "main": "index.js", "bin": { "syl-cli": "./bin/index.js" //告诉package.json,我的bin叫q-cli,它可执行的文件路径是bin/q-cli.js }, "dependencies": { "chalk": "^4.1.0", "commander": "^6.2.0", "download-git-repo": "^3.0.2", "git-clone": "^0.1.0", "inquirer": "^7.3.3", "ora": "^5.1.0", "shelljs": "^0.8.4" }, "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "syl", "license": "ISC"}