@gitlon/version 0.1.1 → 0.1.3
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 +71 -1
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +2 -2
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +103 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +175 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,8 @@ export default defineConfig({
|
|
|
25
25
|
|
|
26
26
|
- 开发态通过 `/version.json` 返回版本信息;
|
|
27
27
|
- 构建态生成 `dist/version.json`;
|
|
28
|
-
-
|
|
28
|
+
- 构建态生成 `dist/refresh.html` 刷新页,开发态同路径响应;
|
|
29
|
+
- 向 HTML 注入 `window["__VERSION__"]`、`window.refreshCache()` 和 `window.checkVersion()`;
|
|
29
30
|
- 通过 Vite `define` 注入 `__VERSION__`;
|
|
30
31
|
- 读取项目最近的 `package.json`,写入 `pkgName`、`pkgVersion`;
|
|
31
32
|
- 开发态 `time` 为 `0`;
|
|
@@ -94,6 +95,9 @@ interface BuildVersionPluginOptions {
|
|
|
94
95
|
globalName?: string
|
|
95
96
|
defineName?: string | false
|
|
96
97
|
injectToHtml?: boolean
|
|
98
|
+
refreshHtml?: boolean | string
|
|
99
|
+
refreshKey?: string
|
|
100
|
+
checkKey?: string
|
|
97
101
|
timeZone?: string
|
|
98
102
|
version?: string | number | ((context: BuildVersionContext) => string | number)
|
|
99
103
|
data?: Record<string, any>
|
|
@@ -130,6 +134,33 @@ buildVersionPlugin({ defineName: false })
|
|
|
130
134
|
|
|
131
135
|
是否注入 HTML,默认 `true`。关闭后仍提供和生成版本 JSON。
|
|
132
136
|
|
|
137
|
+
### `refreshHtml`
|
|
138
|
+
|
|
139
|
+
独立刷新页的文件名,默认 `refresh.html`。传 `false` 不生成,同时不注入 `refreshKey` 方法:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
buildVersionPlugin({ refreshHtml: 'clear.html' })
|
|
143
|
+
buildVersionPlugin({ refreshHtml: false })
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `refreshKey`
|
|
147
|
+
|
|
148
|
+
跳转到刷新页的全局方法名,默认 `refreshCache`,注入为 `window.refreshCache()`:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
buildVersionPlugin({ refreshKey: 'goRefresh' })
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `checkKey`
|
|
155
|
+
|
|
156
|
+
检查是否有新版本的全局方法名,默认 `checkVersion`,注入为 `window.checkVersion()`:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
buildVersionPlugin({ checkKey: 'getVersionStatus' })
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
该方法的注入只受 `injectToHtml` 控制,`refreshHtml: false` 不影响它。
|
|
163
|
+
|
|
133
164
|
### `timeZone`
|
|
134
165
|
|
|
135
166
|
默认版本时间所用时区,默认 `Asia/Shanghai`。
|
|
@@ -206,6 +237,18 @@ if (latest.time !== window.__VERSION__?.time) {
|
|
|
206
237
|
}
|
|
207
238
|
```
|
|
208
239
|
|
|
240
|
+
或直接调用注入的全局方法,一次拿到本地与线上信息:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
const { local, server, update } = await window.checkVersion()
|
|
244
|
+
|
|
245
|
+
console.log(local) // 注入的 __VERSION__
|
|
246
|
+
console.log(server) // 线上 version.json
|
|
247
|
+
if (update) window.refreshCache()
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
返回结构为 `{ local, server, update }`:`update` 由 `server.time !== local.time` 判定。请求失败或响应非 2xx 时不抛错,返回 `server: null`、`update: false`。请求走 `no-store` 并在 URL 上附加时间戳参数二次防缓存,URL 已按 Vite `base` 计算。
|
|
251
|
+
|
|
209
252
|
## 刷新项目缓存
|
|
210
253
|
|
|
211
254
|
```ts
|
|
@@ -233,6 +276,33 @@ await clearUrlCache()
|
|
|
233
276
|
|
|
234
277
|
hash 路由会保留,随机参数写在 `#` 前。该方法仅支持同源 URL;浏览器无法直接删除全部 HTTP 缓存,此方法只尽可能刷新目标项目缓存。
|
|
235
278
|
|
|
279
|
+
## 刷新页
|
|
280
|
+
|
|
281
|
+
插件在 `dist` 中生成 `refresh.html`,作为不依赖应用构建产物的稳定入口:
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
/refresh.html?url=/vue-app/ → 清理 /vue-app/ 缓存后跳转到 /vue-app/
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
流程为:显示 loading → 对 `url` 及其 `index.html` 变体依次发送无缓存请求并加载隐藏 iframe → 完成或 5 秒超时后跳转到 `url`。`url` 支持相对路径,相对 `refresh.html` 所在位置解析;跳转不附加随机参数。
|
|
288
|
+
|
|
289
|
+
不带 `url` 参数时跳转到当前目录的 `index.html`:
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
/refresh.html → /index.html
|
|
293
|
+
/app/refresh.html → /app/index.html
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
业务代码触发刷新(同事在任意位置调用):
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
window.refreshCache()
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
等价于 `location.replace('<刷新页路径>?url=' + encodeURIComponent(location.href))`,携带当前完整 URL 并透传 hash;用 `replace` 而非 `href=`,刷新页不会进入历史栈,避免用户后退时被反复弹回。路径已按 Vite `base` 计算。
|
|
303
|
+
|
|
304
|
+
该页脚本由插件内联生成,不 import 任何带 hash 的构建产物,所以旧版本页面也能通过它清理缓存后进入新版本。dev server 同样响应该路径。
|
|
305
|
+
|
|
236
306
|
## 类型导出
|
|
237
307
|
|
|
238
308
|
主入口导出:
|
package/dist/client.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=`_`,t=`gitlon-version-cache-iframe`,n=`gitlon-version-loading`,r=5e3,i=5;function a(e){document.getElementById(n)?.remove();let t=document.createElement(`div`),r=document.createElement(`div`),i=document.createElement(`div`),a=Date.now()+e*1e3;t.id=n,t.style.cssText=`position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(0,0,0,.65);color:#fff;font:14px/1.5 sans-serif;`,r.style.cssText=`width:36px;height:36px;margin-bottom:16px;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;animation:gitlon-version-loading .8s linear infinite;`,i.textContent=`请稍等,正在检测更新...(${Math.ceil(e)}秒)`;let o=document.createElement(`style`);o.textContent=`@keyframes gitlon-version-loading{to{transform:rotate(360deg)}}`,t.append(o,r,i),document.documentElement.appendChild(t);let s=window.setInterval(()=>{let e=Math.max(0,Math.ceil((a-Date.now())/1e3));i.textContent=`请稍等,正在检测更新...(${e}秒)`},1e3);return()=>{window.clearInterval(s),t.remove()}}function o(t){let n=new URL(t,window.location.href),r=n.hash;return n.hash=``,n.searchParams.set(e,`${Date.now()}-${Math.random().toString(36).slice(2)}`),n.hash=r,n.href}function s(e){let t=new URL(e);return t.pathname.endsWith(`/`)||(t.pathname+=`/`),t.pathname+=`index.html`,t.href}function c(e){return new Promise(n=>{document.getElementById(t)?.remove();let i=document.createElement(`iframe`),a=!1,o=()=>{a||(a=!0,window.clearTimeout(s),n())},s=window.setTimeout(o,r);i.hidden=!0,i.id=t,i.addEventListener(`load`,o,{once:!0}),i.addEventListener(`error`,o,{once:!0}),i.src=e,document.documentElement.appendChild(i)})}async function l(e){let t=new URL(e,window.location.href).href;console.info(`[@gitlon/version] Clear cache for ${t}`),c(o(t)),await Promise.allSettled([fetch(o(t),{cache:`no-store`}).then(e=>e.arrayBuffer()),fetch(t,{cache:`reload`}).then(e=>e.arrayBuffer())])}async function u(e={}){let t=window.location.origin+window.location.pathname,n=e.urls??[t,s(t)],r=e.time,o=typeof r==`number`&&Number.isFinite(r)&&r>0?r:i,c=new URL(e.redirectUrl??n[0]??t,window.location.href).href,u=a(o),d=0;try{await Promise.race([(async()=>{for(let e of n)try{await l(e)}catch{}})(),new Promise(e=>{d=window.setTimeout(e,o*1e3)})])}finally{window.clearTimeout(d),u();try{
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=`_`,t=`gitlon-version-cache-iframe`,n=`gitlon-version-loading`,r=5e3,i=5;function a(e){document.getElementById(n)?.remove();let t=document.createElement(`div`),r=document.createElement(`div`),i=document.createElement(`div`),a=Date.now()+e*1e3;t.id=n,t.style.cssText=`position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(0,0,0,.65);color:#fff;font:14px/1.5 sans-serif;`,r.style.cssText=`width:36px;height:36px;margin-bottom:16px;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;animation:gitlon-version-loading .8s linear infinite;`,i.textContent=`请稍等,正在检测更新...(${Math.ceil(e)}秒)`;let o=document.createElement(`style`);o.textContent=`@keyframes gitlon-version-loading{to{transform:rotate(360deg)}}`,t.append(o,r,i),document.documentElement.appendChild(t);let s=window.setInterval(()=>{let e=Math.max(0,Math.ceil((a-Date.now())/1e3));i.textContent=`请稍等,正在检测更新...(${e}秒)`},1e3);return()=>{window.clearInterval(s),t.remove()}}function o(t){let n=new URL(t,window.location.href),r=n.hash;return n.hash=``,n.searchParams.set(e,`${Date.now()}-${Math.random().toString(36).slice(2)}`),n.hash=r,n.href}function s(e){let t=new URL(e);return t.pathname.endsWith(`/`)||(t.pathname+=`/`),t.pathname+=`index.html`,t.href}function c(e){return new Promise(n=>{document.getElementById(t)?.remove();let i=document.createElement(`iframe`),a=!1,o=()=>{a||(a=!0,window.clearTimeout(s),n())},s=window.setTimeout(o,r);i.hidden=!0,i.id=t,i.addEventListener(`load`,o,{once:!0}),i.addEventListener(`error`,o,{once:!0}),i.src=e,document.documentElement.appendChild(i)})}async function l(e){let t=new URL(e,window.location.href).href;console.info(`[@gitlon/version] Clear cache for ${t}`),c(o(t)),await Promise.allSettled([fetch(o(t),{cache:`no-store`}).then(e=>e.arrayBuffer()),fetch(t,{cache:`reload`}).then(e=>e.arrayBuffer())])}async function u(e={}){let t=window.location.origin+window.location.pathname,n=e.urls??[t,s(t)],r=e.time,o=typeof r==`number`&&Number.isFinite(r)&&r>0?r:i,c=new URL(e.redirectUrl??n[0]??t,window.location.href).href,u=a(o),d=0;try{await Promise.race([(async()=>{for(let e of n)try{await l(e)}catch{}})(),new Promise(e=>{d=window.setTimeout(e,o*1e3)})])}finally{window.clearTimeout(d),u();try{e.onBeforeRedirect?.()}catch{}console.info(`[@gitlon/version] redirectUrl ${c}`),window.location.replace(c)}}exports.clearUrlCache=u;
|
|
2
2
|
//# sourceMappingURL=client.cjs.map
|
package/dist/client.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.cjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["const CACHE_BUST_PARAM = '_'\nconst IFRAME_ID = 'gitlon-version-cache-iframe'\nconst LOADING_ID = 'gitlon-version-loading'\nconst IFRAME_TIMEOUT = 5_000\nconst DEFAULT_REFRESH_TIME = 5\n\nexport interface ClearUrlCacheOptions {\n urls?: string[]\n redirectUrl?: string\n time?: number\n onBeforeRedirect?: () => void | Promise<void>\n}\n\nfunction showLoading(time: number): () => void {\n document.getElementById(LOADING_ID)?.remove()\n\n const loading = document.createElement('div')\n const spinner = document.createElement('div')\n const text = document.createElement('div')\n const deadline = Date.now() + time * 1_000\n\n loading.id = LOADING_ID\n loading.style.cssText =\n 'position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(0,0,0,.65);color:#fff;font:14px/1.5 sans-serif;'\n spinner.style.cssText =\n 'width:36px;height:36px;margin-bottom:16px;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;animation:gitlon-version-loading .8s linear infinite;'\n text.textContent = `请稍等,正在检测更新...(${Math.ceil(time)}秒)`\n\n const style = document.createElement('style')\n style.textContent = '@keyframes gitlon-version-loading{to{transform:rotate(360deg)}}'\n loading.append(style, spinner, text)\n document.documentElement.appendChild(loading)\n\n const countdown = window.setInterval(() => {\n const seconds = Math.max(0, Math.ceil((deadline - Date.now()) / 1_000))\n text.textContent = `请稍等,正在检测更新...(${seconds}秒)`\n }, 1_000)\n\n return () => {\n window.clearInterval(countdown)\n loading.remove()\n }\n}\n\nfunction cacheBustedUrl(url: string): string {\n const target = new URL(url, window.location.href)\n const hash = target.hash\n\n target.hash = ''\n target.searchParams.set(\n CACHE_BUST_PARAM,\n `${Date.now()}-${Math.random().toString(36).slice(2)}`,\n )\n target.hash = hash\n\n return target.href\n}\n\nfunction indexUrl(url: string): string {\n const target = new URL(url)\n if (!target.pathname.endsWith('/')) target.pathname += '/'\n target.pathname += 'index.html'\n return target.href\n}\n\nfunction loadIframe(url: string): Promise<void> {\n return new Promise((resolve) => {\n document.getElementById(IFRAME_ID)?.remove()\n\n const iframe = document.createElement('iframe')\n let completed = false\n\n const complete = () => {\n if (completed) return\n completed = true\n window.clearTimeout(timeout)\n resolve()\n }\n\n const timeout = window.setTimeout(complete, IFRAME_TIMEOUT)\n iframe.hidden = true\n iframe.id = IFRAME_ID\n iframe.addEventListener('load', complete, { once: true })\n iframe.addEventListener('error', complete, { once: true })\n iframe.src = url\n document.documentElement.appendChild(iframe)\n })\n}\n\nasync function refreshUrlCache(url: string): Promise<void> {\n const target = new URL(url, window.location.href).href\n console.info(`[@gitlon/version] Clear cache for ${target}`)\n\n loadIframe(cacheBustedUrl(target))\n await Promise.allSettled([\n fetch(cacheBustedUrl(target), { cache: 'no-store' }).then((response) =>\n response.arrayBuffer(),\n ),\n fetch(target, { cache: 'reload' }).then((response) => response.arrayBuffer()),\n ])\n\n}\n\nexport async function clearUrlCache(options: ClearUrlCacheOptions = {}): Promise<void> {\n const defaultUrl = window.location.origin + window.location.pathname\n const urls = options.urls ?? [defaultUrl, indexUrl(defaultUrl)]\n const configuredTime = options.time\n const time =\n typeof configuredTime === 'number' &&\n Number.isFinite(configuredTime) &&\n configuredTime > 0\n ? configuredTime\n : DEFAULT_REFRESH_TIME\n const redirectUrl = new URL(\n options.redirectUrl ?? urls[0] ?? defaultUrl,\n window.location.href,\n ).href\n const hideLoading = showLoading(time)\n let refreshTimeout = 0\n\n try {\n await Promise.race([\n (async () => {\n for (const url of urls) {\n try {\n await refreshUrlCache(url)\n } catch {\n // 单个 URL 失败不阻断后续 URL。\n }\n }\n })(),\n new Promise<void>((resolve) => {\n refreshTimeout = window.setTimeout(resolve, time * 1_000)\n }),\n ])\n } finally {\n window.clearTimeout(refreshTimeout)\n hideLoading()\n try {\n
|
|
1
|
+
{"version":3,"file":"client.cjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["const CACHE_BUST_PARAM = '_'\nconst IFRAME_ID = 'gitlon-version-cache-iframe'\nconst LOADING_ID = 'gitlon-version-loading'\nconst IFRAME_TIMEOUT = 5_000\nconst DEFAULT_REFRESH_TIME = 5\n\nexport interface ClearUrlCacheOptions {\n urls?: string[]\n redirectUrl?: string\n time?: number\n onBeforeRedirect?: () => void | Promise<void>\n}\n\nfunction showLoading(time: number): () => void {\n document.getElementById(LOADING_ID)?.remove()\n\n const loading = document.createElement('div')\n const spinner = document.createElement('div')\n const text = document.createElement('div')\n const deadline = Date.now() + time * 1_000\n\n loading.id = LOADING_ID\n loading.style.cssText =\n 'position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(0,0,0,.65);color:#fff;font:14px/1.5 sans-serif;'\n spinner.style.cssText =\n 'width:36px;height:36px;margin-bottom:16px;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;animation:gitlon-version-loading .8s linear infinite;'\n text.textContent = `请稍等,正在检测更新...(${Math.ceil(time)}秒)`\n\n const style = document.createElement('style')\n style.textContent = '@keyframes gitlon-version-loading{to{transform:rotate(360deg)}}'\n loading.append(style, spinner, text)\n document.documentElement.appendChild(loading)\n\n const countdown = window.setInterval(() => {\n const seconds = Math.max(0, Math.ceil((deadline - Date.now()) / 1_000))\n text.textContent = `请稍等,正在检测更新...(${seconds}秒)`\n }, 1_000)\n\n return () => {\n window.clearInterval(countdown)\n loading.remove()\n }\n}\n\nfunction cacheBustedUrl(url: string): string {\n const target = new URL(url, window.location.href)\n const hash = target.hash\n\n target.hash = ''\n target.searchParams.set(\n CACHE_BUST_PARAM,\n `${Date.now()}-${Math.random().toString(36).slice(2)}`,\n )\n target.hash = hash\n\n return target.href\n}\n\nfunction indexUrl(url: string): string {\n const target = new URL(url)\n if (!target.pathname.endsWith('/')) target.pathname += '/'\n target.pathname += 'index.html'\n return target.href\n}\n\nfunction loadIframe(url: string): Promise<void> {\n return new Promise((resolve) => {\n document.getElementById(IFRAME_ID)?.remove()\n\n const iframe = document.createElement('iframe')\n let completed = false\n\n const complete = () => {\n if (completed) return\n completed = true\n window.clearTimeout(timeout)\n resolve()\n }\n\n const timeout = window.setTimeout(complete, IFRAME_TIMEOUT)\n iframe.hidden = true\n iframe.id = IFRAME_ID\n iframe.addEventListener('load', complete, { once: true })\n iframe.addEventListener('error', complete, { once: true })\n iframe.src = url\n document.documentElement.appendChild(iframe)\n })\n}\n\nasync function refreshUrlCache(url: string): Promise<void> {\n const target = new URL(url, window.location.href).href\n console.info(`[@gitlon/version] Clear cache for ${target}`)\n\n loadIframe(cacheBustedUrl(target))\n await Promise.allSettled([\n fetch(cacheBustedUrl(target), { cache: 'no-store' }).then((response) =>\n response.arrayBuffer(),\n ),\n fetch(target, { cache: 'reload' }).then((response) => response.arrayBuffer()),\n ])\n\n}\n\nexport async function clearUrlCache(options: ClearUrlCacheOptions = {}): Promise<void> {\n const defaultUrl = window.location.origin + window.location.pathname\n const urls = options.urls ?? [defaultUrl, indexUrl(defaultUrl)]\n const configuredTime = options.time\n const time =\n typeof configuredTime === 'number' &&\n Number.isFinite(configuredTime) &&\n configuredTime > 0\n ? configuredTime\n : DEFAULT_REFRESH_TIME\n const redirectUrl = new URL(\n options.redirectUrl ?? urls[0] ?? defaultUrl,\n window.location.href,\n ).href\n const hideLoading = showLoading(time)\n let refreshTimeout = 0\n\n try {\n await Promise.race([\n (async () => {\n for (const url of urls) {\n try {\n await refreshUrlCache(url)\n } catch {\n // 单个 URL 失败不阻断后续 URL。\n }\n }\n })(),\n new Promise<void>((resolve) => {\n refreshTimeout = window.setTimeout(resolve, time * 1_000)\n }),\n ])\n } finally {\n window.clearTimeout(refreshTimeout)\n hideLoading()\n try {\n options.onBeforeRedirect?.()\n } catch {\n // 回调失败不阻断跳转。\n }\n console.info(`[@gitlon/version] redirectUrl ${redirectUrl}`)\n window.location.replace(redirectUrl)\n }\n}\n"],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -48,9 +48,9 @@ async function s(e = {}) {
|
|
|
48
48
|
} finally {
|
|
49
49
|
window.clearTimeout(u), l();
|
|
50
50
|
try {
|
|
51
|
-
|
|
51
|
+
e.onBeforeRedirect?.();
|
|
52
52
|
} catch {}
|
|
53
|
-
window.location.replace(c);
|
|
53
|
+
console.info(`[@gitlon/version] redirectUrl ${c}`), window.location.replace(c);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
//#endregion
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":[],"sources":["../src/client.ts"],"sourcesContent":["const CACHE_BUST_PARAM = '_'\nconst IFRAME_ID = 'gitlon-version-cache-iframe'\nconst LOADING_ID = 'gitlon-version-loading'\nconst IFRAME_TIMEOUT = 5_000\nconst DEFAULT_REFRESH_TIME = 5\n\nexport interface ClearUrlCacheOptions {\n urls?: string[]\n redirectUrl?: string\n time?: number\n onBeforeRedirect?: () => void | Promise<void>\n}\n\nfunction showLoading(time: number): () => void {\n document.getElementById(LOADING_ID)?.remove()\n\n const loading = document.createElement('div')\n const spinner = document.createElement('div')\n const text = document.createElement('div')\n const deadline = Date.now() + time * 1_000\n\n loading.id = LOADING_ID\n loading.style.cssText =\n 'position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(0,0,0,.65);color:#fff;font:14px/1.5 sans-serif;'\n spinner.style.cssText =\n 'width:36px;height:36px;margin-bottom:16px;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;animation:gitlon-version-loading .8s linear infinite;'\n text.textContent = `请稍等,正在检测更新...(${Math.ceil(time)}秒)`\n\n const style = document.createElement('style')\n style.textContent = '@keyframes gitlon-version-loading{to{transform:rotate(360deg)}}'\n loading.append(style, spinner, text)\n document.documentElement.appendChild(loading)\n\n const countdown = window.setInterval(() => {\n const seconds = Math.max(0, Math.ceil((deadline - Date.now()) / 1_000))\n text.textContent = `请稍等,正在检测更新...(${seconds}秒)`\n }, 1_000)\n\n return () => {\n window.clearInterval(countdown)\n loading.remove()\n }\n}\n\nfunction cacheBustedUrl(url: string): string {\n const target = new URL(url, window.location.href)\n const hash = target.hash\n\n target.hash = ''\n target.searchParams.set(\n CACHE_BUST_PARAM,\n `${Date.now()}-${Math.random().toString(36).slice(2)}`,\n )\n target.hash = hash\n\n return target.href\n}\n\nfunction indexUrl(url: string): string {\n const target = new URL(url)\n if (!target.pathname.endsWith('/')) target.pathname += '/'\n target.pathname += 'index.html'\n return target.href\n}\n\nfunction loadIframe(url: string): Promise<void> {\n return new Promise((resolve) => {\n document.getElementById(IFRAME_ID)?.remove()\n\n const iframe = document.createElement('iframe')\n let completed = false\n\n const complete = () => {\n if (completed) return\n completed = true\n window.clearTimeout(timeout)\n resolve()\n }\n\n const timeout = window.setTimeout(complete, IFRAME_TIMEOUT)\n iframe.hidden = true\n iframe.id = IFRAME_ID\n iframe.addEventListener('load', complete, { once: true })\n iframe.addEventListener('error', complete, { once: true })\n iframe.src = url\n document.documentElement.appendChild(iframe)\n })\n}\n\nasync function refreshUrlCache(url: string): Promise<void> {\n const target = new URL(url, window.location.href).href\n console.info(`[@gitlon/version] Clear cache for ${target}`)\n\n loadIframe(cacheBustedUrl(target))\n await Promise.allSettled([\n fetch(cacheBustedUrl(target), { cache: 'no-store' }).then((response) =>\n response.arrayBuffer(),\n ),\n fetch(target, { cache: 'reload' }).then((response) => response.arrayBuffer()),\n ])\n\n}\n\nexport async function clearUrlCache(options: ClearUrlCacheOptions = {}): Promise<void> {\n const defaultUrl = window.location.origin + window.location.pathname\n const urls = options.urls ?? [defaultUrl, indexUrl(defaultUrl)]\n const configuredTime = options.time\n const time =\n typeof configuredTime === 'number' &&\n Number.isFinite(configuredTime) &&\n configuredTime > 0\n ? configuredTime\n : DEFAULT_REFRESH_TIME\n const redirectUrl = new URL(\n options.redirectUrl ?? urls[0] ?? defaultUrl,\n window.location.href,\n ).href\n const hideLoading = showLoading(time)\n let refreshTimeout = 0\n\n try {\n await Promise.race([\n (async () => {\n for (const url of urls) {\n try {\n await refreshUrlCache(url)\n } catch {\n // 单个 URL 失败不阻断后续 URL。\n }\n }\n })(),\n new Promise<void>((resolve) => {\n refreshTimeout = window.setTimeout(resolve, time * 1_000)\n }),\n ])\n } finally {\n window.clearTimeout(refreshTimeout)\n hideLoading()\n try {\n
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../src/client.ts"],"sourcesContent":["const CACHE_BUST_PARAM = '_'\nconst IFRAME_ID = 'gitlon-version-cache-iframe'\nconst LOADING_ID = 'gitlon-version-loading'\nconst IFRAME_TIMEOUT = 5_000\nconst DEFAULT_REFRESH_TIME = 5\n\nexport interface ClearUrlCacheOptions {\n urls?: string[]\n redirectUrl?: string\n time?: number\n onBeforeRedirect?: () => void | Promise<void>\n}\n\nfunction showLoading(time: number): () => void {\n document.getElementById(LOADING_ID)?.remove()\n\n const loading = document.createElement('div')\n const spinner = document.createElement('div')\n const text = document.createElement('div')\n const deadline = Date.now() + time * 1_000\n\n loading.id = LOADING_ID\n loading.style.cssText =\n 'position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(0,0,0,.65);color:#fff;font:14px/1.5 sans-serif;'\n spinner.style.cssText =\n 'width:36px;height:36px;margin-bottom:16px;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;animation:gitlon-version-loading .8s linear infinite;'\n text.textContent = `请稍等,正在检测更新...(${Math.ceil(time)}秒)`\n\n const style = document.createElement('style')\n style.textContent = '@keyframes gitlon-version-loading{to{transform:rotate(360deg)}}'\n loading.append(style, spinner, text)\n document.documentElement.appendChild(loading)\n\n const countdown = window.setInterval(() => {\n const seconds = Math.max(0, Math.ceil((deadline - Date.now()) / 1_000))\n text.textContent = `请稍等,正在检测更新...(${seconds}秒)`\n }, 1_000)\n\n return () => {\n window.clearInterval(countdown)\n loading.remove()\n }\n}\n\nfunction cacheBustedUrl(url: string): string {\n const target = new URL(url, window.location.href)\n const hash = target.hash\n\n target.hash = ''\n target.searchParams.set(\n CACHE_BUST_PARAM,\n `${Date.now()}-${Math.random().toString(36).slice(2)}`,\n )\n target.hash = hash\n\n return target.href\n}\n\nfunction indexUrl(url: string): string {\n const target = new URL(url)\n if (!target.pathname.endsWith('/')) target.pathname += '/'\n target.pathname += 'index.html'\n return target.href\n}\n\nfunction loadIframe(url: string): Promise<void> {\n return new Promise((resolve) => {\n document.getElementById(IFRAME_ID)?.remove()\n\n const iframe = document.createElement('iframe')\n let completed = false\n\n const complete = () => {\n if (completed) return\n completed = true\n window.clearTimeout(timeout)\n resolve()\n }\n\n const timeout = window.setTimeout(complete, IFRAME_TIMEOUT)\n iframe.hidden = true\n iframe.id = IFRAME_ID\n iframe.addEventListener('load', complete, { once: true })\n iframe.addEventListener('error', complete, { once: true })\n iframe.src = url\n document.documentElement.appendChild(iframe)\n })\n}\n\nasync function refreshUrlCache(url: string): Promise<void> {\n const target = new URL(url, window.location.href).href\n console.info(`[@gitlon/version] Clear cache for ${target}`)\n\n loadIframe(cacheBustedUrl(target))\n await Promise.allSettled([\n fetch(cacheBustedUrl(target), { cache: 'no-store' }).then((response) =>\n response.arrayBuffer(),\n ),\n fetch(target, { cache: 'reload' }).then((response) => response.arrayBuffer()),\n ])\n\n}\n\nexport async function clearUrlCache(options: ClearUrlCacheOptions = {}): Promise<void> {\n const defaultUrl = window.location.origin + window.location.pathname\n const urls = options.urls ?? [defaultUrl, indexUrl(defaultUrl)]\n const configuredTime = options.time\n const time =\n typeof configuredTime === 'number' &&\n Number.isFinite(configuredTime) &&\n configuredTime > 0\n ? configuredTime\n : DEFAULT_REFRESH_TIME\n const redirectUrl = new URL(\n options.redirectUrl ?? urls[0] ?? defaultUrl,\n window.location.href,\n ).href\n const hideLoading = showLoading(time)\n let refreshTimeout = 0\n\n try {\n await Promise.race([\n (async () => {\n for (const url of urls) {\n try {\n await refreshUrlCache(url)\n } catch {\n // 单个 URL 失败不阻断后续 URL。\n }\n }\n })(),\n new Promise<void>((resolve) => {\n refreshTimeout = window.setTimeout(resolve, time * 1_000)\n }),\n ])\n } finally {\n window.clearTimeout(refreshTimeout)\n hideLoading()\n try {\n options.onBeforeRedirect?.()\n } catch {\n // 回调失败不阻断跳转。\n }\n console.info(`[@gitlon/version] redirectUrl ${redirectUrl}`)\n window.location.replace(redirectUrl)\n }\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,104 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});let e=require("node:fs"),t=require("node:path");var n=`@gitlon/version`,r=`version.json`,i=`
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});let e=require("node:fs"),t=require("node:path");var n=`@gitlon/version`,r=`version.json`,i=`refresh.html`,a=`refreshCache`,o=`checkVersion`,s=`url`,c=`_`,l=`__VERSION__`,u=`__VERSION__`,d=`Asia/Shanghai`;function f(e,t=`filename`){let r=e.replace(/\\/g,`/`).replace(/^\/+/,``);if(!r||r.endsWith(`/`))throw Error(`[${n}] "${t}" must point to a file path.`);return r}function p(e,t){if(!e.trim())throw Error(`[${n}] "${t}" cannot be empty.`);return e}function m(e){let t=e.trim();if(!t)throw Error(`[${n}] "timeZone" cannot be empty.`);try{new Intl.DateTimeFormat(`en-US`,{timeZone:t}).format(new Date)}catch{throw Error(`[${n}] "timeZone" is invalid.`)}return t}function h(e){if(e===void 0)return{};if(!e||Array.isArray(e)||typeof e!=`object`)throw Error(`[${n}] "data" must be a plain object.`);return e}function g(e,t){if(e===`serve`)return 0;let n=new Intl.DateTimeFormat(`en-US`,{timeZone:t,year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1,hourCycle:`h23`}).formatToParts(new Date),r=e=>n.find(t=>t.type===e)?.value??``;return[r(`year`),r(`month`),r(`day`),r(`hour`),r(`minute`),r(`second`)].join(``)}function _(n){let r=n;for(;;){let n=(0,t.join)(r,`package.json`);if((0,e.existsSync)(n))return n;let i=(0,t.dirname)(r);if(i===r)return;r=i}}function v(t){let n=_(t);if(!n)return{};try{let t=JSON.parse((0,e.readFileSync)(n,`utf8`)),r={};return t.name!==void 0&&(r.pkgName=t.name),t.version!==void 0&&(r.pkgVersion=t.version),r}catch{return{}}}function y(e){return e?(0,t.resolve)(process.cwd(),e):process.cwd()}function b(e,t,n){return typeof e.version==`function`?e.version(t):e.version===void 0?g(t.command,n):e.version}function x(e,t,n,r,i){let a={...n,...r,time:b(e,t,i),env:t.mode};return e.payload&&(a=e.payload(a)),a}function S(e){return`${JSON.stringify(e,null,2)}\n`}function C(e,t){return`window[${JSON.stringify(e)}] = ${JSON.stringify(t)};`}function w(e){return`<script>${e}<\/script>`}function T(e,t){let n=`${t}?${s}=`;return`window[${JSON.stringify(e)}]=function(){location.replace(${JSON.stringify(n)}+encodeURIComponent(location.href))};`}function E(e,t,n){let r=JSON.stringify(n),i=`${t}?${c}=`,a=`{local:local,server:null,update:false}`;return`window[${JSON.stringify(e)}]=async function(){var local=window[${r}];try{var response=await fetch(${JSON.stringify(i)}+Date.now(),{cache:"no-store"});if(!response.ok)return ${a};var server=await response.json();return{local:local,server:server,update:server.time!==local.time}}catch(error){return ${a}}};`}function D(e,t){if(e.includes(t))return e;let n=`<script>${t}<\/script>`;return e.includes(`</head>`)?e.replace(`</head>`,`${n}</head>`):`${n}${e}`}function O(){return`<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>刷新缓存</title>
|
|
7
|
+
<style>
|
|
8
|
+
html,body{margin:0;height:100%}
|
|
9
|
+
.gv-refresh{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.65)}
|
|
10
|
+
.gv-refresh::after{box-sizing:border-box;width:36px;height:36px;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;content:"";animation:gv-refresh-spin .8s linear infinite}
|
|
11
|
+
@keyframes gv-refresh-spin{to{transform:rotate(360deg)}}
|
|
12
|
+
</style>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div class="gv-refresh"></div>
|
|
16
|
+
<script>
|
|
17
|
+
(function () {
|
|
18
|
+
var QUERY = '${s}'
|
|
19
|
+
var PARAM = '${c}'
|
|
20
|
+
var TIMEOUT = 5000
|
|
21
|
+
var FRAME_TIMEOUT = 5000
|
|
22
|
+
|
|
23
|
+
function resolveTarget() {
|
|
24
|
+
var raw = new URLSearchParams(location.search).get(QUERY)
|
|
25
|
+
if (raw) return new URL(raw, location.href).href
|
|
26
|
+
return new URL(location.pathname.replace(/[^/]*$/, '') + 'index.html', location.href).href
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function cacheTargets(target) {
|
|
30
|
+
var url = new URL(target)
|
|
31
|
+
var path = url.pathname.replace(/index\\.html$/, '')
|
|
32
|
+
if (!path.endsWith('/')) return [url.href]
|
|
33
|
+
return [
|
|
34
|
+
url.origin + path + url.search + url.hash,
|
|
35
|
+
url.origin + path + 'index.html' + url.search + url.hash
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function busted(url) {
|
|
40
|
+
var target = new URL(url)
|
|
41
|
+
var hash = target.hash
|
|
42
|
+
target.hash = ''
|
|
43
|
+
target.searchParams.set(PARAM, Date.now() + '-' + Math.random().toString(36).slice(2))
|
|
44
|
+
target.hash = hash
|
|
45
|
+
return target.href
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function warm(url) {
|
|
49
|
+
return new Promise(function (resolve) {
|
|
50
|
+
var frame = document.createElement('iframe')
|
|
51
|
+
var done = false
|
|
52
|
+
|
|
53
|
+
function finish() {
|
|
54
|
+
if (done) return
|
|
55
|
+
done = true
|
|
56
|
+
clearTimeout(timer)
|
|
57
|
+
frame.remove()
|
|
58
|
+
resolve()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
var timer = setTimeout(finish, FRAME_TIMEOUT)
|
|
62
|
+
frame.hidden = true
|
|
63
|
+
frame.addEventListener('load', finish, { once: true })
|
|
64
|
+
frame.addEventListener('error', finish, { once: true })
|
|
65
|
+
frame.src = url
|
|
66
|
+
document.documentElement.appendChild(frame)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function refresh(url) {
|
|
71
|
+
var target = busted(url)
|
|
72
|
+
warm(target)
|
|
73
|
+
await fetch(target, { cache: 'no-store' })
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function run() {
|
|
77
|
+
var target = resolveTarget()
|
|
78
|
+
var urls = cacheTargets(target)
|
|
79
|
+
|
|
80
|
+
await Promise.race([
|
|
81
|
+
(async function () {
|
|
82
|
+
for (var index = 0; index < urls.length; index++) {
|
|
83
|
+
try {
|
|
84
|
+
await refresh(urls[index])
|
|
85
|
+
} catch (error) {
|
|
86
|
+
// 单个 URL 失败不阻断后续 URL。
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})(),
|
|
90
|
+
new Promise(function (resolve) {
|
|
91
|
+
setTimeout(resolve, TIMEOUT)
|
|
92
|
+
})
|
|
93
|
+
])
|
|
94
|
+
|
|
95
|
+
location.replace(target)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
run()
|
|
99
|
+
})()
|
|
100
|
+
<\/script>
|
|
101
|
+
</body>
|
|
102
|
+
</html>
|
|
103
|
+
`}function k(e){return e?new URL(e,`http://localhost`).pathname:`/`}function A(e,t){let n=e===`./`?`/`:e||`/`,r=n.startsWith(`/`)?n:`/${n}`;return`${r.endsWith(`/`)?r.slice(0,-1):r}/${t}`.replace(/\/{2,}/g,`/`)}function j(e,t){if(e.appType!==`custom`||e.build.copyPublicDir!==!1)return t;let n=f(e.build.assetsDir);return n===`.`||t.startsWith(`${n}/`)?t:`${n}/${t}`}function M(e={}){let t=f(e.filename??r),s=p(e.globalName??l,`globalName`),c=e.defineName!==!1&&p(e.defineName??u,`defineName`),g=e.injectToHtml??!0,_=e.refreshHtml??!0,b=_===!1?void 0:f(_===!0?i:_,`refreshHtml`),M=b===void 0?void 0:p(e.refreshKey??a,`refreshKey`),N=p(e.checkKey??o,`checkKey`),P=m(e.timeZone??d),F=h(e.data),I,L=t,R,z=``,B=``,V=!1,H=(t,n)=>{I=x(e,t,F,v(n),P)},U=()=>I?[C(s,I),M&&z?T(M,z):``,B?E(N,B,s):``].join(``):``,W=e=>{if(g&&I)return D(e,U())},G=()=>!g||!I?``:w(U()),K={name:n,config(e,t){if(I||H({command:t.command,mode:t.mode},y(e.root)),c&&I)return{define:{[c]:JSON.stringify(I)}}},configResolved(e){V=!!e.build.ssr&&!e.build.ssrEmitAssets,L=j(e,t),R=b?j(e,b):void 0,z=R?A(e.base,R):``,B=A(e.base,L),I||H({command:e.command,mode:e.mode},e.root)},configureServer(e){let n=A(e.config.base,t),r=b?A(e.config.base,b):void 0;e.middlewares.use((e,t,i)=>{if(e.method!==`GET`&&e.method!==`HEAD`){i();return}let a=k(e.url);if(r&&a===r){t.statusCode=200,t.setHeader(`Content-Type`,`text/html; charset=utf-8`),t.setHeader(`Cache-Control`,`no-cache`),t.end(e.method===`HEAD`?void 0:O());return}if(a!==n||!I){i();return}t.statusCode=200,t.setHeader(`Content-Type`,`application/json; charset=utf-8`),t.setHeader(`Cache-Control`,`no-cache`),t.end(e.method===`HEAD`?void 0:S(I))})},transformIndexHtml(){if(g&&I)return[{tag:`script`,injectTo:`head`,children:U()}]},generateBundle(){I&&!V&&(this.emitFile({type:`asset`,fileName:L,source:S(I)}),R&&this.emitFile({type:`asset`,fileName:R,source:O()}),e.log?console.log(e.log(I)):(console.log(`[${n}][build finished]`),console.log(S(I))))}};return Object.defineProperty(K,"transformHtml",{value:W}),Object.defineProperty(K,"headScript",{value:G}),K}exports.buildVersionPlugin=M,exports.default=M;
|
|
2
104
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport type { HtmlTagDescriptor, Plugin, UserConfig } from 'vite'\n\nexport interface BuildVersionContext {\n command: 'serve' | 'build'\n mode: string\n}\n\nexport interface BuildVersionPayload {\n time: string | number\n env: string\n [key: string]: any\n}\n\nexport interface BuildVersionPluginOptions {\n filename?: string\n globalName?: string\n defineName?: string | false\n injectToHtml?: boolean\n timeZone?: string\n version?: string | number | ((context: BuildVersionContext) => string | number)\n data?: Record<string, any>\n log?: (content: any) => string\n payload?: (json: any) => any\n}\n\nexport type BuildVersionPlugin = Plugin & {\n transformHtml: (code: string, ...args: unknown[]) => string | undefined\n headScript: () => string\n}\n\nconst PLUGIN_NAME = '@gitlon/version'\nconst DEFAULT_FILENAME = 'version.json'\nconst DEFAULT_GLOBAL_NAME = '__VERSION__'\nconst DEFAULT_DEFINE_NAME = '__VERSION__'\nconst DEFAULT_TIME_ZONE = 'Asia/Shanghai'\n\nfunction normalizeFilename(filename: string): string {\n const value = filename.replace(/\\\\/g, '/').replace(/^\\/+/, '')\n\n if (!value || value.endsWith('/')) {\n throw new Error(`[${PLUGIN_NAME}] \"filename\" must point to a file path.`)\n }\n\n return value\n}\n\nfunction normalizeRequiredName(value: string, option: 'globalName' | 'defineName'): string {\n if (!value.trim()) {\n throw new Error(`[${PLUGIN_NAME}] \"${option}\" cannot be empty.`)\n }\n\n return value\n}\n\nfunction normalizeTimeZone(timeZone: string): string {\n const value = timeZone.trim()\n\n if (!value) {\n throw new Error(`[${PLUGIN_NAME}] \"timeZone\" cannot be empty.`)\n }\n\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: value }).format(new Date())\n } catch {\n throw new Error(`[${PLUGIN_NAME}] \"timeZone\" is invalid.`)\n }\n\n return value\n}\n\nfunction normalizeData(data: BuildVersionPluginOptions['data']): Record<string, any> {\n if (data === undefined) return {}\n\n if (!data || Array.isArray(data) || typeof data !== 'object') {\n throw new Error(`[${PLUGIN_NAME}] \"data\" must be a plain object.`)\n }\n\n return data\n}\n\nfunction createDefaultVersion(command: BuildVersionContext['command'], timeZone: string) {\n if (command === 'serve') return 0\n\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n hourCycle: 'h23',\n }).formatToParts(new Date())\n const part = (type: Intl.DateTimeFormatPartTypes) =>\n parts.find((item) => item.type === type)?.value ?? ''\n\n return [\n part('year'),\n part('month'),\n part('day'),\n part('hour'),\n part('minute'),\n part('second'),\n ].join('')\n}\n\nfunction findPackageJson(root: string): string | undefined {\n let directory = root\n\n while (true) {\n const packageJson = join(directory, 'package.json')\n if (existsSync(packageJson)) return packageJson\n\n const parent = dirname(directory)\n if (parent === directory) return undefined\n directory = parent\n }\n}\n\nfunction readPackageData(root: string): Record<string, any> {\n const filename = findPackageJson(root)\n if (!filename) return {}\n\n try {\n const packageJson = JSON.parse(readFileSync(filename, 'utf8'))\n const result: Record<string, any> = {}\n\n if (packageJson.name !== undefined) result.pkgName = packageJson.name\n if (packageJson.version !== undefined) result.pkgVersion = packageJson.version\n\n return result\n } catch {\n return {}\n }\n}\n\nfunction projectRoot(root: string | undefined): string {\n return root ? resolve(process.cwd(), root) : process.cwd()\n}\n\nfunction versionFor(\n options: BuildVersionPluginOptions,\n context: BuildVersionContext,\n timeZone: string,\n): string | number {\n if (typeof options.version === 'function') return options.version(context)\n if (options.version !== undefined) return options.version\n return createDefaultVersion(context.command, timeZone)\n}\n\nfunction makePayload(\n options: BuildVersionPluginOptions,\n context: BuildVersionContext,\n data: Record<string, any>,\n packageData: Record<string, any>,\n timeZone: string,\n): BuildVersionPayload {\n let payload: BuildVersionPayload = {\n ...data,\n ...packageData,\n time: versionFor(options, context, timeZone),\n env: context.mode,\n }\n\n if (options.payload) payload = options.payload(payload)\n return payload\n}\n\nfunction serialize(payload: BuildVersionPayload): string {\n return `${JSON.stringify(payload, null, 2)}\\n`\n}\n\nfunction assignment(globalName: string, payload: BuildVersionPayload): string {\n return `window[${JSON.stringify(globalName)}] = ${JSON.stringify(payload)};`\n}\n\nfunction scriptTag(globalName: string, payload: BuildVersionPayload): string {\n return `<script>${assignment(globalName, payload)}</script>`\n}\n\nfunction injectIntoHead(html: string, script: string): string {\n if (html.includes(script)) return html\n\n const tag = `<script>${script}</script>`\n return html.includes('</head>') ? html.replace('</head>', `${tag}</head>`) : `${tag}${html}`\n}\n\nfunction pathname(url: string | undefined): string {\n return url ? new URL(url, 'http://localhost').pathname : '/'\n}\n\nfunction requestPath(base: string, filename: string): string {\n const rawBase = base === './' ? '/' : base || '/'\n const leadingBase = rawBase.startsWith('/') ? rawBase : `/${rawBase}`\n const cleanBase = leadingBase.endsWith('/') ? leadingBase.slice(0, -1) : leadingBase\n return `${cleanBase}/${filename}`.replace(/\\/{2,}/g, '/')\n}\n\nfunction nuxtFilename(\n config: {\n appType: string\n build: { assetsDir: string; copyPublicDir: boolean }\n },\n filename: string,\n): string {\n if (config.appType !== 'custom' || config.build.copyPublicDir !== false) return filename\n\n const assetsDir = normalizeFilename(config.build.assetsDir)\n return assetsDir === '.' || filename.startsWith(`${assetsDir}/`)\n ? filename\n : `${assetsDir}/${filename}`\n}\n\nexport function buildVersionPlugin(\n options: BuildVersionPluginOptions = {},\n): BuildVersionPlugin {\n const filename = normalizeFilename(options.filename ?? DEFAULT_FILENAME)\n const globalName = normalizeRequiredName(\n options.globalName ?? DEFAULT_GLOBAL_NAME,\n 'globalName',\n )\n const defineName =\n options.defineName === false\n ? false\n : normalizeRequiredName(options.defineName ?? DEFAULT_DEFINE_NAME, 'defineName')\n const injectToHtml = options.injectToHtml ?? true\n const timeZone = normalizeTimeZone(options.timeZone ?? DEFAULT_TIME_ZONE)\n const data = normalizeData(options.data)\n let payload: BuildVersionPayload | undefined\n let emittedFilename = filename\n let skipEmit = false\n\n const createPayload = (context: BuildVersionContext, root: string) => {\n payload = makePayload(options, context, data, readPackageData(root), timeZone)\n }\n\n const transformHtml = (html: string): string | undefined => {\n if (!injectToHtml || !payload) return undefined\n return injectIntoHead(html, assignment(globalName, payload))\n }\n\n const headScript = (): string => {\n if (!injectToHtml || !payload) return ''\n return scriptTag(globalName, payload)\n }\n\n const plugin: Plugin = {\n name: PLUGIN_NAME,\n\n config(userConfig, environment) {\n if (!payload) {\n createPayload(\n {\n command: environment.command as BuildVersionContext['command'],\n mode: environment.mode,\n },\n projectRoot(userConfig.root),\n )\n }\n\n if (!defineName || !payload) return undefined\n\n return {\n define: {\n [defineName]: JSON.stringify(payload),\n },\n } satisfies UserConfig\n },\n\n configResolved(config) {\n skipEmit = Boolean(config.build.ssr) && !config.build.ssrEmitAssets\n emittedFilename = nuxtFilename(config, filename)\n\n if (!payload) {\n createPayload(\n {\n command: config.command as BuildVersionContext['command'],\n mode: config.mode,\n },\n config.root,\n )\n }\n },\n\n configureServer(server) {\n const targetPath = requestPath(server.config.base, filename)\n\n server.middlewares.use((request, response, next) => {\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n next()\n return\n }\n\n if (pathname(request.url) !== targetPath || !payload) {\n next()\n return\n }\n\n response.statusCode = 200\n response.setHeader('Content-Type', 'application/json; charset=utf-8')\n response.setHeader('Cache-Control', 'no-cache')\n response.end(request.method === 'HEAD' ? undefined : serialize(payload))\n })\n },\n\n transformIndexHtml() {\n if (!injectToHtml || !payload) return undefined\n\n const tags: HtmlTagDescriptor[] = [\n {\n tag: 'script',\n injectTo: 'head',\n children: assignment(globalName, payload),\n },\n ]\n return tags\n },\n\n generateBundle() {\n if (!payload || skipEmit) return\n\n this.emitFile({\n type: 'asset',\n fileName: emittedFilename,\n source: serialize(payload),\n })\n\n if (options.log) {\n console.log(options.log(payload))\n } else {\n console.log(`[${PLUGIN_NAME}][build finished]`)\n console.log(serialize(payload))\n }\n },\n }\n\n Object.defineProperty(plugin, 'transformHtml', { value: transformHtml })\n Object.defineProperty(plugin, 'headScript', { value: headScript })\n\n return plugin as BuildVersionPlugin\n}\n\nexport default buildVersionPlugin\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport type { HtmlTagDescriptor, Plugin, UserConfig } from 'vite'\n\nexport interface BuildVersionContext {\n command: 'serve' | 'build'\n mode: string\n}\n\nexport interface BuildVersionPayload {\n time: string | number\n env: string\n [key: string]: any\n}\n\nexport interface BuildVersionPluginOptions {\n filename?: string\n globalName?: string\n defineName?: string | false\n injectToHtml?: boolean\n refreshHtml?: boolean | string\n refreshKey?: string\n checkKey?: string\n timeZone?: string\n version?: string | number | ((context: BuildVersionContext) => string | number)\n data?: Record<string, any>\n log?: (content: any) => string\n payload?: (json: any) => any\n}\n\nexport type BuildVersionPlugin = Plugin & {\n transformHtml: (code: string, ...args: unknown[]) => string | undefined\n headScript: () => string\n}\n\nconst PLUGIN_NAME = '@gitlon/version'\nconst DEFAULT_FILENAME = 'version.json'\nconst DEFAULT_REFRESH_HTML = 'refresh.html'\nconst DEFAULT_REFRESH_KEY = 'refreshCache'\nconst DEFAULT_CHECK_KEY = 'checkVersion'\nconst REFRESH_QUERY = 'url'\nconst CACHE_BUST_PARAM = '_'\nconst DEFAULT_GLOBAL_NAME = '__VERSION__'\nconst DEFAULT_DEFINE_NAME = '__VERSION__'\nconst DEFAULT_TIME_ZONE = 'Asia/Shanghai'\n\nfunction normalizeFilename(filename: string, option = 'filename'): string {\n const value = filename.replace(/\\\\/g, '/').replace(/^\\/+/, '')\n\n if (!value || value.endsWith('/')) {\n throw new Error(`[${PLUGIN_NAME}] \"${option}\" must point to a file path.`)\n }\n\n return value\n}\n\nfunction normalizeRequiredName(\n value: string,\n option: 'globalName' | 'defineName' | 'refreshKey' | 'checkKey',\n): string {\n if (!value.trim()) {\n throw new Error(`[${PLUGIN_NAME}] \"${option}\" cannot be empty.`)\n }\n\n return value\n}\n\nfunction normalizeTimeZone(timeZone: string): string {\n const value = timeZone.trim()\n\n if (!value) {\n throw new Error(`[${PLUGIN_NAME}] \"timeZone\" cannot be empty.`)\n }\n\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: value }).format(new Date())\n } catch {\n throw new Error(`[${PLUGIN_NAME}] \"timeZone\" is invalid.`)\n }\n\n return value\n}\n\nfunction normalizeData(data: BuildVersionPluginOptions['data']): Record<string, any> {\n if (data === undefined) return {}\n\n if (!data || Array.isArray(data) || typeof data !== 'object') {\n throw new Error(`[${PLUGIN_NAME}] \"data\" must be a plain object.`)\n }\n\n return data\n}\n\nfunction createDefaultVersion(command: BuildVersionContext['command'], timeZone: string) {\n if (command === 'serve') return 0\n\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n hourCycle: 'h23',\n }).formatToParts(new Date())\n const part = (type: Intl.DateTimeFormatPartTypes) =>\n parts.find((item) => item.type === type)?.value ?? ''\n\n return [\n part('year'),\n part('month'),\n part('day'),\n part('hour'),\n part('minute'),\n part('second'),\n ].join('')\n}\n\nfunction findPackageJson(root: string): string | undefined {\n let directory = root\n\n while (true) {\n const packageJson = join(directory, 'package.json')\n if (existsSync(packageJson)) return packageJson\n\n const parent = dirname(directory)\n if (parent === directory) return undefined\n directory = parent\n }\n}\n\nfunction readPackageData(root: string): Record<string, any> {\n const filename = findPackageJson(root)\n if (!filename) return {}\n\n try {\n const packageJson = JSON.parse(readFileSync(filename, 'utf8'))\n const result: Record<string, any> = {}\n\n if (packageJson.name !== undefined) result.pkgName = packageJson.name\n if (packageJson.version !== undefined) result.pkgVersion = packageJson.version\n\n return result\n } catch {\n return {}\n }\n}\n\nfunction projectRoot(root: string | undefined): string {\n return root ? resolve(process.cwd(), root) : process.cwd()\n}\n\nfunction versionFor(\n options: BuildVersionPluginOptions,\n context: BuildVersionContext,\n timeZone: string,\n): string | number {\n if (typeof options.version === 'function') return options.version(context)\n if (options.version !== undefined) return options.version\n return createDefaultVersion(context.command, timeZone)\n}\n\nfunction makePayload(\n options: BuildVersionPluginOptions,\n context: BuildVersionContext,\n data: Record<string, any>,\n packageData: Record<string, any>,\n timeZone: string,\n): BuildVersionPayload {\n let payload: BuildVersionPayload = {\n ...data,\n ...packageData,\n time: versionFor(options, context, timeZone),\n env: context.mode,\n }\n\n if (options.payload) payload = options.payload(payload)\n return payload\n}\n\nfunction serialize(payload: BuildVersionPayload): string {\n return `${JSON.stringify(payload, null, 2)}\\n`\n}\n\nfunction assignment(globalName: string, payload: BuildVersionPayload): string {\n return `window[${JSON.stringify(globalName)}] = ${JSON.stringify(payload)};`\n}\n\nfunction scriptTag(code: string): string {\n return `<script>${code}</script>`\n}\n\nfunction refreshScript(refreshKey: string, refreshPath: string): string {\n const url = `${refreshPath}?${REFRESH_QUERY}=`\n\n return `window[${JSON.stringify(refreshKey)}]=function(){location.replace(${JSON.stringify(url)}+encodeURIComponent(location.href))};`\n}\n\nfunction checkScript(checkKey: string, versionPath: string, globalName: string): string {\n const global = JSON.stringify(globalName)\n const url = `${versionPath}?${CACHE_BUST_PARAM}=`\n const failed = `{local:local,server:null,update:false}`\n\n return `window[${JSON.stringify(checkKey)}]=async function(){var local=window[${global}];try{var response=await fetch(${JSON.stringify(url)}+Date.now(),{cache:\"no-store\"});if(!response.ok)return ${failed};var server=await response.json();return{local:local,server:server,update:server.time!==local.time}}catch(error){return ${failed}}};`\n}\n\nfunction injectIntoHead(html: string, script: string): string {\n if (html.includes(script)) return html\n\n const tag = `<script>${script}</script>`\n return html.includes('</head>') ? html.replace('</head>', `${tag}</head>`) : `${tag}${html}`\n}\n\n// refresh.html 只能内联脚本,这里是 client.ts 清理流程的精简版,改动 client.ts 时需同步。\nfunction refreshHtmlDocument(): string {\n return `<!doctype html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>刷新缓存</title>\n<style>\nhtml,body{margin:0;height:100%}\n.gv-refresh{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.65)}\n.gv-refresh::after{box-sizing:border-box;width:36px;height:36px;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;content:\"\";animation:gv-refresh-spin .8s linear infinite}\n@keyframes gv-refresh-spin{to{transform:rotate(360deg)}}\n</style>\n</head>\n<body>\n<div class=\"gv-refresh\"></div>\n<script>\n(function () {\n var QUERY = '${REFRESH_QUERY}'\n var PARAM = '${CACHE_BUST_PARAM}'\n var TIMEOUT = 5000\n var FRAME_TIMEOUT = 5000\n\n function resolveTarget() {\n var raw = new URLSearchParams(location.search).get(QUERY)\n if (raw) return new URL(raw, location.href).href\n return new URL(location.pathname.replace(/[^/]*$/, '') + 'index.html', location.href).href\n }\n\n function cacheTargets(target) {\n var url = new URL(target)\n var path = url.pathname.replace(/index\\\\.html$/, '')\n if (!path.endsWith('/')) return [url.href]\n return [\n url.origin + path + url.search + url.hash,\n url.origin + path + 'index.html' + url.search + url.hash\n ]\n }\n\n function busted(url) {\n var target = new URL(url)\n var hash = target.hash\n target.hash = ''\n target.searchParams.set(PARAM, Date.now() + '-' + Math.random().toString(36).slice(2))\n target.hash = hash\n return target.href\n }\n\n function warm(url) {\n return new Promise(function (resolve) {\n var frame = document.createElement('iframe')\n var done = false\n\n function finish() {\n if (done) return\n done = true\n clearTimeout(timer)\n frame.remove()\n resolve()\n }\n\n var timer = setTimeout(finish, FRAME_TIMEOUT)\n frame.hidden = true\n frame.addEventListener('load', finish, { once: true })\n frame.addEventListener('error', finish, { once: true })\n frame.src = url\n document.documentElement.appendChild(frame)\n })\n }\n\n async function refresh(url) {\n var target = busted(url)\n warm(target)\n await fetch(target, { cache: 'no-store' })\n }\n\n async function run() {\n var target = resolveTarget()\n var urls = cacheTargets(target)\n\n await Promise.race([\n (async function () {\n for (var index = 0; index < urls.length; index++) {\n try {\n await refresh(urls[index])\n } catch (error) {\n // 单个 URL 失败不阻断后续 URL。\n }\n }\n })(),\n new Promise(function (resolve) {\n setTimeout(resolve, TIMEOUT)\n })\n ])\n\n location.replace(target)\n }\n\n run()\n})()\n</script>\n</body>\n</html>\n`\n}\n\nfunction pathname(url: string | undefined): string {\n return url ? new URL(url, 'http://localhost').pathname : '/'\n}\n\nfunction requestPath(base: string, filename: string): string {\n const rawBase = base === './' ? '/' : base || '/'\n const leadingBase = rawBase.startsWith('/') ? rawBase : `/${rawBase}`\n const cleanBase = leadingBase.endsWith('/') ? leadingBase.slice(0, -1) : leadingBase\n return `${cleanBase}/${filename}`.replace(/\\/{2,}/g, '/')\n}\n\nfunction nuxtFilename(\n config: {\n appType: string\n build: { assetsDir: string; copyPublicDir: boolean }\n },\n filename: string,\n): string {\n if (config.appType !== 'custom' || config.build.copyPublicDir !== false) return filename\n\n const assetsDir = normalizeFilename(config.build.assetsDir)\n return assetsDir === '.' || filename.startsWith(`${assetsDir}/`)\n ? filename\n : `${assetsDir}/${filename}`\n}\n\nexport function buildVersionPlugin(\n options: BuildVersionPluginOptions = {},\n): BuildVersionPlugin {\n const filename = normalizeFilename(options.filename ?? DEFAULT_FILENAME)\n const globalName = normalizeRequiredName(\n options.globalName ?? DEFAULT_GLOBAL_NAME,\n 'globalName',\n )\n const defineName =\n options.defineName === false\n ? false\n : normalizeRequiredName(options.defineName ?? DEFAULT_DEFINE_NAME, 'defineName')\n const injectToHtml = options.injectToHtml ?? true\n const refreshHtml = options.refreshHtml ?? true\n const refreshFilename =\n refreshHtml === false\n ? undefined\n : normalizeFilename(\n refreshHtml === true ? DEFAULT_REFRESH_HTML : refreshHtml,\n 'refreshHtml',\n )\n const refreshKey =\n refreshFilename === undefined\n ? undefined\n : normalizeRequiredName(options.refreshKey ?? DEFAULT_REFRESH_KEY, 'refreshKey')\n const checkKey = normalizeRequiredName(\n options.checkKey ?? DEFAULT_CHECK_KEY,\n 'checkKey',\n )\n const timeZone = normalizeTimeZone(options.timeZone ?? DEFAULT_TIME_ZONE)\n const data = normalizeData(options.data)\n let payload: BuildVersionPayload | undefined\n let emittedFilename = filename\n let emittedRefreshFilename: string | undefined\n let refreshPath = ''\n let versionPath = ''\n let skipEmit = false\n\n const createPayload = (context: BuildVersionContext, root: string) => {\n payload = makePayload(options, context, data, readPackageData(root), timeZone)\n }\n\n const injectedScript = (): string => {\n if (!payload) return ''\n\n return [\n assignment(globalName, payload),\n refreshKey && refreshPath ? refreshScript(refreshKey, refreshPath) : '',\n versionPath ? checkScript(checkKey, versionPath, globalName) : '',\n ].join('')\n }\n\n const transformHtml = (html: string): string | undefined => {\n if (!injectToHtml || !payload) return undefined\n return injectIntoHead(html, injectedScript())\n }\n\n const headScript = (): string => {\n if (!injectToHtml || !payload) return ''\n return scriptTag(injectedScript())\n }\n\n const plugin: Plugin = {\n name: PLUGIN_NAME,\n\n config(userConfig, environment) {\n if (!payload) {\n createPayload(\n {\n command: environment.command as BuildVersionContext['command'],\n mode: environment.mode,\n },\n projectRoot(userConfig.root),\n )\n }\n\n if (!defineName || !payload) return undefined\n\n return {\n define: {\n [defineName]: JSON.stringify(payload),\n },\n } satisfies UserConfig\n },\n\n configResolved(config) {\n skipEmit = Boolean(config.build.ssr) && !config.build.ssrEmitAssets\n emittedFilename = nuxtFilename(config, filename)\n emittedRefreshFilename = refreshFilename\n ? nuxtFilename(config, refreshFilename)\n : undefined\n refreshPath = emittedRefreshFilename\n ? requestPath(config.base, emittedRefreshFilename)\n : ''\n versionPath = requestPath(config.base, emittedFilename)\n\n if (!payload) {\n createPayload(\n {\n command: config.command as BuildVersionContext['command'],\n mode: config.mode,\n },\n config.root,\n )\n }\n },\n\n configureServer(server) {\n const targetPath = requestPath(server.config.base, filename)\n const refreshPath = refreshFilename\n ? requestPath(server.config.base, refreshFilename)\n : undefined\n\n server.middlewares.use((request, response, next) => {\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n next()\n return\n }\n\n const requestTarget = pathname(request.url)\n\n if (refreshPath && requestTarget === refreshPath) {\n response.statusCode = 200\n response.setHeader('Content-Type', 'text/html; charset=utf-8')\n response.setHeader('Cache-Control', 'no-cache')\n response.end(\n request.method === 'HEAD' ? undefined : refreshHtmlDocument(),\n )\n return\n }\n\n if (requestTarget !== targetPath || !payload) {\n next()\n return\n }\n\n response.statusCode = 200\n response.setHeader('Content-Type', 'application/json; charset=utf-8')\n response.setHeader('Cache-Control', 'no-cache')\n response.end(request.method === 'HEAD' ? undefined : serialize(payload))\n })\n },\n\n transformIndexHtml() {\n if (!injectToHtml || !payload) return undefined\n\n const tags: HtmlTagDescriptor[] = [\n {\n tag: 'script',\n injectTo: 'head',\n children: injectedScript(),\n },\n ]\n return tags\n },\n\n generateBundle() {\n if (!payload || skipEmit) return\n\n this.emitFile({\n type: 'asset',\n fileName: emittedFilename,\n source: serialize(payload),\n })\n\n if (emittedRefreshFilename) {\n this.emitFile({\n type: 'asset',\n fileName: emittedRefreshFilename,\n source: refreshHtmlDocument(),\n })\n }\n\n if (options.log) {\n console.log(options.log(payload))\n } else {\n console.log(`[${PLUGIN_NAME}][build finished]`)\n console.log(serialize(payload))\n }\n },\n }\n\n Object.defineProperty(plugin, 'transformHtml', { value: transformHtml })\n Object.defineProperty(plugin, 'headScript', { value: headScript })\n\n return plugin as BuildVersionPlugin\n}\n\nexport default buildVersionPlugin\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,9 @@ export declare interface BuildVersionPluginOptions {
|
|
|
25
25
|
globalName?: string;
|
|
26
26
|
defineName?: string | false;
|
|
27
27
|
injectToHtml?: boolean;
|
|
28
|
+
refreshHtml?: boolean | string;
|
|
29
|
+
refreshKey?: string;
|
|
30
|
+
checkKey?: string;
|
|
28
31
|
timeZone?: string;
|
|
29
32
|
version?: string | number | ((context: BuildVersionContext) => string | number);
|
|
30
33
|
data?: Record<string, any>;
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { existsSync as e, readFileSync as t } from "node:fs";
|
|
2
2
|
import { dirname as n, join as r, resolve as i } from "node:path";
|
|
3
3
|
//#region src/index.ts
|
|
4
|
-
var a = "@gitlon/version", o = "version.json", s = "
|
|
5
|
-
function
|
|
6
|
-
let
|
|
7
|
-
if (!
|
|
8
|
-
return
|
|
4
|
+
var a = "@gitlon/version", o = "version.json", s = "refresh.html", c = "refreshCache", l = "checkVersion", u = "url", d = "_", f = "__VERSION__", p = "__VERSION__", m = "Asia/Shanghai";
|
|
5
|
+
function h(e, t = "filename") {
|
|
6
|
+
let n = e.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
7
|
+
if (!n || n.endsWith("/")) throw Error(`[${a}] "${t}" must point to a file path.`);
|
|
8
|
+
return n;
|
|
9
9
|
}
|
|
10
|
-
function
|
|
10
|
+
function g(e, t) {
|
|
11
11
|
if (!e.trim()) throw Error(`[${a}] "${t}" cannot be empty.`);
|
|
12
12
|
return e;
|
|
13
13
|
}
|
|
14
|
-
function
|
|
14
|
+
function _(e) {
|
|
15
15
|
let t = e.trim();
|
|
16
16
|
if (!t) throw Error(`[${a}] "timeZone" cannot be empty.`);
|
|
17
17
|
try {
|
|
@@ -21,12 +21,12 @@ function f(e) {
|
|
|
21
21
|
}
|
|
22
22
|
return t;
|
|
23
23
|
}
|
|
24
|
-
function
|
|
24
|
+
function v(e) {
|
|
25
25
|
if (e === void 0) return {};
|
|
26
26
|
if (!e || Array.isArray(e) || typeof e != "object") throw Error(`[${a}] "data" must be a plain object.`);
|
|
27
27
|
return e;
|
|
28
28
|
}
|
|
29
|
-
function
|
|
29
|
+
function y(e, t) {
|
|
30
30
|
if (e === "serve") return 0;
|
|
31
31
|
let n = new Intl.DateTimeFormat("en-US", {
|
|
32
32
|
timeZone: t,
|
|
@@ -48,7 +48,7 @@ function m(e, t) {
|
|
|
48
48
|
r("second")
|
|
49
49
|
].join("");
|
|
50
50
|
}
|
|
51
|
-
function
|
|
51
|
+
function b(t) {
|
|
52
52
|
let i = t;
|
|
53
53
|
for (;;) {
|
|
54
54
|
let t = r(i, "package.json");
|
|
@@ -58,8 +58,8 @@ function h(t) {
|
|
|
58
58
|
i = a;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
-
function
|
|
62
|
-
let n =
|
|
61
|
+
function x(e) {
|
|
62
|
+
let n = b(e);
|
|
63
63
|
if (!n) return {};
|
|
64
64
|
try {
|
|
65
65
|
let e = JSON.parse(t(n, "utf8")), r = {};
|
|
@@ -68,98 +68,224 @@ function g(e) {
|
|
|
68
68
|
return {};
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
function
|
|
71
|
+
function S(e) {
|
|
72
72
|
return e ? i(process.cwd(), e) : process.cwd();
|
|
73
73
|
}
|
|
74
|
-
function
|
|
75
|
-
return typeof e.version == "function" ? e.version(t) : e.version === void 0 ?
|
|
74
|
+
function C(e, t, n) {
|
|
75
|
+
return typeof e.version == "function" ? e.version(t) : e.version === void 0 ? y(t.command, n) : e.version;
|
|
76
76
|
}
|
|
77
|
-
function
|
|
77
|
+
function w(e, t, n, r, i) {
|
|
78
78
|
let a = {
|
|
79
79
|
...n,
|
|
80
80
|
...r,
|
|
81
|
-
time:
|
|
81
|
+
time: C(e, t, i),
|
|
82
82
|
env: t.mode
|
|
83
83
|
};
|
|
84
84
|
return e.payload && (a = e.payload(a)), a;
|
|
85
85
|
}
|
|
86
|
-
function
|
|
86
|
+
function T(e) {
|
|
87
87
|
return `${JSON.stringify(e, null, 2)}\n`;
|
|
88
88
|
}
|
|
89
|
-
function
|
|
89
|
+
function E(e, t) {
|
|
90
90
|
return `window[${JSON.stringify(e)}] = ${JSON.stringify(t)};`;
|
|
91
91
|
}
|
|
92
|
-
function
|
|
93
|
-
return `<script>${
|
|
92
|
+
function D(e) {
|
|
93
|
+
return `<script>${e}<\/script>`;
|
|
94
|
+
}
|
|
95
|
+
function O(e, t) {
|
|
96
|
+
let n = `${t}?${u}=`;
|
|
97
|
+
return `window[${JSON.stringify(e)}]=function(){location.replace(${JSON.stringify(n)}+encodeURIComponent(location.href))};`;
|
|
98
|
+
}
|
|
99
|
+
function k(e, t, n) {
|
|
100
|
+
let r = JSON.stringify(n), i = `${t}?${d}=`, a = "{local:local,server:null,update:false}";
|
|
101
|
+
return `window[${JSON.stringify(e)}]=async function(){var local=window[${r}];try{var response=await fetch(${JSON.stringify(i)}+Date.now(),{cache:"no-store"});if(!response.ok)return ${a};var server=await response.json();return{local:local,server:server,update:server.time!==local.time}}catch(error){return ${a}}};`;
|
|
94
102
|
}
|
|
95
|
-
function
|
|
103
|
+
function A(e, t) {
|
|
96
104
|
if (e.includes(t)) return e;
|
|
97
105
|
let n = `<script>${t}<\/script>`;
|
|
98
106
|
return e.includes("</head>") ? e.replace("</head>", `${n}</head>`) : `${n}${e}`;
|
|
99
107
|
}
|
|
100
|
-
function
|
|
108
|
+
function j() {
|
|
109
|
+
return `<!doctype html>
|
|
110
|
+
<html lang="zh-CN">
|
|
111
|
+
<head>
|
|
112
|
+
<meta charset="utf-8">
|
|
113
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
114
|
+
<title>刷新缓存</title>
|
|
115
|
+
<style>
|
|
116
|
+
html,body{margin:0;height:100%}
|
|
117
|
+
.gv-refresh{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.65)}
|
|
118
|
+
.gv-refresh::after{box-sizing:border-box;width:36px;height:36px;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;content:"";animation:gv-refresh-spin .8s linear infinite}
|
|
119
|
+
@keyframes gv-refresh-spin{to{transform:rotate(360deg)}}
|
|
120
|
+
</style>
|
|
121
|
+
</head>
|
|
122
|
+
<body>
|
|
123
|
+
<div class="gv-refresh"></div>
|
|
124
|
+
<script>
|
|
125
|
+
(function () {
|
|
126
|
+
var QUERY = '${u}'
|
|
127
|
+
var PARAM = '${d}'
|
|
128
|
+
var TIMEOUT = 5000
|
|
129
|
+
var FRAME_TIMEOUT = 5000
|
|
130
|
+
|
|
131
|
+
function resolveTarget() {
|
|
132
|
+
var raw = new URLSearchParams(location.search).get(QUERY)
|
|
133
|
+
if (raw) return new URL(raw, location.href).href
|
|
134
|
+
return new URL(location.pathname.replace(/[^/]*$/, '') + 'index.html', location.href).href
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function cacheTargets(target) {
|
|
138
|
+
var url = new URL(target)
|
|
139
|
+
var path = url.pathname.replace(/index\\.html$/, '')
|
|
140
|
+
if (!path.endsWith('/')) return [url.href]
|
|
141
|
+
return [
|
|
142
|
+
url.origin + path + url.search + url.hash,
|
|
143
|
+
url.origin + path + 'index.html' + url.search + url.hash
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function busted(url) {
|
|
148
|
+
var target = new URL(url)
|
|
149
|
+
var hash = target.hash
|
|
150
|
+
target.hash = ''
|
|
151
|
+
target.searchParams.set(PARAM, Date.now() + '-' + Math.random().toString(36).slice(2))
|
|
152
|
+
target.hash = hash
|
|
153
|
+
return target.href
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function warm(url) {
|
|
157
|
+
return new Promise(function (resolve) {
|
|
158
|
+
var frame = document.createElement('iframe')
|
|
159
|
+
var done = false
|
|
160
|
+
|
|
161
|
+
function finish() {
|
|
162
|
+
if (done) return
|
|
163
|
+
done = true
|
|
164
|
+
clearTimeout(timer)
|
|
165
|
+
frame.remove()
|
|
166
|
+
resolve()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
var timer = setTimeout(finish, FRAME_TIMEOUT)
|
|
170
|
+
frame.hidden = true
|
|
171
|
+
frame.addEventListener('load', finish, { once: true })
|
|
172
|
+
frame.addEventListener('error', finish, { once: true })
|
|
173
|
+
frame.src = url
|
|
174
|
+
document.documentElement.appendChild(frame)
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function refresh(url) {
|
|
179
|
+
var target = busted(url)
|
|
180
|
+
warm(target)
|
|
181
|
+
await fetch(target, { cache: 'no-store' })
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function run() {
|
|
185
|
+
var target = resolveTarget()
|
|
186
|
+
var urls = cacheTargets(target)
|
|
187
|
+
|
|
188
|
+
await Promise.race([
|
|
189
|
+
(async function () {
|
|
190
|
+
for (var index = 0; index < urls.length; index++) {
|
|
191
|
+
try {
|
|
192
|
+
await refresh(urls[index])
|
|
193
|
+
} catch (error) {
|
|
194
|
+
// 单个 URL 失败不阻断后续 URL。
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
})(),
|
|
198
|
+
new Promise(function (resolve) {
|
|
199
|
+
setTimeout(resolve, TIMEOUT)
|
|
200
|
+
})
|
|
201
|
+
])
|
|
202
|
+
|
|
203
|
+
location.replace(target)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
run()
|
|
207
|
+
})()
|
|
208
|
+
<\/script>
|
|
209
|
+
</body>
|
|
210
|
+
</html>
|
|
211
|
+
`;
|
|
212
|
+
}
|
|
213
|
+
function M(e) {
|
|
101
214
|
return e ? new URL(e, "http://localhost").pathname : "/";
|
|
102
215
|
}
|
|
103
|
-
function
|
|
216
|
+
function N(e, t) {
|
|
104
217
|
let n = e === "./" ? "/" : e || "/", r = n.startsWith("/") ? n : `/${n}`;
|
|
105
218
|
return `${r.endsWith("/") ? r.slice(0, -1) : r}/${t}`.replace(/\/{2,}/g, "/");
|
|
106
219
|
}
|
|
107
|
-
function
|
|
220
|
+
function P(e, t) {
|
|
108
221
|
if (e.appType !== "custom" || e.build.copyPublicDir !== !1) return t;
|
|
109
|
-
let n =
|
|
222
|
+
let n = h(e.build.assetsDir);
|
|
110
223
|
return n === "." || t.startsWith(`${n}/`) ? t : `${n}/${t}`;
|
|
111
224
|
}
|
|
112
|
-
function
|
|
113
|
-
let t =
|
|
114
|
-
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
|
|
225
|
+
function F(e = {}) {
|
|
226
|
+
let t = h(e.filename ?? o), n = g(e.globalName ?? f, "globalName"), r = e.defineName !== !1 && g(e.defineName ?? p, "defineName"), i = e.injectToHtml ?? !0, u = e.refreshHtml ?? !0, d = u === !1 ? void 0 : h(u === !0 ? s : u, "refreshHtml"), y = d === void 0 ? void 0 : g(e.refreshKey ?? c, "refreshKey"), b = g(e.checkKey ?? l, "checkKey"), C = _(e.timeZone ?? m), F = v(e.data), I, L = t, R, z = "", B = "", V = !1, H = (t, n) => {
|
|
227
|
+
I = w(e, t, F, x(n), C);
|
|
228
|
+
}, U = () => I ? [
|
|
229
|
+
E(n, I),
|
|
230
|
+
y && z ? O(y, z) : "",
|
|
231
|
+
B ? k(b, B, n) : ""
|
|
232
|
+
].join("") : "", W = (e) => {
|
|
233
|
+
if (i && I) return A(e, U());
|
|
234
|
+
}, G = () => !i || !I ? "" : D(U()), K = {
|
|
118
235
|
name: a,
|
|
119
236
|
config(e, t) {
|
|
120
|
-
if (
|
|
237
|
+
if (I || H({
|
|
121
238
|
command: t.command,
|
|
122
239
|
mode: t.mode
|
|
123
|
-
},
|
|
240
|
+
}, S(e.root)), r && I) return { define: { [r]: JSON.stringify(I) } };
|
|
124
241
|
},
|
|
125
242
|
configResolved(e) {
|
|
126
|
-
|
|
243
|
+
V = !!e.build.ssr && !e.build.ssrEmitAssets, L = P(e, t), R = d ? P(e, d) : void 0, z = R ? N(e.base, R) : "", B = N(e.base, L), I || H({
|
|
127
244
|
command: e.command,
|
|
128
245
|
mode: e.mode
|
|
129
246
|
}, e.root);
|
|
130
247
|
},
|
|
131
248
|
configureServer(e) {
|
|
132
|
-
let n =
|
|
133
|
-
e.middlewares.use((e, t,
|
|
249
|
+
let n = N(e.config.base, t), r = d ? N(e.config.base, d) : void 0;
|
|
250
|
+
e.middlewares.use((e, t, i) => {
|
|
134
251
|
if (e.method !== "GET" && e.method !== "HEAD") {
|
|
135
|
-
|
|
252
|
+
i();
|
|
136
253
|
return;
|
|
137
254
|
}
|
|
138
|
-
|
|
139
|
-
|
|
255
|
+
let a = M(e.url);
|
|
256
|
+
if (r && a === r) {
|
|
257
|
+
t.statusCode = 200, t.setHeader("Content-Type", "text/html; charset=utf-8"), t.setHeader("Cache-Control", "no-cache"), t.end(e.method === "HEAD" ? void 0 : j());
|
|
140
258
|
return;
|
|
141
259
|
}
|
|
142
|
-
|
|
260
|
+
if (a !== n || !I) {
|
|
261
|
+
i();
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
t.statusCode = 200, t.setHeader("Content-Type", "application/json; charset=utf-8"), t.setHeader("Cache-Control", "no-cache"), t.end(e.method === "HEAD" ? void 0 : T(I));
|
|
143
265
|
});
|
|
144
266
|
},
|
|
145
267
|
transformIndexHtml() {
|
|
146
|
-
if (i &&
|
|
268
|
+
if (i && I) return [{
|
|
147
269
|
tag: "script",
|
|
148
270
|
injectTo: "head",
|
|
149
|
-
children:
|
|
271
|
+
children: U()
|
|
150
272
|
}];
|
|
151
273
|
},
|
|
152
274
|
generateBundle() {
|
|
153
|
-
|
|
275
|
+
I && !V && (this.emitFile({
|
|
276
|
+
type: "asset",
|
|
277
|
+
fileName: L,
|
|
278
|
+
source: T(I)
|
|
279
|
+
}), R && this.emitFile({
|
|
154
280
|
type: "asset",
|
|
155
|
-
fileName:
|
|
156
|
-
source:
|
|
157
|
-
}), e.log ? console.log(e.log(
|
|
281
|
+
fileName: R,
|
|
282
|
+
source: j()
|
|
283
|
+
}), e.log ? console.log(e.log(I)) : (console.log(`[${a}][build finished]`), console.log(T(I))));
|
|
158
284
|
}
|
|
159
285
|
};
|
|
160
|
-
return Object.defineProperty(
|
|
286
|
+
return Object.defineProperty(K, "transformHtml", { value: W }), Object.defineProperty(K, "headScript", { value: G }), K;
|
|
161
287
|
}
|
|
162
288
|
//#endregion
|
|
163
|
-
export {
|
|
289
|
+
export { F as buildVersionPlugin, F as default };
|
|
164
290
|
|
|
165
291
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport type { HtmlTagDescriptor, Plugin, UserConfig } from 'vite'\n\nexport interface BuildVersionContext {\n command: 'serve' | 'build'\n mode: string\n}\n\nexport interface BuildVersionPayload {\n time: string | number\n env: string\n [key: string]: any\n}\n\nexport interface BuildVersionPluginOptions {\n filename?: string\n globalName?: string\n defineName?: string | false\n injectToHtml?: boolean\n timeZone?: string\n version?: string | number | ((context: BuildVersionContext) => string | number)\n data?: Record<string, any>\n log?: (content: any) => string\n payload?: (json: any) => any\n}\n\nexport type BuildVersionPlugin = Plugin & {\n transformHtml: (code: string, ...args: unknown[]) => string | undefined\n headScript: () => string\n}\n\nconst PLUGIN_NAME = '@gitlon/version'\nconst DEFAULT_FILENAME = 'version.json'\nconst DEFAULT_GLOBAL_NAME = '__VERSION__'\nconst DEFAULT_DEFINE_NAME = '__VERSION__'\nconst DEFAULT_TIME_ZONE = 'Asia/Shanghai'\n\nfunction normalizeFilename(filename: string): string {\n const value = filename.replace(/\\\\/g, '/').replace(/^\\/+/, '')\n\n if (!value || value.endsWith('/')) {\n throw new Error(`[${PLUGIN_NAME}] \"filename\" must point to a file path.`)\n }\n\n return value\n}\n\nfunction normalizeRequiredName(value: string, option: 'globalName' | 'defineName'): string {\n if (!value.trim()) {\n throw new Error(`[${PLUGIN_NAME}] \"${option}\" cannot be empty.`)\n }\n\n return value\n}\n\nfunction normalizeTimeZone(timeZone: string): string {\n const value = timeZone.trim()\n\n if (!value) {\n throw new Error(`[${PLUGIN_NAME}] \"timeZone\" cannot be empty.`)\n }\n\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: value }).format(new Date())\n } catch {\n throw new Error(`[${PLUGIN_NAME}] \"timeZone\" is invalid.`)\n }\n\n return value\n}\n\nfunction normalizeData(data: BuildVersionPluginOptions['data']): Record<string, any> {\n if (data === undefined) return {}\n\n if (!data || Array.isArray(data) || typeof data !== 'object') {\n throw new Error(`[${PLUGIN_NAME}] \"data\" must be a plain object.`)\n }\n\n return data\n}\n\nfunction createDefaultVersion(command: BuildVersionContext['command'], timeZone: string) {\n if (command === 'serve') return 0\n\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n hourCycle: 'h23',\n }).formatToParts(new Date())\n const part = (type: Intl.DateTimeFormatPartTypes) =>\n parts.find((item) => item.type === type)?.value ?? ''\n\n return [\n part('year'),\n part('month'),\n part('day'),\n part('hour'),\n part('minute'),\n part('second'),\n ].join('')\n}\n\nfunction findPackageJson(root: string): string | undefined {\n let directory = root\n\n while (true) {\n const packageJson = join(directory, 'package.json')\n if (existsSync(packageJson)) return packageJson\n\n const parent = dirname(directory)\n if (parent === directory) return undefined\n directory = parent\n }\n}\n\nfunction readPackageData(root: string): Record<string, any> {\n const filename = findPackageJson(root)\n if (!filename) return {}\n\n try {\n const packageJson = JSON.parse(readFileSync(filename, 'utf8'))\n const result: Record<string, any> = {}\n\n if (packageJson.name !== undefined) result.pkgName = packageJson.name\n if (packageJson.version !== undefined) result.pkgVersion = packageJson.version\n\n return result\n } catch {\n return {}\n }\n}\n\nfunction projectRoot(root: string | undefined): string {\n return root ? resolve(process.cwd(), root) : process.cwd()\n}\n\nfunction versionFor(\n options: BuildVersionPluginOptions,\n context: BuildVersionContext,\n timeZone: string,\n): string | number {\n if (typeof options.version === 'function') return options.version(context)\n if (options.version !== undefined) return options.version\n return createDefaultVersion(context.command, timeZone)\n}\n\nfunction makePayload(\n options: BuildVersionPluginOptions,\n context: BuildVersionContext,\n data: Record<string, any>,\n packageData: Record<string, any>,\n timeZone: string,\n): BuildVersionPayload {\n let payload: BuildVersionPayload = {\n ...data,\n ...packageData,\n time: versionFor(options, context, timeZone),\n env: context.mode,\n }\n\n if (options.payload) payload = options.payload(payload)\n return payload\n}\n\nfunction serialize(payload: BuildVersionPayload): string {\n return `${JSON.stringify(payload, null, 2)}\\n`\n}\n\nfunction assignment(globalName: string, payload: BuildVersionPayload): string {\n return `window[${JSON.stringify(globalName)}] = ${JSON.stringify(payload)};`\n}\n\nfunction scriptTag(globalName: string, payload: BuildVersionPayload): string {\n return `<script>${assignment(globalName, payload)}</script>`\n}\n\nfunction injectIntoHead(html: string, script: string): string {\n if (html.includes(script)) return html\n\n const tag = `<script>${script}</script>`\n return html.includes('</head>') ? html.replace('</head>', `${tag}</head>`) : `${tag}${html}`\n}\n\nfunction pathname(url: string | undefined): string {\n return url ? new URL(url, 'http://localhost').pathname : '/'\n}\n\nfunction requestPath(base: string, filename: string): string {\n const rawBase = base === './' ? '/' : base || '/'\n const leadingBase = rawBase.startsWith('/') ? rawBase : `/${rawBase}`\n const cleanBase = leadingBase.endsWith('/') ? leadingBase.slice(0, -1) : leadingBase\n return `${cleanBase}/${filename}`.replace(/\\/{2,}/g, '/')\n}\n\nfunction nuxtFilename(\n config: {\n appType: string\n build: { assetsDir: string; copyPublicDir: boolean }\n },\n filename: string,\n): string {\n if (config.appType !== 'custom' || config.build.copyPublicDir !== false) return filename\n\n const assetsDir = normalizeFilename(config.build.assetsDir)\n return assetsDir === '.' || filename.startsWith(`${assetsDir}/`)\n ? filename\n : `${assetsDir}/${filename}`\n}\n\nexport function buildVersionPlugin(\n options: BuildVersionPluginOptions = {},\n): BuildVersionPlugin {\n const filename = normalizeFilename(options.filename ?? DEFAULT_FILENAME)\n const globalName = normalizeRequiredName(\n options.globalName ?? DEFAULT_GLOBAL_NAME,\n 'globalName',\n )\n const defineName =\n options.defineName === false\n ? false\n : normalizeRequiredName(options.defineName ?? DEFAULT_DEFINE_NAME, 'defineName')\n const injectToHtml = options.injectToHtml ?? true\n const timeZone = normalizeTimeZone(options.timeZone ?? DEFAULT_TIME_ZONE)\n const data = normalizeData(options.data)\n let payload: BuildVersionPayload | undefined\n let emittedFilename = filename\n let skipEmit = false\n\n const createPayload = (context: BuildVersionContext, root: string) => {\n payload = makePayload(options, context, data, readPackageData(root), timeZone)\n }\n\n const transformHtml = (html: string): string | undefined => {\n if (!injectToHtml || !payload) return undefined\n return injectIntoHead(html, assignment(globalName, payload))\n }\n\n const headScript = (): string => {\n if (!injectToHtml || !payload) return ''\n return scriptTag(globalName, payload)\n }\n\n const plugin: Plugin = {\n name: PLUGIN_NAME,\n\n config(userConfig, environment) {\n if (!payload) {\n createPayload(\n {\n command: environment.command as BuildVersionContext['command'],\n mode: environment.mode,\n },\n projectRoot(userConfig.root),\n )\n }\n\n if (!defineName || !payload) return undefined\n\n return {\n define: {\n [defineName]: JSON.stringify(payload),\n },\n } satisfies UserConfig\n },\n\n configResolved(config) {\n skipEmit = Boolean(config.build.ssr) && !config.build.ssrEmitAssets\n emittedFilename = nuxtFilename(config, filename)\n\n if (!payload) {\n createPayload(\n {\n command: config.command as BuildVersionContext['command'],\n mode: config.mode,\n },\n config.root,\n )\n }\n },\n\n configureServer(server) {\n const targetPath = requestPath(server.config.base, filename)\n\n server.middlewares.use((request, response, next) => {\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n next()\n return\n }\n\n if (pathname(request.url) !== targetPath || !payload) {\n next()\n return\n }\n\n response.statusCode = 200\n response.setHeader('Content-Type', 'application/json; charset=utf-8')\n response.setHeader('Cache-Control', 'no-cache')\n response.end(request.method === 'HEAD' ? undefined : serialize(payload))\n })\n },\n\n transformIndexHtml() {\n if (!injectToHtml || !payload) return undefined\n\n const tags: HtmlTagDescriptor[] = [\n {\n tag: 'script',\n injectTo: 'head',\n children: assignment(globalName, payload),\n },\n ]\n return tags\n },\n\n generateBundle() {\n if (!payload || skipEmit) return\n\n this.emitFile({\n type: 'asset',\n fileName: emittedFilename,\n source: serialize(payload),\n })\n\n if (options.log) {\n console.log(options.log(payload))\n } else {\n console.log(`[${PLUGIN_NAME}][build finished]`)\n console.log(serialize(payload))\n }\n },\n }\n\n Object.defineProperty(plugin, 'transformHtml', { value: transformHtml })\n Object.defineProperty(plugin, 'headScript', { value: headScript })\n\n return plugin as BuildVersionPlugin\n}\n\nexport default buildVersionPlugin\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport type { HtmlTagDescriptor, Plugin, UserConfig } from 'vite'\n\nexport interface BuildVersionContext {\n command: 'serve' | 'build'\n mode: string\n}\n\nexport interface BuildVersionPayload {\n time: string | number\n env: string\n [key: string]: any\n}\n\nexport interface BuildVersionPluginOptions {\n filename?: string\n globalName?: string\n defineName?: string | false\n injectToHtml?: boolean\n refreshHtml?: boolean | string\n refreshKey?: string\n checkKey?: string\n timeZone?: string\n version?: string | number | ((context: BuildVersionContext) => string | number)\n data?: Record<string, any>\n log?: (content: any) => string\n payload?: (json: any) => any\n}\n\nexport type BuildVersionPlugin = Plugin & {\n transformHtml: (code: string, ...args: unknown[]) => string | undefined\n headScript: () => string\n}\n\nconst PLUGIN_NAME = '@gitlon/version'\nconst DEFAULT_FILENAME = 'version.json'\nconst DEFAULT_REFRESH_HTML = 'refresh.html'\nconst DEFAULT_REFRESH_KEY = 'refreshCache'\nconst DEFAULT_CHECK_KEY = 'checkVersion'\nconst REFRESH_QUERY = 'url'\nconst CACHE_BUST_PARAM = '_'\nconst DEFAULT_GLOBAL_NAME = '__VERSION__'\nconst DEFAULT_DEFINE_NAME = '__VERSION__'\nconst DEFAULT_TIME_ZONE = 'Asia/Shanghai'\n\nfunction normalizeFilename(filename: string, option = 'filename'): string {\n const value = filename.replace(/\\\\/g, '/').replace(/^\\/+/, '')\n\n if (!value || value.endsWith('/')) {\n throw new Error(`[${PLUGIN_NAME}] \"${option}\" must point to a file path.`)\n }\n\n return value\n}\n\nfunction normalizeRequiredName(\n value: string,\n option: 'globalName' | 'defineName' | 'refreshKey' | 'checkKey',\n): string {\n if (!value.trim()) {\n throw new Error(`[${PLUGIN_NAME}] \"${option}\" cannot be empty.`)\n }\n\n return value\n}\n\nfunction normalizeTimeZone(timeZone: string): string {\n const value = timeZone.trim()\n\n if (!value) {\n throw new Error(`[${PLUGIN_NAME}] \"timeZone\" cannot be empty.`)\n }\n\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: value }).format(new Date())\n } catch {\n throw new Error(`[${PLUGIN_NAME}] \"timeZone\" is invalid.`)\n }\n\n return value\n}\n\nfunction normalizeData(data: BuildVersionPluginOptions['data']): Record<string, any> {\n if (data === undefined) return {}\n\n if (!data || Array.isArray(data) || typeof data !== 'object') {\n throw new Error(`[${PLUGIN_NAME}] \"data\" must be a plain object.`)\n }\n\n return data\n}\n\nfunction createDefaultVersion(command: BuildVersionContext['command'], timeZone: string) {\n if (command === 'serve') return 0\n\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n hourCycle: 'h23',\n }).formatToParts(new Date())\n const part = (type: Intl.DateTimeFormatPartTypes) =>\n parts.find((item) => item.type === type)?.value ?? ''\n\n return [\n part('year'),\n part('month'),\n part('day'),\n part('hour'),\n part('minute'),\n part('second'),\n ].join('')\n}\n\nfunction findPackageJson(root: string): string | undefined {\n let directory = root\n\n while (true) {\n const packageJson = join(directory, 'package.json')\n if (existsSync(packageJson)) return packageJson\n\n const parent = dirname(directory)\n if (parent === directory) return undefined\n directory = parent\n }\n}\n\nfunction readPackageData(root: string): Record<string, any> {\n const filename = findPackageJson(root)\n if (!filename) return {}\n\n try {\n const packageJson = JSON.parse(readFileSync(filename, 'utf8'))\n const result: Record<string, any> = {}\n\n if (packageJson.name !== undefined) result.pkgName = packageJson.name\n if (packageJson.version !== undefined) result.pkgVersion = packageJson.version\n\n return result\n } catch {\n return {}\n }\n}\n\nfunction projectRoot(root: string | undefined): string {\n return root ? resolve(process.cwd(), root) : process.cwd()\n}\n\nfunction versionFor(\n options: BuildVersionPluginOptions,\n context: BuildVersionContext,\n timeZone: string,\n): string | number {\n if (typeof options.version === 'function') return options.version(context)\n if (options.version !== undefined) return options.version\n return createDefaultVersion(context.command, timeZone)\n}\n\nfunction makePayload(\n options: BuildVersionPluginOptions,\n context: BuildVersionContext,\n data: Record<string, any>,\n packageData: Record<string, any>,\n timeZone: string,\n): BuildVersionPayload {\n let payload: BuildVersionPayload = {\n ...data,\n ...packageData,\n time: versionFor(options, context, timeZone),\n env: context.mode,\n }\n\n if (options.payload) payload = options.payload(payload)\n return payload\n}\n\nfunction serialize(payload: BuildVersionPayload): string {\n return `${JSON.stringify(payload, null, 2)}\\n`\n}\n\nfunction assignment(globalName: string, payload: BuildVersionPayload): string {\n return `window[${JSON.stringify(globalName)}] = ${JSON.stringify(payload)};`\n}\n\nfunction scriptTag(code: string): string {\n return `<script>${code}</script>`\n}\n\nfunction refreshScript(refreshKey: string, refreshPath: string): string {\n const url = `${refreshPath}?${REFRESH_QUERY}=`\n\n return `window[${JSON.stringify(refreshKey)}]=function(){location.replace(${JSON.stringify(url)}+encodeURIComponent(location.href))};`\n}\n\nfunction checkScript(checkKey: string, versionPath: string, globalName: string): string {\n const global = JSON.stringify(globalName)\n const url = `${versionPath}?${CACHE_BUST_PARAM}=`\n const failed = `{local:local,server:null,update:false}`\n\n return `window[${JSON.stringify(checkKey)}]=async function(){var local=window[${global}];try{var response=await fetch(${JSON.stringify(url)}+Date.now(),{cache:\"no-store\"});if(!response.ok)return ${failed};var server=await response.json();return{local:local,server:server,update:server.time!==local.time}}catch(error){return ${failed}}};`\n}\n\nfunction injectIntoHead(html: string, script: string): string {\n if (html.includes(script)) return html\n\n const tag = `<script>${script}</script>`\n return html.includes('</head>') ? html.replace('</head>', `${tag}</head>`) : `${tag}${html}`\n}\n\n// refresh.html 只能内联脚本,这里是 client.ts 清理流程的精简版,改动 client.ts 时需同步。\nfunction refreshHtmlDocument(): string {\n return `<!doctype html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>刷新缓存</title>\n<style>\nhtml,body{margin:0;height:100%}\n.gv-refresh{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.65)}\n.gv-refresh::after{box-sizing:border-box;width:36px;height:36px;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;content:\"\";animation:gv-refresh-spin .8s linear infinite}\n@keyframes gv-refresh-spin{to{transform:rotate(360deg)}}\n</style>\n</head>\n<body>\n<div class=\"gv-refresh\"></div>\n<script>\n(function () {\n var QUERY = '${REFRESH_QUERY}'\n var PARAM = '${CACHE_BUST_PARAM}'\n var TIMEOUT = 5000\n var FRAME_TIMEOUT = 5000\n\n function resolveTarget() {\n var raw = new URLSearchParams(location.search).get(QUERY)\n if (raw) return new URL(raw, location.href).href\n return new URL(location.pathname.replace(/[^/]*$/, '') + 'index.html', location.href).href\n }\n\n function cacheTargets(target) {\n var url = new URL(target)\n var path = url.pathname.replace(/index\\\\.html$/, '')\n if (!path.endsWith('/')) return [url.href]\n return [\n url.origin + path + url.search + url.hash,\n url.origin + path + 'index.html' + url.search + url.hash\n ]\n }\n\n function busted(url) {\n var target = new URL(url)\n var hash = target.hash\n target.hash = ''\n target.searchParams.set(PARAM, Date.now() + '-' + Math.random().toString(36).slice(2))\n target.hash = hash\n return target.href\n }\n\n function warm(url) {\n return new Promise(function (resolve) {\n var frame = document.createElement('iframe')\n var done = false\n\n function finish() {\n if (done) return\n done = true\n clearTimeout(timer)\n frame.remove()\n resolve()\n }\n\n var timer = setTimeout(finish, FRAME_TIMEOUT)\n frame.hidden = true\n frame.addEventListener('load', finish, { once: true })\n frame.addEventListener('error', finish, { once: true })\n frame.src = url\n document.documentElement.appendChild(frame)\n })\n }\n\n async function refresh(url) {\n var target = busted(url)\n warm(target)\n await fetch(target, { cache: 'no-store' })\n }\n\n async function run() {\n var target = resolveTarget()\n var urls = cacheTargets(target)\n\n await Promise.race([\n (async function () {\n for (var index = 0; index < urls.length; index++) {\n try {\n await refresh(urls[index])\n } catch (error) {\n // 单个 URL 失败不阻断后续 URL。\n }\n }\n })(),\n new Promise(function (resolve) {\n setTimeout(resolve, TIMEOUT)\n })\n ])\n\n location.replace(target)\n }\n\n run()\n})()\n</script>\n</body>\n</html>\n`\n}\n\nfunction pathname(url: string | undefined): string {\n return url ? new URL(url, 'http://localhost').pathname : '/'\n}\n\nfunction requestPath(base: string, filename: string): string {\n const rawBase = base === './' ? '/' : base || '/'\n const leadingBase = rawBase.startsWith('/') ? rawBase : `/${rawBase}`\n const cleanBase = leadingBase.endsWith('/') ? leadingBase.slice(0, -1) : leadingBase\n return `${cleanBase}/${filename}`.replace(/\\/{2,}/g, '/')\n}\n\nfunction nuxtFilename(\n config: {\n appType: string\n build: { assetsDir: string; copyPublicDir: boolean }\n },\n filename: string,\n): string {\n if (config.appType !== 'custom' || config.build.copyPublicDir !== false) return filename\n\n const assetsDir = normalizeFilename(config.build.assetsDir)\n return assetsDir === '.' || filename.startsWith(`${assetsDir}/`)\n ? filename\n : `${assetsDir}/${filename}`\n}\n\nexport function buildVersionPlugin(\n options: BuildVersionPluginOptions = {},\n): BuildVersionPlugin {\n const filename = normalizeFilename(options.filename ?? DEFAULT_FILENAME)\n const globalName = normalizeRequiredName(\n options.globalName ?? DEFAULT_GLOBAL_NAME,\n 'globalName',\n )\n const defineName =\n options.defineName === false\n ? false\n : normalizeRequiredName(options.defineName ?? DEFAULT_DEFINE_NAME, 'defineName')\n const injectToHtml = options.injectToHtml ?? true\n const refreshHtml = options.refreshHtml ?? true\n const refreshFilename =\n refreshHtml === false\n ? undefined\n : normalizeFilename(\n refreshHtml === true ? DEFAULT_REFRESH_HTML : refreshHtml,\n 'refreshHtml',\n )\n const refreshKey =\n refreshFilename === undefined\n ? undefined\n : normalizeRequiredName(options.refreshKey ?? DEFAULT_REFRESH_KEY, 'refreshKey')\n const checkKey = normalizeRequiredName(\n options.checkKey ?? DEFAULT_CHECK_KEY,\n 'checkKey',\n )\n const timeZone = normalizeTimeZone(options.timeZone ?? DEFAULT_TIME_ZONE)\n const data = normalizeData(options.data)\n let payload: BuildVersionPayload | undefined\n let emittedFilename = filename\n let emittedRefreshFilename: string | undefined\n let refreshPath = ''\n let versionPath = ''\n let skipEmit = false\n\n const createPayload = (context: BuildVersionContext, root: string) => {\n payload = makePayload(options, context, data, readPackageData(root), timeZone)\n }\n\n const injectedScript = (): string => {\n if (!payload) return ''\n\n return [\n assignment(globalName, payload),\n refreshKey && refreshPath ? refreshScript(refreshKey, refreshPath) : '',\n versionPath ? checkScript(checkKey, versionPath, globalName) : '',\n ].join('')\n }\n\n const transformHtml = (html: string): string | undefined => {\n if (!injectToHtml || !payload) return undefined\n return injectIntoHead(html, injectedScript())\n }\n\n const headScript = (): string => {\n if (!injectToHtml || !payload) return ''\n return scriptTag(injectedScript())\n }\n\n const plugin: Plugin = {\n name: PLUGIN_NAME,\n\n config(userConfig, environment) {\n if (!payload) {\n createPayload(\n {\n command: environment.command as BuildVersionContext['command'],\n mode: environment.mode,\n },\n projectRoot(userConfig.root),\n )\n }\n\n if (!defineName || !payload) return undefined\n\n return {\n define: {\n [defineName]: JSON.stringify(payload),\n },\n } satisfies UserConfig\n },\n\n configResolved(config) {\n skipEmit = Boolean(config.build.ssr) && !config.build.ssrEmitAssets\n emittedFilename = nuxtFilename(config, filename)\n emittedRefreshFilename = refreshFilename\n ? nuxtFilename(config, refreshFilename)\n : undefined\n refreshPath = emittedRefreshFilename\n ? requestPath(config.base, emittedRefreshFilename)\n : ''\n versionPath = requestPath(config.base, emittedFilename)\n\n if (!payload) {\n createPayload(\n {\n command: config.command as BuildVersionContext['command'],\n mode: config.mode,\n },\n config.root,\n )\n }\n },\n\n configureServer(server) {\n const targetPath = requestPath(server.config.base, filename)\n const refreshPath = refreshFilename\n ? requestPath(server.config.base, refreshFilename)\n : undefined\n\n server.middlewares.use((request, response, next) => {\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n next()\n return\n }\n\n const requestTarget = pathname(request.url)\n\n if (refreshPath && requestTarget === refreshPath) {\n response.statusCode = 200\n response.setHeader('Content-Type', 'text/html; charset=utf-8')\n response.setHeader('Cache-Control', 'no-cache')\n response.end(\n request.method === 'HEAD' ? undefined : refreshHtmlDocument(),\n )\n return\n }\n\n if (requestTarget !== targetPath || !payload) {\n next()\n return\n }\n\n response.statusCode = 200\n response.setHeader('Content-Type', 'application/json; charset=utf-8')\n response.setHeader('Cache-Control', 'no-cache')\n response.end(request.method === 'HEAD' ? undefined : serialize(payload))\n })\n },\n\n transformIndexHtml() {\n if (!injectToHtml || !payload) return undefined\n\n const tags: HtmlTagDescriptor[] = [\n {\n tag: 'script',\n injectTo: 'head',\n children: injectedScript(),\n },\n ]\n return tags\n },\n\n generateBundle() {\n if (!payload || skipEmit) return\n\n this.emitFile({\n type: 'asset',\n fileName: emittedFilename,\n source: serialize(payload),\n })\n\n if (emittedRefreshFilename) {\n this.emitFile({\n type: 'asset',\n fileName: emittedRefreshFilename,\n source: refreshHtmlDocument(),\n })\n }\n\n if (options.log) {\n console.log(options.log(payload))\n } else {\n console.log(`[${PLUGIN_NAME}][build finished]`)\n console.log(serialize(payload))\n }\n },\n }\n\n Object.defineProperty(plugin, 'transformHtml', { value: transformHtml })\n Object.defineProperty(plugin, 'headScript', { value: headScript })\n\n return plugin as BuildVersionPlugin\n}\n\nexport default buildVersionPlugin\n"],"mappings":"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"}
|