vite-plugin-build-version-file 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,34 +1,33 @@
1
1
  # vite-plugin-build-version-file
2
2
 
3
- 一个面向 `Vue 3 + Vite` 的构建版本插件。
3
+ 一个用于 Vite 项目的构建版本信息插件。
4
4
 
5
- 它会在项目运行和构建时提供统一的版本信息:
5
+ 它负责在开发态和构建产物中暴露统一的版本信息:
6
6
 
7
- - 构建产物中生成 `version.json`
8
- - 可选地向 HTML 注入 `window["__VERSION__"]`
9
- - 支持自定义版本号、文件名、全局变量名和额外数据
7
+ - 开发态通过 dev server 提供 `version.json`
8
+ - 构建后在输出目录生成 `version.json`
9
+ - 普通 Vite HTML 自动注入 `window["__VERSION__"]`
10
+ - VitePress 可通过顶层 `transformHtml` 注入同一份版本信息
11
+ - 支持自定义版本号、输出文件名、全局变量名、时区和额外字段
10
12
 
11
- 这个插件只负责产出和暴露版本信息。
13
+ 插件只负责生成和暴露版本信息,不负责运行时更新策略。
12
14
 
13
- 它不负责这些运行时行为:
15
+ 它不会内置这些业务行为:
14
16
 
15
- - 轮询检查版本
16
- - 弹窗提示用户更新
17
+ - 定时轮询新版本
18
+ - 弹窗提示用户刷新
17
19
  - 用户取消后的重试策略
18
- - 页面刷新时机
19
-
20
- 适合用在这些场景:
20
+ - 自动刷新页面的时机
21
21
 
22
- - 页面显示当前前端版本
23
- - 检测线上是否有新版本并提示刷新
24
- - 灰度发布时对比本地版本和服务端版本
22
+ 这些行为通常应放在业务项目中实现。
25
23
 
26
24
  ## 适用范围
27
25
 
28
- - 仅支持 `Vite`
29
- - 主要面向 `Vue 3` 项目使用
26
+ - Vite 项目
27
+ - Vue 3 + Vite 项目
28
+ - VitePress 项目
30
29
  - `peerDependencies` 要求 `vite >= 5`
31
- - 运行环境要求 `node >= 18`
30
+ - Node.js 要求 `>= 18`
32
31
 
33
32
  ## 安装
34
33
 
@@ -36,7 +35,7 @@
36
35
  pnpm add vite-plugin-build-version-file
37
36
  ```
38
37
 
39
- 也可以使用:
38
+ 也可以使用 npm:
40
39
 
41
40
  ```bash
42
41
  npm install vite-plugin-build-version-file
@@ -44,28 +43,56 @@ npm install vite-plugin-build-version-file
44
43
 
45
44
  ## 快速开始
46
45
 
46
+ ### 普通 Vite / Vue 3 项目
47
+
47
48
  ```ts
48
49
  import { defineConfig } from 'vite'
49
50
  import vue from '@vitejs/plugin-vue'
50
51
  import buildVersionPlugin from 'vite-plugin-build-version-file'
51
52
 
52
53
  export default defineConfig({
53
- plugins: [vue(), buildVersionPlugin()]
54
+ plugins: [
55
+ vue(),
56
+ buildVersionPlugin()
57
+ ]
54
58
  })
55
59
  ```
56
60
 
61
+ ### VitePress 项目
62
+
63
+ VitePress 构建 HTML 时不完全走普通 Vite `index.html` 注入链路。需要复用同一个插件实例,并把它的 `transformHtml` 接到 VitePress 顶层配置上。
64
+
65
+ ```js
66
+ import { defineConfig } from 'vitepress'
67
+ import buildVersionPlugin from 'vite-plugin-build-version-file'
68
+
69
+ const buildVersion = buildVersionPlugin()
70
+
71
+ export default defineConfig({
72
+ vite: {
73
+ plugins: [buildVersion],
74
+ },
75
+ transformHtml: buildVersion.transformHtml,
76
+ })
77
+ ```
78
+
79
+ 注意:这里必须复用同一个 `buildVersion` 实例。不要分别调用两次 `buildVersionPlugin()`,否则 `transformHtml` 拿不到 Vite 插件阶段生成的 payload。
80
+
57
81
  ## 默认行为
58
82
 
59
- 在默认配置下,插件会执行以下行为:
83
+ 默认配置下,插件会做这些事:
60
84
 
61
85
  - 开发态访问 `/version.json` 时返回版本信息
62
- - 构建后在输出目录生成 `version.json`
63
- - 构建后的 HTML 中注入 `window["__VERSION__"]`
64
- - 自动尝试读取业务项目 `package.json` 中的 `name` 和 `version`
86
+ - 构建后输出 `version.json`
87
+ - 向 HTML 注入 `window["__VERSION__"]`
88
+ - 自动读取业务项目最近的 `package.json`
89
+ - 将 `package.json` 的 `name` 写入 `pkgName`
90
+ - 将 `package.json` 的 `version` 写入 `pkgVersion`
65
91
  - 开发态默认版本号为 `0`
66
- - 构建态默认版本号为当前时间戳,格式为 `YYYYMMDDHHmmss`
92
+ - 构建态默认版本号为当前时间,格式为 `YYYYMMDDHHmmss`
93
+ - 默认时区为 `Asia/Shanghai`
67
94
 
68
- 默认生成的版本文件内容如下:
95
+ 构建态默认输出示例:
69
96
 
70
97
  ```json
71
98
  {
@@ -76,7 +103,7 @@ export default defineConfig({
76
103
  }
77
104
  ```
78
105
 
79
- 开发态默认返回:
106
+ 开发态默认输出示例:
80
107
 
81
108
  ```json
82
109
  {
@@ -87,29 +114,6 @@ export default defineConfig({
87
114
  }
88
115
  ```
89
116
 
90
- ## 职责边界
91
-
92
- 这个库的职责是:
93
-
94
- - 生成版本号
95
- - 输出 `version.json`
96
- - 向页面注入版本信息
97
- - 让业务代码能读取当前版本
98
-
99
- 这些逻辑建议写在业务项目里,而不是放进这个库里:
100
-
101
- - 多久检查一次新版本
102
- - 请求失败后是否重试
103
- - 用什么 UI 提示用户
104
- - 用户点击取消后怎么处理
105
- - 是否立即刷新页面
106
-
107
- 如果你的项目要做“检测到新版本后提示刷新”,推荐做法是:
108
-
109
- - 用 `window["__VERSION__"]` 读取当前页面版本
110
- - 请求 `version.json` 读取服务端版本
111
- - 在业务项目里自行决定提示方式和刷新时机
112
-
113
117
  ## 配置项
114
118
 
115
119
  ```ts
@@ -125,6 +129,8 @@ type BuildVersionPluginOptions = {
125
129
  timeZone?: string
126
130
  version?: string | number | ((ctx: BuildVersionContext) => string | number)
127
131
  data?: Record<string, any>
132
+ log?: (content: any) => string
133
+ payload?: (json: any) => any
128
134
  }
129
135
  ```
130
136
 
@@ -132,13 +138,29 @@ type BuildVersionPluginOptions = {
132
138
 
133
139
  - 类型:`string`
134
140
  - 默认值:`"version.json"`
135
- - 作用:控制输出文件名
141
+ - 作用:控制开发态访问路径和构建态输出文件路径
142
+
143
+ 示例:
144
+
145
+ ```ts
146
+ buildVersionPlugin({
147
+ filename: 'meta/version.json'
148
+ })
149
+ ```
150
+
151
+ 构建后输出:
136
152
 
137
- 说明:
153
+ ```text
154
+ dist/meta/version.json
155
+ ```
138
156
 
139
- - 支持相对路径,例如 `meta/version.json`
140
- - 构建时会输出到 `dist/meta/version.json`
141
- - 开发态访问路径也会同步变成对应地址
157
+ 开发态访问:
158
+
159
+ ```text
160
+ /meta/version.json
161
+ ```
162
+
163
+ 如果项目配置了 Vite `base`,访问路径会自动跟随 `base`。
142
164
 
143
165
  ### `globalName`
144
166
 
@@ -154,18 +176,23 @@ type BuildVersionPluginOptions = {
154
176
  </script>
155
177
  ```
156
178
 
157
- 这里注入的是完整对象,不是单独的版本字符串。
179
+ 这里注入的是完整 payload 对象,不是单独版本字符串。
158
180
 
159
181
  ### `injectToHtml`
160
182
 
161
183
  - 类型:`boolean`
162
184
  - 默认值:`true`
163
- - 作用:是否向 HTML 注入 `window` 全局变量
185
+ - 作用:控制是否向 HTML 注入 `window["__VERSION__"]`
164
186
 
165
- 当设置为 `false` 时:
187
+ 设置为 `false` 后,仍会生成和提供 `version.json`。
166
188
 
167
- - 仍然会生成 `version.json`
168
- - 不会注入 `window["__VERSION__"]`
189
+ ### `timeZone`
190
+
191
+ - 类型:`string`
192
+ - 默认值:`"Asia/Shanghai"`
193
+ - 作用:控制默认构建时间按哪个时区生成
194
+
195
+ 只影响插件默认生成的 `time`。如果你传了 `version`,则以自定义逻辑为准。
169
196
 
170
197
  ### `version`
171
198
 
@@ -173,85 +200,274 @@ type BuildVersionPluginOptions = {
173
200
  - 默认值:开发态为 `0`,构建态为当前时间戳
174
201
  - 作用:自定义版本号生成逻辑
175
202
 
176
- 如果传入函数,函数会收到:
203
+ ```ts
204
+ buildVersionPlugin({
205
+ version: ({ command, mode }) => {
206
+ if (command === 'serve') {
207
+ return 0
208
+ }
209
+
210
+ return `${mode}-20260401153045`
211
+ }
212
+ })
213
+ ```
214
+
215
+ ### `data`
216
+
217
+ - 类型:`Record<string, any>`
218
+ - 默认值:`{}`
219
+ - 作用:向最终 payload 追加自定义字段
177
220
 
178
221
  ```ts
222
+ buildVersionPlugin({
223
+ data: {
224
+ appName: 'admin',
225
+ gitBranch: 'release/2026-04',
226
+ commitSha: 'abc1234'
227
+ }
228
+ })
229
+ ```
230
+
231
+ 生成结果示例:
232
+
233
+ ```json
179
234
  {
180
- command: 'serve' | 'build',
181
- mode: string
235
+ "appName": "admin",
236
+ "gitBranch": "release/2026-04",
237
+ "commitSha": "abc1234",
238
+ "pkgName": "your-app-name",
239
+ "pkgVersion": "1.0.0",
240
+ "time": "20260401153045",
241
+ "env": "production"
182
242
  }
183
243
  ```
184
244
 
185
- ### `timeZone`
245
+ 字段覆盖顺序:
186
246
 
187
- - 类型:`string`
188
- - 默认值:`"Asia/Shanghai"`
189
- - 作用:控制默认时间按哪个时区生成
247
+ - `data` 会先写入
248
+ - `pkgName`、`pkgVersion` 会使用项目 `package.json` 的值覆盖同名字段
249
+ - `time`、`env` 会使用插件生成的值覆盖同名字段
250
+ - `payload` 可以在最后改写整个结果
190
251
 
191
- 说明:
252
+ ### `payload`
192
253
 
193
- - 只影响插件默认生成的 `time`
194
- - 如果构建环境是 UTC,也会按设置的时区输出
195
- - 如果传了 `version`,则以你自己的 `version` 逻辑为准
254
+ - 类型:`(json: any) => any`
255
+ - 作用:最终改写 payload
196
256
 
197
- ### `data`
257
+ 适合做字段裁剪、字段重命名或兼容旧系统。
198
258
 
199
- - 类型:`Record<string, any>`
200
- - 默认值:`{}`
201
- - 作用:向最终生成的版本信息对象中追加自定义字段
259
+ ```ts
260
+ buildVersionPlugin({
261
+ payload: (json) => ({
262
+ version: json.time,
263
+ env: json.env,
264
+ name: json.pkgName
265
+ })
266
+ })
267
+ ```
202
268
 
203
- 说明:
269
+ 建议返回对象,避免业务端读取结构不稳定。
204
270
 
205
- - 插件会自动从业务项目的 `package.json` 中读取 `name` 和 `version`,分别写入 `pkgName` 和 `pkgVersion`
206
- - 查找位置是从当前 Vite 项目根目录开始,向上寻找最近的 `package.json`
207
- - 会同时影响开发态接口返回、构建产物中的 `version.json` 和注入到 `window` 的对象
208
- - 如果 `data` 里存在 `time` 或 `env` 同名字段,最终仍以插件生成的值为准
209
- - 如果 `data` 里存在 `pkgName` 或 `pkgVersion`,读取到业务项目 `package.json` 时会以自动读取的值为准;读取不到时会保留 `data` 里的值
271
+ ### `log`
210
272
 
211
- ## 使用示例
273
+ - 类型:`(content: any) => string`
274
+ - 作用:自定义构建完成后的控制台输出
212
275
 
213
- ### 1. 使用默认配置
276
+ ```ts
277
+ buildVersionPlugin({
278
+ log: (payload) => `build version: ${payload.time}`
279
+ })
280
+ ```
281
+
282
+ ## 读取版本信息
283
+
284
+ ### 读取全局变量
214
285
 
215
286
  ```ts
216
- import { defineConfig } from 'vite'
217
- import vue from '@vitejs/plugin-vue'
218
- import buildVersionPlugin from 'vite-plugin-build-version-file'
287
+ const payload = window['__VERSION__']
219
288
 
220
- export default defineConfig({
221
- plugins: [vue(), buildVersionPlugin()]
289
+ console.log(payload.time)
290
+ console.log(payload.env)
291
+ ```
292
+
293
+ 适用于需要知道“当前页面构建版本”的场景。
294
+
295
+ ### 读取 `version.json`
296
+
297
+ ```ts
298
+ const response = await fetch('/version.json', {
299
+ cache: 'no-store'
222
300
  })
301
+ const payload = await response.json()
302
+
303
+ console.log(payload.time)
304
+ console.log(payload.env)
223
305
  ```
224
306
 
225
- 默认情况下,插件会尝试从业务项目的 `package.json` 中读取:
307
+ 适用于和服务器上的最新版本做对比。
226
308
 
227
- - `name` -> `pkgName`
228
- - `version` -> `pkgVersion`
309
+ ## 做版本检查
229
310
 
230
- ### 2. 自定义全局变量名
311
+ 插件不内置版本检查 UI。推荐业务项目自行处理:
231
312
 
232
- ```ts
233
- import { defineConfig } from 'vite'
234
- import vue from '@vitejs/plugin-vue'
235
- import buildVersionPlugin from 'vite-plugin-build-version-file'
313
+ ```js
314
+ const CHECK_INTERVAL = 60 * 1000
315
+ const VERSION_URL = '/version.json'
236
316
 
237
- export default defineConfig({
238
- plugins: [
239
- vue(),
240
- buildVersionPlugin({
241
- globalName: '__APP_VERSION__'
317
+ async function checkVersion() {
318
+ try {
319
+ const response = await fetch(VERSION_URL, {
320
+ cache: 'no-store'
242
321
  })
243
- ]
244
- })
322
+ const latestPayload = await response.json()
323
+ const currentPayload = window.__VERSION__
324
+
325
+ if (currentPayload && latestPayload.time !== currentPayload.time) {
326
+ const shouldReload = window.confirm('检测到新版本,是否刷新页面?')
327
+
328
+ if (shouldReload) {
329
+ window.location.reload()
330
+ return
331
+ }
332
+ }
333
+ } catch (error) {
334
+ console.warn('check version failed', error)
335
+ }
336
+
337
+ window.setTimeout(checkVersion, CHECK_INTERVAL)
338
+ }
339
+
340
+ checkVersion()
245
341
  ```
246
342
 
247
- 注入后可以这样读取:
343
+ 建议:
248
344
 
249
- ```ts
250
- const payload = window['__APP_VERSION__']
251
- const time = payload.time
345
+ - 通常比较 `payload.time` 即可
346
+ - 请求 `version.json` 时使用 `cache: 'no-store'`
347
+ - 轮询间隔不要过短,通常 1 分钟或更长
348
+ - 弹窗、通知、静默刷新等策略放在业务项目里
349
+ - 请求失败后继续下一轮检查,避免一次失败中断流程
350
+
351
+ ## 开发态行为
352
+
353
+ 开发态下,插件不会向业务项目目录写入文件。
354
+
355
+ 它会通过 Vite dev server 直接返回 JSON:
356
+
357
+ - 不污染 `public/`
358
+ - 不需要生成临时文件
359
+ - 修改插件配置后重启 dev server 即可生效
360
+
361
+ ## 本地开发
362
+
363
+ 仓库提供了两个调试用示例项目:
364
+
365
+ - 普通 Vite / Vue 3:`debug/fixture-app`
366
+ - VitePress:`debug/vitepress-app`
367
+ - 引用方式:直接引用根目录 `dist/index.mjs`
368
+
369
+ 这意味着示例项目测试的是打包后的真实产物。
370
+
371
+ ```bash
372
+ pnpm install
373
+ pnpm build
374
+ pnpm dev:fixture
375
+ ```
376
+
377
+ 调试 VitePress 示例项目:
378
+
379
+ ```bash
380
+ pnpm dev:vitepress
381
+ ```
382
+
383
+ 构建示例项目:
384
+
385
+ ```bash
386
+ pnpm build:fixture
387
+ pnpm build:vitepress
388
+ ```
389
+
390
+ ## 发布
391
+
392
+ ```bash
393
+ pnpm build
394
+ pnpm build:fixture
395
+ pnpm build:vitepress
396
+ npm publish --access public
397
+ ```
398
+
399
+ 发布前建议确认:
400
+
401
+ - `dist/index.mjs` 已生成
402
+ - `dist/index.cjs` 已生成
403
+ - `dist/index.d.ts` 已生成
404
+ - `debug/fixture-app` 可以正常构建
405
+ - `debug/vitepress-app` 可以正常构建
406
+ - `version.json` 输出正常
407
+ - 普通 Vite HTML 注入正常
408
+ - VitePress `transformHtml` 接入后注入正常
409
+
410
+ 查看最终发包内容:
411
+
412
+ ```bash
413
+ npm pack
414
+ ```
415
+
416
+ 正常发布包应只包含:
417
+
418
+ - `dist`
419
+ - `README.md`
420
+ - `LICENSE`
421
+ - `package.json`
422
+
423
+ ---
424
+
425
+ # English
426
+
427
+ `vite-plugin-build-version-file` is a build metadata plugin for Vite projects.
428
+
429
+ It exposes one consistent version payload in development and production:
430
+
431
+ - serves `version.json` through the Vite dev server
432
+ - emits `version.json` into the build output directory
433
+ - injects `window["__VERSION__"]` into normal Vite HTML
434
+ - supports VitePress through its top-level `transformHtml` hook
435
+ - supports custom versions, filenames, global variable names, time zones, and extra payload fields
436
+
437
+ The plugin only generates and exposes version metadata. It does not decide how your app should check for updates or refresh the page.
438
+
439
+ It intentionally does not include:
440
+
441
+ - polling logic
442
+ - update prompts
443
+ - retry rules
444
+ - reload timing
445
+
446
+ Those behaviors should stay in your application code.
447
+
448
+ ## Compatibility
449
+
450
+ - Vite projects
451
+ - Vue 3 + Vite projects
452
+ - VitePress projects
453
+ - `vite >= 5`
454
+ - Node.js `>= 18`
455
+
456
+ ## Installation
457
+
458
+ ```bash
459
+ pnpm add vite-plugin-build-version-file
252
460
  ```
253
461
 
254
- ### 3. 只保留 `version.json`,不注入 `window`
462
+ Or:
463
+
464
+ ```bash
465
+ npm install vite-plugin-build-version-file
466
+ ```
467
+
468
+ ## Quick Start
469
+
470
+ ### Vite / Vue 3
255
471
 
256
472
  ```ts
257
473
  import { defineConfig } from 'vite'
@@ -261,168 +477,275 @@ import buildVersionPlugin from 'vite-plugin-build-version-file'
261
477
  export default defineConfig({
262
478
  plugins: [
263
479
  vue(),
264
- buildVersionPlugin({
265
- injectToHtml: false
266
- })
480
+ buildVersionPlugin()
267
481
  ]
268
482
  })
269
483
  ```
270
484
 
271
- ### 4. 自定义输出路径
485
+ ### VitePress
272
486
 
273
- ```ts
274
- import { defineConfig } from 'vite'
275
- import vue from '@vitejs/plugin-vue'
487
+ 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.
488
+
489
+ ```js
490
+ import { defineConfig } from 'vitepress'
276
491
  import buildVersionPlugin from 'vite-plugin-build-version-file'
277
492
 
493
+ const buildVersion = buildVersionPlugin()
494
+
278
495
  export default defineConfig({
279
- plugins: [
280
- vue(),
281
- buildVersionPlugin({
282
- filename: 'meta/version.json'
283
- })
284
- ]
496
+ vite: {
497
+ plugins: [buildVersion],
498
+ },
499
+ transformHtml: buildVersion.transformHtml,
285
500
  })
286
501
  ```
287
502
 
288
- 构建后输出:
503
+ 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.
289
504
 
290
- ```text
291
- dist/meta/version.json
505
+ ## Default Behavior
506
+
507
+ With default options, the plugin:
508
+
509
+ - serves `/version.json` in development
510
+ - emits `version.json` after build
511
+ - injects `window["__VERSION__"]` into HTML
512
+ - reads the nearest project `package.json`
513
+ - writes `name` as `pkgName`
514
+ - writes `version` as `pkgVersion`
515
+ - uses `0` as the development version
516
+ - uses `YYYYMMDDHHmmss` as the production version
517
+ - uses `Asia/Shanghai` as the default time zone
518
+
519
+ Production payload example:
520
+
521
+ ```json
522
+ {
523
+ "pkgName": "your-app-name",
524
+ "pkgVersion": "1.0.0",
525
+ "time": "20260401153045",
526
+ "env": "production"
527
+ }
528
+ ```
529
+
530
+ Development payload example:
531
+
532
+ ```json
533
+ {
534
+ "pkgName": "your-app-name",
535
+ "pkgVersion": "1.0.0",
536
+ "time": 0,
537
+ "env": "development"
538
+ }
292
539
  ```
293
540
 
294
- ### 5. 自定义版本号生成逻辑
541
+ ## Options
295
542
 
296
543
  ```ts
297
- import { defineConfig } from 'vite'
298
- import vue from '@vitejs/plugin-vue'
299
- import buildVersionPlugin from 'vite-plugin-build-version-file'
544
+ type BuildVersionContext = {
545
+ command: 'serve' | 'build'
546
+ mode: string
547
+ }
300
548
 
301
- export default defineConfig({
302
- plugins: [
303
- vue(),
304
- buildVersionPlugin({
305
- version: ({ command, mode }) => {
306
- if (command === 'serve') {
307
- return 0
308
- }
549
+ type BuildVersionPluginOptions = {
550
+ filename?: string
551
+ globalName?: string
552
+ injectToHtml?: boolean
553
+ timeZone?: string
554
+ version?: string | number | ((ctx: BuildVersionContext) => string | number)
555
+ data?: Record<string, any>
556
+ log?: (content: any) => string
557
+ payload?: (json: any) => any
558
+ }
559
+ ```
309
560
 
310
- return `${mode}-20260401153045`
311
- }
312
- })
313
- ]
561
+ ### `filename`
562
+
563
+ - Type: `string`
564
+ - Default: `"version.json"`
565
+ - Controls the dev URL and build output path.
566
+
567
+ ```ts
568
+ buildVersionPlugin({
569
+ filename: 'meta/version.json'
314
570
  })
315
571
  ```
316
572
 
317
- ### 6. 指定时区生成时间
573
+ Build output:
574
+
575
+ ```text
576
+ dist/meta/version.json
577
+ ```
578
+
579
+ Development URL:
580
+
581
+ ```text
582
+ /meta/version.json
583
+ ```
584
+
585
+ If your project configures Vite `base`, the URL follows that base.
586
+
587
+ ### `globalName`
588
+
589
+ - Type: `string`
590
+ - Default: `"__VERSION__"`
591
+ - Controls the property name injected on `window`.
592
+
593
+ Default injection:
594
+
595
+ ```html
596
+ <script>
597
+ window["__VERSION__"] = { "time": "20260401153045", "env": "production" };
598
+ </script>
599
+ ```
600
+
601
+ The injected value is the full payload object, not a single version string.
602
+
603
+ ### `injectToHtml`
604
+
605
+ - Type: `boolean`
606
+ - Default: `true`
607
+ - Controls whether the plugin injects `window["__VERSION__"]`.
608
+
609
+ When set to `false`, `version.json` is still served and emitted.
610
+
611
+ ### `timeZone`
612
+
613
+ - Type: `string`
614
+ - Default: `"Asia/Shanghai"`
615
+ - Controls the time zone used by the default production timestamp.
616
+
617
+ This only affects the default `time`. If you pass `version`, your custom logic wins.
618
+
619
+ ### `version`
620
+
621
+ - Type: `string | number | ((ctx) => string | number)`
622
+ - Default: `0` in development, current timestamp in production
623
+ - Provides custom version generation.
318
624
 
319
625
  ```ts
320
- import { defineConfig } from 'vite'
321
- import vue from '@vitejs/plugin-vue'
322
- import buildVersionPlugin from 'vite-plugin-build-version-file'
626
+ buildVersionPlugin({
627
+ version: ({ command, mode }) => {
628
+ if (command === 'serve') {
629
+ return 0
630
+ }
323
631
 
324
- export default defineConfig({
325
- plugins: [
326
- vue(),
327
- buildVersionPlugin({
328
- timeZone: 'Asia/Shanghai'
329
- })
330
- ]
632
+ return `${mode}-20260401153045`
633
+ }
331
634
  })
332
635
  ```
333
636
 
334
- ### 7. 追加自定义数据
637
+ ### `data`
335
638
 
336
- ```ts
337
- import { defineConfig } from 'vite'
338
- import vue from '@vitejs/plugin-vue'
339
- import buildVersionPlugin from 'vite-plugin-build-version-file'
639
+ - Type: `Record<string, any>`
640
+ - Default: `{}`
641
+ - Adds custom fields to the final payload.
340
642
 
341
- export default defineConfig({
342
- plugins: [
343
- vue(),
344
- buildVersionPlugin({
345
- data: {
346
- appName: 'admin',
347
- gitBranch: 'release/2026-04',
348
- commitSha: 'abc1234'
349
- }
350
- })
351
- ]
643
+ ```ts
644
+ buildVersionPlugin({
645
+ data: {
646
+ appName: 'admin',
647
+ gitBranch: 'release/2026-04',
648
+ commitSha: 'abc1234'
649
+ }
352
650
  })
353
651
  ```
354
652
 
355
- 生成结果示例:
653
+ Example output:
356
654
 
357
655
  ```json
358
656
  {
359
657
  "appName": "admin",
360
658
  "gitBranch": "release/2026-04",
361
659
  "commitSha": "abc1234",
660
+ "pkgName": "your-app-name",
661
+ "pkgVersion": "1.0.0",
362
662
  "time": "20260401153045",
363
663
  "env": "production"
364
664
  }
365
665
  ```
366
666
 
367
- ## 在项目中读取版本
667
+ Merge order:
368
668
 
369
- 插件提供两种方式读取版本号,可根据场景选择:
669
+ - `data` is written first
670
+ - `pkgName` and `pkgVersion` from `package.json` override matching fields
671
+ - `time` and `env` from the plugin override matching fields
672
+ - `payload` can rewrite the final object
370
673
 
371
- ### 方式一:读取注入的全局变量
674
+ ### `payload`
372
675
 
373
- ```ts
374
- const payload = window['__VERSION__']
676
+ - Type: `(json: any) => any`
677
+ - Rewrites the final payload.
375
678
 
376
- console.log(payload.time)
377
- console.log(payload.env)
679
+ Use it to trim fields, rename fields, or keep compatibility with an older contract.
680
+
681
+ ```ts
682
+ buildVersionPlugin({
683
+ payload: (json) => ({
684
+ version: json.time,
685
+ env: json.env,
686
+ name: json.pkgName
687
+ })
688
+ })
378
689
  ```
379
690
 
380
- **说明:**
691
+ Returning an object is recommended so application reads stay stable.
381
692
 
382
- - 需要确保 `injectToHtml` 为 `true`(默认值)
383
- - 这里读到的是完整对象,不是单独字符串
384
- - 如果配置了 `data`,这里也可以直接读取对应字段
693
+ ### `log`
385
694
 
386
- ### 方式二:读取 `version.json`
695
+ - Type: `(content: any) => string`
696
+ - Customizes the build-complete console output.
387
697
 
388
698
  ```ts
389
- const response = await fetch('/version.json')
390
- const payload = await response.json()
699
+ buildVersionPlugin({
700
+ log: (payload) => `build version: ${payload.time}`
701
+ })
702
+ ```
703
+
704
+ ## Reading Version Metadata
705
+
706
+ ### Read the injected global
707
+
708
+ ```ts
709
+ const payload = window['__VERSION__']
391
710
 
392
711
  console.log(payload.time)
393
712
  console.log(payload.env)
394
713
  ```
395
714
 
396
- 如果你配置了自定义 `filename`,这里的请求路径也需要改成对应地址。
715
+ Use this when you need the version of the currently loaded page.
397
716
 
398
- 如果你配置了 `data`,返回结果中也会包含这些额外字段。
717
+ ### Read `version.json`
399
718
 
400
- 默认情况下,如果业务项目存在 `package.json`,返回结果中也会包含:
719
+ ```ts
720
+ const response = await fetch('/version.json', {
721
+ cache: 'no-store'
722
+ })
723
+ const payload = await response.json()
401
724
 
402
- - `pkgName`
403
- - `pkgVersion`
725
+ console.log(payload.time)
726
+ console.log(payload.env)
727
+ ```
404
728
 
405
- ## 在项目中做版本检查
729
+ Use this when you need to compare the current page against the latest deployed version.
406
730
 
407
- 推荐把“检查新版本并提示刷新”的逻辑写在业务项目里。
731
+ ## Checking for Updates
408
732
 
409
- 通用伪代码:
733
+ The plugin does not include update-checking UI. Keep that logic in your app.
410
734
 
411
735
  ```js
412
736
  const CHECK_INTERVAL = 60 * 1000
413
- const VERSION_URL = `${import.meta.env.VITE_BASE_PATH}/version.json`
737
+ const VERSION_URL = '/version.json'
414
738
 
415
739
  async function checkVersion() {
416
740
  try {
417
741
  const response = await fetch(VERSION_URL, {
418
742
  cache: 'no-store'
419
743
  })
420
-
421
- const payload = await response.json()
744
+ const latestPayload = await response.json()
422
745
  const currentPayload = window.__VERSION__
423
746
 
424
- if (currentPayload && payload.time !== currentPayload.time) {
425
- const shouldReload = await showUpdatePrompt()
747
+ if (currentPayload && latestPayload.time !== currentPayload.time) {
748
+ const shouldReload = window.confirm('A new version is available. Reload now?')
426
749
 
427
750
  if (shouldReload) {
428
751
  window.location.reload()
@@ -433,126 +756,86 @@ async function checkVersion() {
433
756
  console.warn('check version failed', error)
434
757
  }
435
758
 
436
- scheduleNextCheck()
437
- }
438
-
439
- function scheduleNextCheck() {
440
- setTimeout(checkVersion, CHECK_INTERVAL)
441
- }
442
-
443
- function showUpdatePrompt() {
444
- // 这里替换成你自己的弹窗、消息组件或确认框
445
- return Promise.resolve(window.confirm('检测到新版本,是否刷新页面?'))
759
+ window.setTimeout(checkVersion, CHECK_INTERVAL)
446
760
  }
447
761
 
448
762
  checkVersion()
449
763
  ```
450
764
 
451
- 建议:
452
-
453
- - 版本比较只判断 `payload.time` 和当前页面里的 `window.__VERSION__.time` 是否一致
454
- - 提示方式由业务项目自己决定,可以是弹窗、消息条、通知或静默刷新
455
- - 轮询间隔不要过短,通常用 1 分钟或更长更合适
456
- - 请求失败后继续下一轮检查,避免一次失败后整个流程中断
457
-
458
- 上面这段逻辑故意没有放进插件里,因为不同项目通常会有不同要求:
765
+ Recommendations:
459
766
 
460
- - 轮询频率不同
461
- - 请求路径可能不同
462
- - 弹窗组件不同
463
- - 刷新策略不同
464
- - 失败重试策略不同
767
+ - Compare `payload.time` in most cases.
768
+ - Fetch `version.json` with `cache: 'no-store'`.
769
+ - Avoid very short polling intervals. One minute or longer is usually enough.
770
+ - Keep prompts, notifications, silent reloads, and retry rules in application code.
771
+ - Continue polling after failed requests.
465
772
 
466
- ## 开发态行为说明
773
+ ## Development Behavior
467
774
 
468
- 开发态下,插件不会往项目的 `public/` 目录写文件。
775
+ In development, the plugin does not write files into your project directory.
469
776
 
470
- 它会直接通过 Vite dev server 返回版本 JSON,因此:
777
+ It serves JSON directly through the Vite dev server:
471
778
 
472
- - 不会污染业务项目目录
473
- - 更适合本地调试
474
- - 修改配置后重新启动 dev server 即可看到结果
779
+ - no `public/` pollution
780
+ - no temporary file generation
781
+ - restart the dev server after changing plugin options
475
782
 
476
- 如果项目配置了 `base`,版本文件访问路径会自动跟随 `base`。
783
+ ## Local Development
477
784
 
478
- ## 本地开发与联调
785
+ The repository contains two fixture apps:
479
786
 
480
- 仓库中已经提供了一个专门用于调试的 `Vite + Vue 3` 测试项目:
787
+ - Vite / Vue 3: `debug/fixture-app`
788
+ - VitePress: `debug/vitepress-app`
789
+ - They import the built package from `dist/index.mjs`
481
790
 
482
- - 路径:`debug/fixture-app`
483
- - 引用方式:直接引用根目录 `dist/index.mjs`
484
-
485
- 这意味着它测试的是“打包后的真实产物”,而不是源码直连。
486
-
487
- ### 安装依赖
791
+ This verifies the real build output instead of importing source files directly.
488
792
 
489
793
  ```bash
490
794
  pnpm install
491
- ```
492
-
493
- ### 构建插件
494
-
495
- ```bash
496
795
  pnpm build
497
- ```
498
-
499
- ### 启动测试项目
500
-
501
- ```bash
502
796
  pnpm dev:fixture
503
797
  ```
504
798
 
505
- 启动后可以在页面里直接看到:
506
-
507
- - `window["__VERSION__"]`
508
- - `/version.json` 的返回结果
509
-
510
- ### 构建测试项目
799
+ Run the VitePress fixture:
511
800
 
512
801
  ```bash
513
- pnpm build:fixture
802
+ pnpm dev:vitepress
514
803
  ```
515
804
 
516
- 这个命令会先构建插件,再构建测试项目,用来验证 `dist` 产物能否被正常消费。
517
-
518
- ## 发布到 npm
519
-
520
- ### 1. 登录 npm
805
+ Build fixture apps:
521
806
 
522
807
  ```bash
523
- npm login
808
+ pnpm build:fixture
809
+ pnpm build:vitepress
524
810
  ```
525
811
 
526
- ### 2. 构建并验证
812
+ ## Publishing
527
813
 
528
814
  ```bash
529
815
  pnpm build
530
816
  pnpm build:fixture
531
- ```
532
-
533
- ### 3. 发布公开包
534
-
535
- ```bash
817
+ pnpm build:vitepress
536
818
  npm publish --access public
537
819
  ```
538
820
 
539
- ## 发布前检查建议
821
+ Before publishing, check:
540
822
 
541
- 发布前建议至少确认以下几点:
542
-
543
- - `dist/index.mjs`、`dist/index.cjs`、`dist/index.d.ts` 已生成
544
- - `debug/fixture-app` 可以正常运行或构建
545
- - `version.json` 输出正常
546
- - HTML 注入结果符合预期
547
- - `package.json` 中的 `name`、`version`、`exports`、`publishConfig` 正确
823
+ - `dist/index.mjs`
824
+ - `dist/index.cjs`
825
+ - `dist/index.d.ts`
826
+ - fixture app build
827
+ - VitePress fixture build
828
+ - `version.json` output
829
+ - normal Vite HTML injection
830
+ - VitePress `transformHtml` injection
548
831
 
549
- 如果你要进一步检查最终发布内容,可以执行:
832
+ Inspect the publish package:
550
833
 
551
834
  ```bash
552
835
  npm pack
553
836
  ```
554
837
 
555
- 正常情况下,发布包中应只包含:
838
+ The package should contain:
556
839
 
557
840
  - `dist`
558
841
  - `README.md`
package/dist/index.cjs CHANGED
@@ -129,6 +129,19 @@ function serializePayload(payload) {
129
129
  return `${JSON.stringify(payload, null, 2)}
130
130
  `;
131
131
  }
132
+ function createInjectScript(globalName, payload) {
133
+ return `window[${JSON.stringify(globalName)}] = ${JSON.stringify(payload)};`;
134
+ }
135
+ function injectScriptToHead(code, script) {
136
+ const scriptTag = `<script>${script}</script>`;
137
+ if (code.includes(script)) {
138
+ return code;
139
+ }
140
+ if (code.includes("</head>")) {
141
+ return code.replace("</head>", `${scriptTag}</head>`);
142
+ }
143
+ return `${scriptTag}${code}`;
144
+ }
132
145
  function parsePathname(requestUrl) {
133
146
  if (!requestUrl) {
134
147
  return "/";
@@ -171,7 +184,13 @@ function buildVersionPlugin(options = {}) {
171
184
  const timeZone = normalizeTimeZone(options.timeZone ?? DEFAULT_TIME_ZONE);
172
185
  const data = normalizeData(options.data);
173
186
  let payload;
174
- return {
187
+ const transformHtml = (code, ..._args) => {
188
+ if (!injectToHtml || !payload) {
189
+ return void 0;
190
+ }
191
+ return injectScriptToHead(code, createInjectScript(globalName, payload));
192
+ };
193
+ const plugin = {
175
194
  name: PLUGIN_NAME,
176
195
  configResolved(config) {
177
196
  const context = {
@@ -207,7 +226,7 @@ function buildVersionPlugin(options = {}) {
207
226
  {
208
227
  tag: "script",
209
228
  injectTo: "head",
210
- children: `window[${JSON.stringify(globalName)}] = ${JSON.stringify(payload)};`
229
+ children: createInjectScript(globalName, payload)
211
230
  }
212
231
  ];
213
232
  return tags;
@@ -229,6 +248,10 @@ function buildVersionPlugin(options = {}) {
229
248
  }
230
249
  }
231
250
  };
251
+ Object.defineProperty(plugin, "transformHtml", {
252
+ value: transformHtml
253
+ });
254
+ return plugin;
232
255
  }
233
256
  var index_default = buildVersionPlugin;
234
257
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.d.cts CHANGED
@@ -19,6 +19,9 @@ interface BuildVersionPluginOptions {
19
19
  log?: (content: any) => string;
20
20
  payload?: (json: any) => any;
21
21
  }
22
- declare function buildVersionPlugin(options?: BuildVersionPluginOptions): Plugin;
22
+ type BuildVersionPlugin = Plugin & {
23
+ transformHtml: (code: string, ...args: unknown[]) => string | undefined;
24
+ };
25
+ declare function buildVersionPlugin(options?: BuildVersionPluginOptions): BuildVersionPlugin;
23
26
 
24
- export { type BuildVersionContext, type BuildVersionPayload, type BuildVersionPluginOptions, buildVersionPlugin, buildVersionPlugin as default };
27
+ export { type BuildVersionContext, type BuildVersionPayload, type BuildVersionPlugin, type BuildVersionPluginOptions, buildVersionPlugin, buildVersionPlugin as default };
package/dist/index.d.ts CHANGED
@@ -19,6 +19,9 @@ interface BuildVersionPluginOptions {
19
19
  log?: (content: any) => string;
20
20
  payload?: (json: any) => any;
21
21
  }
22
- declare function buildVersionPlugin(options?: BuildVersionPluginOptions): Plugin;
22
+ type BuildVersionPlugin = Plugin & {
23
+ transformHtml: (code: string, ...args: unknown[]) => string | undefined;
24
+ };
25
+ declare function buildVersionPlugin(options?: BuildVersionPluginOptions): BuildVersionPlugin;
23
26
 
24
- export { type BuildVersionContext, type BuildVersionPayload, type BuildVersionPluginOptions, buildVersionPlugin, buildVersionPlugin as default };
27
+ export { type BuildVersionContext, type BuildVersionPayload, type BuildVersionPlugin, type BuildVersionPluginOptions, buildVersionPlugin, buildVersionPlugin as default };
package/dist/index.mjs CHANGED
@@ -104,6 +104,19 @@ function serializePayload(payload) {
104
104
  return `${JSON.stringify(payload, null, 2)}
105
105
  `;
106
106
  }
107
+ function createInjectScript(globalName, payload) {
108
+ return `window[${JSON.stringify(globalName)}] = ${JSON.stringify(payload)};`;
109
+ }
110
+ function injectScriptToHead(code, script) {
111
+ const scriptTag = `<script>${script}</script>`;
112
+ if (code.includes(script)) {
113
+ return code;
114
+ }
115
+ if (code.includes("</head>")) {
116
+ return code.replace("</head>", `${scriptTag}</head>`);
117
+ }
118
+ return `${scriptTag}${code}`;
119
+ }
107
120
  function parsePathname(requestUrl) {
108
121
  if (!requestUrl) {
109
122
  return "/";
@@ -146,7 +159,13 @@ function buildVersionPlugin(options = {}) {
146
159
  const timeZone = normalizeTimeZone(options.timeZone ?? DEFAULT_TIME_ZONE);
147
160
  const data = normalizeData(options.data);
148
161
  let payload;
149
- return {
162
+ const transformHtml = (code, ..._args) => {
163
+ if (!injectToHtml || !payload) {
164
+ return void 0;
165
+ }
166
+ return injectScriptToHead(code, createInjectScript(globalName, payload));
167
+ };
168
+ const plugin = {
150
169
  name: PLUGIN_NAME,
151
170
  configResolved(config) {
152
171
  const context = {
@@ -182,7 +201,7 @@ function buildVersionPlugin(options = {}) {
182
201
  {
183
202
  tag: "script",
184
203
  injectTo: "head",
185
- children: `window[${JSON.stringify(globalName)}] = ${JSON.stringify(payload)};`
204
+ children: createInjectScript(globalName, payload)
186
205
  }
187
206
  ];
188
207
  return tags;
@@ -204,6 +223,10 @@ function buildVersionPlugin(options = {}) {
204
223
  }
205
224
  }
206
225
  };
226
+ Object.defineProperty(plugin, "transformHtml", {
227
+ value: transformHtml
228
+ });
229
+ return plugin;
207
230
  }
208
231
  var index_default = buildVersionPlugin;
209
232
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-build-version-file",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "A Vite plugin that emits version.json and injects version metadata into HTML.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,7 +23,9 @@
23
23
  "scripts": {
24
24
  "build": "tsup --config tsup.config.ts",
25
25
  "build:fixture": "pnpm build && vite build --config debug/fixture-app/vite.config.ts",
26
+ "build:vitepress": "pnpm build && vitepress build debug/vitepress-app",
26
27
  "dev:fixture": "pnpm build && vite --config debug/fixture-app/vite.config.ts",
28
+ "dev:vitepress": "pnpm build && vitepress dev debug/vitepress-app",
27
29
  "prepublishOnly": "pnpm build:fixture"
28
30
  },
29
31
  "keywords": [
@@ -49,6 +51,7 @@
49
51
  "tsup": "^8.0.0",
50
52
  "typescript": "^5.0.0",
51
53
  "vite": "^5.0.0",
54
+ "vitepress": "^1.6.4",
52
55
  "vue": "^3.4.0"
53
56
  }
54
57
  }