自定义命令行

创建阶段

1.首先创建一个目录 my-climy-cli 中创建一个 index.js 作为入口文件

2.通过 npm init -y (yes) 初始化一个 package.json 文件

3.在主入口文件 index.js 第一行加入 #!/usr/bin/env node 用于指明该脚本文件要使用node来执行。

/usr/bin/env 用来告诉用户到path目录下去寻找node,#!/usr/bin/env node 可以让系统动态的去查找node,目的是 解决不同电脑node存放路径不一致问题。

注意: 该命令必须放在第一行, 否者不会生效

4.接着,在初始化好的 package.json 中创建一个 属于自己的命令,如下图所示。

5.以上都完成过后通过一个指令 npm link,完成之后会看到自己本地npm目录下多出了三个文件(如下图所示)

测试输出

通过自定义的命令 mycli 会看到如约而至的输出了 尝试使用 node index.js

创建自己的CLI脚手架(scaffold)过程。

在前面的基础铺垫完成后,我们将进入正式的创建一个属于自己的脚手架(scaffold)过程。

在这里推荐一个帮助开发者快速搭建一个属于自己的命令行工具 Commander

首先安装:npm install commander

获取属于自己的版本号

一个属于自己最不能失去的就是自己的版本号,就好像自己的老婆一样(不要经常更换哦)

首先我们先获取到 package.json 中 属于自己的版本号

我们通过 cumin --help 可以看到当前只有两种指令可选择玩弄。

有点孤独寂寞,我们还是多自定义几个好伙伴一个玩吧!!毕竟CSGO都是 5 v 5 的。

自定义属于自己的 -h --help

看图再解释,方面又理解

创建好后,使用 cumin --help 会发现多了两种选择

program有个方法option是用来自定义一个属于自己的 -help

program.option(‘标记’, '描述’)

-d--dest 是一样的 -d 只不过是简写

<dest> 表示你将要干什么,例如在 src 目录下创建一个 components 目录,可以这样写(目前还没实现这个功能,这里只是测试,在后面会实现。)(program.option(’’-c --cumin ‘’) 也是一样的)。

目前 Options 中只有四个选项,那么将来可能有很多,代码会冗余,所以我们创建放入一个新的目录内。

首先创建一个lib目录

lib中创建一个core目录

core下创建一个core.js

接下来分割代码 如下图所示

现在已经完成了 --help 自定义指令的方法了。

类似于 (vue create my-project) 该如何通过自定义命令创建?

首先我们得完成以下五个步骤

创建解析 create 命令 (在 create.js 文件中完成)

我们的项目是基于可扩展的所以我们将在lib目录下的core中接着创建一个 create.js 文件,并写入以下代码。

const program = require('commander')const { createProjectAction } = require('./action')const createProjects = () => {program.command('create <project> [others...]').description('clone a repository into a folder') // 英译:将存储库克隆到文件夹中.action((project, outhers) => {console.log(project, outhers) // my-cuminProject,[ 'abc', 'cba' ]})
}module.exports = createProjects
  • command(‘创建命令的名称’) 在终端就可以这样使用 cumin create <项目名称> 紧跟项目名称后面写的任何内容都会被保存到[others...]中。
  • description(‘描述信息,对create指令的描述信息’)
  • action (project, outhers) 是一个回调函数
    • project 返回创建目录的名称
    • outhers 返回一个数组,紧跟 “项目名称” 后面所有的值 (abc cba) (这个参数不常用)

导出后在index.js主入口文件中导入后,此时我们可以通过 cumin create my-cuminProject abc cba 来打印一下信息,会通过action函数返回。

Return(返回)

此时我们得注意要到一个可扩展性的问题, 将来我们都要在 action 回调函数中操作,这样的话避免不了会产生很多代码,所以我们再拆出一个文件来,专门用于处理action的回调函数。

步骤:

  1. lib目录下的core中再创建一个 action.js
  2. 将action回调函数拆出来,放入 action.js

如图:

从此 action回调函数 我们就可以在 action.js 中处理了。

现在为止我们的目录是这样的:你也是这样的吗?

接下来我们就可以在 action.js 中开始编码

通过 download-git-repo 从代码仓库中下载代码

首先安装 npm install download-git-repo

简单理解 npm install download-git-repo:从远程代码仓库中下载代码

  // 引入 download-git-repo
const download =require('download-git-repo')const createProjectAction = (project, outhers) => {// 1. clone 克隆项目// download用法:download("克隆的地址ClonePath", "类型", callback(回调函数))download() // 2. 执行 npm install// 3. 执行 npm run serve// 4. 打开浏览器
}module.exports = {createProjectAction
}

download-git-repo官网原图

我们看到都是以回调函数作为我们的返回结果的,那么将一定会有很多回调函数,那么就会产生我们常见的 回调地狱 这个说法,然而我们的 download-git-repo 作者并没有提供 promise async await 等方式帮我们处理,回调地狱,所以我们得自己提供以下方式解决。首先通过 NodeJs内部的utilAPI 来解决。

const { promisify } = require('util') 被包裹的函数会以 promise 风格返回

现在我们可以通过这种方式返回const download = promisify(require('download-git-repo'))

此时我们的 download 会以 promise 风格返回了, 接着我们可以利用ES7 中的 async await 来异步执行,同步加载了.

Example:

const { promisify } = require('util')
const download = promisify(require('download-git-repo'))// callback -> promisify(callback) -> promise -> async await
const createProjectAction = async (project, outhers) => {// download用法:download("克隆的地址ClonePath", "克隆后存放的地址", callback(回调函数))await download() // 2. 执行 npm install// 3. 执行 npm run serve// 4. 打开浏览器
}module.exports = {createProjectAction
}

download函数中克隆的地址ClonePath地址模板我们可能不是唯一的,还是这句话我们要及提供可扩展性所以我们继续在lib目录下创建一个 config 目录,并在config目录中创建一个 repo-config.js存放地址。

如下图所示

接着在 repo-config.js 导入来自githubcoderwhy 老师的 vue 模板.

direct:直接使用http从直接url下载

// clone path
let vueRepo = 'direct:https://github.com/lzw1254348304/hy-vue-temp.git'module.exports = {vueRepo
}

回到 action.js 中写下

const { promisify } = require('util')
const download = promisify(require('download-git-repo'))// clone path
const {vueRepo
} = require('../config/repo-config')// callback -> promisify(callback) -> promise -> async await
const createProjectAction = async (project) => {console.log('In the process of cloning...')// 1. clone 克隆项目/*** vueRepo: 克隆地址* project: 项目名称* { clone: true }: 克隆整个项目* callback: 回到函数*/await download(vueRepo, project, { clone: true }, (err) => {if (!err) {console.log('cloning finish')}})// 2. 执行 npm install// 3. 执行 npm run serve// 4. 打开浏览器
}module.exports = {createProjectAction
}

现在我们在任意目录下输入 cumin create mydemo 就会生成一个 vue 模板了。

Vue模板虽然说生成了,但还是需要我们自己手动 npm install 因为还有依赖没有安装,为了更加的方便快捷,我们接下来要完成的是。

创建Vue模板时自动帮我们 npm install 安装依赖

我们将来会在 cumin create <my project>时候自动帮我们执行 npm install npm run serve open browser 所以代码会比较多,又说道可扩展性与可维护性了,所以我们得再创建一个util 目录。并在 util 目录下创建一个 terminal.js

先来理解一下概念:

{当前进程} 正在帮助我们执行JavaScript代码(忙碌中的),所以如果我们要执行{ npm install }时,可以使用
NodeAPI中的 { child-process(子进程模块) } 创建子进程来帮我们执行。child-process:child_process (子进程模块)一旦使用 child_process 里面的某个方法时,是会为我们开启一个新进程的。通常我们在执行 { npm install } { npm run serve } 时,本质上都是会为我们开启另外一个进程,在另外一个进程中帮助我们完成相关的任务。

好了,理解之后我们来介绍一下child-process 中的 spawn 方法:

spawn(command, args, options) 会异步地衍生子进程,且不阻塞 Node.js 事件循环。

详情见: http://nodejs.cn/api/child_process.html#child_process_child_process_spawn_command_args_options

terminal.js部分代码解析

// `sqawn()函数` 本质上会开启一个进程,并返回一个子进程(childProcess)
const childProcess = spawn()
  • spawn简单理解就是创建一个进程并返回给childProcess,里面有三个参数 spawn('使用什么命令','执行方式','当前目录')

    例如

      伪代码:spawn('npm', ['install'], { cwd: <myProject> })
    
  • childProcess进程就是帮我们自动执行 npm install 的,所以我们希望在执行chindProcess进程时,打印执行过程中的信息(在执行命令过程中,进程会打印很多的信息,在执行 npm install 的时候终端会打印很多信息(默认是不会打印的)

  • 为了让它在执行过程中能显示很多打印信息我们可以这样做:

       /*** `stdout` : 标准输出流* `stderr` : 标准错误流* `stdout`中返回一个 `pipe` 函数,简称`管道`* 这两个标准流是存在于 `childProcess` 进程中的,接下来我们可以这样使用它*//*** `process` 对象是一个全局变量,提供了有关当前 `Node.js` 进程的信息并对其进行控制。 *  作为全局变量,它始终可供 `Node.js` 应用程序使用,无需使用 `require()`。 它也可以使用 *     `require()` 显式地访问。*/childProcess.stdout.pipe(process.stdout)childProcess.stderr.pipe(process.stderr)
    
  • /*** `npm install` 是处于一个阻塞的状态的,执行完毕后,我们接下来要执行 `npm run serve` * 为了可以让终端自动执行 npm install 完毕后,执行 npm run serve,我们该这样做:*/
    childProcess.on('close', () => {resolve('npm install execution')
    })
    

terminal.js中的完整代码

// 执行终端命令相关代码
// Spawn 会衍生子进程,且不会阻塞 Node.js 事件循环
const { spawn } = require('child_process')const execCommand = (...args) => {return new Promise((resolve, reject) => {// `sqawn()函数` 本质上会开启一个进程,并返回一个 子进程(childProcess)const childProcess = spawn(...args)// 执行过程中能显示很多打印信息childProcess.stdout.pipe(process.stdout)childProcess.stderr.pipe(process.stderr)childProcess.on('close', () => {resolve('npm install execution')})})
}module.exports = {execCommand
}

action.js导入

const { promisify } = require('util')
const download = promisify(require('download-git-repo'))// clone path
const { vueRepo } = require('../config/repo-config')// 自动执行 npm install
const { execCommand } = require('../util/terminal')// callback -> promisify(callback) -> promise -> async await
const createProjectAction = async (project) => {console.log('In the process of cloning...')// 1. clone 克隆项目await download(vueRepo, project, { clone: true })// 2. 执行 npm installlet exec = process.platform === 'win32' ? 'npm.cmd' : 'npm'const execution = await execCommand(exec, ['install'], { cwd: `./${project}` })console.log(execution)// 3. 执行 npm run serveexecCommand(exec, ['run', 'serve'], { cwd: `./${project}` })// 4. 打开浏览器`见下文`
}module.exports = {createProjectAction
}
  • 其实在我们执行npm的时候,系统会默认帮我们执行npm.cmd的,注意在execCommand的第一个参数中,在Mac\linux操作系统中执行没问题,是因为Mac\linux操作系统会默认自动帮我们调用 npm.cmd但是在 window 操作系统中会报错的,这是因为 window 操作系统 默认不会帮我们调用 npm.cmd
  • 解决:
  //  兼容 window 操作系统let exec = process.platform === 'win32' ? 'npm.cmd' : 'npm'

自动运行 npm run serve

利用上面封装的execCommand函数我们可以加以利用

  // 3. 执行 npm run serveexecCommand(exec, ['run', 'serve'], { cwd: `./${project}` })

自动打开浏览器

因为在下载的 vue 模板中,我们已经加上了 --open "serve": "vue-cli-service serve --open" 所以自动运行完npm run serve,后会自动打开默认浏览器。

创建属于自己脚手架的模板

创建四个模板,这是我们会使用到ejs这个模板库

vue-component.vue.ejs

vue-router.js.ejs

vue-vuex.js.ejs

vue-vuex-types.js.ejs

首先创建一个 VUE 组件模板,例:

vue-component.vue.ejs

<template><div class="<%= data.lowerName %>"><h2>{{ message }}</h2></div>
</template><script>export default {name: "<%= data.name %>",components: {},mixins: [],props: {},data: function() {return {message: "Hello <%= data.name %>"}},created: function() {},mounted: function() {},computed: {},methods: {}}
</script><style scoped>.<%= data.lowerName %> {}
</style>

接着我们来创建相对应的命令来生成组件,还是之前那句话 为了可扩展性

  program.command('cVue <project> [others...]').description('create a VUE component template').action(createVueComponentTemplateAction)

现在我们就可以通过 cumin cVue HelloWrold指令就可以创建一个HelloWorld.vue组件了,我们来看看 createVueComponentTemplateAction函数是如何实现的。

// 导入 compiler函数
const { compiler } = require('../util/utils')// create VUE component templateconst createVueComponentTemplateAction = async (project) => {// 编译 ejs 模板const result = await compiler( // utils.js 导出'vue-template.vue.ejs', { name: project, lowerName: project.toLowerCase() })console.log(result)// 将 result 写入 .vue 文件中
TODO................// 将 .vue 文件放入文件夹中
}

我们在 util目录下创建了 utils.js文件,如下代码

compiler函数

const ejs = require('ejs')
const path = require('path')const compiler = (templateName, data) => {// 根据用户执行的命令名称,拿到指定路径的模板,进行渲染创建const templateCurrentPath = `../templates/${templateName}`const templateAbsolutePath = path.resolve(__dirname, templateCurrentPath)// 读取 HTML 标签return new Promise((resolve, reject) => {ejs.renderFile(templateAbsolutePath, { data }, {}, (err, result) => {if (err) {reject(err)return}resolve(result)})})
}// 导出 compiler 函数
module.exports = {compiler
}

接下来我们要将它写入指定的文件内,通过Node API 中的 fs 模块写入文件内。

compiler函数返回的内容是Vue模板,所以我们可以将Vue模板写入到Components目录下,为什么是写到Components目录下而不是其他目录呢?是因为 cumin cVue <my-project>这个指令就是为的帮我们快速的在Components目录下创建.vue文件。

fs.writeFile('components目录下',result)

还有其他三个,乃至自己想出来的,都可以继续写下去了。都是一样的逻辑套路。

完成代码:https://github.com/cumin-coder/cumin-cli

弄一个属于自己的vue-cli脚手架。相关推荐

  1. 猿创征文|【Vue五分钟】 Vue Cli脚手架创建一个项目

    目录 前言 一.创建项目的操作步骤 选择路由模式 选择CSS预编译器 选择如何存放配置 自动下载项目所需的包文件 二.启动vue项目 1.项目目录 2.启动项目 3.浏览器打开项目首页界面 三.项目的 ...

  2. Vue2.x 核心基础(Vue概述,Vue基本使用,@vue/cli脚手架,Element-UI 的基本使用,Vue模板语法)

    1. Vue概述 尤雨溪:Vue.js的创建者 2014年2月,Vue.js正式发布 2015年10月27日,正式发布1.0.0 2016年4月27日,发布2.0的预览版本 Vue:渐进式JavaSc ...

  3. 如何查看vue版本号以及vue/cli脚手架版本号

    查看vue版本号 方法一:直接在项目的package.json文件,找到dependencies就能看到了 方法二:输入命令npm ls vue (或者npm list vue) 查看vue/cli脚 ...

  4. 1. Vue CLI脚手架

    1.1 介绍 Vue脚手架指的是vue-cli,它是一个专门为单页面应用快速搭建的脚手架,它可以轻松的创建新的应用程序而且可用于自动生成vue和webpack的项目模板,是Vue官方提供的标准化开发工 ...

  5. 前端框架vue04~~vue.cli脚手架的基本使用

    文章目录 [1. Vue-CLI脚手架](https://cli.vuejs.org/zh/guide/) [2. 安装](https://cli.vuejs.org/zh/guide/install ...

  6. Vue学习(十一)Vue CLI脚手架

    文章目录 初始化脚手架 说明 步骤 Vue 脚手架创建项目文件说明 render函数 脚手架中不同版本的Vue 脚手架启动注意 vue.config.js配置文件 初始化脚手架 说明 Vue脚手架是V ...

  7. Vue 全家桶之 vue cli (脚手架)

    一.Vue cli 简介 cli --------------------- c : command (命令) l : line ( 行 ) i : interface ( 接口 ) 命令行接口 命令 ...

  8. vue CLI脚手架搭项目

    1.安装 node.js环境 官网下载:https://nodejs.org/en/download/ 一直默认就行,路径可以改变但要记得到 安装完成后cmd打开终端,输入node -v ,npm - ...

  9. Vue CLI 脚手架详解:快速构建 Vue.js 项目的利器

    目录 一.安装和创建项目 二.项目结构 三.开发和构建 四.插件和配置 Vue CLI 是 Vue.js 官方提供的脚手架工具,它可以帮助开发者快速搭建 Vue.js 项目的基础结构,并提供了丰富的功 ...

  10. #VUE CLI 脚手架的安装及初识脚手架(一)

    目录 vue cli 安装vue cli cli是什么? 1.CLI英文为Command-Line Interface,翻译为命令行工具,通俗来讲为脚手架. 2.使用vue-cli可以快速搭建Vue开 ...

最新文章

  1. python随机生成30个8_Python生成六万个随机,唯一的8位数字和数字组成的随机字符串实例...
  2. oracle构造过程实例
  3. est.java 2 错误 找不到符号_在命令行上用junit-4.12.jar和hamcrest-core-1.3.jar编译并运行测试?错误:是抽象的,无法实例化,找不到符号...
  4. 直播 | WWW 2021:用先验知识指导BERT注意力机制的语义文本匹配
  5. Redis淘汰删除策略
  6. Android + Eclipse + PhoneGap 2.9.0 安卓最新环境配置,部分资料整合网上资料,已成功安装....
  7. 马云:未来无工可打,人工智能发展红利还在10年以后
  8. jpanel把原本内容覆盖掉_暖冬遇上倒春寒,花被大雪覆盖,小心一夜回到解放前...
  9. 离线安装老版本android sdk,亲测,linux、windows、mac通用
  10. 编程实现英语句子反转python_Python字符串处理实现单词反转
  11. Spring中FrameMaker中文乱码
  12. C++MYSQL:获取表结构:MYSQL_FEILD
  13. 2016 China CADCG 参会总结 -- day1
  14. 元宇宙的“42条共识” ,全网阅读量超1000万!
  15. Review Troller
  16. weblogic 启动常见错误解决
  17. 富文本编辑器 Kindeditor 的使用和 常见错误
  18. 【菜鸟进阶之路】P2141 珠心算测验 - 洛谷
  19. 安装hmc会依赖bios时间吗_Vmware 5.5下安装HMC7.3.2并安装Vmware tools
  20. EXCEL---VBA

热门文章

  1. Python源码解析:内存管理(DEBUG模式)的几个理解点
  2. 北京大学给所有Python自学者,分享的一份Python书单,入门的小白不可不读!
  3. 腾讯云服务器开Minecraft配置选择说明
  4. mysql中的restrict_Mysql 的 Cascade/Restrict/No action
  5. java爬取并下载US六仔搭建酷狗TOP500歌曲
  6. 注册表文件缺失oracle,win7开机提示由于系统注册表文件丢失或损坏因此无法加载怎么办...
  7. 喜时不诺,怒时不争,哀时不语,倦时有终
  8. ECMA5forEach
  9. 【图像处理知识复习】14 Laplacian 二阶微分算子Matlab实现
  10. Simply JavaScript