vite-sw-cacher-plugin 0.0.2 → 0.0.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/dist/index.cjs +8 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -36,9 +36,10 @@ __export(index_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
var import_ejs = __toESM(require("ejs"), 1);
|
|
38
38
|
var import_terser = require("terser");
|
|
39
|
+
var import_mime_types = require("mime-types");
|
|
39
40
|
|
|
40
41
|
// src/templates/sw.ejs
|
|
41
|
-
var sw_default = 'const CACHE_NAME = <%- cacheNameJson %>;\nconst MAX_AGE_MS = <%- ttlMs %>;\nconst MAX_ITEMS = <%- maxItems %>;\nconst EXTENSIONS = <%- extensionsJson %>;\nconst URL_PATTERN = <%- urlPatternSource %>;\n\nself.addEventListener("install", () => self.skipWaiting());\n\nself.addEventListener("activate", (event) => {\n event.waitUntil(cleanUpCache().then(() => self.clients.claim()));\n});\n\nself.addEventListener("fetch", (event) => {\n const { request } = event;\n if (!shouldHandleRequest(request)) return;\n\n event.respondWith(handleRequest(request));\n});\n\nconst shouldHandleRequest = (request) => {\n if (request.method !== "GET") return false;\n const url = request.url;\n return matchesPattern(url)
|
|
42
|
+
var sw_default = 'const CACHE_NAME = <%- cacheNameJson %>;\nconst MAX_AGE_MS = <%- ttlMs %>;\nconst MAX_ITEMS = <%- maxItems %>;\nconst EXTENSIONS = <%- extensionsJson %>;\nconst ALLOWED_CONTENT_TYPES = <%- allowedContentTypesJson %>;\nconst URL_PATTERN = <%- urlPatternSource %>;\n\nself.addEventListener("install", () => self.skipWaiting());\n\nself.addEventListener("activate", (event) => {\n event.waitUntil(cleanUpCache().then(() => self.clients.claim()));\n});\n\nself.addEventListener("fetch", (event) => {\n const { request } = event;\n if (!shouldHandleRequest(request)) return;\n\n event.respondWith(handleRequest(request));\n});\n\nconst shouldHandleRequest = (request) => {\n if (request.method !== "GET") return false;\n const url = request.url;\n return matchesPattern(url);\n};\n\nconst matchesPattern = (url) => {\n if (!URL_PATTERN) return true;\n return URL_PATTERN.test(url);\n};\n\nconst matchesExtension = (url) => {\n if (!EXTENSIONS.length) return true;\n try {\n const pathname = new URL(url).pathname.toLowerCase();\n return EXTENSIONS.some((ext) => pathname.endsWith(ext));\n } catch {\n return false;\n }\n};\n\nconst matchesContentType = (response) => {\n if (!ALLOWED_CONTENT_TYPES.length) return false;\n const header = response.headers.get("content-type");\n if (!header) return false;\n const contentType = header.split(";")[0]?.trim().toLowerCase();\n if (!contentType) return false;\n return ALLOWED_CONTENT_TYPES.includes(contentType);\n};\n\nconst handleRequest = async (request) => {\n try {\n const response = await fetch(request);\n\n if (response && response.ok) {\n const shouldCache =\n matchesExtension(request.url) || matchesContentType(response);\n if (shouldCache) {\n await putInCache(request, response);\n }\n return response;\n }\n\n if (response && response.status === 404) {\n const cached = await caches.match(request);\n return cached || response;\n }\n\n return response;\n } catch (error) {\n const cached = await caches.match(request);\n if (cached) return cached;\n throw error;\n }\n};\n\nconst putInCache = async (request, response) => {\n const cache = await caches.open(CACHE_NAME);\n const headers = new Headers(response.headers);\n headers.set("sw-cache-time", Date.now().toString());\n const body = await response.clone().arrayBuffer();\n const responseToCache = new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n\n await cache.put(request, responseToCache);\n limitCacheSize().catch(() => undefined);\n};\n\nconst cleanUpCache = async () => {\n if (MAX_AGE_MS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n const now = Date.now();\n\n for (const request of keys) {\n const response = await cache.match(request);\n if (!response) continue;\n\n const storedTime = response.headers.get("sw-cache-time");\n const dateHeader = response.headers.get("date");\n const timestamp = storedTime ? Number(storedTime) : dateHeader ? new Date(dateHeader).getTime() : NaN;\n\n if (!Number.isFinite(timestamp)) continue;\n if (now - timestamp > MAX_AGE_MS) {\n await cache.delete(request);\n }\n }\n};\n\nconst limitCacheSize = async () => {\n if (MAX_ITEMS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n\n if (keys.length <= MAX_ITEMS) return;\n const itemsToDelete = keys.slice(0, keys.length - MAX_ITEMS);\n for (const request of itemsToDelete) {\n await cache.delete(request);\n }\n};\n';
|
|
42
43
|
|
|
43
44
|
// src/templates/inline-script.ejs
|
|
44
45
|
var inline_script_default = '(() => {\n if (!("serviceWorker" in navigator)) return;\n window.addEventListener("load", () => {\n navigator.serviceWorker.register(<%- swUrlJson %>).catch((error) => {\n console.warn("[vite-sw-cacher-plugin] SW registration failed", error);\n });\n });\n})();\n';
|
|
@@ -101,11 +102,17 @@ var minifyScript = async (code) => {
|
|
|
101
102
|
var buildServiceWorkerSource = async (options) => {
|
|
102
103
|
const patternSource = options.pattern ? wildcardToRegexSource(options.pattern) : null;
|
|
103
104
|
const urlPatternSource = patternSource ? `new RegExp(${JSON.stringify(patternSource)})` : "null";
|
|
105
|
+
const allowedContentTypes = Array.from(
|
|
106
|
+
new Set(
|
|
107
|
+
options.extensions.map((ext) => (0, import_mime_types.lookup)(ext)).filter((value) => Boolean(value)).map((value) => value.toLowerCase())
|
|
108
|
+
)
|
|
109
|
+
);
|
|
104
110
|
const source = renderTemplate(sw_default, {
|
|
105
111
|
cacheNameJson: JSON.stringify(options.cacheName),
|
|
106
112
|
ttlMs: Math.max(0, options.ttlMs),
|
|
107
113
|
maxItems: options.maxItems,
|
|
108
114
|
extensionsJson: JSON.stringify(options.extensions),
|
|
115
|
+
allowedContentTypesJson: JSON.stringify(allowedContentTypes),
|
|
109
116
|
urlPatternSource
|
|
110
117
|
});
|
|
111
118
|
return minifyScript(source);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/templates/sw.ejs","../src/templates/inline-script.ejs"],"sourcesContent":["import type { OutputAsset, OutputBundle, OutputChunk } from \"rollup\";\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport ejs from \"ejs\";\nimport { minify } from \"terser\";\nimport swTemplate from \"./templates/sw.ejs\";\nimport inlineScriptTemplate from \"./templates/inline-script.ejs\";\n\nexport interface ViteSwCacherPluginOptions {\n extensions?: string[];\n pattern?: string;\n ttl?: number;\n maxItemsCount?: number;\n cacheName?: string;\n}\n\nconst DEFAULT_EXTENSIONS = [\n \".html\",\n \".css\",\n \".js\",\n \".svg\",\n \".png\",\n \".jpeg\",\n \".jpg\",\n \".gif\",\n \".webp\",\n \".avif\",\n \".bmp\",\n \".ico\",\n \".tif\",\n \".tiff\",\n];\n\nconst DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_CACHE_NAME = \"vite-sw-cacher-plugin\";\nconst DEFAULT_SW_FILE_NAME = \"sw-cacher.js\";\n\nconst normalizeExtensions = (extensions?: string[]): string[] => {\n const list = extensions?.length ? extensions : DEFAULT_EXTENSIONS;\n const normalized = list.map((ext) => {\n const trimmed = ext.trim();\n if (!trimmed) return \"\";\n return trimmed.startsWith(\".\") ? trimmed.toLowerCase() : `.${trimmed.toLowerCase()}`;\n });\n\n return Array.from(new Set(normalized.filter(Boolean)));\n};\n\nconst wildcardToRegexSource = (pattern: string): string => {\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return `^${escaped.replace(/\\*/g, \".*\")}$`;\n};\n\nconst countStaticOutputs = (bundle: OutputBundle, extensions: string[]): number => {\n if (!extensions.length) return 0;\n const normalized = extensions.map((ext) => ext.toLowerCase());\n const items = Object.values(bundle) as Array<OutputAsset | OutputChunk>;\n return items.filter((item) => {\n const name = item.fileName.toLowerCase();\n return normalized.some((ext) => name.endsWith(ext));\n }).length;\n};\n\nconst joinBase = (base: string, fileName: string): string => {\n const normalizedBase = base.endsWith(\"/\") ? base : `${base}/`;\n return `${normalizedBase}${fileName}`;\n};\n\nconst renderTemplate = (template: string, data: Record<string, unknown>): string =>\n ejs.render(template, data);\n\nconst minifyScript = async (code: string): Promise<string> => {\n const result = await minify(code, {\n compress: true,\n mangle: true,\n format: { comments: false },\n });\n return result.code ?? code;\n};\n\nconst buildServiceWorkerSource = async (options: {\n cacheName: string;\n ttlMs: number;\n maxItems: number;\n extensions: string[];\n pattern?: string;\n}): Promise<string> => {\n const patternSource = options.pattern ? wildcardToRegexSource(options.pattern) : null;\n const urlPatternSource = patternSource\n ? `new RegExp(${JSON.stringify(patternSource)})`\n : \"null\";\n\n const source = renderTemplate(swTemplate, {\n cacheNameJson: JSON.stringify(options.cacheName),\n ttlMs: Math.max(0, options.ttlMs),\n maxItems: options.maxItems,\n extensionsJson: JSON.stringify(options.extensions),\n urlPatternSource,\n });\n\n return minifyScript(source);\n};\n\nexport const viteSwCacherPlugin = (\n options: ViteSwCacherPluginOptions = {},\n): Plugin => {\n let resolvedConfig: ResolvedConfig | null = null;\n const swFileName = DEFAULT_SW_FILE_NAME;\n\n return {\n name: \"vite-sw-cacher-plugin\",\n apply: \"build\",\n configResolved(config) {\n resolvedConfig = config;\n },\n async transformIndexHtml(html) {\n const base = resolvedConfig?.base ?? \"/\";\n const swUrl = joinBase(base, swFileName);\n const inlineScript = renderTemplate(inlineScriptTemplate, {\n swUrlJson: JSON.stringify(swUrl),\n });\n const minifiedInlineScript = await minifyScript(inlineScript);\n\n return {\n html,\n tags: [\n {\n tag: \"script\",\n injectTo: \"head\",\n children: minifiedInlineScript,\n },\n ],\n };\n },\n async generateBundle(_, bundle) {\n const extensions = normalizeExtensions(options.extensions);\n const staticCount = countStaticOutputs(bundle, extensions);\n const ttlMs = options.ttl ?? DEFAULT_TTL_MS;\n const maxItems =\n options.maxItemsCount ?? staticCount * 2;\n const cacheName = options.cacheName ?? DEFAULT_CACHE_NAME;\n\n const swSource = await buildServiceWorkerSource({\n cacheName,\n ttlMs,\n maxItems,\n extensions,\n pattern: options.pattern,\n });\n\n this.emitFile({\n type: \"asset\",\n fileName: swFileName,\n source: swSource,\n });\n },\n };\n};\n\nexport default viteSwCacherPlugin;\n","const CACHE_NAME = <%- cacheNameJson %>;\nconst MAX_AGE_MS = <%- ttlMs %>;\nconst MAX_ITEMS = <%- maxItems %>;\nconst EXTENSIONS = <%- extensionsJson %>;\nconst URL_PATTERN = <%- urlPatternSource %>;\n\nself.addEventListener(\"install\", () => self.skipWaiting());\n\nself.addEventListener(\"activate\", (event) => {\n event.waitUntil(cleanUpCache().then(() => self.clients.claim()));\n});\n\nself.addEventListener(\"fetch\", (event) => {\n const { request } = event;\n if (!shouldHandleRequest(request)) return;\n\n event.respondWith(handleRequest(request));\n});\n\nconst shouldHandleRequest = (request) => {\n if (request.method !== \"GET\") return false;\n const url = request.url;\n return matchesPattern(url) && matchesExtension(url);\n};\n\nconst matchesPattern = (url) => {\n if (!URL_PATTERN) return true;\n return URL_PATTERN.test(url);\n};\n\nconst matchesExtension = (url) => {\n if (!EXTENSIONS.length) return true;\n try {\n const pathname = new URL(url).pathname.toLowerCase();\n return EXTENSIONS.some((ext) => pathname.endsWith(ext));\n } catch {\n return false;\n }\n};\n\nconst handleRequest = async (request) => {\n try {\n const response = await fetch(request);\n\n if (response && response.ok) {\n await putInCache(request, response);\n return response;\n }\n\n if (response && response.status === 404) {\n const cached = await caches.match(request);\n return cached || response;\n }\n\n return response;\n } catch (error) {\n const cached = await caches.match(request);\n if (cached) return cached;\n throw error;\n }\n};\n\nconst putInCache = async (request, response) => {\n const cache = await caches.open(CACHE_NAME);\n const headers = new Headers(response.headers);\n headers.set(\"sw-cache-time\", Date.now().toString());\n const body = await response.clone().arrayBuffer();\n const responseToCache = new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n\n await cache.put(request, responseToCache);\n limitCacheSize().catch(() => undefined);\n};\n\nconst cleanUpCache = async () => {\n if (MAX_AGE_MS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n const now = Date.now();\n\n for (const request of keys) {\n const response = await cache.match(request);\n if (!response) continue;\n\n const storedTime = response.headers.get(\"sw-cache-time\");\n const dateHeader = response.headers.get(\"date\");\n const timestamp = storedTime ? Number(storedTime) : dateHeader ? new Date(dateHeader).getTime() : NaN;\n\n if (!Number.isFinite(timestamp)) continue;\n if (now - timestamp > MAX_AGE_MS) {\n await cache.delete(request);\n }\n }\n};\n\nconst limitCacheSize = async () => {\n if (MAX_ITEMS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n\n if (keys.length <= MAX_ITEMS) return;\n const itemsToDelete = keys.slice(0, keys.length - MAX_ITEMS);\n for (const request of itemsToDelete) {\n await cache.delete(request);\n }\n};\n","(() => {\n if (!(\"serviceWorker\" in navigator)) return;\n window.addEventListener(\"load\", () => {\n navigator.serviceWorker.register(<%- swUrlJson %>).catch((error) => {\n console.warn(\"[vite-sw-cacher-plugin] SW registration failed\", error);\n });\n });\n})();\n"],"mappings":"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","names":["ejs"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/templates/sw.ejs","../src/templates/inline-script.ejs"],"sourcesContent":["import type { OutputAsset, OutputBundle, OutputChunk } from \"rollup\";\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport ejs from \"ejs\";\nimport { minify } from \"terser\";\nimport { lookup as lookupMimeType } from \"mime-types\";\nimport swTemplate from \"./templates/sw.ejs\";\nimport inlineScriptTemplate from \"./templates/inline-script.ejs\";\n\nexport interface ViteSwCacherPluginOptions {\n extensions?: string[];\n pattern?: string;\n ttl?: number;\n maxItemsCount?: number;\n cacheName?: string;\n}\n\nconst DEFAULT_EXTENSIONS = [\n \".html\",\n \".css\",\n \".js\",\n \".svg\",\n \".png\",\n \".jpeg\",\n \".jpg\",\n \".gif\",\n \".webp\",\n \".avif\",\n \".bmp\",\n \".ico\",\n \".tif\",\n \".tiff\",\n];\n\nconst DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_CACHE_NAME = \"vite-sw-cacher-plugin\";\nconst DEFAULT_SW_FILE_NAME = \"sw-cacher.js\";\n\nconst normalizeExtensions = (extensions?: string[]): string[] => {\n const list = extensions?.length ? extensions : DEFAULT_EXTENSIONS;\n const normalized = list.map((ext) => {\n const trimmed = ext.trim();\n if (!trimmed) return \"\";\n return trimmed.startsWith(\".\") ? trimmed.toLowerCase() : `.${trimmed.toLowerCase()}`;\n });\n\n return Array.from(new Set(normalized.filter(Boolean)));\n};\n\nconst wildcardToRegexSource = (pattern: string): string => {\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return `^${escaped.replace(/\\*/g, \".*\")}$`;\n};\n\nconst countStaticOutputs = (bundle: OutputBundle, extensions: string[]): number => {\n if (!extensions.length) return 0;\n const normalized = extensions.map((ext) => ext.toLowerCase());\n const items = Object.values(bundle) as Array<OutputAsset | OutputChunk>;\n return items.filter((item) => {\n const name = item.fileName.toLowerCase();\n return normalized.some((ext) => name.endsWith(ext));\n }).length;\n};\n\nconst joinBase = (base: string, fileName: string): string => {\n const normalizedBase = base.endsWith(\"/\") ? base : `${base}/`;\n return `${normalizedBase}${fileName}`;\n};\n\nconst renderTemplate = (template: string, data: Record<string, unknown>): string =>\n ejs.render(template, data);\n\nconst minifyScript = async (code: string): Promise<string> => {\n const result = await minify(code, {\n compress: true,\n mangle: true,\n format: { comments: false },\n });\n return result.code ?? code;\n};\n\nconst buildServiceWorkerSource = async (options: {\n cacheName: string;\n ttlMs: number;\n maxItems: number;\n extensions: string[];\n pattern?: string;\n}): Promise<string> => {\n const patternSource = options.pattern ? wildcardToRegexSource(options.pattern) : null;\n const urlPatternSource = patternSource\n ? `new RegExp(${JSON.stringify(patternSource)})`\n : \"null\";\n const allowedContentTypes = Array.from(\n new Set(\n options.extensions\n .map((ext) => lookupMimeType(ext))\n .filter((value): value is string => Boolean(value))\n .map((value) => value.toLowerCase()),\n ),\n );\n\n const source = renderTemplate(swTemplate, {\n cacheNameJson: JSON.stringify(options.cacheName),\n ttlMs: Math.max(0, options.ttlMs),\n maxItems: options.maxItems,\n extensionsJson: JSON.stringify(options.extensions),\n allowedContentTypesJson: JSON.stringify(allowedContentTypes),\n urlPatternSource,\n });\n\n return minifyScript(source);\n};\n\nexport const viteSwCacherPlugin = (\n options: ViteSwCacherPluginOptions = {},\n): Plugin => {\n let resolvedConfig: ResolvedConfig | null = null;\n const swFileName = DEFAULT_SW_FILE_NAME;\n\n return {\n name: \"vite-sw-cacher-plugin\",\n apply: \"build\",\n configResolved(config) {\n resolvedConfig = config;\n },\n async transformIndexHtml(html) {\n const base = resolvedConfig?.base ?? \"/\";\n const swUrl = joinBase(base, swFileName);\n const inlineScript = renderTemplate(inlineScriptTemplate, {\n swUrlJson: JSON.stringify(swUrl),\n });\n const minifiedInlineScript = await minifyScript(inlineScript);\n\n return {\n html,\n tags: [\n {\n tag: \"script\",\n injectTo: \"head\",\n children: minifiedInlineScript,\n },\n ],\n };\n },\n async generateBundle(_, bundle) {\n const extensions = normalizeExtensions(options.extensions);\n const staticCount = countStaticOutputs(bundle, extensions);\n const ttlMs = options.ttl ?? DEFAULT_TTL_MS;\n const maxItems =\n options.maxItemsCount ?? staticCount * 2;\n const cacheName = options.cacheName ?? DEFAULT_CACHE_NAME;\n\n const swSource = await buildServiceWorkerSource({\n cacheName,\n ttlMs,\n maxItems,\n extensions,\n pattern: options.pattern,\n });\n\n this.emitFile({\n type: \"asset\",\n fileName: swFileName,\n source: swSource,\n });\n },\n };\n};\n\nexport default viteSwCacherPlugin;\n","const CACHE_NAME = <%- cacheNameJson %>;\nconst MAX_AGE_MS = <%- ttlMs %>;\nconst MAX_ITEMS = <%- maxItems %>;\nconst EXTENSIONS = <%- extensionsJson %>;\nconst ALLOWED_CONTENT_TYPES = <%- allowedContentTypesJson %>;\nconst URL_PATTERN = <%- urlPatternSource %>;\n\nself.addEventListener(\"install\", () => self.skipWaiting());\n\nself.addEventListener(\"activate\", (event) => {\n event.waitUntil(cleanUpCache().then(() => self.clients.claim()));\n});\n\nself.addEventListener(\"fetch\", (event) => {\n const { request } = event;\n if (!shouldHandleRequest(request)) return;\n\n event.respondWith(handleRequest(request));\n});\n\nconst shouldHandleRequest = (request) => {\n if (request.method !== \"GET\") return false;\n const url = request.url;\n return matchesPattern(url);\n};\n\nconst matchesPattern = (url) => {\n if (!URL_PATTERN) return true;\n return URL_PATTERN.test(url);\n};\n\nconst matchesExtension = (url) => {\n if (!EXTENSIONS.length) return true;\n try {\n const pathname = new URL(url).pathname.toLowerCase();\n return EXTENSIONS.some((ext) => pathname.endsWith(ext));\n } catch {\n return false;\n }\n};\n\nconst matchesContentType = (response) => {\n if (!ALLOWED_CONTENT_TYPES.length) return false;\n const header = response.headers.get(\"content-type\");\n if (!header) return false;\n const contentType = header.split(\";\")[0]?.trim().toLowerCase();\n if (!contentType) return false;\n return ALLOWED_CONTENT_TYPES.includes(contentType);\n};\n\nconst handleRequest = async (request) => {\n try {\n const response = await fetch(request);\n\n if (response && response.ok) {\n const shouldCache =\n matchesExtension(request.url) || matchesContentType(response);\n if (shouldCache) {\n await putInCache(request, response);\n }\n return response;\n }\n\n if (response && response.status === 404) {\n const cached = await caches.match(request);\n return cached || response;\n }\n\n return response;\n } catch (error) {\n const cached = await caches.match(request);\n if (cached) return cached;\n throw error;\n }\n};\n\nconst putInCache = async (request, response) => {\n const cache = await caches.open(CACHE_NAME);\n const headers = new Headers(response.headers);\n headers.set(\"sw-cache-time\", Date.now().toString());\n const body = await response.clone().arrayBuffer();\n const responseToCache = new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n\n await cache.put(request, responseToCache);\n limitCacheSize().catch(() => undefined);\n};\n\nconst cleanUpCache = async () => {\n if (MAX_AGE_MS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n const now = Date.now();\n\n for (const request of keys) {\n const response = await cache.match(request);\n if (!response) continue;\n\n const storedTime = response.headers.get(\"sw-cache-time\");\n const dateHeader = response.headers.get(\"date\");\n const timestamp = storedTime ? Number(storedTime) : dateHeader ? new Date(dateHeader).getTime() : NaN;\n\n if (!Number.isFinite(timestamp)) continue;\n if (now - timestamp > MAX_AGE_MS) {\n await cache.delete(request);\n }\n }\n};\n\nconst limitCacheSize = async () => {\n if (MAX_ITEMS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n\n if (keys.length <= MAX_ITEMS) return;\n const itemsToDelete = keys.slice(0, keys.length - MAX_ITEMS);\n for (const request of itemsToDelete) {\n await cache.delete(request);\n }\n};\n","(() => {\n if (!(\"serviceWorker\" in navigator)) return;\n window.addEventListener(\"load\", () => {\n navigator.serviceWorker.register(<%- swUrlJson %>).catch((error) => {\n console.warn(\"[vite-sw-cacher-plugin] SW registration failed\", error);\n });\n });\n})();\n"],"mappings":"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","names":["ejs","lookupMimeType"]}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import ejs from "ejs";
|
|
3
3
|
import { minify } from "terser";
|
|
4
|
+
import { lookup as lookupMimeType } from "mime-types";
|
|
4
5
|
|
|
5
6
|
// src/templates/sw.ejs
|
|
6
|
-
var sw_default = 'const CACHE_NAME = <%- cacheNameJson %>;\nconst MAX_AGE_MS = <%- ttlMs %>;\nconst MAX_ITEMS = <%- maxItems %>;\nconst EXTENSIONS = <%- extensionsJson %>;\nconst URL_PATTERN = <%- urlPatternSource %>;\n\nself.addEventListener("install", () => self.skipWaiting());\n\nself.addEventListener("activate", (event) => {\n event.waitUntil(cleanUpCache().then(() => self.clients.claim()));\n});\n\nself.addEventListener("fetch", (event) => {\n const { request } = event;\n if (!shouldHandleRequest(request)) return;\n\n event.respondWith(handleRequest(request));\n});\n\nconst shouldHandleRequest = (request) => {\n if (request.method !== "GET") return false;\n const url = request.url;\n return matchesPattern(url)
|
|
7
|
+
var sw_default = 'const CACHE_NAME = <%- cacheNameJson %>;\nconst MAX_AGE_MS = <%- ttlMs %>;\nconst MAX_ITEMS = <%- maxItems %>;\nconst EXTENSIONS = <%- extensionsJson %>;\nconst ALLOWED_CONTENT_TYPES = <%- allowedContentTypesJson %>;\nconst URL_PATTERN = <%- urlPatternSource %>;\n\nself.addEventListener("install", () => self.skipWaiting());\n\nself.addEventListener("activate", (event) => {\n event.waitUntil(cleanUpCache().then(() => self.clients.claim()));\n});\n\nself.addEventListener("fetch", (event) => {\n const { request } = event;\n if (!shouldHandleRequest(request)) return;\n\n event.respondWith(handleRequest(request));\n});\n\nconst shouldHandleRequest = (request) => {\n if (request.method !== "GET") return false;\n const url = request.url;\n return matchesPattern(url);\n};\n\nconst matchesPattern = (url) => {\n if (!URL_PATTERN) return true;\n return URL_PATTERN.test(url);\n};\n\nconst matchesExtension = (url) => {\n if (!EXTENSIONS.length) return true;\n try {\n const pathname = new URL(url).pathname.toLowerCase();\n return EXTENSIONS.some((ext) => pathname.endsWith(ext));\n } catch {\n return false;\n }\n};\n\nconst matchesContentType = (response) => {\n if (!ALLOWED_CONTENT_TYPES.length) return false;\n const header = response.headers.get("content-type");\n if (!header) return false;\n const contentType = header.split(";")[0]?.trim().toLowerCase();\n if (!contentType) return false;\n return ALLOWED_CONTENT_TYPES.includes(contentType);\n};\n\nconst handleRequest = async (request) => {\n try {\n const response = await fetch(request);\n\n if (response && response.ok) {\n const shouldCache =\n matchesExtension(request.url) || matchesContentType(response);\n if (shouldCache) {\n await putInCache(request, response);\n }\n return response;\n }\n\n if (response && response.status === 404) {\n const cached = await caches.match(request);\n return cached || response;\n }\n\n return response;\n } catch (error) {\n const cached = await caches.match(request);\n if (cached) return cached;\n throw error;\n }\n};\n\nconst putInCache = async (request, response) => {\n const cache = await caches.open(CACHE_NAME);\n const headers = new Headers(response.headers);\n headers.set("sw-cache-time", Date.now().toString());\n const body = await response.clone().arrayBuffer();\n const responseToCache = new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n\n await cache.put(request, responseToCache);\n limitCacheSize().catch(() => undefined);\n};\n\nconst cleanUpCache = async () => {\n if (MAX_AGE_MS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n const now = Date.now();\n\n for (const request of keys) {\n const response = await cache.match(request);\n if (!response) continue;\n\n const storedTime = response.headers.get("sw-cache-time");\n const dateHeader = response.headers.get("date");\n const timestamp = storedTime ? Number(storedTime) : dateHeader ? new Date(dateHeader).getTime() : NaN;\n\n if (!Number.isFinite(timestamp)) continue;\n if (now - timestamp > MAX_AGE_MS) {\n await cache.delete(request);\n }\n }\n};\n\nconst limitCacheSize = async () => {\n if (MAX_ITEMS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n\n if (keys.length <= MAX_ITEMS) return;\n const itemsToDelete = keys.slice(0, keys.length - MAX_ITEMS);\n for (const request of itemsToDelete) {\n await cache.delete(request);\n }\n};\n';
|
|
7
8
|
|
|
8
9
|
// src/templates/inline-script.ejs
|
|
9
10
|
var inline_script_default = '(() => {\n if (!("serviceWorker" in navigator)) return;\n window.addEventListener("load", () => {\n navigator.serviceWorker.register(<%- swUrlJson %>).catch((error) => {\n console.warn("[vite-sw-cacher-plugin] SW registration failed", error);\n });\n });\n})();\n';
|
|
@@ -66,11 +67,17 @@ var minifyScript = async (code) => {
|
|
|
66
67
|
var buildServiceWorkerSource = async (options) => {
|
|
67
68
|
const patternSource = options.pattern ? wildcardToRegexSource(options.pattern) : null;
|
|
68
69
|
const urlPatternSource = patternSource ? `new RegExp(${JSON.stringify(patternSource)})` : "null";
|
|
70
|
+
const allowedContentTypes = Array.from(
|
|
71
|
+
new Set(
|
|
72
|
+
options.extensions.map((ext) => lookupMimeType(ext)).filter((value) => Boolean(value)).map((value) => value.toLowerCase())
|
|
73
|
+
)
|
|
74
|
+
);
|
|
69
75
|
const source = renderTemplate(sw_default, {
|
|
70
76
|
cacheNameJson: JSON.stringify(options.cacheName),
|
|
71
77
|
ttlMs: Math.max(0, options.ttlMs),
|
|
72
78
|
maxItems: options.maxItems,
|
|
73
79
|
extensionsJson: JSON.stringify(options.extensions),
|
|
80
|
+
allowedContentTypesJson: JSON.stringify(allowedContentTypes),
|
|
74
81
|
urlPatternSource
|
|
75
82
|
});
|
|
76
83
|
return minifyScript(source);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/templates/sw.ejs","../src/templates/inline-script.ejs"],"sourcesContent":["import type { OutputAsset, OutputBundle, OutputChunk } from \"rollup\";\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport ejs from \"ejs\";\nimport { minify } from \"terser\";\nimport swTemplate from \"./templates/sw.ejs\";\nimport inlineScriptTemplate from \"./templates/inline-script.ejs\";\n\nexport interface ViteSwCacherPluginOptions {\n extensions?: string[];\n pattern?: string;\n ttl?: number;\n maxItemsCount?: number;\n cacheName?: string;\n}\n\nconst DEFAULT_EXTENSIONS = [\n \".html\",\n \".css\",\n \".js\",\n \".svg\",\n \".png\",\n \".jpeg\",\n \".jpg\",\n \".gif\",\n \".webp\",\n \".avif\",\n \".bmp\",\n \".ico\",\n \".tif\",\n \".tiff\",\n];\n\nconst DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_CACHE_NAME = \"vite-sw-cacher-plugin\";\nconst DEFAULT_SW_FILE_NAME = \"sw-cacher.js\";\n\nconst normalizeExtensions = (extensions?: string[]): string[] => {\n const list = extensions?.length ? extensions : DEFAULT_EXTENSIONS;\n const normalized = list.map((ext) => {\n const trimmed = ext.trim();\n if (!trimmed) return \"\";\n return trimmed.startsWith(\".\") ? trimmed.toLowerCase() : `.${trimmed.toLowerCase()}`;\n });\n\n return Array.from(new Set(normalized.filter(Boolean)));\n};\n\nconst wildcardToRegexSource = (pattern: string): string => {\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return `^${escaped.replace(/\\*/g, \".*\")}$`;\n};\n\nconst countStaticOutputs = (bundle: OutputBundle, extensions: string[]): number => {\n if (!extensions.length) return 0;\n const normalized = extensions.map((ext) => ext.toLowerCase());\n const items = Object.values(bundle) as Array<OutputAsset | OutputChunk>;\n return items.filter((item) => {\n const name = item.fileName.toLowerCase();\n return normalized.some((ext) => name.endsWith(ext));\n }).length;\n};\n\nconst joinBase = (base: string, fileName: string): string => {\n const normalizedBase = base.endsWith(\"/\") ? base : `${base}/`;\n return `${normalizedBase}${fileName}`;\n};\n\nconst renderTemplate = (template: string, data: Record<string, unknown>): string =>\n ejs.render(template, data);\n\nconst minifyScript = async (code: string): Promise<string> => {\n const result = await minify(code, {\n compress: true,\n mangle: true,\n format: { comments: false },\n });\n return result.code ?? code;\n};\n\nconst buildServiceWorkerSource = async (options: {\n cacheName: string;\n ttlMs: number;\n maxItems: number;\n extensions: string[];\n pattern?: string;\n}): Promise<string> => {\n const patternSource = options.pattern ? wildcardToRegexSource(options.pattern) : null;\n const urlPatternSource = patternSource\n ? `new RegExp(${JSON.stringify(patternSource)})`\n : \"null\";\n\n const source = renderTemplate(swTemplate, {\n cacheNameJson: JSON.stringify(options.cacheName),\n ttlMs: Math.max(0, options.ttlMs),\n maxItems: options.maxItems,\n extensionsJson: JSON.stringify(options.extensions),\n urlPatternSource,\n });\n\n return minifyScript(source);\n};\n\nexport const viteSwCacherPlugin = (\n options: ViteSwCacherPluginOptions = {},\n): Plugin => {\n let resolvedConfig: ResolvedConfig | null = null;\n const swFileName = DEFAULT_SW_FILE_NAME;\n\n return {\n name: \"vite-sw-cacher-plugin\",\n apply: \"build\",\n configResolved(config) {\n resolvedConfig = config;\n },\n async transformIndexHtml(html) {\n const base = resolvedConfig?.base ?? \"/\";\n const swUrl = joinBase(base, swFileName);\n const inlineScript = renderTemplate(inlineScriptTemplate, {\n swUrlJson: JSON.stringify(swUrl),\n });\n const minifiedInlineScript = await minifyScript(inlineScript);\n\n return {\n html,\n tags: [\n {\n tag: \"script\",\n injectTo: \"head\",\n children: minifiedInlineScript,\n },\n ],\n };\n },\n async generateBundle(_, bundle) {\n const extensions = normalizeExtensions(options.extensions);\n const staticCount = countStaticOutputs(bundle, extensions);\n const ttlMs = options.ttl ?? DEFAULT_TTL_MS;\n const maxItems =\n options.maxItemsCount ?? staticCount * 2;\n const cacheName = options.cacheName ?? DEFAULT_CACHE_NAME;\n\n const swSource = await buildServiceWorkerSource({\n cacheName,\n ttlMs,\n maxItems,\n extensions,\n pattern: options.pattern,\n });\n\n this.emitFile({\n type: \"asset\",\n fileName: swFileName,\n source: swSource,\n });\n },\n };\n};\n\nexport default viteSwCacherPlugin;\n","const CACHE_NAME = <%- cacheNameJson %>;\nconst MAX_AGE_MS = <%- ttlMs %>;\nconst MAX_ITEMS = <%- maxItems %>;\nconst EXTENSIONS = <%- extensionsJson %>;\nconst URL_PATTERN = <%- urlPatternSource %>;\n\nself.addEventListener(\"install\", () => self.skipWaiting());\n\nself.addEventListener(\"activate\", (event) => {\n event.waitUntil(cleanUpCache().then(() => self.clients.claim()));\n});\n\nself.addEventListener(\"fetch\", (event) => {\n const { request } = event;\n if (!shouldHandleRequest(request)) return;\n\n event.respondWith(handleRequest(request));\n});\n\nconst shouldHandleRequest = (request) => {\n if (request.method !== \"GET\") return false;\n const url = request.url;\n return matchesPattern(url) && matchesExtension(url);\n};\n\nconst matchesPattern = (url) => {\n if (!URL_PATTERN) return true;\n return URL_PATTERN.test(url);\n};\n\nconst matchesExtension = (url) => {\n if (!EXTENSIONS.length) return true;\n try {\n const pathname = new URL(url).pathname.toLowerCase();\n return EXTENSIONS.some((ext) => pathname.endsWith(ext));\n } catch {\n return false;\n }\n};\n\nconst handleRequest = async (request) => {\n try {\n const response = await fetch(request);\n\n if (response && response.ok) {\n await putInCache(request, response);\n return response;\n }\n\n if (response && response.status === 404) {\n const cached = await caches.match(request);\n return cached || response;\n }\n\n return response;\n } catch (error) {\n const cached = await caches.match(request);\n if (cached) return cached;\n throw error;\n }\n};\n\nconst putInCache = async (request, response) => {\n const cache = await caches.open(CACHE_NAME);\n const headers = new Headers(response.headers);\n headers.set(\"sw-cache-time\", Date.now().toString());\n const body = await response.clone().arrayBuffer();\n const responseToCache = new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n\n await cache.put(request, responseToCache);\n limitCacheSize().catch(() => undefined);\n};\n\nconst cleanUpCache = async () => {\n if (MAX_AGE_MS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n const now = Date.now();\n\n for (const request of keys) {\n const response = await cache.match(request);\n if (!response) continue;\n\n const storedTime = response.headers.get(\"sw-cache-time\");\n const dateHeader = response.headers.get(\"date\");\n const timestamp = storedTime ? Number(storedTime) : dateHeader ? new Date(dateHeader).getTime() : NaN;\n\n if (!Number.isFinite(timestamp)) continue;\n if (now - timestamp > MAX_AGE_MS) {\n await cache.delete(request);\n }\n }\n};\n\nconst limitCacheSize = async () => {\n if (MAX_ITEMS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n\n if (keys.length <= MAX_ITEMS) return;\n const itemsToDelete = keys.slice(0, keys.length - MAX_ITEMS);\n for (const request of itemsToDelete) {\n await cache.delete(request);\n }\n};\n","(() => {\n if (!(\"serviceWorker\" in navigator)) return;\n window.addEventListener(\"load\", () => {\n navigator.serviceWorker.register(<%- swUrlJson %>).catch((error) => {\n console.warn(\"[vite-sw-cacher-plugin] SW registration failed\", error);\n });\n });\n})();\n"],"mappings":";AAEA,OAAO,SAAS;AAChB,SAAS,cAAc;;;ACHvB;;;ACAA;;;AFeA,IAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,iBAAiB,KAAK,KAAK,KAAK;AACtC,IAAM,qBAAqB;AAC3B,IAAM,uBAAuB;AAE7B,IAAM,sBAAsB,CAAC,eAAoC;AAC/D,QAAM,OAAO,YAAY,SAAS,aAAa;AAC/C,QAAM,aAAa,KAAK,IAAI,CAAC,QAAQ;AACnC,UAAM,UAAU,IAAI,KAAK;AACzB,QAAI,CAAC,QAAS,QAAO;AACrB,WAAO,QAAQ,WAAW,GAAG,IAAI,QAAQ,YAAY,IAAI,IAAI,QAAQ,YAAY,CAAC;AAAA,EACpF,CAAC;AAED,SAAO,MAAM,KAAK,IAAI,IAAI,WAAW,OAAO,OAAO,CAAC,CAAC;AACvD;AAEA,IAAM,wBAAwB,CAAC,YAA4B;AACzD,QAAM,UAAU,QAAQ,QAAQ,sBAAsB,MAAM;AAC5D,SAAO,IAAI,QAAQ,QAAQ,OAAO,IAAI,CAAC;AACzC;AAEA,IAAM,qBAAqB,CAAC,QAAsB,eAAiC;AACjF,MAAI,CAAC,WAAW,OAAQ,QAAO;AAC/B,QAAM,aAAa,WAAW,IAAI,CAAC,QAAQ,IAAI,YAAY,CAAC;AAC5D,QAAM,QAAQ,OAAO,OAAO,MAAM;AAClC,SAAO,MAAM,OAAO,CAAC,SAAS;AAC5B,UAAM,OAAO,KAAK,SAAS,YAAY;AACvC,WAAO,WAAW,KAAK,CAAC,QAAQ,KAAK,SAAS,GAAG,CAAC;AAAA,EACpD,CAAC,EAAE;AACL;AAEA,IAAM,WAAW,CAAC,MAAc,aAA6B;AAC3D,QAAM,iBAAiB,KAAK,SAAS,GAAG,IAAI,OAAO,GAAG,IAAI;AAC1D,SAAO,GAAG,cAAc,GAAG,QAAQ;AACrC;AAEA,IAAM,iBAAiB,CAAC,UAAkB,SACxC,IAAI,OAAO,UAAU,IAAI;AAE3B,IAAM,eAAe,OAAO,SAAkC;AAC5D,QAAM,SAAS,MAAM,OAAO,MAAM;AAAA,IAChC,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,QAAQ,EAAE,UAAU,MAAM;AAAA,EAC5B,CAAC;AACD,SAAO,OAAO,QAAQ;AACxB;AAEA,IAAM,2BAA2B,OAAO,YAMjB;AACrB,QAAM,gBAAgB,QAAQ,UAAU,sBAAsB,QAAQ,OAAO,IAAI;AACjF,QAAM,mBAAmB,gBACrB,cAAc,KAAK,UAAU,aAAa,CAAC,MAC3C;AAEJ,QAAM,SAAS,eAAe,YAAY;AAAA,IACxC,eAAe,KAAK,UAAU,QAAQ,SAAS;AAAA,IAC/C,OAAO,KAAK,IAAI,GAAG,QAAQ,KAAK;AAAA,IAChC,UAAU,QAAQ;AAAA,IAClB,gBAAgB,KAAK,UAAU,QAAQ,UAAU;AAAA,IACjD;AAAA,EACF,CAAC;AAED,SAAO,aAAa,MAAM;AAC5B;AAEO,IAAM,qBAAqB,CAChC,UAAqC,CAAC,MAC3B;AACX,MAAI,iBAAwC;AAC5C,QAAM,aAAa;AAEnB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,eAAe,QAAQ;AACrB,uBAAiB;AAAA,IACnB;AAAA,IACA,MAAM,mBAAmB,MAAM;AAC7B,YAAM,OAAO,gBAAgB,QAAQ;AACrC,YAAM,QAAQ,SAAS,MAAM,UAAU;AACvC,YAAM,eAAe,eAAe,uBAAsB;AAAA,QACxD,WAAW,KAAK,UAAU,KAAK;AAAA,MACjC,CAAC;AACD,YAAM,uBAAuB,MAAM,aAAa,YAAY;AAE5D,aAAO;AAAA,QACL;AAAA,QACA,MAAM;AAAA,UACJ;AAAA,YACE,KAAK;AAAA,YACL,UAAU;AAAA,YACV,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,eAAe,GAAG,QAAQ;AAC9B,YAAM,aAAa,oBAAoB,QAAQ,UAAU;AACzD,YAAM,cAAc,mBAAmB,QAAQ,UAAU;AACzD,YAAM,QAAQ,QAAQ,OAAO;AAC7B,YAAM,WACJ,QAAQ,iBAAiB,cAAc;AACzC,YAAM,YAAY,QAAQ,aAAa;AAEvC,YAAM,WAAW,MAAM,yBAAyB;AAAA,QAC9C;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,QAAQ;AAAA,MACnB,CAAC;AAED,WAAK,SAAS;AAAA,QACZ,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/templates/sw.ejs","../src/templates/inline-script.ejs"],"sourcesContent":["import type { OutputAsset, OutputBundle, OutputChunk } from \"rollup\";\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport ejs from \"ejs\";\nimport { minify } from \"terser\";\nimport { lookup as lookupMimeType } from \"mime-types\";\nimport swTemplate from \"./templates/sw.ejs\";\nimport inlineScriptTemplate from \"./templates/inline-script.ejs\";\n\nexport interface ViteSwCacherPluginOptions {\n extensions?: string[];\n pattern?: string;\n ttl?: number;\n maxItemsCount?: number;\n cacheName?: string;\n}\n\nconst DEFAULT_EXTENSIONS = [\n \".html\",\n \".css\",\n \".js\",\n \".svg\",\n \".png\",\n \".jpeg\",\n \".jpg\",\n \".gif\",\n \".webp\",\n \".avif\",\n \".bmp\",\n \".ico\",\n \".tif\",\n \".tiff\",\n];\n\nconst DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_CACHE_NAME = \"vite-sw-cacher-plugin\";\nconst DEFAULT_SW_FILE_NAME = \"sw-cacher.js\";\n\nconst normalizeExtensions = (extensions?: string[]): string[] => {\n const list = extensions?.length ? extensions : DEFAULT_EXTENSIONS;\n const normalized = list.map((ext) => {\n const trimmed = ext.trim();\n if (!trimmed) return \"\";\n return trimmed.startsWith(\".\") ? trimmed.toLowerCase() : `.${trimmed.toLowerCase()}`;\n });\n\n return Array.from(new Set(normalized.filter(Boolean)));\n};\n\nconst wildcardToRegexSource = (pattern: string): string => {\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return `^${escaped.replace(/\\*/g, \".*\")}$`;\n};\n\nconst countStaticOutputs = (bundle: OutputBundle, extensions: string[]): number => {\n if (!extensions.length) return 0;\n const normalized = extensions.map((ext) => ext.toLowerCase());\n const items = Object.values(bundle) as Array<OutputAsset | OutputChunk>;\n return items.filter((item) => {\n const name = item.fileName.toLowerCase();\n return normalized.some((ext) => name.endsWith(ext));\n }).length;\n};\n\nconst joinBase = (base: string, fileName: string): string => {\n const normalizedBase = base.endsWith(\"/\") ? base : `${base}/`;\n return `${normalizedBase}${fileName}`;\n};\n\nconst renderTemplate = (template: string, data: Record<string, unknown>): string =>\n ejs.render(template, data);\n\nconst minifyScript = async (code: string): Promise<string> => {\n const result = await minify(code, {\n compress: true,\n mangle: true,\n format: { comments: false },\n });\n return result.code ?? code;\n};\n\nconst buildServiceWorkerSource = async (options: {\n cacheName: string;\n ttlMs: number;\n maxItems: number;\n extensions: string[];\n pattern?: string;\n}): Promise<string> => {\n const patternSource = options.pattern ? wildcardToRegexSource(options.pattern) : null;\n const urlPatternSource = patternSource\n ? `new RegExp(${JSON.stringify(patternSource)})`\n : \"null\";\n const allowedContentTypes = Array.from(\n new Set(\n options.extensions\n .map((ext) => lookupMimeType(ext))\n .filter((value): value is string => Boolean(value))\n .map((value) => value.toLowerCase()),\n ),\n );\n\n const source = renderTemplate(swTemplate, {\n cacheNameJson: JSON.stringify(options.cacheName),\n ttlMs: Math.max(0, options.ttlMs),\n maxItems: options.maxItems,\n extensionsJson: JSON.stringify(options.extensions),\n allowedContentTypesJson: JSON.stringify(allowedContentTypes),\n urlPatternSource,\n });\n\n return minifyScript(source);\n};\n\nexport const viteSwCacherPlugin = (\n options: ViteSwCacherPluginOptions = {},\n): Plugin => {\n let resolvedConfig: ResolvedConfig | null = null;\n const swFileName = DEFAULT_SW_FILE_NAME;\n\n return {\n name: \"vite-sw-cacher-plugin\",\n apply: \"build\",\n configResolved(config) {\n resolvedConfig = config;\n },\n async transformIndexHtml(html) {\n const base = resolvedConfig?.base ?? \"/\";\n const swUrl = joinBase(base, swFileName);\n const inlineScript = renderTemplate(inlineScriptTemplate, {\n swUrlJson: JSON.stringify(swUrl),\n });\n const minifiedInlineScript = await minifyScript(inlineScript);\n\n return {\n html,\n tags: [\n {\n tag: \"script\",\n injectTo: \"head\",\n children: minifiedInlineScript,\n },\n ],\n };\n },\n async generateBundle(_, bundle) {\n const extensions = normalizeExtensions(options.extensions);\n const staticCount = countStaticOutputs(bundle, extensions);\n const ttlMs = options.ttl ?? DEFAULT_TTL_MS;\n const maxItems =\n options.maxItemsCount ?? staticCount * 2;\n const cacheName = options.cacheName ?? DEFAULT_CACHE_NAME;\n\n const swSource = await buildServiceWorkerSource({\n cacheName,\n ttlMs,\n maxItems,\n extensions,\n pattern: options.pattern,\n });\n\n this.emitFile({\n type: \"asset\",\n fileName: swFileName,\n source: swSource,\n });\n },\n };\n};\n\nexport default viteSwCacherPlugin;\n","const CACHE_NAME = <%- cacheNameJson %>;\nconst MAX_AGE_MS = <%- ttlMs %>;\nconst MAX_ITEMS = <%- maxItems %>;\nconst EXTENSIONS = <%- extensionsJson %>;\nconst ALLOWED_CONTENT_TYPES = <%- allowedContentTypesJson %>;\nconst URL_PATTERN = <%- urlPatternSource %>;\n\nself.addEventListener(\"install\", () => self.skipWaiting());\n\nself.addEventListener(\"activate\", (event) => {\n event.waitUntil(cleanUpCache().then(() => self.clients.claim()));\n});\n\nself.addEventListener(\"fetch\", (event) => {\n const { request } = event;\n if (!shouldHandleRequest(request)) return;\n\n event.respondWith(handleRequest(request));\n});\n\nconst shouldHandleRequest = (request) => {\n if (request.method !== \"GET\") return false;\n const url = request.url;\n return matchesPattern(url);\n};\n\nconst matchesPattern = (url) => {\n if (!URL_PATTERN) return true;\n return URL_PATTERN.test(url);\n};\n\nconst matchesExtension = (url) => {\n if (!EXTENSIONS.length) return true;\n try {\n const pathname = new URL(url).pathname.toLowerCase();\n return EXTENSIONS.some((ext) => pathname.endsWith(ext));\n } catch {\n return false;\n }\n};\n\nconst matchesContentType = (response) => {\n if (!ALLOWED_CONTENT_TYPES.length) return false;\n const header = response.headers.get(\"content-type\");\n if (!header) return false;\n const contentType = header.split(\";\")[0]?.trim().toLowerCase();\n if (!contentType) return false;\n return ALLOWED_CONTENT_TYPES.includes(contentType);\n};\n\nconst handleRequest = async (request) => {\n try {\n const response = await fetch(request);\n\n if (response && response.ok) {\n const shouldCache =\n matchesExtension(request.url) || matchesContentType(response);\n if (shouldCache) {\n await putInCache(request, response);\n }\n return response;\n }\n\n if (response && response.status === 404) {\n const cached = await caches.match(request);\n return cached || response;\n }\n\n return response;\n } catch (error) {\n const cached = await caches.match(request);\n if (cached) return cached;\n throw error;\n }\n};\n\nconst putInCache = async (request, response) => {\n const cache = await caches.open(CACHE_NAME);\n const headers = new Headers(response.headers);\n headers.set(\"sw-cache-time\", Date.now().toString());\n const body = await response.clone().arrayBuffer();\n const responseToCache = new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n\n await cache.put(request, responseToCache);\n limitCacheSize().catch(() => undefined);\n};\n\nconst cleanUpCache = async () => {\n if (MAX_AGE_MS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n const now = Date.now();\n\n for (const request of keys) {\n const response = await cache.match(request);\n if (!response) continue;\n\n const storedTime = response.headers.get(\"sw-cache-time\");\n const dateHeader = response.headers.get(\"date\");\n const timestamp = storedTime ? Number(storedTime) : dateHeader ? new Date(dateHeader).getTime() : NaN;\n\n if (!Number.isFinite(timestamp)) continue;\n if (now - timestamp > MAX_AGE_MS) {\n await cache.delete(request);\n }\n }\n};\n\nconst limitCacheSize = async () => {\n if (MAX_ITEMS <= 0) return;\n const cache = await caches.open(CACHE_NAME);\n const keys = await cache.keys();\n\n if (keys.length <= MAX_ITEMS) return;\n const itemsToDelete = keys.slice(0, keys.length - MAX_ITEMS);\n for (const request of itemsToDelete) {\n await cache.delete(request);\n }\n};\n","(() => {\n if (!(\"serviceWorker\" in navigator)) return;\n window.addEventListener(\"load\", () => {\n navigator.serviceWorker.register(<%- swUrlJson %>).catch((error) => {\n console.warn(\"[vite-sw-cacher-plugin] SW registration failed\", error);\n });\n });\n})();\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-sw-cacher-plugin",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Vite 7 plugin that injects a SW cacher",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"types": "dist/index.d.ts",
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"ejs": "^3.1.10",
|
|
20
|
+
"mime-types": "^2.1.35",
|
|
20
21
|
"terser": "^5.36.0"
|
|
21
22
|
},
|
|
22
23
|
"files": [
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@types/ejs": "^3.1.5",
|
|
38
|
+
"@types/mime-types": "^2.1.4",
|
|
37
39
|
"@types/node": "^22.10.0",
|
|
38
40
|
"rollup": "^4.0.0",
|
|
39
41
|
"tsup": "^8.0.0",
|