效果

上代码

#! /usr/bin/env node
const {program} = require('commander')
const {version} = require('../package.json')
// cnpm i commander -D
/*** 当前我们需要做的就是将多个自定义命令的信息用一种数据格式保存起来* 因为这些个命令的信息将来肯定都需要被使用到,因此这个存储的格式应该是可迭代* 这里我们就选择对象*/// 存储自定义的命令信息
const actionsMap = {'create' : {alias: 'crt',des: '创建本地项目',examples: ['lgg create <projectname>']},'config' : {alias: 'cfg',des: '修改配置信息',examples: ['lgg config set <k> <v>','lgg config get <k>',]},
}// 将上述的信息渲染至命令行终端里
Reflect.ownKeys(actionsMap).forEach((aName) => {// 当前我们可以拿到 自定义的命令名称了,但是我们最终想要做的事情是利用这个键名// 找到它所对应的 命令信息,然后再使用 program 操作,将它们渲染在终端里program.command(aName).alias(actionsMap[aName].alias).description(actionsMap[aName].des).action(() => {console.log(aName, '执行了')})
})program.on('--help', () => {console.log('Examples: ')// 此时我们可以监听到 --help 事件的发生,那么就可以想办法将// 之前我们存放在数组里的示例代码显示出来Reflect.ownKeys(actionsMap).forEach((aName) => {actionsMap[aName].examples.forEach((item) => {console.log(" " + item)})})
})program.version(version).parse(process.argv)

// 下拉代码是的loading效果

上代码

/*** ora 专门用于添加 loading 效果 * 安装 Ora cnpm i ora -D*/
const ora = require('ora')const spinner = ora('正在拉取')
spinner.start()
// 中间存放的就是耗时操作
setTimeout(() => {console.log('正在下载.......')spinner.succeed('拉取成功')// fail() info()
}, 2000)

字体颜色

上代码

// cnpm i chalk -D
const chalk = require('chalk')// 1 设置文字颜色
console.log(chalk.green('zce'))
console.log(chalk.red('zcegg'))
console.log(chalk.white('lg'))// 2 设置背景色
console.log(chalk.bgBlueBright(chalk.green('拉勾教育66')))// 3 输出带色的段落文字
console.log(chalk`{green.bold 车马慢}{red 从前...慢,一生只够爱一次, 要专一}
`)

问题选择

const inquirer = require('inquirer')
// cnpm i inquire -D
const chalk = require('chalk')// 这个包可以解决:让我输入、让我选、让我决定的交互问题/*** 01 我们需要先设计好要交互的问题* 02 将这个问题交给 inquirer 来处理,用户按交互执行操作之后,就可以获取到它的输入结果*/// 01 设计问题
const quesList = [{type: 'checkbox',   // 这是一个多选型问题name: 'feature', message: '选择默认配置',pageSize: 2,choices: ['eslint', 'jest', 'babel', 'webpack', 'vue', 'zce', 'zce的朋友']}
]// 2 处理问题
inquirer.prompt(quesList).then((answer) => {console.log(answer)
})

que.js

module.exports = [{type: 'confirm',name: 'private',message: '是否为私有仓库'},{type: 'input',name: 'author',message: 'author'},{type: 'input',name: 'description',message: 'description'},{type: 'input',name: 'license',message: 'license'},
]
const axios = require('axios')
let fs = require('fs')
let ncp = require('ncp') //拷贝包的工具  npm i ncp -D
let { promisify } = require('util') // node自带的包
const inquirer = require('inquirer')
let downLoadFn = require('downLoad-git-repo') // npm i downLoad-git-repo -D
let ora = require('ora')
let Metalsmith = require('metalsmith')// npm i metalsmith -D 渲染用的包
let {render} = require('consolidate').ejs// npm i consolidate -D      npm i ejs -D渲染模板用的包  package.json
const { Promise, resolve, reject } = require('bluebird')
const { async } = require('rxjs')
downLoadFn = promisify(downLoadFn) // 让downLoadFn 可以支持 async + await
// 工具方法:添加耗时等待
const addLoading = function(fn) {return async function(...args) {let spinner = ora('拉取开始...')spinner.start()try {let ret = await fn(...args)spinner.succeed('拉取成功')return ret} catch (err) {console.log(err)spinner.fail('拉取失败')}}
}
// 工具方法: 获取仓库工具列表const fetchRepoList = async function() {let {data} = await axios.get('https://api.github.com/users/zcegg/repos')let repos = data.map(item => item.name)return repos
}
// 工具方法: 获取 tags 列表
const fetchTagList = async function(reponame){let {data} = await axios.get(`https://api.github.com/repos/zcegg/${reponame}/tags`)let repos = data.map(item => item.name)return repos
}// 工具方法:自定义函数完成 git 仓库下载操作
const downLoadRepo = async function(repo, tag) {// downLoad-git-repo + 缓存// 定义缓存目录let cacheDir = `${process.env[process.platform == 'win32' ? 'USERPROFILE' : 'HOME']}/.tmp`// 处理 downLoad-git-repo 导出的函数的调用规则 downLoadFn(zcegg/create-nm#tagv)let api = `zcegg/${repo}`if (tag) {api += `#${tag}`}// 3自定义一个模板下载后的输出目录let dest = path.resolve(cacheDir, repo)let flag = fs.existsSync(dest)// 4执行下载操作let spinner = ora('开始下载')spinner.start()if (flag) {spinner.stop()return dest} else {await downLoadFn(api, dest)spinner.succeed('模板下载成功')return dest}
}module.exports = async function (proName) {// 1 在远端平台上查询当前目标模板是否存在/*** 01 此时我在 github 上已经准备好了二个模板仓库* 02 在使用 lgg 生成项目的时候,我就想去用这二个模块* 03 所以我们应该先去 github 上看一下这二个项目是否存在.......* 04 如果去看呢?*  发请求---》github提供相应的 API 支持--->*/// 此时我们就可以在我们的代码里来获取到目标仓库里所提供的列表// github 官方 API 有一个限次操作,每小时60次// 同时他也提供了一些方法让我们可以解限速  5000// 此时我们可以利用  github 生成 token 的方式来解除限制// 1 获取模板列表let repos = await addLoading(fetchRepoList)()console.log(repos)// 交互问题设置let {tmpname} = await inquirer.prompt({type: 'list',name: 'tmpname',message: '请选择目标仓库模板',choices: repos})// 拉取tags列表let tags = await addLoading(fetchTagList)(tmpname)// 4依据拉回来的tags 分支进行处理 ([v1, v2...] [])let dest = nullif (tags.length) {let {tagv} = await inquirer.prompt({type: 'confirm',name: 'tagv',message: '请选择目标版本',choices: tagv})console.log(tagv)// 依据选择的模板名称和仓库版本号完成具体的下载操作dest = await downLoadRepo(tmpname, tagv)console.log(dest)} else {// 当代码运行到这里就说明当前仓库是不存在多个 tag版本let {isDownLoad} = await inquirer.prompt({type: 'confirm',name: 'isDownLoad',message: '当前不存在多个tag是否直接下载'})if(isDownLoad) {dest = await downLoadRepo(tmpname)console.log(dest)} else {return}}// 将模板下载完成后在本地的自定义缓存中存在着具体文件// 利用这些文件就可以初始化我们的项目// 两种情况:一种是项目初始化过程中需要用户动态提供数据,一种是项目中不需要渲染动态数据,直接拷贝即可// ncp  拷贝包的工具  npm i ncp -Dif (fs.existsSync(path.join(dest, 'que.js'))) { // que.js是自己放入目录的 用于判断console.log('当前是需要渲染数据的')await new Promise((resolve, reject) => {Metalsmith(__dirname) // __dirname其实也没用 只不过不传参数会报错.source(dest).destination(path.resolve(proname)).use((files, metal, done) => {console.log(files)let quesArr = require(path.join(dest, 'que.js'))let answers = await inquirer.prompt(quesArr)// 当前answers是传递过来的参数, 我们需要在下一个 use 中使用// 利用metal.metadata() 来保存所有数据,交给下一个use 进行命名用即可let meta = metal.metadata()Object.assign(meta, answers)delete files['que.js']done()}).use((files, metal, done) => {console.log(files)let data = metal.metadata()// 找到哪些需要渲染的具体文件,找到之后将他们里的内容转为字符串方式// 转为字符串之后,接下来就可以针对于字符串进行替换实现渲染Reflect.ownKeys(files).forEach(async (file) => {if (file.includes('js') || file.includes('json')) {let content = files[file].contents.toString()if (content.includes('<%')) {content = await render(content, data)files[file].contents = Buffer.from(content)}}})done()}).build((err) => {if (err) {reject()} else {resolve()}})})} else {console.log('当前是不需要渲染数据的')ncp(dest, proname)}
}/*** 1. 选择目标仓库模板列表 【create-nm, create-vue】* 2. 用户通过交互性问题选择目标模板: create-nm* 3. 获取目标仓库模板列表:【v0.1.0, v0.1.1】* 4. 选择指定版本* 5. 下载之前处理缓存目录, 判断缓存是否存在create-nm , 如果存在则直接返回路径进行使用* 6. 如果缓存中不存在, 则直接下载, 然后返回缓存目录* 7. 判断当前缓存目录里的模板文件中是否存在 que.js文件, 如果存在则证明需要渲染,如果不存在则证明不需要渲染* 8. 如果不需要渲染, 我们就直接将文件拷贝到当前项目根目录下即可
*/

commander、ora、chalk、inquirer使用相关推荐

  1. Nodejs开发简单的脚手架工具

    脚手架,这个名词对于作为前端的我们来说,也许并不陌生吧,像vue-cli,react-native-cli等,全局安装后,只需要在命令行中敲入一个简单的命令,便可帮我们快速的生成一个初始项目,如vue ...

  2. 使用 Node.js 开发简单的脚手架工具

    前言 像我们熟悉的 vue-cli,react-native-cli 等脚手架,只需要输入简单的命令 vue init webpack project,即可快速帮我们生成一个初始项目.在实际工作中,我 ...

  3. 仿 vue-cli 搭建属于自己的脚手架

    脚手架是啥 从前我总觉得脚手架是个很高大上的东西,好像得牛叉?一点的人才写的出来,可望而不可即.其实并不是因为困难使我们放弃,而是因为放弃才显得困难(这是个好词好句?).只要你肯花个一天半天的时间✊, ...

  4. 一步步教你完成一个自己的脚手架并且发布到npm

    相信你一定用过vue-cli或者create-react-app或者你公司自己的脚手架.当我第一次用这些脚手架的时候,会觉得这一定是种很高级的玩意,其实了解过之后就发现,脚手架也并不是多么深奥的东西. ...

  5. 如何自己搭一个脚手架

    前言 做前端也有三四年了,自己带了个五人前端小团队,第一次写脚手架,也是第一次写分享文章.文笔太差,勿喷.勿喷.勿喷 每次人肉搬运代码的时候,就想自己能不能给团队做一个跟vue-cli一样的脚手架?想 ...

  6. 从0搭建前端脚手架详解(小白也可以搭建)

    本篇文章用来为大家提供一个搭建简易前端脚手架的思路. 先来看一眼实现的效果. 从图上来看这个脚手架的功能非常的简单只有一个创建的命令,其他都是帮助和显示版本号的. 也就是上图这句,创建一个新项目,只需 ...

  7. 前端脚手架搭建(part 1)

    本篇主要介绍如何搭建前端脚手架,一步一步地实现通过搭建的脚手架下载对应的项目模板. 通过脚手架的创建,可以快速搭建项目的基础配置和模板,在部门项目开发的规范中尤其总要. 初始化项目: 创建一个文件夹, ...

  8. node工程默认url_node命令行工具之实现项目工程自动初始化的标准流程

    一.目的 传统的前端项目初始流程一般是这样: 可以看出,传统的初始化步骤,花费的时间并不少.而且,人工操作的情况下,总有改漏的情况出现.这个缺点有时很致命. 甚至有马大哈,没有更新项目仓库地址,导致提 ...

  9. 历时8个月,10w字!前端知识体系+大厂面试笔记(工程化篇)

    前言 本文是10w字 前端知识体系+大厂面试总结 的 第三篇 其余3篇为基础知识篇.算法篇.前端框架和浏览器原理篇,如果还没有阅读,建议了解下 工程化目的是为了提升团队的开发效率.提高项目的质量 例如 ...

  10. 10w字!前端知识体系+大厂面试笔记(工程化篇)

    作者主页: https://juejin.cn/user/2594503172831208 正文 工程化目的是为了提升团队的开发效率.提高项目的质量 例如大家所熟悉的构建工具.性能分析与优化.组件库等 ...

最新文章

  1. .net中日至框架log4net.dll如何使用
  2. 微软输入法TSF SampleIME 代码浅说
  3. 第三代:集成电路计算机
  4. 桌面虚拟化最佳实践4—存储规划(下)
  5. 【C++】 为什么C++空类占一个字节
  6. leetcode1721. 交换链表中的节点
  7. center os php,Center OS 7 Apache安装配置
  8. android 自定义相机,Android应用开发之android 7自定义相机预览及拍照功能
  9. [MetalKit]37-Using-ARKit-with-Metal使用ARKit与Metal
  10. 计算机窗口显示不出来的,任务栏不显示打开的窗口,详细教您打开的窗口在任务栏上显示不出来...
  11. 一个EXCEL数组公式的解析
  12. 计算机二级您是系统管理用户,全国计算机等级考试(NCRE)考务管理系统考生网报手册...
  13. 服务器端渲染-Vue SSR搭建
  14. 深入了解电容(二):电容的参数指标解释
  15. VUE引入Echarts 打包文件过大——解决方法2:CDN引入
  16. 把Excel批注的“红三角”放在单元格左上角_干货!《跟王佩丰学Excel教程》笔记...
  17. 日本电产尼得科Nidec研发出超薄直线振动马达
  18. 零基础同学自学编程的正确姿势
  19. ECCV2022 | 基于整合IMU运动动力学的无监督单目深度估计
  20. c语言程序输入中能不能输入中文,如何在C语言中输入中文?

热门文章

  1. java配置环境变量path(JAVA配置环境变量失败)
  2. Ruby On Rails的傻瓜安装
  3. Asterisk AGI 详解
  4. 管中窥豹,看看可能是谁在卖港股.
  5. 第2章 C语言的数值数据与非数值数据 (二)
  6. fd和FILE结构体
  7. 程序员眼中看到的网页是如何制作出来的?
  8. 网络基础服务器 与SMP、NUMA、MPP 三大体系结构科普
  9. 生日悖论分析python_Python关于生日悖论分析
  10. opentsdb性能测试