vite-plugin-build-version-file
v0.1.12
Published
A Vite plugin that emits version.json and injects version metadata into HTML.
Maintainers
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.json的name写入pkgName - 将
package.json的version写入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 关闭。
如果同时开启 defineName 和 globalName,两者会共用同一份 payload:
defineName写入编译后的 JSglobalName注入运行时 HTMLversion.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会先写入pkgName、pkgVersion会使用项目package.json的值覆盖同名字段time、env会使用插件生成的值覆盖同名字段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包含time、env、pkgName、pkgVersiondebug/fixture-app/dist/index.html只注入一次window["__VERSION__"]debug/vitepress-app/.vitepress/dist/meta/version.json包含fixture、time、env- 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正常发布包应只包含:
distREADME.mdLICENSEpackage.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.jsonthrough the Vite dev server - emits
version.jsoninto the build output directory - injects
window["__VERSION__"]into normal Vite HTML - supports VitePress through its top-level
transformHtmlhook - 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-fileOr:
npm install vite-plugin-build-version-fileQuick 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.jsonin development - emits
version.jsonafter build - injects
window["__VERSION__"]into HTML - reads the nearest project
package.json - writes
nameaspkgName - writes
versionaspkgVersion - uses
0as the development version - uses
YYYYMMDDHHmmssas the production version - uses
Asia/Shanghaias 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.jsonDevelopment URL:
/meta/version.jsonIf 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:
0in 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:
datais written firstpkgNameandpkgVersionfrompackage.jsonoverride matching fieldstimeandenvfrom the plugin override matching fieldspayloadcan 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.timein most cases. - Fetch
version.jsonwithcache: '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:fixtureRun the VitePress fixture:
pnpm dev:vitepressBuild fixture apps:
pnpm build:fixture
pnpm build:vitepressFull local package check:
pnpm build
pnpm build:fixture
pnpm build:vitepress
npm pack --dry-runCheck:
debug/fixture-app/dist/version.jsoncontainstime,env,pkgName, andpkgVersiondebug/fixture-app/dist/index.htmlinjectswindow["__VERSION__"]oncedebug/vitepress-app/.vitepress/dist/meta/version.jsoncontainsfixture,time, andenv- 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 publicBefore publishing, check:
dist/index.mjsdist/index.cjsdist/index.d.ts- fixture app build
- VitePress fixture build
version.jsonoutput- normal Vite HTML injection
- VitePress
transformHtmlinjection - no duplicate plugin payload logs during VitePress build
Inspect the publish package:
npm packThe package should contain:
distREADME.mdLICENSEpackage.json
License
MIT
