vite-plugin-build-version-file 0.1.18 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,8 +1,216 @@
1
1
  // src/index.ts
2
2
  import { existsSync, readFileSync } from "fs";
3
3
  import { dirname, join, resolve } from "path";
4
+
5
+ // src/loading.html
6
+ var loading_default = `<!doctype html>
7
+ <html lang="zh-CN">
8
+ <head>
9
+ <meta charset="UTF-8" />
10
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
11
+ <title>Loading</title>
12
+ <style>
13
+ html,
14
+ body {
15
+ margin: 0;
16
+ height: 100%;
17
+ }
18
+
19
+ #vite-build-version-loading {
20
+ position: fixed;
21
+ inset: 0;
22
+ z-index: 2147483647;
23
+ display: flex;
24
+ flex-direction: column;
25
+ align-items: center;
26
+ justify-content: center;
27
+ gap: 16px;
28
+ color: #333333;
29
+ font: 14px/1.5 sans-serif;
30
+ }
31
+
32
+ #vite-build-version-loading .spinner {
33
+ width: 36px;
34
+ height: 36px;
35
+ border: 3px solid rgba(0, 0, 0, 0.15);
36
+ border-top-color: rgba(0, 0, 0, 0.6);
37
+ border-radius: 50%;
38
+ animation: vite-build-version-loading 0.8s linear infinite;
39
+ }
40
+
41
+ @keyframes vite-build-version-loading {
42
+ to {
43
+ transform: rotate(360deg);
44
+ }
45
+ }
46
+ </style>
47
+ </head>
48
+ <body>
49
+ <div id="vite-build-version-loading">
50
+ <div class="spinner"></div>
51
+ </div>
52
+ <script>
53
+ ;(function () {
54
+ var IFRAME_ID = 'vite-plugin-build-version-file-cache-iframe'
55
+ var IFRAME_TIMEOUT = 5000
56
+ var DEFAULT_REFRESH_TIME = 5
57
+ var DEFAULT_CLEAR_TIMES = 2
58
+
59
+ var params = new URLSearchParams(window.location.search)
60
+ // \u53C2\u6570\u540D\u968F\u673A\u751F\u6210\uFF0Cloading \u9875\u6309\u503C\u89E3\u6790\uFF1A\u4EFB\u4F55\u503C\u662F JSON \u7684\u53C2\u6570\u90FD\u89C6\u4E3A\u6E05\u7406\u914D\u7F6E\u3002
61
+ var config = (function () {
62
+ var entries = Array.from(params.entries())
63
+
64
+ for (var i = 0; i < entries.length; i++) {
65
+ try {
66
+ var value = JSON.parse(entries[i][1])
67
+
68
+ if (value && typeof value === 'object' && Array.isArray(value.urls)) {
69
+ return value
70
+ }
71
+ } catch (error) {
72
+ // \u975E JSON \u53C2\u6570\uFF08\u4F8B\u5982\u81EA\u5B9A\u4E49 query\uFF09\u76F4\u63A5\u8DF3\u8FC7\u3002
73
+ }
74
+ }
75
+
76
+ return {}
77
+ })()
78
+ // \u9ED8\u8BA4\u628A\u672C\u9875\u9762\u6240\u5728\u76EE\u5F55\u5F53\u4F5C\u5E94\u7528\u6839\u8DEF\u5F84\u3002
79
+ var directoryUrl = new URL('.', window.location.href).href
80
+ var urls = Array.isArray(config.urls)
81
+ ? config.urls.filter(Boolean)
82
+ : []
83
+ var time = Number(config.time)
84
+ var times = Math.floor(Number(config.times))
85
+ var redirectUrl = config.redirectUrl || directoryUrl
86
+ var title = config.title
87
+
88
+ if (urls.length === 0) {
89
+ urls = [directoryUrl, new URL('index.html', directoryUrl).href]
90
+ }
91
+
92
+ if (!Number.isFinite(time) || time <= 0) {
93
+ time = DEFAULT_REFRESH_TIME
94
+ }
95
+
96
+ if (!Number.isFinite(times) || times < 1) {
97
+ times = DEFAULT_CLEAR_TIMES
98
+ }
99
+
100
+ if (title) {
101
+ var titleElement = document.createElement('div')
102
+ titleElement.textContent = title
103
+ document.getElementById('vite-build-version-loading').appendChild(titleElement)
104
+ }
105
+
106
+ function createCacheBustKey() {
107
+ // \u53C2\u6570\u540D\u968F\u673A\uFF0C\u907F\u514D\u670D\u52A1\u5668\u6216 CDN \u9488\u5BF9\u56FA\u5B9A\u53C2\u6570\u540D\uFF08\u5982 _\u3001t\uFF09\u505A\u7F13\u5B58\u5904\u7406\u3002
108
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 8)
109
+ }
110
+
111
+ function createCacheBustedUrl(url) {
112
+ var targetUrl = new URL(url, window.location.href)
113
+ var hash = targetUrl.hash
114
+
115
+ // hash \u4E0D\u53C2\u4E0E HTTP \u8BF7\u6C42\uFF0C\u5148\u4FDD\u7559\u4E1A\u52A1\u8DEF\u7531\uFF0C\u518D\u5411\u771F\u5B9E\u8BF7\u6C42 URL \u5199\u5165\u7F13\u5B58\u6233\u3002
116
+ targetUrl.hash = ''
117
+ targetUrl.searchParams.set(
118
+ createCacheBustKey(),
119
+ Date.now() + '-' + Math.random().toString(36).slice(2)
120
+ )
121
+ targetUrl.hash = hash
122
+
123
+ return targetUrl.href
124
+ }
125
+
126
+ function loadIframe(url) {
127
+ return new Promise(function (resolve) {
128
+ // \u540C\u4E00\u9875\u9762\u53EA\u4FDD\u7559\u4E00\u4E2A\u7F13\u5B58\u5237\u65B0 iframe\uFF0C\u521B\u5EFA\u524D\u5148\u9500\u6BC1\u65E7\u5B9E\u4F8B\u3002
129
+ var existing = document.getElementById(IFRAME_ID)
130
+
131
+ if (existing) {
132
+ existing.remove()
133
+ }
134
+
135
+ var iframe = document.createElement('iframe')
136
+ var frameTimeout = 0
137
+ var completed = false
138
+
139
+ function complete() {
140
+ if (completed) {
141
+ return
142
+ }
143
+
144
+ completed = true
145
+ window.clearTimeout(frameTimeout)
146
+ resolve()
147
+ }
148
+
149
+ frameTimeout = window.setTimeout(complete, IFRAME_TIMEOUT)
150
+
151
+ iframe.hidden = true
152
+ iframe.id = IFRAME_ID
153
+ iframe.addEventListener('load', complete, { once: true })
154
+ iframe.addEventListener('error', complete, { once: true })
155
+ iframe.src = url
156
+ document.documentElement.appendChild(iframe)
157
+ })
158
+ }
159
+
160
+ function clearTargetUrlCache(url) {
161
+ var targetUrl = new URL(url, window.location.href).href
162
+ console.info('[vite-plugin-build-version] Clear cache for ' + targetUrl)
163
+
164
+ // \u968F\u673A URL \u7ED5\u8FC7\u65E7\u7F13\u5B58\uFF0C\u539F URL \u4F7F\u7528 reload \u6A21\u5F0F\u5237\u65B0\u5BF9\u5E94\u7F13\u5B58\u6761\u76EE\u3002
165
+ // \u7B49\u5F85\u54CD\u5E94\u4F53\u5B8C\u6574\u8BFB\u53D6\uFF0C\u907F\u514D\u540E\u7EED\u8DF3\u8F6C\u63D0\u524D\u53D6\u6D88\u7F13\u5B58\u5237\u65B0\u8BF7\u6C42\u3002
166
+ return Promise.allSettled([
167
+ fetch(createCacheBustedUrl(targetUrl), { cache: 'no-store' }).then(
168
+ function (response) {
169
+ return response.arrayBuffer()
170
+ }
171
+ ),
172
+ fetch(targetUrl, { cache: 'reload' }).then(function (response) {
173
+ return response.arrayBuffer()
174
+ })
175
+ ]).then(function () {
176
+ // iframe \u518D\u89E6\u53D1\u4E00\u6B21\u9875\u9762\u8D44\u6E90\u52A0\u8F7D\uFF0C\u8D85\u65F6\u6216\u5931\u8D25\u7531\u5916\u5C42\u7EE7\u7EED\u5904\u7406\u540E\u7EED URL\u3002
177
+ return loadIframe(createCacheBustedUrl(targetUrl))
178
+ })
179
+ }
180
+
181
+ var refreshTimeoutId = 0
182
+ var clearAllDone = (async function () {
183
+ // \u6574\u5957\u6E05\u7406\u64CD\u4F5C\u6309 times \u91CD\u590D\u6267\u884C\uFF0C\u9ED8\u8BA4 2 \u6B21\u3002
184
+ for (var round = 0; round < times; round++) {
185
+ for (var i = 0; i < urls.length; i++) {
186
+ try {
187
+ await clearTargetUrlCache(urls[i])
188
+ } catch (error) {
189
+ // \u5355\u7EC4\u5931\u8D25\u4E0D\u963B\u65AD\u540E\u7EED URL\uFF0C\u4FDD\u8BC1\u914D\u7F6E\u7684\u68C0\u6D4B\u7EC4\u90FD\u80FD\u6267\u884C\u3002
190
+ }
191
+ }
192
+ }
193
+ })()
194
+ var limitReached = new Promise(function (resolve) {
195
+ // \u5230\u8FBE\u603B\u7B49\u5F85\u65F6\u9650\u540E\u76F4\u63A5\u8DF3\u8F6C\uFF0C\u4E0D\u53D7\u7F13\u5B58\u8BF7\u6C42\u72B6\u6001\u5F71\u54CD\u3002
196
+ refreshTimeoutId = window.setTimeout(resolve, time * 1000)
197
+ })
198
+
199
+ Promise.race([clearAllDone, limitReached]).then(function () {
200
+ window.clearTimeout(refreshTimeoutId)
201
+ // \u6700\u7EC8\u8DF3\u8F6C\u539F\u6837\u4F7F\u7528\u76EE\u6807\u5730\u5740\uFF0C\u4E0D\u9644\u52A0\u7F13\u5B58\u6233\u3002
202
+ window.location.replace(redirectUrl)
203
+ })
204
+ })()
205
+ </script>
206
+ </body>
207
+ </html>
208
+ `;
209
+
210
+ // src/index.ts
4
211
  var PLUGIN_NAME = "vite-plugin-build-version-file";
5
212
  var DEFAULT_FILENAME = "version.json";
213
+ var DEFAULT_LOADING_PAGE = "loading";
6
214
  var DEFAULT_GLOBAL_NAME = "__VERSION__";
7
215
  var DEFAULT_DEFINE_NAME = "__VERSION__";
8
216
  var DEFAULT_TIME_ZONE = "Asia/Shanghai";
@@ -40,6 +248,16 @@ function normalizeFileName(filename) {
40
248
  }
41
249
  return normalized;
42
250
  }
251
+ function normalizeLoadingPage(loadingPage) {
252
+ if (loadingPage === false) {
253
+ return false;
254
+ }
255
+ const normalized = (loadingPage ?? DEFAULT_LOADING_PAGE).trim().replace(/^\/+/, "");
256
+ if (!normalized) {
257
+ throw new Error(`[${PLUGIN_NAME}] "loadingPage" cannot be empty.`);
258
+ }
259
+ return normalized.endsWith(".html") ? normalized : `${normalized}.html`;
260
+ }
43
261
  function normalizeGlobalName(globalName) {
44
262
  if (!globalName.trim()) {
45
263
  throw new Error(`[${PLUGIN_NAME}] "globalName" cannot be empty.`);
@@ -142,6 +360,13 @@ function toRequestPath(base, fileName) {
142
360
  const baseWithoutTrailingSlash = withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
143
361
  return `${baseWithoutTrailingSlash}/${fileName}`.replace(/\/{2,}/g, "/");
144
362
  }
363
+ function resolveNuxtFileName(config, fileName) {
364
+ if (config.appType !== "custom" || config.build.copyPublicDir !== false) {
365
+ return fileName;
366
+ }
367
+ const assetDir = normalizeFileName(config.build.assetsDir);
368
+ return assetDir === "." || fileName.startsWith(`${assetDir}/`) ? fileName : `${assetDir}/${fileName}`;
369
+ }
145
370
  function resolveProjectRoot(root) {
146
371
  return root ? resolve(process.cwd(), root) : process.cwd();
147
372
  }
@@ -168,6 +393,7 @@ function createPayload(options, context, data, timeZone, packageInfo = {}) {
168
393
  }
169
394
  function buildVersionPlugin(options = {}) {
170
395
  const fileName = normalizeFileName(options.filename ?? DEFAULT_FILENAME);
396
+ const loadingPage = normalizeLoadingPage(options.loadingPage);
171
397
  const globalName = normalizeGlobalName(
172
398
  options.globalName ?? DEFAULT_GLOBAL_NAME
173
399
  );
@@ -179,6 +405,8 @@ function buildVersionPlugin(options = {}) {
179
405
  const data = normalizeData(options.data);
180
406
  let payload;
181
407
  let skipBundleEmit = false;
408
+ let bundleFileName = fileName;
409
+ let bundleLoadingFileName = loadingPage;
182
410
  const transformHtml = (code, ..._args) => {
183
411
  if (!injectToHtml || !payload) {
184
412
  return void 0;
@@ -213,6 +441,10 @@ function buildVersionPlugin(options = {}) {
213
441
  },
214
442
  configResolved(config) {
215
443
  skipBundleEmit = Boolean(config.build.ssr) && !config.build.ssrEmitAssets;
444
+ bundleFileName = resolveNuxtFileName(config, fileName);
445
+ if (loadingPage) {
446
+ bundleLoadingFileName = resolveNuxtFileName(config, loadingPage);
447
+ }
216
448
  if (payload) {
217
449
  return;
218
450
  }
@@ -225,11 +457,21 @@ function buildVersionPlugin(options = {}) {
225
457
  },
226
458
  configureServer(server) {
227
459
  const requestPath = toRequestPath(server.config.base, fileName);
460
+ const loadingRequestPath = loadingPage ? toRequestPath(server.config.base, loadingPage) : void 0;
228
461
  server.middlewares.use((request, response, next) => {
229
462
  if (request.method !== "GET" && request.method !== "HEAD") {
230
463
  next();
231
464
  return;
232
465
  }
466
+ if (loadingRequestPath && parsePathname(request.url) === loadingRequestPath) {
467
+ response.statusCode = 200;
468
+ response.setHeader("Content-Type", "text/html; charset=utf-8");
469
+ response.setHeader("Cache-Control", "no-cache");
470
+ response.end(
471
+ request.method === "HEAD" ? void 0 : loading_default
472
+ );
473
+ return;
474
+ }
233
475
  if (parsePathname(request.url) !== requestPath || !payload) {
234
476
  next();
235
477
  return;
@@ -260,9 +502,16 @@ function buildVersionPlugin(options = {}) {
260
502
  }
261
503
  this.emitFile({
262
504
  type: "asset",
263
- fileName,
505
+ fileName: bundleFileName,
264
506
  source: serializePayload(payload)
265
507
  });
508
+ if (bundleLoadingFileName) {
509
+ this.emitFile({
510
+ type: "asset",
511
+ fileName: bundleLoadingFileName,
512
+ source: loading_default
513
+ });
514
+ }
266
515
  if (options.log) {
267
516
  console.log(options.log(payload));
268
517
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-build-version-file",
3
- "version": "0.1.18",
3
+ "version": "0.2.0",
4
4
  "description": "A Vite plugin that emits version.json and injects version metadata into HTML.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,14 +25,6 @@
25
25
  "README.md",
26
26
  "LICENSE"
27
27
  ],
28
- "scripts": {
29
- "build": "tsup --config tsup.config.ts",
30
- "build:fixture": "pnpm build && vite build --config debug/fixture-app/vite.config.ts",
31
- "build:vitepress": "pnpm build && vitepress build debug/vitepress-app",
32
- "dev:fixture": "pnpm build && vite --config debug/fixture-app/vite.config.ts",
33
- "dev:vitepress": "pnpm build && vitepress dev debug/vitepress-app",
34
- "prepublishOnly": "pnpm build:fixture"
35
- },
36
28
  "keywords": [
37
29
  "vite",
38
30
  "vite-plugin",
@@ -58,5 +50,12 @@
58
50
  "vite": "^5.0.0",
59
51
  "vitepress": "^1.6.4",
60
52
  "vue": "^3.4.0"
53
+ },
54
+ "scripts": {
55
+ "build": "tsup --config tsup.config.ts",
56
+ "build:fixture": "pnpm build && vite build --config debug/fixture-app/vite.config.ts",
57
+ "build:vitepress": "pnpm build && vitepress build debug/vitepress-app",
58
+ "dev:fixture": "pnpm build && vite --config debug/fixture-app/vite.config.ts",
59
+ "dev:vitepress": "pnpm build && vitepress dev debug/vitepress-app"
61
60
  }
62
- }
61
+ }