# 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命令会在下一版本会提到。