npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vite-plugin-build-version-file

v0.1.12

Published

A Vite plugin that emits version.json and injects version metadata into HTML.

Readme

vite-plugin-build-version-file

一个用于 Vite 项目的构建版本信息插件。

它负责在开发态和构建产物中暴露统一的版本信息:

  • 开发态通过 dev server 提供 version.json
  • 构建后在输出目录生成 version.json
  • 普通 Vite HTML 自动注入 window["__VERSION__"]
  • 可选通过 define 将同一份 payload 编译进 JS 产物
  • VitePress 可通过顶层 transformHtml 注入同一份版本信息
  • 支持自定义版本号、输出文件名、全局变量名、时区和额外字段

插件只负责生成和暴露版本信息,不负责运行时更新策略。

它不会内置这些业务行为:

  • 定时轮询新版本
  • 弹窗提示用户刷新
  • 用户取消后的重试策略
  • 自动刷新页面的时机

这些行为通常应放在业务项目中实现。

适用范围

  • Vite 项目
  • Vue 3 + Vite 项目
  • VitePress 项目
  • peerDependencies 要求 vite >= 5
  • Node.js 要求 >= 18

安装

pnpm add vite-plugin-build-version-file

也可以使用 npm:

npm install vite-plugin-build-version-file

快速开始

普通 Vite / Vue 3 项目

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import buildVersionPlugin from 'vite-plugin-build-version-file'

export default defineConfig({
  plugins: [
    vue(),
    buildVersionPlugin()
  ]
})

VitePress 项目

VitePress 构建 HTML 时不完全走普通 Vite index.html 注入链路。需要复用同一个插件实例,并把它的 transformHtml 接到 VitePress 顶层配置上。

import { defineConfig } from 'vitepress'
import buildVersionPlugin from 'vite-plugin-build-version-file'

const buildVersion = buildVersionPlugin()

export default defineConfig({
  vite: {
    plugins: [buildVersion],
  },
  transformHtml: buildVersion.transformHtml,
})

注意:这里必须复用同一个 buildVersion 实例。不要分别调用两次 buildVersionPlugin(),否则 transformHtml 拿不到 Vite 插件阶段生成的 payload。

VitePress 构建会包含 client bundle 和 SSR bundle。插件只在最终静态产物 bundle 输出 version.json,避免同一次 VitePress build 出现重复日志或重复 emit。

默认行为

默认配置下,插件会做这些事:

  • 开发态访问 /version.json 时返回版本信息
  • 构建后输出 version.json
  • 向 HTML 注入 window["__VERSION__"]
  • 自动读取业务项目最近的 package.json
  • package.jsonname 写入 pkgName
  • package.jsonversion 写入 pkgVersion
  • 开发态默认版本号为 0
  • 构建态默认版本号为当前时间,格式为 YYYYMMDDHHmmss
  • 默认时区为 Asia/Shanghai

构建态默认输出示例:

{
  "pkgName": "your-app-name",
  "pkgVersion": "1.0.0",
  "time": "20260401153045",
  "env": "production"
}

开发态默认输出示例:

{
  "pkgName": "your-app-name",
  "pkgVersion": "1.0.0",
  "time": 0,
  "env": "development"
}

配置项

type BuildVersionContext = {
  command: 'serve' | 'build'
  mode: string
}

type BuildVersionPluginOptions = {
  filename?: string
  globalName?: string
  defineName?: string | false
  injectToHtml?: boolean
  timeZone?: string
  version?: string | number | ((ctx: BuildVersionContext) => string | number)
  data?: Record<string, any>
  log?: (content: any) => string
  payload?: (json: any) => any
}

filename

  • 类型:string
  • 默认值:"version.json"
  • 作用:控制开发态访问路径和构建态输出文件路径

示例:

buildVersionPlugin({
  filename: 'meta/version.json'
})

构建后输出:

dist/meta/version.json

开发态访问:

/meta/version.json

如果项目配置了 Vite base,访问路径会自动跟随 base

globalName

  • 类型:string
  • 默认值:"__VERSION__"
  • 作用:控制注入到 window 上的属性名

默认注入效果:

<script>
  window["__VERSION__"] = { "time": "20260401153045", "env": "production" };
</script>

这里注入的是完整 payload 对象,不是单独版本字符串。

defineName

  • 类型:string | false
  • 默认值:"__VERSION__"
  • 作用:控制是否通过 Vite define 将完整 payload 编译进 JS 产物

示例:

buildVersionPlugin({
  defineName: '__APP_VERSION__'
})

业务代码可直接读取:

console.log(__APP_VERSION__)

打包后会在编译阶段被替换成字面量对象,因此适合给 Vue、TS、JS 模块直接消费。

默认情况下会注入 __VERSION__。如果你不需要编译期常量,可显式传 defineName: false 关闭。

如果同时开启 defineNameglobalName,两者会共用同一份 payload:

  • defineName 写入编译后的 JS
  • globalName 注入运行时 HTML
  • version.json 仍照常输出

injectToHtml

  • 类型:boolean
  • 默认值:true
  • 作用:控制是否向 HTML 注入 window["__VERSION__"]

设置为 false 后,仍会生成和提供 version.json

timeZone

  • 类型:string
  • 默认值:"Asia/Shanghai"
  • 作用:控制默认构建时间按哪个时区生成

只影响插件默认生成的 time。如果你传了 version,则以自定义逻辑为准。

version

  • 类型:string | number | ((ctx) => string | number)
  • 默认值:开发态为 0,构建态为当前时间戳
  • 作用:自定义版本号生成逻辑
buildVersionPlugin({
  version: ({ command, mode }) => {
    if (command === 'serve') {
      return 0
    }

    return `${mode}-20260401153045`
  }
})

data

  • 类型:Record<string, any>
  • 默认值:{}
  • 作用:向最终 payload 追加自定义字段
buildVersionPlugin({
  data: {
    appName: 'admin',
    gitBranch: 'release/2026-04',
    commitSha: 'abc1234'
  }
})

生成结果示例:

{
  "appName": "admin",
  "gitBranch": "release/2026-04",
  "commitSha": "abc1234",
  "pkgName": "your-app-name",
  "pkgVersion": "1.0.0",
  "time": "20260401153045",
  "env": "production"
}

字段覆盖顺序:

  • data 会先写入
  • pkgNamepkgVersion 会使用项目 package.json 的值覆盖同名字段
  • timeenv 会使用插件生成的值覆盖同名字段
  • payload 可以在最后改写整个结果

payload

  • 类型:(json: any) => any
  • 作用:最终改写 payload

适合做字段裁剪、字段重命名或兼容旧系统。

buildVersionPlugin({
  payload: (json) => ({
    version: json.time,
    env: json.env,
    name: json.pkgName
  })
})

建议返回对象,避免业务端读取结构不稳定。

log

  • 类型:(content: any) => string
  • 作用:自定义构建完成后的控制台输出
buildVersionPlugin({
  log: (payload) => `build version: ${payload.time}`
})

读取版本信息

读取全局变量

const payload = window['__VERSION__']

console.log(payload.time)
console.log(payload.env)

适用于需要知道“当前页面构建版本”的场景。

读取 version.json

const response = await fetch('/version.json', {
  cache: 'no-store'
})
const payload = await response.json()

console.log(payload.time)
console.log(payload.env)

适用于和服务器上的最新版本做对比。

做版本检查

插件不内置版本检查 UI。推荐业务项目自行处理:

const CHECK_INTERVAL = 60 * 1000
const VERSION_URL = '/version.json'

async function checkVersion() {
  try {
    const response = await fetch(VERSION_URL, {
      cache: 'no-store'
    })
    const latestPayload = await response.json()
    const currentPayload = window.__VERSION__

    if (currentPayload && latestPayload.time !== currentPayload.time) {
      const shouldReload = window.confirm('检测到新版本,是否刷新页面?')

      if (shouldReload) {
        window.location.reload()
        return
      }
    }
  } catch (error) {
    console.warn('check version failed', error)
  }

  window.setTimeout(checkVersion, CHECK_INTERVAL)
}

checkVersion()

建议:

  • 通常比较 payload.time 即可
  • 请求 version.json 时使用 cache: 'no-store'
  • 轮询间隔不要过短,通常 1 分钟或更长
  • 弹窗、通知、静默刷新等策略放在业务项目里
  • 请求失败后继续下一轮检查,避免一次失败中断流程

开发态行为

开发态下,插件不会向业务项目目录写入文件。

它会通过 Vite dev server 直接返回 JSON:

  • 不污染 public/
  • 不需要生成临时文件
  • 修改插件配置后重启 dev server 即可生效

本地开发

仓库提供了两个调试用示例项目:

  • 普通 Vite / Vue 3:debug/fixture-app
  • VitePress:debug/vitepress-app
  • 引用方式:直接引用根目录 dist/index.mjs

这意味着示例项目测试的是打包后的真实产物。

pnpm install
pnpm build
pnpm dev:fixture

调试 VitePress 示例项目:

pnpm dev:vitepress

构建示例项目:

pnpm build:fixture
pnpm build:vitepress

完整检查打包流程:

pnpm build
pnpm build:fixture
pnpm build:vitepress
npm pack --dry-run

建议检查:

  • debug/fixture-app/dist/version.json 包含 timeenvpkgNamepkgVersion
  • debug/fixture-app/dist/index.html 只注入一次 window["__VERSION__"]
  • debug/vitepress-app/.vitepress/dist/meta/version.json 包含 fixturetimeenv
  • VitePress 每个 HTML 只注入一次 window["__VERSION__"]
  • VitePress build 日志只输出一次插件 payload

发布

pnpm build
pnpm build:fixture
pnpm build:vitepress
npm publish --access public

发布前建议确认:

  • dist/index.mjs 已生成
  • dist/index.cjs 已生成
  • dist/index.d.ts 已生成
  • debug/fixture-app 可以正常构建
  • debug/vitepress-app 可以正常构建
  • version.json 输出正常
  • 普通 Vite HTML 注入正常
  • VitePress transformHtml 接入后注入正常
  • VitePress build 没有重复输出插件 payload 日志

查看最终发包内容:

npm pack

正常发布包应只包含:

  • dist
  • README.md
  • LICENSE
  • package.json

English

vite-plugin-build-version-file is a build metadata plugin for Vite projects.

It exposes one consistent version payload in development and production:

  • serves version.json through the Vite dev server
  • emits version.json into the build output directory
  • injects window["__VERSION__"] into normal Vite HTML
  • supports VitePress through its top-level transformHtml hook
  • supports custom versions, filenames, global variable names, time zones, and extra payload fields

The plugin only generates and exposes version metadata. It does not decide how your app should check for updates or refresh the page.

It intentionally does not include:

  • polling logic
  • update prompts
  • retry rules
  • reload timing

Those behaviors should stay in your application code.

Compatibility

  • Vite projects
  • Vue 3 + Vite projects
  • VitePress projects
  • vite >= 5
  • Node.js >= 18

Installation

pnpm add vite-plugin-build-version-file

Or:

npm install vite-plugin-build-version-file

Quick Start

Vite / Vue 3

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import buildVersionPlugin from 'vite-plugin-build-version-file'

export default defineConfig({
  plugins: [
    vue(),
    buildVersionPlugin()
  ]
})

VitePress

VitePress does not write production HTML through the same index.html flow as a normal Vite app. Reuse one plugin instance and pass its transformHtml helper to the top-level VitePress config.

import { defineConfig } from 'vitepress'
import buildVersionPlugin from 'vite-plugin-build-version-file'

const buildVersion = buildVersionPlugin()

export default defineConfig({
  vite: {
    plugins: [buildVersion],
  },
  transformHtml: buildVersion.transformHtml,
})

Use the same buildVersion instance in both places. Calling buildVersionPlugin() twice creates two isolated instances, and the VitePress transformHtml helper will not receive the payload created during the Vite plugin lifecycle.

VitePress build includes a client bundle and an SSR bundle. The plugin emits version.json only from the final static bundle, so one VitePress build does not print duplicate payload logs or emit the same asset twice.

Default Behavior

With default options, the plugin:

  • serves /version.json in development
  • emits version.json after build
  • injects window["__VERSION__"] into HTML
  • reads the nearest project package.json
  • writes name as pkgName
  • writes version as pkgVersion
  • uses 0 as the development version
  • uses YYYYMMDDHHmmss as the production version
  • uses Asia/Shanghai as the default time zone

Production payload example:

{
  "pkgName": "your-app-name",
  "pkgVersion": "1.0.0",
  "time": "20260401153045",
  "env": "production"
}

Development payload example:

{
  "pkgName": "your-app-name",
  "pkgVersion": "1.0.0",
  "time": 0,
  "env": "development"
}

Options

type BuildVersionContext = {
  command: 'serve' | 'build'
  mode: string
}

type BuildVersionPluginOptions = {
  filename?: string
  globalName?: string
  injectToHtml?: boolean
  timeZone?: string
  version?: string | number | ((ctx: BuildVersionContext) => string | number)
  data?: Record<string, any>
  log?: (content: any) => string
  payload?: (json: any) => any
}

filename

  • Type: string
  • Default: "version.json"
  • Controls the dev URL and build output path.
buildVersionPlugin({
  filename: 'meta/version.json'
})

Build output:

dist/meta/version.json

Development URL:

/meta/version.json

If your project configures Vite base, the URL follows that base.

globalName

  • Type: string
  • Default: "__VERSION__"
  • Controls the property name injected on window.

Default injection:

<script>
  window["__VERSION__"] = { "time": "20260401153045", "env": "production" };
</script>

The injected value is the full payload object, not a single version string.

injectToHtml

  • Type: boolean
  • Default: true
  • Controls whether the plugin injects window["__VERSION__"].

When set to false, version.json is still served and emitted.

timeZone

  • Type: string
  • Default: "Asia/Shanghai"
  • Controls the time zone used by the default production timestamp.

This only affects the default time. If you pass version, your custom logic wins.

version

  • Type: string | number | ((ctx) => string | number)
  • Default: 0 in development, current timestamp in production
  • Provides custom version generation.
buildVersionPlugin({
  version: ({ command, mode }) => {
    if (command === 'serve') {
      return 0
    }

    return `${mode}-20260401153045`
  }
})

data

  • Type: Record<string, any>
  • Default: {}
  • Adds custom fields to the final payload.
buildVersionPlugin({
  data: {
    appName: 'admin',
    gitBranch: 'release/2026-04',
    commitSha: 'abc1234'
  }
})

Example output:

{
  "appName": "admin",
  "gitBranch": "release/2026-04",
  "commitSha": "abc1234",
  "pkgName": "your-app-name",
  "pkgVersion": "1.0.0",
  "time": "20260401153045",
  "env": "production"
}

Merge order:

  • data is written first
  • pkgName and pkgVersion from package.json override matching fields
  • time and env from the plugin override matching fields
  • payload can rewrite the final object

payload

  • Type: (json: any) => any
  • Rewrites the final payload.

Use it to trim fields, rename fields, or keep compatibility with an older contract.

buildVersionPlugin({
  payload: (json) => ({
    version: json.time,
    env: json.env,
    name: json.pkgName
  })
})

Returning an object is recommended so application reads stay stable.

log

  • Type: (content: any) => string
  • Customizes the build-complete console output.
buildVersionPlugin({
  log: (payload) => `build version: ${payload.time}`
})

Reading Version Metadata

Read the injected global

const payload = window['__VERSION__']

console.log(payload.time)
console.log(payload.env)

Use this when you need the version of the currently loaded page.

Read version.json

const response = await fetch('/version.json', {
  cache: 'no-store'
})
const payload = await response.json()

console.log(payload.time)
console.log(payload.env)

Use this when you need to compare the current page against the latest deployed version.

Checking for Updates

The plugin does not include update-checking UI. Keep that logic in your app.

const CHECK_INTERVAL = 60 * 1000
const VERSION_URL = '/version.json'

async function checkVersion() {
  try {
    const response = await fetch(VERSION_URL, {
      cache: 'no-store'
    })
    const latestPayload = await response.json()
    const currentPayload = window.__VERSION__

    if (currentPayload && latestPayload.time !== currentPayload.time) {
      const shouldReload = window.confirm('A new version is available. Reload now?')

      if (shouldReload) {
        window.location.reload()
        return
      }
    }
  } catch (error) {
    console.warn('check version failed', error)
  }

  window.setTimeout(checkVersion, CHECK_INTERVAL)
}

checkVersion()

Recommendations:

  • Compare payload.time in most cases.
  • Fetch version.json with cache: 'no-store'.
  • Avoid very short polling intervals. One minute or longer is usually enough.
  • Keep prompts, notifications, silent reloads, and retry rules in application code.
  • Continue polling after failed requests.

Development Behavior

In development, the plugin does not write files into your project directory.

It serves JSON directly through the Vite dev server:

  • no public/ pollution
  • no temporary file generation
  • restart the dev server after changing plugin options

Local Development

The repository contains two fixture apps:

  • Vite / Vue 3: debug/fixture-app
  • VitePress: debug/vitepress-app
  • They import the built package from dist/index.mjs

This verifies the real build output instead of importing source files directly.

pnpm install
pnpm build
pnpm dev:fixture

Run the VitePress fixture:

pnpm dev:vitepress

Build fixture apps:

pnpm build:fixture
pnpm build:vitepress

Full local package check:

pnpm build
pnpm build:fixture
pnpm build:vitepress
npm pack --dry-run

Check:

  • debug/fixture-app/dist/version.json contains time, env, pkgName, and pkgVersion
  • debug/fixture-app/dist/index.html injects window["__VERSION__"] once
  • debug/vitepress-app/.vitepress/dist/meta/version.json contains fixture, time, and env
  • each VitePress HTML file injects window["__VERSION__"] once
  • VitePress build prints the plugin payload log once

Publishing

pnpm build
pnpm build:fixture
pnpm build:vitepress
npm publish --access public

Before publishing, check:

  • dist/index.mjs
  • dist/index.cjs
  • dist/index.d.ts
  • fixture app build
  • VitePress fixture build
  • version.json output
  • normal Vite HTML injection
  • VitePress transformHtml injection
  • no duplicate plugin payload logs during VitePress build

Inspect the publish package:

npm pack

The package should contain:

  • dist
  • README.md
  • LICENSE
  • package.json

License

MIT