写一个vite插件!查看项目的git信息

项目开发中,我们经常面临这样的场景:如何方便地查看当前部署的版本信息,以便快速定位和解决问题?今天,我们写一个简单小巧的Vite插件,轻松实现这一需求。

首先梳理一下需求,希望在代码打包部署上线后,能够直接在控制台查看当前部署的版本信息,比如最后一次 commit 的 hash,最后一次 commit 的时间等信息。

那么我们的实现思路是,利用 git 相关命令,输出对应的信息,然后利用 vite 插件的 transformindexhtml 钩子的将信息添加到 dom 结构中。

使用 vite 插件钩子

接下来,我们将了解如何使用Vite插件钩子来实现这个功能。 先启动一个 vite 项目,在根目录创建一个文件(vitePluginGitRevisionInfo.ts)写个插件的基础结构试试功能:

vitePluginGitRevisionInfo.ts 复制代码
import { Plugin } from 'vite';

function vitePluginGitRevisionInfo(): Plugin {
    return {
        name: 'vite-plugin-git-revision-info',
        transformIndexHtml() {
          const HtmlStr = `const gitInfo = 'aa'`;
          // 将htmlStr插到body里
          return [
            {
              tag: 'script',
              attrs: { defer: true },
              children: HtmlStr,
              injectTo: 'body',
            },
          ];
        },
    };
}

export { vitePluginGitRevisionInfo as default };

然后在 vite.config.ts 里加入我们写的这个小插件:

vite.config.ts 复制代码
import vitePluginGitRevisionInfo from './vitePluginGitRevisionInfo';
export default defineConfig(()=>{
    return {
        plugins:[vitePluginGitRevisionInfo(),]
    }
})

打开控制台调试,可以看到我们添加的内容出现在了 dom 结构中,在控制台中也可以打印出相关信息:

接下来我们完善插件内容,以便获得git相关信息。

展示最近的 commit 等信息

transformIndexHtml 这个钩子允许使用异步函数,使我们可以输出 git 相关信息。

这里我们用到了 Node.js 的 child_process 模块执行 shell 命令,使用 promisify 将回调风格的 exec 函数转成了返回 Promise 的函数。

ts 复制代码
import ChildProcess from 'child_process';
import { promisify } from 'util';
import { Plugin } from 'vite';

const { exec } = ChildProcess;
// 使用 promise 的方式执行命令
const execAsync = promisify(exec);

function vitePluginGitRevisionInfo(): Plugin {
  return {
    name: 'vite-plugin-git-revision-info',
    async transformIndexHtml() {
      const res = await execAsync('git log -1 --format=%cI');
      const HtmlStr = `const gitInfo = ${JSON.stringify(res)}`;
      // 将htmlStr插到body里
      return [
        {
          tag: 'script',
          attrs: { defer: true },
          children: HtmlStr,
          injectTo: 'body',
        },
      ];
    },
  };
}

很顺利拿到了相关信息:

我们可以打印出更多有价值的信息,那我们将执行命令的函数提取出来,并且提取返回值中的 stdout,stdout 是我们需要的值。

ts 复制代码
/**
 * Executes a specified Git command on a given Git work tree
 * @param gitWorkTree The path to the Git work tree
 * @param command The Git command to be executed.
 * @returns The standard output of the Git command
 */
async function runGitCommand(gitWorkTree: string | undefined, command: string) {
  try {
    const gitBaseCommand = gitWorkTree
      ? `git --git-dir=${path.join(
          gitWorkTree,
          '.git',
        )} --work-tree=${gitWorkTree}`
      : 'git';

    const { stdout } = await execAsync(`${gitBaseCommand} ${command}`);
    return removeEmptyLines(stdout);
  } catch (error) {
    console.error('Error executing git command:', error);
    return `Error executing git command`;
  }
}

/**
 * Removes trailing empty lines and whitespace from a given string.
 * @param string The input string.
 * @returns A new string with trailing empty lines and whitespace removed.
 */
function removeEmptyLines(string: string) {
  return string.replace(/[\s\r\n]+$/, '');
}

前面我们拿到了最后一次 commit 的时间,当然我们可以从 git 中拿到更多有用的信息,比如:

  • 最后一次 commit 的 hash --- git rev-parse HEAD
  • 最后一次 commit 的时间 --- log -1 --format=%cI
  • 最后一次 commit 的信息 --- git log -1 --format=%s
  • 最近一次标签 --- git describe --always
  • 当前分支 --- git rev-parse --abbrev-ref HEAD

将这些命令定义一下,并且给他们提供一个 key,使其更易理解和整洁:

ts 复制代码
const COMMITHASH_COMMAND = 'rev-parse HEAD';
const VERSION_COMMAND = 'describe --always';
const BRANCH_COMMAND = 'rev-parse --abbrev-ref HEAD';
const LASTCOMMITTIME_COMMAND = 'log -1 --format=%cI';
const LASTCOMMITMSG_COMMAND = 'log -1 --format=%s';
const COMMITHASH_VAR = 'GIT_COMMITHASH';
const VERSION_VAR = 'GIT_VERSION';
const BRANCH_VAR = 'GIT_BRANCH';
const LASTCOMMITTIME_VAR = 'GIT_LASTCOMMITTIME';
const LASTCOMMITMSG_VAR = 'GIT_LASTCOMMITMSG';

那么如果我们想拿到最后一次 commit 的时间:

ts 复制代码
function getLastCommitDateTime() {
  return runGitCommand(undefined, LASTCOMMITMSG_COMMAND);
}

如果想拿到其他的值也都类似,并且我们把每个值是否要生成变成可以控制的,每个值的 key 也变成可以配置的,使插件更为灵活和通用。

ts 复制代码
async function generateGitData(options) {
  const data: { [key: string]: string } = {};

  if (options.commitHash) {
    data[options.commitHashVar || COMMITHASH_VAR] = JSON.stringify(
      await getCommitHash(options),
    );
  }
  if (options.version) {
    data[options.versionVar || VERSION_VAR] = JSON.stringify(
      await getVersion(options),
    );
  }
  if (options.branch) {
    data[options.branchVar || BRANCH_VAR] = JSON.stringify(
      await getBranch(options),
    );
  }
  if (options.lastCommitTime) {
    data[options.lastCommitTimeVar||LASTCOMMITTIME_VAR] =
      JSON.stringify(await getLastCommitDateTime(options));

  return data
}

然后将 data 插入到 vite 的 transformIndexHtml 钩子里面就生成了我们想要的信息。

当然如果我们想要添加自己的自定义命令也是支持的,可以改写里面的命令。

我们也可以通过 vite 中的共享变量 define 将 git 信息共享为全局变量,只需要在返回的时候把需要的变量添加在 config.define 上就可以了,便可以在整个应用的任何地方读取这个全局变量。

这个变量在构建时会被静态替换为它的实际值,不会在运行时产生额外性能开销,而且这个变量会嵌入到构建的最终产物中,使得它们也可以在生产环境中被访问和使用。

ts 复制代码
async function vitePluginGitRevisionInfo(
  options: GitRevisionPluginOptions,
): Promise<Plugin> {
  const res = JSON.stringify(await generateGitData(options));

  return {
    name: 'vite-plugin-git-revision-info',
    apply: 'build', // 在构建时使用
    config() {
      return {
        // 全局变量,可以在整个应用中使用
        define: {
          __GIT_REVISION_INFO__: res,
        },
      };
    },
    transformIndexHtml() {},
  };
}

我们可以在项目的任意位置使用这些Git信息:

ts 复制代码
console.log(__GIT_REVISION_INFO__)

总结

写一个小的 vite 插件是不是相当简单呢。在本文中我们学习了如何使用 git 命令获取和使用 git 信息,并可以根据需要和配置动态生成包含 git 信息的对象,另外可以通过 vite 的 define 选项将这些 git 信息定义为全局变量。

本文中的插件已上传至 github-vite-plugin-git-revision-info,并发布在 npm,欢迎使用。

特别感谢的是本文的实现主要来源于webpack 的 git 插件git-revision-webpack-plugin ,使用webpack的话可以使用该插件。

欢迎交流~

Reference

相关推荐
隐形喷火龙3 分钟前
element ui--下拉根据拼音首字母过滤
前端·vue.js·ui
m0_7482411216 分钟前
Selenium之Web元素定位
前端·selenium·测试工具
风无雨22 分钟前
react杂乱笔记(一)
前端·笔记·react.js
鑫~阳34 分钟前
快速建站(网站如何在自己的电脑里跑起来) 详细步骤 一
前端·内容管理系统cms
egekm_sefg39 分钟前
webrtc学习----前端推流拉流,局域网socket版,一对多
前端·学习·webrtc
m0_7482343439 分钟前
前端工作中问题点拆分
前端
艾斯特_1 小时前
JavaScript甘特图 dhtmlx-gantt
前端·javascript·甘特图
北海天空1 小时前
reactHooks到底钩到了什么?
前端·react.js
兩尛1 小时前
HTML-CSS(day01)
前端·html
我爱学习_zwj1 小时前
AJAX与Axios
前端·javascript·ajax