node命令行工具之实现项目工程自动初始化的标准流程
一、目的
传统的前端项目初始流程一般是这样:
可以看出,传统的初始化步骤,花费的时间并不少。而且,人工操作的情况下,总有改漏的情况出现。这个缺点有时很致命。
甚至有马大哈,没有更新项目仓库地址,导致提交代码到旧仓库,这就很尴尬了。。。
基于这些情况,编写命令行工具(CLI)的目的就很明确:
- 用于新项目工程的初始化
- 利用工具进行初始化,可以节省项目初期的准备时间
- 避免出现改漏的情况
- 杜绝未更新项目版本仓库地址的问题
以下是新的流程示意图:
二、自动化流程分析
以下是自动化流程图:
从流程图可以得出两个重要的信息:
- 配置信息
- 模板文件
命令行工具的角色,是负责将两个信息进行融合,提供一个交互平台给用户。
三、工具准备
3.1 配置信息工具
配置信息的获得,需要靠和用户进行交互。由于程序员一般是用终端输入命令进行项目操作。所以,这里选择了两个工具进行支撑。
- commander
借鉴Ruby commander理念实现的命令行执行补全解决方案
commander
可以接收命令行传入的参数
例子:
npg-cli --help
♫ ♫♬♪♫ npm-package-cli ♫ ♫♬♪♫
Usage: npg-cli [options]
Options:
-V, --version output the version number
-h, --help output usage information
run testcli and edit the setting.
- inquirer
常用交互式命令行用户界面的集合。
inquirer
用询问式的语句,与用户进行交互,接收参数
例子:
npg-cli
♫ ♫♬♪♫ npm-package-cli ♫ ♫♬♪♫
Follow the prompts to complete the project configuration.
? project name test
? version 1.0.0
? description
3.2 模板信息工具
前端的JavaScript 模板引擎,比如ejs,jade等。可以根据传入的参数,对模板标签进行替换,最终生成html。
如果把所有项目文件,不管文件后缀名,都看成是ejs模板,则可以在文件内容中使用ejs语法。
再根据配置信息进行替换,最终生成新文件。
其实,业界依据这个想法,已经有成熟的工具产生。
- mem-fs
mem-fs
是对文件进行读取,存入内存中。
- mem-fs-editor
mem-fs-editor
是对内存中的文件信息,使用ejs语法进行编译。最后调用commit
方法输出最终文件。
3.3 提示信息工具
提示信息,除了console.log
,还可以使用色彩更丰富的chalk
。
这样,可以输出更直观、友好的提示。
3.4 文件操作
文件操作,有业界成熟的shelljs
。
利用shelljs
,可以在项目中简化以下步骤:
- 一些项目文件,不需要修改,只用直接copy。可以使用
shelljs.copySync
同步方式生成。 - 一些文件夹,需要提前构建,可以使用
shelljs.mkdir
进行创建
四、实现
以下按我做的开源项目——npm-package-cli
的创作过程进行分拆、讲解。
4.1 初始化
新建项目文件夹npm-package-cli
,并在该文件夹下运行npm init
,生成package.json
。
项目结构如下:
npm-package-cli
|-- package.json
4.2 生成全局指令
这里要生成的全局指令是npg-cli
。
4.2.1 新建执行文件
新建文件夹bin
,并在文件夹下新建名称为cli
的shell脚本文件(注意:不能有后缀名)。
cli
shell脚本文件内容如下:
#!/usr/bin/env node
console.log('hello world');
其中,#!/usr/bin/env node
是告诉编译器,以node
的方式,运行代码。
并在package.json
加入以下内容:
"bin": {
"npg-cli": "bin/cli"
}
此时,项目结构如下:
npm-package-cli
|-- bin
|-- cli
|-- package.json
4.2.2 链接指令到全局
链接指令有两种方式:
npm link
npm install -g
两种方式,都需要在npm-package-cli
文件夹下运行,才能生效。
作用是把npg-cli
指令,指向全局的bin
文件下,实现软链。
4.2.3 运行
在任意文件夹下运行命令:
npg-cli
# 输出
hello world
到这里,一个基本的指令就算完成了,接下来是指令的工作内容细化。
4.3 初始化操作类Creation
Creation
的作用是整合所有操作,并提供接口给指令文件cli
。
Creation
的结构如下:
class Creation{
constructor(){
// code
}
do(){
// code
}
// other function
}
其中do
方法暴露给脚本文件cli
调用。
Creation
类放在src/index.js
中。
此时,项目结构如下:
npm-package-cli
|-- bin
|-- cli
|-- src
|-- index.js
|-- package.json
4.4 修改cli
文件
#!/usr/bin/env node
const Creator = require('../src/index.js');
const project = new Creator();
project.do();
这样,只要实现好do
方法,就可以完成npg-cli
指令的运行了。
4.5 实现命令行参数读取
实现npg-cli --help
,需要借助上文提到的工具commander
。
新建src/command.js
文件,文件内容如下:
const commander = require('commander');
const chalk = require('chalk');
const packageJson = require('../package.json');
const log = console.log;
function initCommand(){
commander.version(packageJson.version)
.on('--help', ()=>{
log(chalk.green(' run testcli and edit the setting.'));
})
.parse(process.argv);
}
module.exports = initCommand;
此时,项目结构如下:
npm-package-cli
|-- bin
|-- cli
|-- src
|-- command.js
|-- index.js
|-- package.json
然后在Creation.do
方法内执行initCommand()
即可生效。
// src/index.js Creation
const initCommand = require('./command');
class Creation{
// other code
do(){
initCommand();
}
}
此时,运行npg-cli --help
指令,就可以看到:
Usage: npg-cli [options]
Options:
-V, --version output the version number
-h, --help output usage information
run testcli and edit the setting.
4.6 获取用户输入配置信息
要获取用户输入的信息,需要借助工具inquirer
。
新建src/setting.js
文件,文件内容如下:
const inquirer = require('inquirer');
const fse = require('fs-extra');
function initSetting(){
let prompt = [
{
type: 'input',
name: 'projectName',
message: 'project name',
validate(input){
if(!input){
return 'project name is required.'
}
if(fse.existsSync(input)){
return 'project name of folder is exist.'
}
return true;
}
},
// other prompt
];
return inquirer.prompt(prompt);
}
module.exports = initSetting;
此时,项目结构如下:
npm-package-cli
|-- bin
|-- cli
|-- src
|-- command.js
|-- index.js
|-- setting.js
|-- package.json
然后在Creation.do
方法内执行initSetting()
即可生效。
// src/index.js Creation
const initCommand = require('./command');
const initSetting = require('./setting');
class Creation{
// other code
do(){
initCommand();
initSetting().then(setting => {
// 用户输入完成后,会得到全部输入信息的json数据 setting
});
}
}
这里,inquirer.prompt
方法装载好要收集的问题后,返回的是Promise
对象。收集完成之后,要在then
方法内拿到配置信息,以便进行下一步模板替换的操作。
4.7 模板文件替换输出
模板文件替换,要用到工具mem-fs
和mem-fs-editor
。
文件操作,要用到工具shelljs
。
新建src/output.js
文件,文件内容如下(删除了部分代码,以下只是示例,完整项目看最后分享链接):
const chalk = require('chalk');
const fse = require('fs-extra');
const path = require('path');
const log = console.log;
function output(creation){
return new Promise((resolve, reject)=>{
// 拿到配置信息
const setting = creation._setting;
const {
projectName
} = setting;
// 获取当前命令行执行环境所在文件夹
const cwd = process.cwd();
// 初始化文件夹path
const projectPath = path.join(cwd, projectName);
const projectResolve = getProjectResolve(projectPath);
// 新建项目文件夹
fse.mkdirSync(projectPath);
// copy文件夹
creation.copy('src', projectResolve('src'));
// 根据配置信息,替换文件内容
creation.copyTpl('package.json', projectResolve('package.json'), setting);
// 将内存中的文件,输出到硬盘上
creation._mfs.commit(() => {
resolve();
});
});
}
module.exports = output;
output
方法的作用:
- 新建项目文件夹
- 把模板文件读取出来,根据配置信息,进行替换(调用的是
mem-fs-editor
的copyTpl
方法) - 拷贝其他文件
- 输出最终文件到硬盘上
这里最重要的一步,是调用mem-fs-editor
的方法后,要执行mem-fs-editor
的commit
方法,输出内存中的文件到硬盘上。
在Creation.do
方法中,调用output
方法即可输出新项目文件。
打开src/index.js
文件,文件内容增加如下方法:
// src/index.js Creation
const initCommand = require('./command');
const initSetting = require('./setting');
const output = require('./output');
class Creation{
// other code
do(){
initCommand();
initSetting().then(setting => {
// 用户输入完成后,会得到全部输入信息的json数据 setting
this._setting = Object.assign({}, this._setting, setting);
// 输出文件
output(this).then(res => {
// 项目输出完成
});
});
}
}
4.8 阶段小结
自动初始化一个项目的流程不外乎以下三点:
- 读取用户配置
- 读取模板文件
- 根据配置,编译模板文件,输出最终文件
命令行工具,是对这三点的有效整合,串连成一个规范的流程。
五、发布npm包的注意点
5.1 安装依赖包的方式
命令行工具中,使用的第三方工具包,都需要用--save
的方式安装。
体现在package.json
的表现是dependencies
字段:
"dependencies": {
"chalk": "^2.4.2",
"commander": "^3.0.0",
"fs-extra": "^8.1.0",
"inquirer": "^6.5.0",
"mem-fs": "^1.1.3",
"mem-fs-editor": "^6.0.0",
"shelljs": "^0.8.3"
},
这样,其他用户在安装你发布的CLI工具时,才会自动安装这些依赖。
5.2 .gitignore
文件
npm官方是默认去除.gitignore文件的,不管你用任何方式声明.gitignore
文件需要publish
。
解决方式是:将.gitignore
改名称,比如改为gitignore
。当使用CLI工具时,再将文件名改回来。
例子:
creation.copy('gitignore', projectResolve('.gitignore'));
六、项目开源
我创作的npm-package-cli
,是专门用于生成个人npm package
项目的CLI工具。
生成的项目,囊括以下功能点:
- 支持TypeScrpt
- mocha+chai自动化测试,支持使用TypeScript编写测试用例
- 支持测试覆盖率
coverage
- 支持eslint,包括对TypeScript的lint检查
- Git commit规范提交
- Git版本自动打标签(standard-version),更新
CHANGELOG.md
- 输出的npm包支持各种模块规范(AMD、CMD、CommonJS、ESModule)
CLI工具安装方式:
npm install -g npm-package-cli
开源仓库地址:https://github.com/wall-wxk/npm-package-cli
如果对你有所帮助,麻烦给个Star,你的肯定是我前进的动力~
node命令行工具之实现项目工程自动初始化的标准流程的更多相关文章
- 十分钟用 Node 命令行工具打造 react-cli 脚手架
如果你有以下想法: 每次新开项目需要copy一堆文件/文件夹,太烦!想要快速建立工程 用了vue-cli.react-app,羡慕!想要自己做一个 你只需花十分钟时间,做一个Node命令行工具,打造属 ...
- node命令行工具—cf-cli
音乐分享: 钢心 - <龙王> 初喜<冠军>后喜<龙王> (PS:听一次钢心乐队的演出后采访才知道 “龙王”隐喻的是一起喝酒的老铁....) ——————————— ...
- 【Nodejs】326- 从零开发一个node命令行工具
本文由 IMWeb 社区授权转载自腾讯内部 KM 论坛.点击阅读原文查看 IMWeb 社区更多精彩文章. 什么是命令行工具? 命令行工具(Cmmand Line Interface)简称cli,顾名思 ...
- 【amad】cookiecutter -- 一个命令行工具,使用项目模版来构建项目
动机 简介 个人评分 动机 一般的框架都有脚手架工具,但是并不会让所有人满意. 简介 cookiecutter1是一个Python实现的命令行工具,可以通过项目模版来构建项目. 它的特性包括: 跨平台 ...
- 让你如绅士般基于描述编写 Python 命令行工具的开源项目:docopt
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- 让你如“老”绅士般编写 Python 命令行工具的开源项目:docopt
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- 快速写个node命令行工具
1.package.json-bin配置 [创建bat文件,把bat路径添加到PATH中]这些固定的工作可以由npm帮我们完成.package.json中有个bin字段配置: bin: { " ...
- 手动封装一个node命令集工具
了解NPM安装模块时与项目配置文件中的bin配置发生了什么 了解nodejs在控制台中的运行环境及上下文 基于自定义命令集工具集成Yeoman 一.NPM模块安装内幕与nodejs控制台运行环境 1. ...
- 由angular命令行工具(angular-cli)生成的目录和文件
e2e目录:是端到端的测试目录,包含基本的测试桩.是用来做自动测试的. src:应用源代码目录.我们写的所有代码都应该在这里面. app:包括应用的组件和模块.我们自己写的绝大部分代码都是写在这个目录 ...
随机推荐
- Codeforces 776D:The Door Problem(DFS染色)
http://codeforces.com/problemset/problem/776/D 题意:有n个门,m个开关,每个门有一个当前的状态(0表示关闭,1表示打开),每个开关控制k个门,但是每个门 ...
- 基于缓存或zookeeper的分布式锁实现
缓存锁 我们常常将缓存作为分布式锁的解决方案,但是却不能单纯的判断某个 key 是否存在 来作为锁的获得依据,因为无论是 exists 和 get 命名都不是线程安全的,都无法保证只有一个线程可以获 ...
- WordPress教程之如何创建博客内容
上两篇教程的链接: Wordpress教程之初识WordPress Wordpress教程之如何入门WordPress Hostwinds共享主机vps限时五折优惠链接 现在,你的 WordPress ...
- 使用Mysql执行SQL语句基础操作
SQL: 又叫结构化语言,是一种用来操作RDBMS的数据库语言,在关系型数据库中都支持使用SQL语句,如oracle.mysql等等. 注意: 在关系型数据库中sql语句是通用的,而在非关系型数据库 ...
- 【基础算法-模拟-例题-*校长的问题】-C++
为什么在题目前面打上星号呢? 这道题的正解不是模拟! 正解树状数组! 正解树状数组! 正解树状数组! 重要的事情说够三遍了! 但是,歪解模拟因为数据水都能AC! 因为这道题放在模拟专题中,所以我们就讨 ...
- 个人永久性免费-Excel催化剂功能第27波-Excel工作表设置快捷操作
Excel催化剂在完善了数据分析场景的插件需求后,决定再补充一些日常绝大多数Excel用户同样可以使用到的小功能,欢迎小白入场,在不违背太多Excel最佳实践的前提下,Excel催化剂乐意为广大Exc ...
- Excel催化剂开源第2波-自动检测Excel的位数选择对应位数的xll文件安装
Excel插件的部署问题难倒了不了的用户,特别是VSTO的部署,用ExcelDna开发的xll文件部署方便,不挑用户机器环境,是其开发Excel插件的一大优势. 其开发出来的xll文件,最终还是需要考 ...
- SQLServer 问题(一)
出现这种错误: [DBNETLIB][ConnectionOpen(Connect()).]SQL Server 不存在或拒绝访问 数据库错误 原因: 1.查看是不是没有在数据库中添加数据库服务器地址 ...
- 《VR入门系列教程》之7---DK2和Crescent Bay
The DK2 于2014年春,Oculus发布了第二代开发版头显设备,代号为DK2.与DK1相比,Oculus Rift DK2的外观有很大改进,并且轻了许多,体积仍然比较大,可以罩住大部分 ...
- jsp的简介(1)
一.什么是Java Server Pages? JSP全称Java Server Pages,是一种动态网页开发技术.它使用JSP标签在HTML网页中插入Java代码.标签通常以<%开头以%&g ...