一, 目的
传统的前端项目初始流程一般是这样:
可以看出, 传统的初始化步骤, 花费的时间并不少. 而且, 人工操作的情况下, 总有改漏的情况出现. 这个缺点有时很致命.
甚至有马大哈, 没有更新项目仓库地址, 导致提交代码到旧仓库, 这就很尴尬了...
基于这些情况, 编写命令行工具 (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 脚本文件(注意: 不能有后缀名).
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-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 包的注意点
命令行工具中, 使用的第三方工具包, 都需要用 --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, 你的肯定是我前进的动力~
来源: https://www.cnblogs.com/walls/p/11337718.html