用commander.js构建自己的脚手架工具
随着前端技术的发展,工程化逐渐成为了一种趋势。但在实际开发时,搭建项目是一件很繁琐的事情,尤其是在对一个框架的用法还不熟悉的时候。于是很多框架都自带一套脚手架工具,在初始化前端项目的时候就可以不用自己从头搭建,只要在命令行输入初始化命令即可。
那么,如果想自行开发出这样一个命令行工具来初始化自定义项目,该怎么做呢?研究的过程中,偶然间发现了 commander.js 这个模块,可以帮助命令行工具的开发。于是边研究边整理了这篇笔记。
一、commander.js的基本用法
1. 安装
mkdir commander-example && cd commander-example
npm install commander --save
2. 使用
新建一个bin目录,然后在该目录下新建一个test.js文件,文件内容:
// 引入依赖
var program = require('commander'); // 定义版本和参数选项
program
.version('0.1.0', '-v, --version')
.option('-i, --init', 'init something')
.option('-g, --generate', 'generate something')
.option('-r, --remove', 'remove something'); // 必须在.parse()之前,因为node的emit()是即时的
program.on('--help', function(){
console.log(' Examples:');
console.log('');
console.log(' this is an example');
console.log('');
}); program.parse(process.argv); if(program.init) {
console.log('init something')
} if(program.generate) {
console.log('generate something')
} if(program.remove) {
console.log('remove something')
}
然后在命令行里输入测试:
node bin\test --help
得到如下结果:
Usage: test [options]
Options:
-v, --version output the version number
-i, --init init something
-g, --generate generate something
-r, --remove remove something
-h, --help output usage information
Examples:
3. API解析
· version
作用:定义命令程序的版本号
用法示例:.version('0.0.1', '-v, --version')
参数解析:
① 版本号<必须>
② 自定义标志<可省略>:默认为 -V 和 --version
· option
作用:用于定义命令选项
用法示例:.option('-n, --name<path>', 'name description', 'default name')
参数解析:
① 自定义标志<必须>:分为长短标识,中间用逗号、竖线或者空格分割;标志后面可跟必须参数或可选参数,前者用 <> 包含,后者用 [] 包含
② 选项描述<省略不报错>:在使用 --help 命令时显示标志描述
③ 默认值<可省略>
· command
作用:添加命令名称
用法示例:.command('rmdir <dir> [otherDirs...]', 'install description', opts)
参数解析:
① 命令名称<必须>:命令后面可跟用 <> 或 [] 包含的参数;命令的最后一个参数可以是可变的,像实例中那样在数组后面加入 ... 标志;在命令后面传入的参数会被传入到 action 的回调函数以及 program.args 数组中
② 命令描述<可省略>:如果存在,且没有显示调用action(fn),就会启动子命令程序,否则会报错
③ 配置选项<可省略>:可配置noHelp、isDefault等
· description
作用:定义命令的描述
用法示例:.description('rmdir desc')
· action
作用:定义命令的回调函数
用法示例:.action(fn)
· parse
作用:用于解析process.argv,设置options以及触发commands
用法示例:.parse(process.argv)
二、使用commander.js开发本地模块init-commander-tool
1. 新建目录如下:
init-commander-tool
|-bin
|-init-project.js
|-lib
|-install.js
|-templates
2. 运行 npm init 来初始化项目,项目名称设置为init-commander-tool,并安装依赖包:
```
"devDependencies": {
"chalk": "^2.4.1",
"commander": "^2.15.1",
"fs-extra": "^6.0.1",
"path": "^0.12.7",
"through2": "^2.0.3",
"vinyl-fs": "^3.0.3",
"which": "^1.3.1"
}
```
npm init
npm install
3. init-porject.js中代码如下
// 指定脚本的执行程序
#! /usr/bin/env node // 引入依赖
var program = require('commander');
var vfs = require('vinyl-fs');
var through = require('through2');
const chalk = require('chalk');
const fs = require('fs-extra');
const path = require('path'); // 定义版本号以及命令选项
program
.version('1.0.0')
.option('-i --init [name]', 'init a project', 'myFirstProject') program.parse(process.argv); if(program.init) {
// 获取将要构建的项目根目录
var projectPath = path.resolve(program.init);
// 获取将要构建的的项目名称
var projectName = path.basename(projectPath); console.log(`Start to init a project in ${chalk.green(projectPath)}`); // 根据将要构建的项目名称创建文件夹
fs.ensureDirSync(projectName); // 获取本地模块下的demo1目录
var cwd = path.join(__dirname, '../templates/demo1'); // 从demo1目录中读取除node_modules目录下的所有文件并筛选处理
vfs.src(['**/*', '!node_modules/**/*'], {cwd: cwd, dot: true})
.pipe(through.obj(function(file, enc, callback){
if(!file.stat.isFile()) {
return callback();
} this.push(file);
return callback();
}))
// 将从demo1目录下读取的文件流写入到之前创建的文件夹中
.pipe(vfs.dest(projectPath))
.on('end', function() {
console.log('Installing packages...') // 将node工作目录更改成构建的项目根目录下
process.chdir(projectPath); // 执行安装命令
require('../lib/install');
})
.resume();
}
· #! /usr/bin/env node
指定脚本的执行程序,这里是node。也可以用!/usr/bin/node,但如果用户将node安装在非默认路径下,会找不到node。所以最好选择用env(包含环境变量)来查找node安装目录。
· vinyl-fs:
Vinyl用于描述文件的元数据对象;该模块主要暴露了两个方法src和dest,它们各自返回数据流;不同的是前者提供Vinyl对象,后者使用Vinyl对象;简单的说就是一个读取文件,另一个往磁盘写文件
param@src:src(globs[, options])
第一个参数为字符串或字符串数组,表明文件位置。如果是数组,则会按照从前到后的顺序来执行,但带有!(非)符号的路径应该放在后面。第二个参数为选项对象,查看具体配置
param@dest:dest(folder[, options])
第一个参数为文件夹路径或函数,如果是后者,则它会被用于处理每一个Vinyl文件对象,且该函数必须返回一个文件夹路径。第二个参数为选项对象,查看具体配置。该方法返回文件对象流,并将他们写入磁盘以及传递到管道下游,所以你可以继续在管道中进行操作。如果文件拥有symlink属性,就会创建一个符号链接。
· through2:
through2主要是对node中streams.Transform的简单封装,让其使用起来更加简单。具体用法可查看through2**
4. install.js中代码如下:
// 引入依赖
var which = require('which');
const chalk = require('chalk'); var childProcess = require('child_process'); // 开启子进程来执行npm install命令
function runCmd(cmd, args, fn) {
args = args || [];
var runner = childProcess.spawn(cmd, args, {
stdio: 'inherit'
}); runner.on('close', function(code) {
if(fn) {
fn(code);
}
})
} // 查找系统中用于安装依赖包的命令
function findNpm() {
var npms = ['tnpm', 'cnpm', 'npm'];
for(var i = 0; i < npms.length; i++) {
try {
// 查找环境变量下指定的可执行文件的第一个实例
which.sync(npms[i]);
console.log('use npm: ' + npms[i]);
return npms[i]
}catch(e) {
}
}
throw new Error(chalk.red('please install npm'));
} var npm = findNpm();
runCmd(which.sync(npm), ['install'], function() {
console.log(npm + ' install end');
})
三、构建项目demo
此demo用于初始化项目副本,因此可以根据自己的需要构建。我们可以利用一些脚手架工具来初始化项目,也可以自己一步步搭建。
将搭建的项目复制进templates目录下(node_modules下的文件及文件夹可以不用复制),并重命名为demo1;然后在init-commander-tool目录下运行 node bin\init-project --help 测试所有命令是否能正常显示。
四、全局使用
在package.json里面添加bin字段:
// bin项用来指定各个内部命令对应的可执行文件的位置
"bin": {
"initP": "./bin/init-project"
},
然后在init-commander-tool目录下运行 npm link,将本地模块链接到全局环境下,这样就可以在任何地方使用initP命令了。
在命令行中键入:initP --help,出现以下内容则代表可以正常使用。
Usage: init-project [options]
Options:
-V, --version output the version number
-i --init [name] init a project (default: myFirstProject)
-h, --help output usage information
最后在需要初始化项目的地方运行 initP --init myProject 即可,项目名称可以自己定义。
用commander.js构建自己的脚手架工具的更多相关文章
- commander.js 制作简易的 MINA CLI 脚手架
出发点并不是小程序本身,是想要做一个脚手架(command-line interface),看过 VUE / REACT 脚手架,觉得很厉害,但是并不太知道里面是怎么做成的,所以最近研究了研究,看能不 ...
- 【AMAD】cookiecutter-django -- 是一个构建Django项目的脚手架工具
动机 简介 个人评分 动机 Django内置的命令django-admin startproject其实并不好用,在你上线之前八成已经把它改的面目全非了. 简介 cookiecutter-django ...
- 【vue系列之一】使用vue脚手架工具搭建vue-webpack项目
对于Vue.js来说,如果你想要快速开始,那么只需要在你的html中引入一个<script>标签,加上CDN的地址即可.但是,这并不算是一个完整的vue实际应用.在实际应用中,我们必须要一 ...
- 1.1.1 vue-cli脚手架工具
参考文档: windows下npm安装vue(以下教程大部分都是参考这篇博客的,按照着这篇博客自己实现了一遍) npm安装vue-cli脚手架 一.前言 npm:nodejs下的包管理器,安装好nod ...
- Vue.js 相关知识(脚手架)
1. vue-cli 简介 Vue-cli 是 vue的设计者,为提升开发效率而提供的一个脚手架工具,可通过vue-cli快速构造项目结构 2. vue-cli 安装步骤 安装npm 或 cnpm n ...
- 使用vue脚手架工具搭建vue-webpack项目
对于Vue.js来说,如果你想要快速开始,那么只需要在你的html中引入一个<script>标签,加上CDN的地址即可.但是,这并不算是一个完整的vue实际应用.在实际应用中,我们必须要一 ...
- Web API 2 入门——使用ASP.NET Web API和Angular.js构建单页应用程序(SPA)(谷歌翻译)
在这篇文章中 概观 演习 概要 由网络营 下载网络营训练包 在传统的Web应用程序中,客户机(浏览器)通过请求页面启动与服务器的通信.然后,服务器处理请求,并将页面的HTML发送给客户端.在与页面的后 ...
- 用vue快速开发app的脚手架工具
前言 多页面应用于结构较于简单的页面,因为简答的页面使用router又过于麻烦.本脚手架出于这样的场景被开发出来. 使用脚手架搭配Hbuilder也同样可以快速使用vue开发安卓和IOS APP. 本 ...
- 使用webpack+vue.js构建前端工程化
参考文章:https://blog.csdn.net/qq_40208605/article/details/80661572 使用webpack+vue.js构建前端工程化本篇主要介绍三块知识点: ...
随机推荐
- mssql sqlserver null数据类型专题
摘要: 下文将详细讲述sql server NULL(空值)的相关知识,如下所示: 实验环境: sql server 2008 R2 NULL(空值)简介: mssql sqlserver null数 ...
- SpringBoot自定义属性配置以及@ConfigurationProperties注解与@Value注解区别
我们可以在application.properties中配置自定义的属性值,为了获取这些值,我们可以使用spring提供的@value注解,还可以使用springboot提供的@Configurati ...
- 【PAT】B1008 数组元素循环右移问题
猥琐方法 直接分成两部分输出数组元素,注意空格的问题 #include<stdio.h> int arr[101]; void Priarr(int a,int b){ if(a<= ...
- LeetCode算法题-Power Of Two(Java实现)
这是悦乐书的第194次更新,第200篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第56题(顺位题号是231).给定一个整数,写一个函数来确定它是否是2的幂.例如: 输入 ...
- Ubuntu 12.04上安装R语言
Ubuntu 12.04上安装R语言 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ R的安装 sudo gedit /etc/apt/sources. ...
- 序列对象(bytearray, bytes,list, str, tuple)
列表: L.append(x) # x追加到L尾部 L.count(x) # 返回x在L中出现的次数 L.extend(m) # Iterable m的项追加到L末尾 L += m # 功能同L.ex ...
- python mysql数据库操作
一.pymysql 模块安装(本文博客推荐:https://www.cnblogs.com/clschao/articles/10023248.html) pip3 install pymysql 二 ...
- 什么是数据库ACID?
原子性:由于操作失败导致的数据碎片错误: 一致性:由于并发导致的数据库数据错误(与预期不一致): 隔离性:由于并发导致的当前使用数据(应用端)错误: 事务在当今的企业系统无处不在,即使在高并发环境下也 ...
- HTTP请求报文解剖
转自:https://www.iteye.com/topic/1124408 HTTP请求报文由3部分组成(请求行+请求头+请求体): 下面是一个实际的请求报文: ①是请求方法,GET和POST是最常 ...
- 在Ubuntu18.04上使用Anaconda(python3.7)环境中安装tensorflow1.13.1
由于清华镜像源迟迟没有更新tensorflow1.13.1导致python3.7不能使用tensorflow 这里有一个解决方法 管理员模式打开(一定要管理员模式 不然会导致权限不足) 输入 pip ...