Webpack插件(Plugin)开发——获取git版本信息

2,635 阅读2分钟

起因

有一个需求是在单页面应用(vue,react)执行打包的时候可以自动生成一个版本信息输出到目标目录下(dist)。这样在线上出现Bug时解决问题可以知道在哪个版本下进行修改和定位问题。

解决过程

手工添加版本信息

当然可以手工添加一些版本信息在输出目录下的version.txt中,但每次发布包都需要手动添加,很显然不符合程序员的作风,重复的事情当然需要用代码替代解决

写一个nodejs脚本

  • 首先想到的是可以写一个nodejs来调用git相关命令获取当前版本信息,然后写入到version.txt中,执行的时机是执行npm run build之后。package.json修改如下:
"script:": {
    "build": "script/build.js && node script/version.js"
}
  • js脚本大致如下:
// version.js
const execSync = require('child_process').execSync;
const fs = require('fs');

// 获取git信息的相关命令
const COMMITHASH_COMMAND = 'rev-parse HEAD';
const VERSION_COMMAND = 'describe --always';
const BRANCH_COMMAND = 'rev-parse --abbrev-ref HEAD';
try {
    const d = new Date();
    const versionStr =`
        Revision: ${execSync(`git ${COMMITHASH_COMMAND}`)}
        Branch: ${execSync(`git ${BRANCH_COMMAND}`)}
        Release: ${execSync(`git ${VERSION_COMMAND}`)}
        X-PackingTime: ${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}${d.getHours()}:${d.getMinutes()}
        `;
    fs.writeFileSync(`dist/version.txt`, versionStr);
} catch (e) {
    throw new Error(e);
}
  • 有了以上两个步骤直接执行npm run build执行便可以在执行Webpack打包后自动执行version.js脚本,自动将版本信息写入到dist/version.txt中,解放双手。

通过以上两个步骤的分析已经可以实现一个自动生成版本信息的脚本,但仍然存在一些问题:

  • 脚本执行方式不灵活,且无法传入参数来适应不同项目
  • 每个项目都需要将version.js文件复制到指定的目录,然后添加到需要执行的命令行后面使用node执行
  • 项目不同可能需要单独手动维护脚本,这样并不利于该脚本的维护,如果有统一的修改则需要涉及到每个项目都进行修改,仍有很多重复的工作要做

通过Webpack插件写入版本信息

以上分析我们的主角就呼之欲出了,我的解决方案是:

  • 编写一个Webpack插件的npm包,利用webpack提供的钩子函数在构建完毕后执行获取版本信息并写入到本地
  • 发布到公司的私有仓库上(确实也没达到可以发布到公网npm的高度😅)
  • 使用npm安装后直接配置到Webpackplugins里面并可以传入不同参数来适应不同的项目

要想开发一个npm包并可以发布到npm或者npm私有仓库需要了解一些npm包的开发和发布流程

开发流程(使用npm link):

  • 在包跟目录下npm link,将开发的npm包建立一个全局链接
  • 在使用的项目根目录下npm line package-name,package-name是package.json中配置的包名
  • 取消npm unlink

发布流程:

  • 准备npm账号,如果是私服的话需要将本地npm仓库地址设置为私服地址
  • 登录npm login
  • 发布npm publish
  • 使用npm install xxx [options]

编写Webpack插件

学习新的东西最好的老师应该就是官方提供的文档了,通过文档可以快速的写出自己需要的插件功能

  • 首先插件均有一个apply方法,apply方法有一个compiler参数:
const pluginName = 'RepoRevisionWebpackPlugin';

// 获取git信息的相关命令
const COMMITHASH_COMMAND = 'rev-parse HEAD';
const VERSION_COMMAND = 'describe --always';
const BRANCH_COMMAND = 'rev-parse --abbrev-ref HEAD';

class RepoRevisionWebpackPlugin {
  constructor (options = {}) {}
  apply(compiler) {
    // compiler编译器实例有webpack执行的各个生命周期的钩子
    // tap方法第一个参数便是以驼峰方式命名的插件名称,回调函数可以做自己想做的业务逻辑
    compiler.hooks.run.tap(pluginName, (compilation) => {
      console.log('webpack 构建过程开始!');
    });
    // 这里我使用的是done事件表示构建完成后执行我的业务逻辑
    compiler.hooks.done.tap(pluginName, () => {
        console.log('webpack 构建过程结束!');
        try {
            const d = new Date();
            const versionStr =`
                Revision: ${execSync(`git ${COMMITHASH_COMMAND}`)}
                Branch: ${execSync(`git ${BRANCH_COMMAND}`)}
                Release: ${execSync(`git ${VERSION_COMMAND}`)}
                X-PackingTime: ${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}${d.getHours()}:${d.getMinutes()}
                `;
            // compiler实例可以获取到屌用plugin时传入的参数    
            fs.writeFileSync(`${compiler.options.output.path}/version.txt`, versionStr);
        } catch (e) {
            throw new Error(e);
        }
    })
  }
}

module.exports = RepoRevisionWebpackPlugin;
  • 使用就很简单了,通过上文提到的开发流程可以建立一个npm链接,然后直接在webpack配置文件中引入并配置到plugins
const RepoRevisionWebpackPlugin = require('RepoRevisionWebpackPlugin');
module.exports = {
  plugins: [
    new RepoRevisionWebpackPlugin({}),
  ]
}

上面通过简单介绍写出了一个webpack插件来实现获取git版本信息的功能,可以结合发布流程发布一个简陋的npm包供其他项目使用了。

当然以上的功能实现很简单,只作为一个示意,也有很多需要完善的地方,比如:

  • 单元测试
  • 利用compiler和compilation钩子做更多的事情(下文提到了这两个钩子提供的事件函数可以赋能开发者做更多的事情)

番外

  • 需要注意Webpack版本的问题,Webpack4之前的钩子函数和之后的钩子函数是不一样的,需要确定是否需要兼容低版本的Webpack
  • 安装npm私有仓库,这里可以通过Docker的方式快速安装一个自己npm私服

搬砖