vite-plugin-update-detector 1.0.0

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 ADDED
@@ -0,0 +1,93 @@
1
+ # vite-plugin-update-detector
2
+
3
+ Vite 插件:自动检测网页更新并提示用户刷新。
4
+
5
+ 当项目重新构建后,已打开的页面会检测到版本变化,并提示用户刷新页面以加载最新版本。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install vite-plugin-update-detector -D
11
+ # 或
12
+ pnpm add vite-plugin-update-detector -D
13
+ ```
14
+
15
+ ## 使用
16
+
17
+ ```ts
18
+ // vite.config.ts
19
+ import { defineConfig } from 'vite';
20
+ import updateDetector from 'vite-plugin-update-detector';
21
+
22
+ export default defineConfig({
23
+ plugins: [
24
+ updateDetector({
25
+ interval: 60000, // 检测间隔,默认 60 秒
26
+ versionFile: 'version.json', // 版本文件路径
27
+ customNotifier: 'myNotify', // 自定义通知函数名
28
+ enable: true // 是否启用插件
29
+ })
30
+ ]
31
+ });
32
+ ```
33
+
34
+ ## 配置项
35
+
36
+ | 选项 | 类型 | 默认值 | 说明 |
37
+ |------|------|--------|------|
38
+ | `interval` | `number` | `60000` | 检测间隔时间(毫秒) |
39
+ | `versionFile` | `string` | `'version.json'` | 构建生成的版本文件路径 |
40
+ | `customNotifier` | `string` | - | 挂载到 `window` 上的自定义通知函数名 |
41
+ | `enable` | `boolean \| (() => boolean) \| (() => Promise<boolean>)` | `true` | 是否启用插件,支持函数形式动态判断 |
42
+
43
+ ## 自定义通知
44
+
45
+ 默认行为是弹窗确认刷新。你可以通过 `customNotifier` 指定一个挂载在 `window` 上的函数来替代默认行为:
46
+
47
+ ```html
48
+ <script>
49
+ // 定义自定义通知函数,参数为新版本时间戳
50
+ window.myNotify = function(newVersion) {
51
+ // 展示你的自定义 UI
52
+ showUpdateBanner();
53
+ };
54
+ </script>
55
+ ```
56
+
57
+ ```ts
58
+ // vite.config.ts
59
+ updateDetector({
60
+ customNotifier: 'myNotify'
61
+ })
62
+ ```
63
+
64
+ ### 动态启用插件
65
+
66
+ `enable` 选项支持函数形式,可根据环境动态判断是否启用:
67
+
68
+ ```ts
69
+ updateDetector({
70
+ // 仅在生产构建时启用
71
+ enable: () => process.env.NODE_ENV === 'production'
72
+ })
73
+
74
+ updateDetector({
75
+ // 支持异步判断
76
+ enable: async () => {
77
+ const config = await fetchConfig();
78
+ return config.enableUpdateCheck;
79
+ }
80
+ })
81
+ ```
82
+
83
+ ## 原理
84
+
85
+ 1. **构建时**:插件在 `buildStart` 记录时间戳,在 `generateBundle` 生成 `version.json`(含时间戳)
86
+ 2. **注入代码**:通过 `transformIndexHtml` 将检测脚本内联注入 HTML `<head>`
87
+ 3. **运行时**:客户端代码定期轮询 `version.json`,比对时间戳判断是否更新
88
+ 4. **检测到更新**:优先调用自定义通知函数,否则弹窗确认刷新
89
+ 5. **可见性监听**:页面从后台切回前台时也会立即检查一次
90
+
91
+ ## License
92
+
93
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ default: () => updateDetector
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ function updateDetector(options = {}) {
27
+ const {
28
+ interval = 6e4,
29
+ versionFile = "version.json",
30
+ customNotifier,
31
+ enable
32
+ } = options;
33
+ let config;
34
+ let buildTimestamp;
35
+ async function resolveEnable() {
36
+ if (enable === void 0) {
37
+ return true;
38
+ }
39
+ if (typeof enable === "function") {
40
+ return await enable();
41
+ }
42
+ return enable;
43
+ }
44
+ function getClientCode() {
45
+ return `
46
+ (function() {
47
+ var INTERVAL = ${interval};
48
+ var VERSION_FILE = '${versionFile}';
49
+ var CUSTOM_NOTIFIER = ${customNotifier ? "'" + customNotifier + "'" : "null"};
50
+
51
+ var currentVersion = null;
52
+ var timer = null;
53
+
54
+ // \u9ED8\u8BA4\u901A\u77E5\uFF1A\u5F39\u51FA\u786E\u8BA4\u6846\u5E76\u5237\u65B0
55
+ function defaultNotifier() {
56
+ if (confirm('\u68C0\u6D4B\u5230\u65B0\u7248\u672C\uFF0C\u662F\u5426\u5237\u65B0\u9875\u9762\uFF1F')) {
57
+ window.location.reload();
58
+ }
59
+ }
60
+
61
+ // \u6267\u884C\u901A\u77E5\u903B\u8F91
62
+ function notify(newVersion) {
63
+ console.log('[update-detector] \u68C0\u6D4B\u5230\u65B0\u7248\u672C:', newVersion);
64
+ // \u4F18\u5148\u4F7F\u7528\u81EA\u5B9A\u4E49\u901A\u77E5\u51FD\u6570
65
+ if (CUSTOM_NOTIFIER && typeof window[CUSTOM_NOTIFIER] === 'function') {
66
+ window[CUSTOM_NOTIFIER](newVersion);
67
+ return;
68
+ }
69
+ // \u9ED8\u8BA4\u5F39\u7A97\u901A\u77E5
70
+ defaultNotifier();
71
+ }
72
+
73
+ // \u68C0\u67E5\u7248\u672C\u66F4\u65B0
74
+ function checkUpdate() {
75
+ fetch(VERSION_FILE + '?t=' + Date.now())
76
+ .then(function(response) {
77
+ if (!response.ok) return response;
78
+ return response.json();
79
+ })
80
+ .then(function(data) {
81
+ if (!data) return;
82
+ if (!currentVersion) {
83
+ currentVersion = data.timestamp;
84
+ return;
85
+ }
86
+ if (currentVersion !== data.timestamp) {
87
+ notify(data.timestamp);
88
+ currentVersion = data.timestamp;
89
+ }
90
+ })
91
+ .catch(function(error) {
92
+ console.error('[update-detector] \u7248\u672C\u68C0\u67E5\u5931\u8D25:', error);
93
+ });
94
+ }
95
+
96
+ // \u9875\u9762\u52A0\u8F7D\u540E\u7ACB\u5373\u68C0\u67E5\u4E00\u6B21
97
+ window.addEventListener('load', checkUpdate);
98
+ // \u5B9A\u671F\u8F6E\u8BE2\u68C0\u67E5
99
+ timer = setInterval(checkUpdate, INTERVAL);
100
+ // \u9875\u9762\u53EF\u89C1\u6027\u53D8\u5316\u65F6\u68C0\u67E5\uFF08\u4ECE\u540E\u53F0\u5207\u56DE\u524D\u53F0\uFF09
101
+ document.addEventListener('visibilitychange', function() {
102
+ if (document.visibilityState === 'visible') {
103
+ checkUpdate();
104
+ }
105
+ });
106
+ })();
107
+ `;
108
+ }
109
+ return {
110
+ name: "vite-plugin-update-detector",
111
+ enforce: "post",
112
+ configResolved(resolvedConfig) {
113
+ config = resolvedConfig;
114
+ },
115
+ // 构建时记录时间戳
116
+ buildStart() {
117
+ buildTimestamp = Date.now();
118
+ },
119
+ // 构建产物中生成版本文件
120
+ async generateBundle() {
121
+ if (!await resolveEnable()) return;
122
+ this.emitFile({
123
+ type: "asset",
124
+ fileName: versionFile,
125
+ source: JSON.stringify({ timestamp: buildTimestamp })
126
+ });
127
+ },
128
+ // 在 HTML 中直接内联注入客户端检测代码
129
+ async transformIndexHtml(html) {
130
+ if (!await resolveEnable()) return html;
131
+ return [
132
+ {
133
+ tag: "script",
134
+ children: getClientCode(),
135
+ injectTo: "head"
136
+ }
137
+ ];
138
+ }
139
+ };
140
+ }
141
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Plugin, ResolvedConfig } from 'vite';\r\n\r\nexport interface UpdateDetectorOptions {\r\n /** 检测间隔时间(毫秒),默认 60000 */\r\n interval?: number;\r\n /** 构建版本文件路径,默认 'version.json' */\r\n versionFile?: string;\r\n /** 自定义通知函数名称(挂载到 window 上),如 'myNotifyFn' */\r\n customNotifier?: string;\r\n /** 是否启用插件,默认 true */\r\n enable?: boolean | (() => boolean) | (() => Promise<boolean>);\r\n}\r\n\r\nexport default function updateDetector(options: UpdateDetectorOptions = {}): Plugin {\r\n const {\r\n interval = 60000,\r\n versionFile = 'version.json',\r\n customNotifier,\r\n enable\r\n } = options;\r\n\r\n let config: ResolvedConfig;\r\n let buildTimestamp: number;\r\n\r\n /** 解析 enable 配置值 */\r\n async function resolveEnable(): Promise<boolean> {\r\n if (enable === undefined) {\r\n return true;\r\n }\r\n if (typeof enable === 'function') {\r\n return await enable();\r\n }\r\n return enable;\r\n }\r\n\r\n /**\r\n * 生成客户端检测代码(直接内联注入,避免虚拟模块在生产构建中无法解析)\r\n */\r\n function getClientCode(): string {\r\n return `\r\n (function() {\r\n var INTERVAL = ${interval};\r\n var VERSION_FILE = '${versionFile}';\r\n var CUSTOM_NOTIFIER = ${customNotifier ? \"'\" + customNotifier + \"'\" : 'null'};\r\n\r\n var currentVersion = null;\r\n var timer = null;\r\n\r\n // 默认通知:弹出确认框并刷新\r\n function defaultNotifier() {\r\n if (confirm('检测到新版本,是否刷新页面?')) {\r\n window.location.reload();\r\n }\r\n }\r\n\r\n // 执行通知逻辑\r\n function notify(newVersion) {\r\n console.log('[update-detector] 检测到新版本:', newVersion);\r\n // 优先使用自定义通知函数\r\n if (CUSTOM_NOTIFIER && typeof window[CUSTOM_NOTIFIER] === 'function') {\r\n window[CUSTOM_NOTIFIER](newVersion);\r\n return;\r\n }\r\n // 默认弹窗通知\r\n defaultNotifier();\r\n }\r\n\r\n // 检查版本更新\r\n function checkUpdate() {\r\n fetch(VERSION_FILE + '?t=' + Date.now())\r\n .then(function(response) {\r\n if (!response.ok) return response;\r\n return response.json();\r\n })\r\n .then(function(data) {\r\n if (!data) return;\r\n if (!currentVersion) {\r\n currentVersion = data.timestamp;\r\n return;\r\n }\r\n if (currentVersion !== data.timestamp) {\r\n notify(data.timestamp);\r\n currentVersion = data.timestamp;\r\n }\r\n })\r\n .catch(function(error) {\r\n console.error('[update-detector] 版本检查失败:', error);\r\n });\r\n }\r\n\r\n // 页面加载后立即检查一次\r\n window.addEventListener('load', checkUpdate);\r\n // 定期轮询检查\r\n timer = setInterval(checkUpdate, INTERVAL);\r\n // 页面可见性变化时检查(从后台切回前台)\r\n document.addEventListener('visibilitychange', function() {\r\n if (document.visibilityState === 'visible') {\r\n checkUpdate();\r\n }\r\n });\r\n })();\r\n `;\r\n }\r\n\r\n return {\r\n name: 'vite-plugin-update-detector',\r\n enforce: 'post',\r\n\r\n configResolved(resolvedConfig) {\r\n config = resolvedConfig;\r\n },\r\n\r\n // 构建时记录时间戳\r\n buildStart() {\r\n buildTimestamp = Date.now();\r\n },\r\n\r\n // 构建产物中生成版本文件\r\n async generateBundle() {\r\n if (!(await resolveEnable())) return;\r\n this.emitFile({\r\n type: 'asset',\r\n fileName: versionFile,\r\n source: JSON.stringify({ timestamp: buildTimestamp })\r\n });\r\n },\r\n\r\n // 在 HTML 中直接内联注入客户端检测代码\r\n async transformIndexHtml(html) {\r\n if (!(await resolveEnable())) return html;\r\n\r\n return [\r\n {\r\n tag: 'script',\r\n children: getClientCode(),\r\n injectTo: 'head' as const\r\n }\r\n ];\r\n }\r\n };\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAae,SAAR,eAAgC,UAAiC,CAAC,GAAW;AAClF,QAAM;AAAA,IACJ,WAAW;AAAA,IACX,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,MAAI;AACJ,MAAI;AAGJ,iBAAe,gBAAkC;AAC/C,QAAI,WAAW,QAAW;AACxB,aAAO;AAAA,IACT;AACA,QAAI,OAAO,WAAW,YAAY;AAChC,aAAO,MAAM,OAAO;AAAA,IACtB;AACA,WAAO;AAAA,EACT;AAKA,WAAS,gBAAwB;AAC/B,WAAO;AAAA;AAAA,yBAEc,QAAQ;AAAA,8BACH,WAAW;AAAA,gCACT,iBAAiB,MAAM,iBAAiB,MAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2DlF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IAET,eAAe,gBAAgB;AAC7B,eAAS;AAAA,IACX;AAAA;AAAA,IAGA,aAAa;AACX,uBAAiB,KAAK,IAAI;AAAA,IAC5B;AAAA;AAAA,IAGA,MAAM,iBAAiB;AACrB,UAAI,CAAE,MAAM,cAAc,EAAI;AAC9B,WAAK,SAAS;AAAA,QACZ,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ,KAAK,UAAU,EAAE,WAAW,eAAe,CAAC;AAAA,MACtD,CAAC;AAAA,IACH;AAAA;AAAA,IAGA,MAAM,mBAAmB,MAAM;AAC7B,UAAI,CAAE,MAAM,cAAc,EAAI,QAAO;AAErC,aAAO;AAAA,QACL;AAAA,UACE,KAAK;AAAA,UACL,UAAU,cAAc;AAAA,UACxB,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,15 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface UpdateDetectorOptions {
4
+ /** 检测间隔时间(毫秒),默认 60000 */
5
+ interval?: number;
6
+ /** 构建版本文件路径,默认 'version.json' */
7
+ versionFile?: string;
8
+ /** 自定义通知函数名称(挂载到 window 上),如 'myNotifyFn' */
9
+ customNotifier?: string;
10
+ /** 是否启用插件,默认 true */
11
+ enable?: boolean | (() => boolean) | (() => Promise<boolean>);
12
+ }
13
+ declare function updateDetector(options?: UpdateDetectorOptions): Plugin;
14
+
15
+ export { type UpdateDetectorOptions, updateDetector as default };
@@ -0,0 +1,15 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface UpdateDetectorOptions {
4
+ /** 检测间隔时间(毫秒),默认 60000 */
5
+ interval?: number;
6
+ /** 构建版本文件路径,默认 'version.json' */
7
+ versionFile?: string;
8
+ /** 自定义通知函数名称(挂载到 window 上),如 'myNotifyFn' */
9
+ customNotifier?: string;
10
+ /** 是否启用插件,默认 true */
11
+ enable?: boolean | (() => boolean) | (() => Promise<boolean>);
12
+ }
13
+ declare function updateDetector(options?: UpdateDetectorOptions): Plugin;
14
+
15
+ export { type UpdateDetectorOptions, updateDetector as default };
package/dist/index.js ADDED
@@ -0,0 +1,120 @@
1
+ // src/index.ts
2
+ function updateDetector(options = {}) {
3
+ const {
4
+ interval = 6e4,
5
+ versionFile = "version.json",
6
+ customNotifier,
7
+ enable
8
+ } = options;
9
+ let config;
10
+ let buildTimestamp;
11
+ async function resolveEnable() {
12
+ if (enable === void 0) {
13
+ return true;
14
+ }
15
+ if (typeof enable === "function") {
16
+ return await enable();
17
+ }
18
+ return enable;
19
+ }
20
+ function getClientCode() {
21
+ return `
22
+ (function() {
23
+ var INTERVAL = ${interval};
24
+ var VERSION_FILE = '${versionFile}';
25
+ var CUSTOM_NOTIFIER = ${customNotifier ? "'" + customNotifier + "'" : "null"};
26
+
27
+ var currentVersion = null;
28
+ var timer = null;
29
+
30
+ // \u9ED8\u8BA4\u901A\u77E5\uFF1A\u5F39\u51FA\u786E\u8BA4\u6846\u5E76\u5237\u65B0
31
+ function defaultNotifier() {
32
+ if (confirm('\u68C0\u6D4B\u5230\u65B0\u7248\u672C\uFF0C\u662F\u5426\u5237\u65B0\u9875\u9762\uFF1F')) {
33
+ window.location.reload();
34
+ }
35
+ }
36
+
37
+ // \u6267\u884C\u901A\u77E5\u903B\u8F91
38
+ function notify(newVersion) {
39
+ console.log('[update-detector] \u68C0\u6D4B\u5230\u65B0\u7248\u672C:', newVersion);
40
+ // \u4F18\u5148\u4F7F\u7528\u81EA\u5B9A\u4E49\u901A\u77E5\u51FD\u6570
41
+ if (CUSTOM_NOTIFIER && typeof window[CUSTOM_NOTIFIER] === 'function') {
42
+ window[CUSTOM_NOTIFIER](newVersion);
43
+ return;
44
+ }
45
+ // \u9ED8\u8BA4\u5F39\u7A97\u901A\u77E5
46
+ defaultNotifier();
47
+ }
48
+
49
+ // \u68C0\u67E5\u7248\u672C\u66F4\u65B0
50
+ function checkUpdate() {
51
+ fetch(VERSION_FILE + '?t=' + Date.now())
52
+ .then(function(response) {
53
+ if (!response.ok) return response;
54
+ return response.json();
55
+ })
56
+ .then(function(data) {
57
+ if (!data) return;
58
+ if (!currentVersion) {
59
+ currentVersion = data.timestamp;
60
+ return;
61
+ }
62
+ if (currentVersion !== data.timestamp) {
63
+ notify(data.timestamp);
64
+ currentVersion = data.timestamp;
65
+ }
66
+ })
67
+ .catch(function(error) {
68
+ console.error('[update-detector] \u7248\u672C\u68C0\u67E5\u5931\u8D25:', error);
69
+ });
70
+ }
71
+
72
+ // \u9875\u9762\u52A0\u8F7D\u540E\u7ACB\u5373\u68C0\u67E5\u4E00\u6B21
73
+ window.addEventListener('load', checkUpdate);
74
+ // \u5B9A\u671F\u8F6E\u8BE2\u68C0\u67E5
75
+ timer = setInterval(checkUpdate, INTERVAL);
76
+ // \u9875\u9762\u53EF\u89C1\u6027\u53D8\u5316\u65F6\u68C0\u67E5\uFF08\u4ECE\u540E\u53F0\u5207\u56DE\u524D\u53F0\uFF09
77
+ document.addEventListener('visibilitychange', function() {
78
+ if (document.visibilityState === 'visible') {
79
+ checkUpdate();
80
+ }
81
+ });
82
+ })();
83
+ `;
84
+ }
85
+ return {
86
+ name: "vite-plugin-update-detector",
87
+ enforce: "post",
88
+ configResolved(resolvedConfig) {
89
+ config = resolvedConfig;
90
+ },
91
+ // 构建时记录时间戳
92
+ buildStart() {
93
+ buildTimestamp = Date.now();
94
+ },
95
+ // 构建产物中生成版本文件
96
+ async generateBundle() {
97
+ if (!await resolveEnable()) return;
98
+ this.emitFile({
99
+ type: "asset",
100
+ fileName: versionFile,
101
+ source: JSON.stringify({ timestamp: buildTimestamp })
102
+ });
103
+ },
104
+ // 在 HTML 中直接内联注入客户端检测代码
105
+ async transformIndexHtml(html) {
106
+ if (!await resolveEnable()) return html;
107
+ return [
108
+ {
109
+ tag: "script",
110
+ children: getClientCode(),
111
+ injectTo: "head"
112
+ }
113
+ ];
114
+ }
115
+ };
116
+ }
117
+ export {
118
+ updateDetector as default
119
+ };
120
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Plugin, ResolvedConfig } from 'vite';\r\n\r\nexport interface UpdateDetectorOptions {\r\n /** 检测间隔时间(毫秒),默认 60000 */\r\n interval?: number;\r\n /** 构建版本文件路径,默认 'version.json' */\r\n versionFile?: string;\r\n /** 自定义通知函数名称(挂载到 window 上),如 'myNotifyFn' */\r\n customNotifier?: string;\r\n /** 是否启用插件,默认 true */\r\n enable?: boolean | (() => boolean) | (() => Promise<boolean>);\r\n}\r\n\r\nexport default function updateDetector(options: UpdateDetectorOptions = {}): Plugin {\r\n const {\r\n interval = 60000,\r\n versionFile = 'version.json',\r\n customNotifier,\r\n enable\r\n } = options;\r\n\r\n let config: ResolvedConfig;\r\n let buildTimestamp: number;\r\n\r\n /** 解析 enable 配置值 */\r\n async function resolveEnable(): Promise<boolean> {\r\n if (enable === undefined) {\r\n return true;\r\n }\r\n if (typeof enable === 'function') {\r\n return await enable();\r\n }\r\n return enable;\r\n }\r\n\r\n /**\r\n * 生成客户端检测代码(直接内联注入,避免虚拟模块在生产构建中无法解析)\r\n */\r\n function getClientCode(): string {\r\n return `\r\n (function() {\r\n var INTERVAL = ${interval};\r\n var VERSION_FILE = '${versionFile}';\r\n var CUSTOM_NOTIFIER = ${customNotifier ? \"'\" + customNotifier + \"'\" : 'null'};\r\n\r\n var currentVersion = null;\r\n var timer = null;\r\n\r\n // 默认通知:弹出确认框并刷新\r\n function defaultNotifier() {\r\n if (confirm('检测到新版本,是否刷新页面?')) {\r\n window.location.reload();\r\n }\r\n }\r\n\r\n // 执行通知逻辑\r\n function notify(newVersion) {\r\n console.log('[update-detector] 检测到新版本:', newVersion);\r\n // 优先使用自定义通知函数\r\n if (CUSTOM_NOTIFIER && typeof window[CUSTOM_NOTIFIER] === 'function') {\r\n window[CUSTOM_NOTIFIER](newVersion);\r\n return;\r\n }\r\n // 默认弹窗通知\r\n defaultNotifier();\r\n }\r\n\r\n // 检查版本更新\r\n function checkUpdate() {\r\n fetch(VERSION_FILE + '?t=' + Date.now())\r\n .then(function(response) {\r\n if (!response.ok) return response;\r\n return response.json();\r\n })\r\n .then(function(data) {\r\n if (!data) return;\r\n if (!currentVersion) {\r\n currentVersion = data.timestamp;\r\n return;\r\n }\r\n if (currentVersion !== data.timestamp) {\r\n notify(data.timestamp);\r\n currentVersion = data.timestamp;\r\n }\r\n })\r\n .catch(function(error) {\r\n console.error('[update-detector] 版本检查失败:', error);\r\n });\r\n }\r\n\r\n // 页面加载后立即检查一次\r\n window.addEventListener('load', checkUpdate);\r\n // 定期轮询检查\r\n timer = setInterval(checkUpdate, INTERVAL);\r\n // 页面可见性变化时检查(从后台切回前台)\r\n document.addEventListener('visibilitychange', function() {\r\n if (document.visibilityState === 'visible') {\r\n checkUpdate();\r\n }\r\n });\r\n })();\r\n `;\r\n }\r\n\r\n return {\r\n name: 'vite-plugin-update-detector',\r\n enforce: 'post',\r\n\r\n configResolved(resolvedConfig) {\r\n config = resolvedConfig;\r\n },\r\n\r\n // 构建时记录时间戳\r\n buildStart() {\r\n buildTimestamp = Date.now();\r\n },\r\n\r\n // 构建产物中生成版本文件\r\n async generateBundle() {\r\n if (!(await resolveEnable())) return;\r\n this.emitFile({\r\n type: 'asset',\r\n fileName: versionFile,\r\n source: JSON.stringify({ timestamp: buildTimestamp })\r\n });\r\n },\r\n\r\n // 在 HTML 中直接内联注入客户端检测代码\r\n async transformIndexHtml(html) {\r\n if (!(await resolveEnable())) return html;\r\n\r\n return [\r\n {\r\n tag: 'script',\r\n children: getClientCode(),\r\n injectTo: 'head' as const\r\n }\r\n ];\r\n }\r\n };\r\n}\r\n"],"mappings":";AAae,SAAR,eAAgC,UAAiC,CAAC,GAAW;AAClF,QAAM;AAAA,IACJ,WAAW;AAAA,IACX,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,MAAI;AACJ,MAAI;AAGJ,iBAAe,gBAAkC;AAC/C,QAAI,WAAW,QAAW;AACxB,aAAO;AAAA,IACT;AACA,QAAI,OAAO,WAAW,YAAY;AAChC,aAAO,MAAM,OAAO;AAAA,IACtB;AACA,WAAO;AAAA,EACT;AAKA,WAAS,gBAAwB;AAC/B,WAAO;AAAA;AAAA,yBAEc,QAAQ;AAAA,8BACH,WAAW;AAAA,gCACT,iBAAiB,MAAM,iBAAiB,MAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2DlF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IAET,eAAe,gBAAgB;AAC7B,eAAS;AAAA,IACX;AAAA;AAAA,IAGA,aAAa;AACX,uBAAiB,KAAK,IAAI;AAAA,IAC5B;AAAA;AAAA,IAGA,MAAM,iBAAiB;AACrB,UAAI,CAAE,MAAM,cAAc,EAAI;AAC9B,WAAK,SAAS;AAAA,QACZ,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ,KAAK,UAAU,EAAE,WAAW,eAAe,CAAC;AAAA,MACtD,CAAC;AAAA,IACH;AAAA;AAAA,IAGA,MAAM,mBAAmB,MAAM;AAC7B,UAAI,CAAE,MAAM,cAAc,EAAI,QAAO;AAErC,aAAO;AAAA,QACL;AAAA,UACE,KAAK;AAAA,UACL,UAAU,cAAc;AAAA,UACxB,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "vite-plugin-update-detector",
3
+ "version": "1.0.0",
4
+ "description": "Vite 插件:自动检测网页更新并提示用户刷新",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "dev": "tsup --watch",
21
+ "build": "tsup",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "keywords": [
25
+ "vite",
26
+ "plugin",
27
+ "update",
28
+ "detector",
29
+ "reload",
30
+ "version"
31
+ ],
32
+ "author": "ct568@live.com",
33
+ "license": "MIT",
34
+ "peerDependencies": {
35
+ "vite": "^5 || ^6 || ^7 || ^8"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^25.9.1",
39
+ "tsup": "^8.5.1",
40
+ "typescript": "^6.0.3",
41
+ "vite": "^5"
42
+ }
43
+ }