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


Posted in Javascript onAugust 12, 2019

一、目的

传统的前端项目初始流程一般是这样:

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

可以看出,传统的初始化步骤,花费的时间并不少。而且,人工操作的情况下,总有改漏的情况出现。这个缺点有时很致命。

甚至有马大哈,没有更新项目仓库地址,导致提交代码到旧仓库,这就很尴尬了。。。
基于这些情况,编写命令行工具(CLI)的目的就很明确:

用于新项目工程的初始化利用工具进行初始化,可以节省项目初期的准备时间避免出现改漏的情况杜绝未更新项目版本仓库地址的问题

以下是新的流程示意图:

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

二、自动化流程分析

以下是自动化流程图:

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

从流程图可以得出两个重要的信息:

配置信息模板文件

命令行工具的角色,是负责将两个信息进行融合,提供一个交互平台给用户。

三、工具准备

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脚本文件(注意:不能有后缀名)。
clishell脚本文件内容如下:

#!/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-fsmem-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-editorcommit方法,输出内存中的文件到硬盘上。

在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包的注意点

命令行工具中,使用的第三方工具包,都需要用--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工具时,才会自动安装这些依赖。

六、项目开源

我创作的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命令行工具之实现项目工程自动初始化的标准流程,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
JavaScript的类型转换(字符转数字 数字转字符)
Aug 30 Javascript
js arguments对象应用介绍
Nov 28 Javascript
Javascript判断对象是否相等实现代码
Mar 18 Javascript
JavaScript自动设置IFrame高度的小例子
Jun 08 Javascript
jq实现酷炫的鼠标经过图片翻滚效果
Mar 12 Javascript
javascript结合CSS实现苹果开关按钮特效
Apr 07 Javascript
Javascript实现网络监测的方法
Jul 31 Javascript
Bootstrap树形组件jqTree的简单封装
Jan 25 Javascript
JS简单封装的图片无缝滚动效果示例【测试可用】
Mar 22 Javascript
详解Vue.js组件可复用性的混合(mixin)方式和自定义指令
Sep 06 Javascript
在vue中通过render函数给子组件设置ref操作
Nov 17 Vue.js
vue 项目@change多个参数传值多个事件的操作
Jan 29 Vue.js
五分钟搞懂Vuex实用知识(小结)
Aug 12 #Javascript
no-vnc和node.js实现web远程桌面的完整步骤
Aug 11 #Javascript
Angular8基础应用之表单及其验证
Aug 11 #Javascript
浅谈javascript错误处理
Aug 11 #Javascript
axios异步提交表单数据的几种方法
Aug 11 #Javascript
node.js实现带进度条的多文件上传
Mar 27 #Javascript
基于Express框架使用POST传递Form数据
Aug 10 #Javascript
You might like
虹吸式咖啡探讨–研磨
2021/03/03 冲泡冲煮
drupal 代码实现URL重写
2011/05/04 PHP
PHP利用func_get_args和func_num_args函数实现函数重载实例
2014/11/12 PHP
PHP递归遍历多维数组实现无限分类的方法
2016/05/06 PHP
PHP实现的简单适配器模式示例
2017/06/22 PHP
PHP实现超简单的SSL加密解密、验证及签名的方法示例
2017/08/28 PHP
php生成word并下载代码实例
2019/03/15 PHP
encode脚本和normal脚本混用的问题与解决方法
2007/03/08 Javascript
jQuery中live方法的重复绑定说明
2011/10/21 Javascript
JavaScript实现三阶幻方算法谜题解答
2014/12/29 Javascript
javascript图片延迟加载实现方法及思路
2015/12/31 Javascript
微信小程序之仿微信漂流瓶实例
2016/12/09 Javascript
微信小程序 基础知识css样式media标签
2017/02/15 Javascript
Vue-router结合transition实现app前进后退动画切换效果的实例
2017/10/11 Javascript
vue-devtools的安装步骤
2018/04/23 Javascript
解决Vue.js父组件$on无法监听子组件$emit触发事件的问题
2018/09/12 Javascript
微信小程序自定义单项选择器样式
2019/07/25 Javascript
javascript合并两个数组最简单的实现方法
2019/09/14 Javascript
js实现无缝轮播图
2020/03/09 Javascript
[46:48]DOTA2上海特级锦标赛A组小组赛#2 Secret VS CDEC第三局
2016/02/25 DOTA
Python开发的单词频率统计工具wordsworth使用方法
2014/06/25 Python
Python中字典的基础知识归纳小结
2015/08/19 Python
python实现log日志的示例代码
2018/04/28 Python
在PyCharm导航区中打开多个Project的关闭方法
2019/01/17 Python
从0开始的Python学习014面向对象编程(推荐)
2019/04/02 Python
Python基于pyecharts实现关联图绘制
2020/03/27 Python
Django用户认证系统如何实现自定义
2020/11/12 Python
python爬虫scrapy图书分类实例讲解
2020/11/23 Python
CSS3五个技巧给你的网站带来出色的效果
2009/04/02 HTML / CSS
英国著名药妆店:Superdrug
2021/02/13 全球购物
房地产开发计划书
2014/01/10 职场文书
cf收人广告词大全
2014/03/14 职场文书
化学工程专业求职信
2014/08/10 职场文书
实习介绍信模板
2015/01/30 职场文书
MySQL 分区表中分区键为什么必须是主键的一部分
2022/03/17 MySQL
JS前端轻量fabric.js系列物体基类
2022/08/05 Javascript