传统的前端项目初始流程通常是这样:javascript
能够看出,传统的初始化步骤,花费的时间并很多。并且,人工操做的状况下,总有改漏的状况出现。这个缺点有时很致命。
甚至有马大哈,没有更新项目仓库地址,致使提交代码到旧仓库,这就很尴尬了。。。
基于这些状况,编写命令行工具(CLI)的目的就很明确:html
如下是新的流程示意图:前端
如下是自动化流程图:java
从流程图能够得出两个重要的信息:node
命令行工具的角色,是负责将两个信息进行融合,提供一个交互平台给用户。git
配置信息的得到,须要靠和用户进行交互。因为程序员通常是用终端输入命令进行项目操做。因此,这里选择了两个工具进行支撑。程序员
借鉴Ruby commander理念实现的命令行执行补全解决方案github
commander
能够接收命令行传入的参数shell
例子:npm
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
用询问式的语句,与用户进行交互,接收参数
例子:
npg-cli
♫ ♫♬♪♫ npm-package-cli ♫ ♫♬♪♫
Follow the prompts to complete the project configuration.
? project name test
? version 1.0.0
? description
复制代码
前端的JavaScript 模板引擎,好比ejs,jade等。能够根据传入的参数,对模板标签进行替换,最终生成html。
若是把全部项目文件,无论文件后缀名,都当作是ejs模板,则能够在文件内容中使用ejs语法。
再根据配置信息进行替换,最终生成新文件。
其实,业界依据这个想法,已经有成熟的工具产生。
mem-fs
是对文件进行读取,存入内存中。
mem-fs-editor
是对内存中的文件信息,使用ejs语法进行编译。最后调用commit
方法输出最终文件。
提示信息,除了console.log
,还可使用色彩更丰富的chalk
。
这样,能够输出更直观、友好的提示。
文件操做,有业界成熟的shelljs
。
利用shelljs
,能够在项目中简化如下步骤:
shelljs.copySync
同步方式生成。shelljs.mkdir
进行建立如下按我作的开源项目——npm-package-cli
的创做过程进行分拆、讲解。
新建项目文件夹npm-package-cli
,并在该文件夹下运行npm init
,生成package.json
。
项目结构以下:
npm-package-cli
|-- package.json
复制代码
这里要生成的全局指令是npg-cli
。
新建文件夹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
复制代码
连接指令有两种方式:
npm link
npm install -g
两种方式,都须要在npm-package-cli
文件夹下运行,才能生效。
做用是把npg-cli
指令,指向全局的bin
文件下,实现软链。
在任意文件夹下运行命令:
npg-cli
# 输出
hello world
复制代码
到这里,一个基本的指令就算完成了,接下来是指令的工做内容细化。
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
复制代码
cli
文件#!/usr/bin/env node
const Creator = require('../src/index.js');
const project = new Creator();
project.do();
复制代码
这样,只要实现好do
方法,就能够完成npg-cli
指令的运行了。
实现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.
复制代码
要获取用户输入的信息,须要借助工具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
方法内拿到配置信息,以便进行下一步模板替换的操做。
模板文件替换,要用到工具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 => {
// 项目输出完成
});
});
}
}
复制代码
自动初始化一个项目的流程不外乎如下三点:
命令行工具,是对这三点的有效整合,串连成一个规范的流程。
命令行工具中,使用的第三方工具包,都须要用--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工具时,才会自动安装这些依赖。
.gitignore
文件npm官方是默认去除.gitignore文件的,无论你用任何方式声明.gitignore
文件须要publish
。
解决方式是:将.gitignore
更名称,好比改成gitignore
。当使用CLI工具时,再将文件名改回来。
例子:
creation.copy('gitignore', projectResolve('.gitignore'));
复制代码
我创做的npm-package-cli
,是专门用于生成我的npm package
项目的CLI工具。
生成的项目,囊括如下功能点:
coverage
CHANGELOG.md
CLI工具安装方式:
npm install -g npm-package-cli
复制代码
开源仓库地址:github.com/wall-wxk/np…
若是对你有所帮助,麻烦给个Star,你的确定是我前进的动力~
喜欢我文章的朋友,能够经过如下方式关注我: