经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Node.js » 查看文章
node命令行工具之实现项目工程自动初始化
来源:cnblogs  作者:我是leon  时间:2019/8/12 9:52:03  对本文有异议

一、目的

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

传统流程

可以看出,传统的初始化步骤,花费的时间并不少。而且,人工操作的情况下,总有改漏的情况出现。这个缺点有时很致命。
甚至有马大哈,没有更新项目仓库地址,导致提交代码到旧仓库,这就很尴尬了。。。
基于这些情况,编写命令行工具(CLI)的目的就很明确:

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

以下是新的流程示意图:

新的流程

二、自动化流程分析

以下是自动化流程图:

自动化流程分析

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

  • 配置信息
  • 模板文件

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

三、工具准备

3.1 配置信息工具

配置信息的获得,需要靠和用户进行交互。由于程序员一般是用终端输入命令进行项目操作。所以,这里选择了两个工具进行支撑。

  • commander

借鉴Ruby commander理念实现的命令行执行补全解决方案

commander可以接收命令行传入的参数

例子:

  1. npg-cli --help
  2. ? ???? npm-package-cli ? ????
  3. Usage: npg-cli [options]
  4. Options:
  5. -V, --version output the version number
  6. -h, --help output usage information
  7. run testcli and edit the setting.
  • inquirer

常用交互式命令行用户界面的集合。

inquirer用询问式的语句,与用户进行交互,接收参数

例子:

  1. npg-cli
  2. ? ???? npm-package-cli ? ????
  3. Follow the prompts to complete the project configuration.
  4. ? project name test
  5. ? version 1.0.0
  6. ? 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
项目结构如下:

  1. npm-package-cli
  2. |-- package.json

4.2 生成全局指令

这里要生成的全局指令是npg-cli

4.2.1 新建执行文件

新建文件夹bin,并在文件夹下新建名称为cli的shell脚本文件(注意:不能有后缀名)。
clishell脚本文件内容如下:

  1. #!/usr/bin/env node
  2. console.log('hello world');

其中,#!/usr/bin/env node是告诉编译器,以node的方式,运行代码。

并在package.json加入以下内容:

  1. "bin": {
  2. "npg-cli": "bin/cli"
  3. }

此时,项目结构如下:

  1. npm-package-cli
  2. |-- bin
  3. |-- cli
  4. |-- package.json

4.2.2 链接指令到全局

链接指令有两种方式:

  • npm link
  • npm install -g

两种方式,都需要在npm-package-cli文件夹下运行,才能生效。
作用是把npg-cli指令,指向全局的bin文件下,实现软链。

4.2.3 运行

在任意文件夹下运行命令:

  1. npg-cli
  2. # 输出
  3. hello world

到这里,一个基本的指令就算完成了,接下来是指令的工作内容细化。

4.3 初始化操作类Creation

Creation的作用是整合所有操作,并提供接口给指令文件cli
Creation的结构如下:

  1. class Creation{
  2. constructor(){
  3. // code
  4. }
  5. do(){
  6. // code
  7. }
  8. // other function
  9. }

其中do方法暴露给脚本文件cli调用。

Creation类放在src/index.js中。

此时,项目结构如下:

  1. npm-package-cli
  2. |-- bin
  3. |-- cli
  4. |-- src
  5. |-- index.js
  6. |-- package.json

4.4 修改cli文件

  1. #!/usr/bin/env node
  2. const Creator = require('../src/index.js');
  3. const project = new Creator();
  4. project.do();

这样,只要实现好do方法,就可以完成npg-cli指令的运行了。

4.5 实现命令行参数读取

实现npg-cli --help,需要借助上文提到的工具commander
新建src/command.js文件,文件内容如下:

  1. const commander = require('commander');
  2. const chalk = require('chalk');
  3. const packageJson = require('../package.json');
  4. const log = console.log;
  5. function initCommand(){
  6. commander.version(packageJson.version)
  7. .on('--help', ()=>{
  8. log(chalk.green(' run testcli and edit the setting.'));
  9. })
  10. .parse(process.argv);
  11. }
  12. module.exports = initCommand;

此时,项目结构如下:

  1. npm-package-cli
  2. |-- bin
  3. |-- cli
  4. |-- src
  5. |-- command.js
  6. |-- index.js
  7. |-- package.json

然后在Creation.do方法内执行initCommand()即可生效。

  1. // src/index.js Creation
  2. const initCommand = require('./command');
  3. class Creation{
  4. // other code
  5. do(){
  6. initCommand();
  7. }
  8. }

此时,运行npg-cli --help指令,就可以看到:

  1. Usage: npg-cli [options]
  2. Options:
  3. -V, --version output the version number
  4. -h, --help output usage information
  5. run testcli and edit the setting.

4.6 获取用户输入配置信息

要获取用户输入的信息,需要借助工具inquirer
新建src/setting.js文件,文件内容如下:

  1. const inquirer = require('inquirer');
  2. const fse = require('fs-extra');
  3. function initSetting(){
  4. let prompt = [
  5. {
  6. type: 'input',
  7. name: 'projectName',
  8. message: 'project name',
  9. validate(input){
  10. if(!input){
  11. return 'project name is required.'
  12. }
  13. if(fse.existsSync(input)){
  14. return 'project name of folder is exist.'
  15. }
  16. return true;
  17. }
  18. },
  19. // other prompt
  20. ];
  21. return inquirer.prompt(prompt);
  22. }
  23. module.exports = initSetting;

此时,项目结构如下:

  1. npm-package-cli
  2. |-- bin
  3. |-- cli
  4. |-- src
  5. |-- command.js
  6. |-- index.js
  7. |-- setting.js
  8. |-- package.json

然后在Creation.do方法内执行initSetting()即可生效。

  1. // src/index.js Creation
  2. const initCommand = require('./command');
  3. const initSetting = require('./setting');
  4. class Creation{
  5. // other code
  6. do(){
  7. initCommand();
  8. initSetting().then(setting => {
  9. // 用户输入完成后,会得到全部输入信息的json数据 setting
  10. });
  11. }
  12. }

这里,inquirer.prompt方法装载好要收集的问题后,返回的是Promise对象。收集完成之后,要在then方法内拿到配置信息,以便进行下一步模板替换的操作。

4.7 模板文件替换输出

模板文件替换,要用到工具mem-fsmem-fs-editor
文件操作,要用到工具shelljs

新建src/output.js文件,文件内容如下(删除了部分代码,以下只是示例,完整项目看最后分享链接):

  1. const chalk = require('chalk');
  2. const fse = require('fs-extra');
  3. const path = require('path');
  4. const log = console.log;
  5. function output(creation){
  6. return new Promise((resolve, reject)=>{
  7. // 拿到配置信息
  8. const setting = creation._setting;
  9. const {
  10. projectName
  11. } = setting;
  12. // 获取当前命令行执行环境所在文件夹
  13. const cwd = process.cwd();
  14. // 初始化文件夹path
  15. const projectPath = path.join(cwd, projectName);
  16. const projectResolve = getProjectResolve(projectPath);
  17. // 新建项目文件夹
  18. fse.mkdirSync(projectPath);
  19. // copy文件夹
  20. creation.copy('src', projectResolve('src'));
  21. // 根据配置信息,替换文件内容
  22. creation.copyTpl('package.json', projectResolve('package.json'), setting);
  23. // 将内存中的文件,输出到硬盘上
  24. creation._mfs.commit(() => {
  25. resolve();
  26. });
  27. });
  28. }
  29. module.exports = output;

output方法的作用:

  • 新建项目文件夹
  • 把模板文件读取出来,根据配置信息,进行替换(调用的是mem-fs-editorcopyTpl方法)
  • 拷贝其他文件
  • 输出最终文件到硬盘上

这里最重要的一步,是调用mem-fs-editor的方法后,要执行mem-fs-editorcommit方法,输出内存中的文件到硬盘上。

Creation.do方法中,调用output方法即可输出新项目文件。 打开src/index.js文件,文件内容增加如下方法:

  1. // src/index.js Creation
  2. const initCommand = require('./command');
  3. const initSetting = require('./setting');
  4. const output = require('./output');
  5. class Creation{
  6. // other code
  7. do(){
  8. initCommand();
  9. initSetting().then(setting => {
  10. // 用户输入完成后,会得到全部输入信息的json数据 setting
  11. this._setting = Object.assign({}, this._setting, setting);
  12. // 输出文件
  13. output(this).then(res => {
  14. // 项目输出完成
  15. });
  16. });
  17. }
  18. }

4.8 阶段小结

自动初始化一个项目的流程不外乎以下三点:

  • 读取用户配置
  • 读取模板文件
  • 根据配置,编译模板文件,输出最终文件

命令行工具,是对这三点的有效整合,串连成一个规范的流程。

五、发布npm包的注意点

命令行工具中,使用的第三方工具包,都需要用--save的方式安装。
体现在package.json的表现是dependencies字段:

  1. "dependencies": {
  2. "chalk": "^2.4.2",
  3. "commander": "^3.0.0",
  4. "fs-extra": "^8.1.0",
  5. "inquirer": "^6.5.0",
  6. "mem-fs": "^1.1.3",
  7. "mem-fs-editor": "^6.0.0",
  8. "shelljs": "^0.8.3"
  9. },

这样,其他用户在安装你发布的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工具安装方式:

  1. npm install -g npm-package-cli

开源仓库地址:https://github.com/wall-wxk/npm-package-cli
如果对你有所帮助,麻烦给个Star,你的肯定是我前进的动力~

原文链接:http://www.cnblogs.com/walls/p/11337718.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号