@gitlon/version 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -299,7 +299,7 @@ hash 路由会保留,随机参数写在 `#` 前。该方法仅支持同源 URL
299
299
  window.refreshCache()
300
300
  ```
301
301
 
302
- 等价于 `location.replace('<刷新页路径>?url=' + encodeURIComponent(location.href))`,携带当前完整 URL 并透传 hash;用 `replace` 而非 `href=`,刷新页不会进入历史栈,避免用户后退时被反复弹回。路径已按 Vite `base` 计算。
302
+ 等价于 `location.replace('<刷新页路径>?_=' + Date.now() + '&url=' + encodeURIComponent(location.href))`,携带当前完整 URL 并透传 hash,刷新页请求本身也带时间戳防缓存;用 `replace` 而非 `href=`,刷新页不会进入历史栈,避免用户后退时被反复弹回。路径已按 Vite `base` 计算。
303
303
 
304
304
  该页脚本由插件内联生成,不 import 任何带 hash 的构建产物,所以旧版本页面也能通过它清理缓存后进入新版本。dev server 同样响应该路径。
305
305
 
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
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>
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}?${c}=`,r=`&${s}=`;return`window[${JSON.stringify(e)}]=function(){location.replace(${JSON.stringify(n)}+Date.now()+${JSON.stringify(r)}+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
2
  <html lang="zh-CN">
3
3
  <head>
4
4
  <meta charset="utf-8">
@@ -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 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":"+IAmCA,IAAM,EAAc,kBACd,EAAmB,eACnB,EAAuB,eACvB,EAAsB,eACtB,EAAoB,eACpB,EAAgB,MAChB,EAAmB,IACnB,EAAsB,cACtB,EAAsB,cACtB,EAAoB,gBAE1B,SAAS,EAAkB,EAAkB,EAAS,WAAoB,CACxE,IAAM,EAAQ,EAAS,QAAQ,MAAO,GAAG,CAAC,CAAC,QAAQ,OAAQ,EAAE,EAE7D,GAAI,CAAC,GAAS,EAAM,SAAS,GAAG,EAC9B,MAAU,MAAM,IAAI,EAAY,KAAK,EAAO,6BAA6B,EAG3E,OAAO,CACT,CAEA,SAAS,EACP,EACA,EACQ,CACR,GAAI,CAAC,EAAM,KAAK,EACd,MAAU,MAAM,IAAI,EAAY,KAAK,EAAO,mBAAmB,EAGjE,OAAO,CACT,CAEA,SAAS,EAAkB,EAA0B,CACnD,IAAM,EAAQ,EAAS,KAAK,EAE5B,GAAI,CAAC,EACH,MAAU,MAAM,IAAI,EAAY,8BAA8B,EAGhE,GAAI,CACF,IAAI,KAAK,eAAe,QAAS,CAAE,SAAU,CAAM,CAAC,CAAC,CAAC,OAAO,IAAI,IAAM,CACzE,MAAQ,CACN,MAAU,MAAM,IAAI,EAAY,yBAAyB,CAC3D,CAEA,OAAO,CACT,CAEA,SAAS,EAAc,EAA8D,CACnF,GAAI,IAAS,IAAA,GAAW,MAAO,CAAC,EAEhC,GAAI,CAAC,GAAQ,MAAM,QAAQ,CAAI,GAAK,OAAO,GAAS,SAClD,MAAU,MAAM,IAAI,EAAY,iCAAiC,EAGnE,OAAO,CACT,CAEA,SAAS,EAAqB,EAAyC,EAAkB,CACvF,GAAI,IAAY,QAAS,MAAO,GAEhC,IAAM,EAAQ,IAAI,KAAK,eAAe,QAAS,CAC7C,WACA,KAAM,UACN,MAAO,UACP,IAAK,UACL,KAAM,UACN,OAAQ,UACR,OAAQ,UACR,OAAQ,GACR,UAAW,KACb,CAAC,CAAC,CAAC,cAAc,IAAI,IAAM,EACrB,EAAQ,GACZ,EAAM,KAAM,GAAS,EAAK,OAAS,CAAI,CAAC,EAAE,OAAS,GAErD,MAAO,CACL,EAAK,MAAM,EACX,EAAK,OAAO,EACZ,EAAK,KAAK,EACV,EAAK,MAAM,EACX,EAAK,QAAQ,EACb,EAAK,QAAQ,CACf,CAAC,CAAC,KAAK,EAAE,CACX,CAEA,SAAS,EAAgB,EAAkC,CACzD,IAAI,EAAY,EAEhB,OAAa,CACX,IAAM,GAAA,EAAc,EAAA,KAAA,CAAK,EAAW,cAAc,EAClD,IAAA,EAAI,EAAA,WAAA,CAAW,CAAW,EAAG,OAAO,EAEpC,IAAM,GAAA,EAAS,EAAA,QAAA,CAAQ,CAAS,EAChC,GAAI,IAAW,EAAW,OAC1B,EAAY,CACd,CACF,CAEA,SAAS,EAAgB,EAAmC,CAC1D,IAAM,EAAW,EAAgB,CAAI,EACrC,GAAI,CAAC,EAAU,MAAO,CAAC,EAEvB,GAAI,CACF,IAAM,EAAc,KAAK,OAAA,EAAM,EAAA,aAAA,CAAa,EAAU,MAAM,CAAC,EACvD,EAA8B,CAAC,EAKrC,OAHI,EAAY,OAAS,IAAA,KAAW,EAAO,QAAU,EAAY,MAC7D,EAAY,UAAY,IAAA,KAAW,EAAO,WAAa,EAAY,SAEhE,CACT,MAAQ,CACN,MAAO,CAAC,CACV,CACF,CAEA,SAAS,EAAY,EAAkC,CACrD,OAAO,GAAA,EAAO,EAAA,QAAA,CAAQ,QAAQ,IAAI,EAAG,CAAI,EAAI,QAAQ,IAAI,CAC3D,CAEA,SAAS,EACP,EACA,EACA,EACiB,CAGjB,OAFI,OAAO,EAAQ,SAAY,WAAmB,EAAQ,QAAQ,CAAO,EACrE,EAAQ,UAAY,IAAA,GACjB,EAAqB,EAAQ,QAAS,CAAQ,EADX,EAAQ,OAEpD,CAEA,SAAS,EACP,EACA,EACA,EACA,EACA,EACqB,CACrB,IAAI,EAA+B,CACjC,GAAG,EACH,GAAG,EACH,KAAM,EAAW,EAAS,EAAS,CAAQ,EAC3C,IAAK,EAAQ,IACf,EAGA,OADI,EAAQ,UAAS,EAAU,EAAQ,QAAQ,CAAO,GAC/C,CACT,CAEA,SAAS,EAAU,EAAsC,CACvD,MAAO,GAAG,KAAK,UAAU,EAAS,KAAM,CAAC,EAAE,GAC7C,CAEA,SAAS,EAAW,EAAoB,EAAsC,CAC5E,MAAO,UAAU,KAAK,UAAU,CAAU,EAAE,MAAM,KAAK,UAAU,CAAO,EAAE,EAC5E,CAEA,SAAS,EAAU,EAAsB,CACvC,MAAO,WAAW,EAAK,WACzB,CAEA,SAAS,EAAc,EAAoB,EAA6B,CACtE,IAAM,EAAM,GAAG,EAAY,GAAG,EAAc,GAE5C,MAAO,UAAU,KAAK,UAAU,CAAU,EAAE,gCAAgC,KAAK,UAAU,CAAG,EAAE,sCAClG,CAEA,SAAS,EAAY,EAAkB,EAAqB,EAA4B,CACtF,IAAM,EAAS,KAAK,UAAU,CAAU,EAClC,EAAM,GAAG,EAAY,GAAG,EAAiB,GACzC,EAAS,yCAEf,MAAO,UAAU,KAAK,UAAU,CAAQ,EAAE,sCAAsC,EAAO,iCAAiC,KAAK,UAAU,CAAG,EAAE,yDAAyD,EAAO,0HAA0H,EAAO,IAC/U,CAEA,SAAS,EAAe,EAAc,EAAwB,CAC5D,GAAI,EAAK,SAAS,CAAM,EAAG,OAAO,EAElC,IAAM,EAAM,WAAW,EAAO,YAC9B,OAAO,EAAK,SAAS,SAAS,EAAI,EAAK,QAAQ,UAAW,GAAG,EAAI,QAAQ,EAAI,GAAG,IAAM,GACxF,CAGA,SAAS,GAA8B,CACrC,MAAO;;;;;;;;;;;;;;;;;iBAiBQ,EAAc;iBACd,EAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqFlC,CAEA,SAAS,EAAS,EAAiC,CACjD,OAAO,EAAM,IAAI,IAAI,EAAK,kBAAkB,CAAC,CAAC,SAAW,GAC3D,CAEA,SAAS,EAAY,EAAc,EAA0B,CAC3D,IAAM,EAAU,IAAS,KAAO,IAAM,GAAQ,IACxC,EAAc,EAAQ,WAAW,GAAG,EAAI,EAAU,IAAI,IAE5D,MAAO,GADW,EAAY,SAAS,GAAG,EAAI,EAAY,MAAM,EAAG,EAAE,EAAI,EACrD,GAAG,IAAW,QAAQ,UAAW,GAAG,CAC1D,CAEA,SAAS,EACP,EAIA,EACQ,CACR,GAAI,EAAO,UAAY,UAAY,EAAO,MAAM,gBAAkB,GAAO,OAAO,EAEhF,IAAM,EAAY,EAAkB,EAAO,MAAM,SAAS,EAC1D,OAAO,IAAc,KAAO,EAAS,WAAW,GAAG,EAAU,EAAE,EAC3D,EACA,GAAG,EAAU,GAAG,GACtB,CAEA,SAAgB,EACd,EAAqC,CAAC,EAClB,CACpB,IAAM,EAAW,EAAkB,EAAQ,UAAY,CAAgB,EACjE,EAAa,EACjB,EAAQ,YAAc,EACtB,YACF,EACM,EACJ,EAAQ,aAAe,IAEnB,EAAsB,EAAQ,YAAc,EAAqB,YAAY,EAC7E,EAAe,EAAQ,cAAgB,GACvC,EAAc,EAAQ,aAAe,GACrC,EACJ,IAAgB,GACZ,IAAA,GACA,EACE,IAAgB,GAAO,EAAuB,EAC9C,aACF,EACA,EACJ,IAAoB,IAAA,GAChB,IAAA,GACA,EAAsB,EAAQ,YAAc,EAAqB,YAAY,EAC7E,EAAW,EACf,EAAQ,UAAY,EACpB,UACF,EACM,EAAW,EAAkB,EAAQ,UAAY,CAAiB,EAClE,EAAO,EAAc,EAAQ,IAAI,EACnC,EACA,EAAkB,EAClB,EACA,EAAc,GACd,EAAc,GACd,EAAW,GAET,GAAiB,EAA8B,IAAiB,CACpE,EAAU,EAAY,EAAS,EAAS,EAAM,EAAgB,CAAI,EAAG,CAAQ,CAC/E,EAEM,MACC,EAEE,CACL,EAAW,EAAY,CAAO,EAC9B,GAAc,EAAc,EAAc,EAAY,CAAW,EAAI,GACrE,EAAc,EAAY,EAAU,EAAa,CAAU,EAAI,EACjE,CAAC,CAAC,KAAK,EAAE,EANY,GASjB,EAAiB,GAAqC,CACtD,GAAC,GAAiB,EACtB,OAAO,EAAe,EAAM,EAAe,CAAC,CAC9C,EAEM,MACA,CAAC,GAAgB,CAAC,EAAgB,GAC/B,EAAU,EAAe,CAAC,EAG7B,EAAiB,CACrB,KAAM,EAEN,OAAO,EAAY,EAAa,CAC9B,GAAK,GACH,EACE,CACE,QAAS,EAAY,QACrB,KAAM,EAAY,IACpB,EACA,EAAY,EAAW,IAAI,CAC7B,EAGG,GAAe,EAEpB,MAAO,CACL,OAAQ,EACL,GAAa,KAAK,UAAU,CAAO,CACtC,CACF,CACF,EAEA,eAAe,EAAQ,CACrB,EAAW,EAAQ,EAAO,MAAM,KAAQ,CAAC,EAAO,MAAM,cACtD,EAAkB,EAAa,EAAQ,CAAQ,EAC/C,EAAyB,EACrB,EAAa,EAAQ,CAAe,EACpC,IAAA,GACJ,EAAc,EACV,EAAY,EAAO,KAAM,CAAsB,EAC/C,GACJ,EAAc,EAAY,EAAO,KAAM,CAAe,EAEjD,GACH,EACE,CACE,QAAS,EAAO,QAChB,KAAM,EAAO,IACf,EACA,EAAO,IACT,CAEJ,EAEA,gBAAgB,EAAQ,CACtB,IAAM,EAAa,EAAY,EAAO,OAAO,KAAM,CAAQ,EACrD,EAAc,EAChB,EAAY,EAAO,OAAO,KAAM,CAAe,EAC/C,IAAA,GAEJ,EAAO,YAAY,KAAK,EAAS,EAAU,IAAS,CAClD,GAAI,EAAQ,SAAW,OAAS,EAAQ,SAAW,OAAQ,CACzD,EAAK,EACL,MACF,CAEA,IAAM,EAAgB,EAAS,EAAQ,GAAG,EAE1C,GAAI,GAAe,IAAkB,EAAa,CAChD,EAAS,WAAa,IACtB,EAAS,UAAU,eAAgB,0BAA0B,EAC7D,EAAS,UAAU,gBAAiB,UAAU,EAC9C,EAAS,IACP,EAAQ,SAAW,OAAS,IAAA,GAAY,EAAoB,CAC9D,EACA,MACF,CAEA,GAAI,IAAkB,GAAc,CAAC,EAAS,CAC5C,EAAK,EACL,MACF,CAEA,EAAS,WAAa,IACtB,EAAS,UAAU,eAAgB,iCAAiC,EACpE,EAAS,UAAU,gBAAiB,UAAU,EAC9C,EAAS,IAAI,EAAQ,SAAW,OAAS,IAAA,GAAY,EAAU,CAAO,CAAC,CACzE,CAAC,CACH,EAEA,oBAAqB,CACf,GAAC,GAAiB,EAStB,MAAO,CANL,CACE,IAAK,SACL,SAAU,OACV,SAAU,EAAe,CAC3B,CAEK,CACT,EAEA,gBAAiB,CACV,GAAW,KAEhB,KAAK,SAAS,CACZ,KAAM,QACN,SAAU,EACV,OAAQ,EAAU,CAAO,CAC3B,CAAC,EAEG,GACF,KAAK,SAAS,CACZ,KAAM,QACN,SAAU,EACV,OAAQ,EAAoB,CAC9B,CAAC,EAGC,EAAQ,IACV,QAAQ,IAAI,EAAQ,IAAI,CAAO,CAAC,GAEhC,QAAQ,IAAI,IAAI,EAAY,kBAAkB,EAC9C,QAAQ,IAAI,EAAU,CAAO,CAAC,GAElC,CACF,EAKA,OAHA,OAAO,eAAe,EAAQ,gBAAiB,CAAE,MAAO,CAAc,CAAC,EACvE,OAAO,eAAe,EAAQ,aAAc,CAAE,MAAO,CAAW,CAAC,EAE1D,CACT"}
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}?${CACHE_BUST_PARAM}=`\n const query = `&${REFRESH_QUERY}=`\n\n return `window[${JSON.stringify(refreshKey)}]=function(){location.replace(${JSON.stringify(url)}+Date.now()+${JSON.stringify(query)}+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.js CHANGED
@@ -93,8 +93,8 @@ function D(e) {
93
93
  return `<script>${e}<\/script>`;
94
94
  }
95
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))};`;
96
+ let n = `${t}?${d}=`, r = `&${u}=`;
97
+ return `window[${JSON.stringify(e)}]=function(){location.replace(${JSON.stringify(n)}+Date.now()+${JSON.stringify(r)}+encodeURIComponent(location.href))};`;
98
98
  }
99
99
  function k(e, t, n) {
100
100
  let r = JSON.stringify(n), i = `${t}?${d}=`, a = "{local:local,server:null,update:false}";
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 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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"}
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}?${CACHE_BUST_PARAM}=`\n const query = `&${REFRESH_QUERY}=`\n\n return `window[${JSON.stringify(refreshKey)}]=function(){location.replace(${JSON.stringify(url)}+Date.now()+${JSON.stringify(query)}+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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlon/version",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Vite 构建版本插件:生成 version.json、注入版本信息并提供客户端缓存刷新能力",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",