# cli-project **Repository Path**: Rofu/cli-project ## Basic Information - **Project Name**: cli-project - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-07-01 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # cli_eslint ### 必备模块 ```markdown commander :参数解析 --help inquirer :交互式命令行工具,可以实现命令行的选择功能 download-git-repo :在git中下载模板 metalsmith :读取所有文件,实现模板渲染 consolidate :统一模板引擎 ``` ### 工程创建 ```git npm init -y # 初始化package.json npm install eslint # eslint是负责代码校验工作,husky提供了git钩子功能 npx eslint --init # 初始化eslint配置文件 选择的顺序: To check syntax, find problems, and enforce code style 回车 CommonJS (require/exports) 回车 None of these 回车 NO 回车 Node 回车 Use a popular style guide 回车 Airbnb: https://github.com/airbnb/javascript 回车 JavaScript 回车 yes 回车 等待安装好包! npm link //在命令行中使用zhu-cli命令,并且执行main.js文件 npm i commander //安装commander包 自动生成help 解析选项参数 npm i axios //ajax数据 npm i ora inquirer //loading的样式 选择模板 npm i download-git-repo //选择好项目模板名称和对应的版本,直接下载 npm i ncp //实现文件的拷贝功能 npm i metalsmith ejs consolidate //遍历文件夹 借用ejs模板 使用多种模板引擎统一 ``` ### 创建文件夹 ```git ├── bin │ └── www // 全局命令执行的根文件 ├── package.json ├── src │ ├── main.js // 入口文件 │── .eslintrc.json // 代码规范校验 ``` src/.eslintrc.json文件 ```javascript {... rules: { "no-console":"off" }, ...} ``` bin/www文件 ```javascript #! /usr/bin/env node //声明node环境,是一个可执行文件。 require('../src/main.js'); ``` package.json文件 ```javascript {... "bin": { "zhu-cli": "./bin/www"//执行bin/www文件 }, ... } ``` src/main.js文件 ```javascript //找到要执行的核心文件 console.log('hello!') //运行zhu-cli ``` ### 配置指令命令 create命令的主要作用就是去git仓库中拉取模板并下载对应的版本到本地,如果有模板则根据用户填写的信息渲染好模板,生成到当前运行命令的目录下。
config.js 的主要作用其实就是配置文件的读写操作,当然如果配置文件不存在,需要提供默认的值。
src/main.js文件 ```javascript const program = require('commander'); const {version} = require('./constants');//获取版本号 // console.log(process.argv);//当前进程的参数 zhu-cli --help运行出结果 // console.log(program) const path=require('path'); //多个指令命令的集合 运行zhu-cli create xx const mapAction = { create: {//创建模板 alias: 'c', //配置命令的别称 description: 'create a project', //命令相对应的描述 examples: [ 'zhu-cli create ' ], }, config: {//配置文件 alias: 'conf', description: 'config project variable', examples: [ 'zhu-cli config set ', 'zhu-cli config get ', ], }, '*': {//根据自己的情况配置别的命令 alias: '', description: 'command not found', examples: [], } } //相等于 Object.key() 循环遍历创建命令 Reflect.ownKeys(mapAction).forEach((action) => { program.command(action) //配置命令的名字 .alias(mapAction[action].alias) //命令别的名称 .description(mapAction[action].description) //命令对应的描述 .action(() => { if (action === '*') { //访问不到对应的命令 就打印找不到命令 console.log(mapAction[action].description) } else { console.log(action); //运行zhu-cli create xxx 解析后是[node , zhu-cli , create , xxx] require(path.resolve(__dirname,action))(...process.argv.slice(3)) } }) }); //用户监听help事件打印出信息 运行zhu-cli --help program.on('--help',()=>{ console.log('\r\nExamples:'); Reflect.ownKeys(mapAction).forEach((action)=>{ mapAction[action].examples.forEach(example=>{ console.log(' '+example) }) }) }); //运行zhu-cli --version结果为当前的版本 //解析用户传过来的参数 zhu-cli --help 运行出来结果 program.version(version).parse(process.argv); ``` 新建src/constans.js文件 ```javascript //存放用户所需要的常量 const { version}=require('../package.json'); //存储模板的位置 下载前先找临时目录存放下载的文件 //console.log(process.platform) const downLoadDirectory=`${process.env[process.platform === 'darwin'?'HOME':'USERPROFILE']}\\.template`; //console.log(downLoadDirectory); module.exports={ version, downLoadDirectory } ```

新建src/create.js文件 ```javascript //create 所有的逻辑在这儿 const axios = require('axios'); const ora = require('ora');//loading的样式 const inquirer = require('inquirer');//选择模板 let { promisify } = require('util'); let path = require('path'); let fs = require('fs'); let downloadGit = require('download-git-repo');//拉取模板 //可以把异步的api转换成promise形式 downloadGit = promisify(downloadGit); let { downLoadDirectory } = require('./constants'); let MetalSmith = require('metalsmith'); //遍历文件夹,找需不需要渲染 //consolidate 统一所有的模板引擎 let {render } = require('consolidate').ejs; render = promisify(render); let ncp = require('ncp'); ncp = promisify(ncp); //create的功能是创建项目 //拉取你自己的所有的项目列出来 让用户选 安装哪个项目 projectName //选择完成后,在显示所有的版本号 1.0 //可能还需要用户配置一些数据来结合来渲染项目 //https://api.github.com/orgs/zhu-cli/repos 获取组织下的仓库 //获取项目列表 const fetchRepoList = async () => { let { data } = await axios.get('https://api.github.com/orgs/zhu-cli/repos'); return data; } //封装loading const waitFnloading = (fn, message) => async (...args) => { let spinner = ora(message); spinner.start(); let result = await fn(...args) spinner.succeed(); return result; } //抓取tag列表 const fetchTagList = async (repo) => { let {data} = await axios.get(`https://api.github.com/repos/zhu-cli/${repo}/tags`); return data; } let download = async (repo, tag) => { let api = `zhu-cli/${repo}`; if (tag) { api += `#${tag}`; } let dest = `${downLoadDirectory}/${repo}`; await downloadGit(api, dest); return dest; //显示下载的最终目录 } module.exports = async (projectName) => {//projectName是src/mian.js文件第42行的传进参数 let repos = await waitFnloading(fetchRepoList, 'fetch template ....')(); repos = repos.map(item => item.name); //在获取之前 显示loading 关闭loading //选择模板 inquirer let { repo } = await inquirer.prompt({//在命令行中询问客户问题 name: 'repo', //获取选择后的结果 type: 'list', message: 'please choise a template to create project', choices: repos, }); // console.log(repo); //通过当前选择的项目,拉取对应的版本 //获取对应的版本号 let tags = await waitFnloading(fetchTagList, 'fetch tags ....')(repo); tags = tags.map((item) => item.name); // console.log(tags); let { tag } = await inquirer.prompt({ name: 'tag', type: 'list', message: 'please choise tags to create project', choices: tags }); // console.log(repo,tag);//下载模板 版本 //把模板放到一个临时目录里存好,以备后期使用 let result = await waitFnloading(download, 'download template')(repo, tag); //console.log(result);//下载的目录 //拿到下载的目录 直接拷贝当前执行的目录下即可 ncp //有的时候用户可以定制下载模板中的内容,拿package.json文件为例,用户可以根据提示给项目命名、 //设置描述等,生成最终的package.json文件 ask.json网址:https://github.com/zhu-cli/vue-template/blob/master/ask.js //如果有ask.js文件直接下载 if (!fs.existsSync(path.join(result, 'ask.js'))) { //复杂的需要模板熏染 渲染后再拷贝 //把template下的文件 拷贝到执行命令的目录下 //在这个目录下 项目名字是否已经存在 如果存在示当前已经存在 await ncp(result, path.resolve(projectName)); } else { //复杂的模板 把git上的项目下载下来,如果有ask文件就是一个复杂的模板,我们需要用户选择,选择后编译模板 // metalsmith--模板编译需要这个包 //需要渲染模板的接口:https://github.com/zhu-cli/vue-template/blob/master/package.json //1.让用户填信息 await new Promise((resolve, reject) => { MetalSmith(__dirname) //如果你传入路径,默认遍历当前路径下的src文件夹 .source(result) .destination(path.resolve(projectName)) .use(async (files, metal, done) => { // console.log(files) let args= require(path.join(result,'ask.js')); let obj=await inquirer.prompt(args); // console.log(obj);//用户填写的结果 let meta=metal.metadata(); Object.assign(meta,obj); delete files['ask.js']; done(); }) .use((files, metal, done) => { let obj=metal.metadata() Reflect.ownKeys(files).forEach(async file=>{ //是要处理的文件 if(file.includes('js')|| file.includes('json')){ let content= files[file].contents.toString();//文件的内容 if(content.includes('<%')){ conteny=await render(content,obj); files[file].contents=Buffer.from(content);//渲染结果 } } }) //2.让用户填写的信息取渲染模板 //根据用户新的输入 下载模板 // console.log(metal.metadata()) done(); }) .build((err) => { if (err) { reject() }else{ resolve(); } }) }) } } ``` 关于在config命令会在下一版本会提到。