index.mjs 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
  2. get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
  3. }) : x)(function(x) {
  4. if (typeof require !== "undefined")
  5. return require.apply(this, arguments);
  6. throw new Error('Dynamic require of "' + x + '" is not supported');
  7. });
  8. // src/context.ts
  9. function createContext(userOptions) {
  10. return {
  11. userOptions,
  12. options: void 0,
  13. viteConfig: void 0,
  14. useImportRegister: false,
  15. devEnvironment: false
  16. };
  17. }
  18. // src/constants.ts
  19. var FILE_SW_REGISTER = "registerSW.js";
  20. var VIRTUAL_MODULES_MAP = {
  21. "virtual:pwa-register": "register",
  22. "virtual:pwa-register/vue": "vue",
  23. "virtual:pwa-register/svelte": "svelte",
  24. "virtual:pwa-register/react": "react",
  25. "virtual:pwa-register/preact": "preact",
  26. "virtual:pwa-register/solid": "solid"
  27. };
  28. var VIRTUAL_MODULES_RESOLVE_PREFIX = "/@vite-plugin-pwa/";
  29. var VIRTUAL_MODULES = Object.keys(VIRTUAL_MODULES_MAP);
  30. var defaultInjectManifestVitePlugins = [
  31. "alias",
  32. "commonjs",
  33. "vite:resolve",
  34. "vite:esbuild",
  35. "replace",
  36. "vite:define",
  37. "rollup-plugin-dynamic-import-variables",
  38. "vite:esbuild-transpile",
  39. "vite:json",
  40. "vite:terser"
  41. ];
  42. var PWA_INFO_VIRTUAL = "virtual:pwa-info";
  43. var RESOLVED_PWA_INFO_VIRTUAL = `\0${PWA_INFO_VIRTUAL}`;
  44. var DEV_SW_NAME = "dev-sw.js?dev-sw";
  45. var DEV_SW_VIRTUAL = `${VIRTUAL_MODULES_RESOLVE_PREFIX}pwa-entry-point-loaded`;
  46. var RESOLVED_DEV_SW_VIRTUAL = `\0${DEV_SW_VIRTUAL}`;
  47. var DEV_READY_NAME = "vite-pwa-plugin:dev-ready";
  48. var DEV_REGISTER_SW_NAME = "vite-plugin-pwa:register-sw";
  49. // src/html.ts
  50. function generateSimpleSWRegister(options2, dev) {
  51. const path = dev ? `${options2.base}${DEV_SW_NAME}` : `${options2.base}${options2.filename}`;
  52. if (dev) {
  53. const swType = options2.devOptions.type ?? "classic";
  54. return `if('serviceWorker' in navigator) navigator.serviceWorker.register('${path}', { scope: '${options2.scope}', type: '${swType}' })`;
  55. }
  56. return `
  57. if('serviceWorker' in navigator) {
  58. window.addEventListener('load', () => {
  59. navigator.serviceWorker.register('${path}', { scope: '${options2.scope}' })
  60. })
  61. }`.replace(/\n/g, "");
  62. }
  63. function injectServiceWorker(html, options2, dev) {
  64. const manifest = generateWebManifest(options2, dev);
  65. if (!dev) {
  66. const script = generateRegisterSW(options2, dev);
  67. if (script) {
  68. return html.replace(
  69. "</head>",
  70. `${manifest}${script}</head>`
  71. );
  72. }
  73. }
  74. return html.replace(
  75. "</head>",
  76. `${manifest}</head>`
  77. );
  78. }
  79. function generateWebManifest(options2, dev) {
  80. const crossorigin = options2.useCredentials ? ' crossorigin="use-credentials"' : "";
  81. if (dev) {
  82. const name = options2.devOptions.webManifestUrl ?? `${options2.base}${options2.manifestFilename}`;
  83. return options2.manifest ? `<link rel="manifest" href="${name}"${crossorigin}>` : "";
  84. } else {
  85. return options2.manifest ? `<link rel="manifest" href="${options2.base}${options2.manifestFilename}"${crossorigin}>` : "";
  86. }
  87. }
  88. function generateRegisterSW(options2, dev) {
  89. if (options2.injectRegister === "inline")
  90. return `<script id="vite-plugin-pwa:inline-sw">${generateSimpleSWRegister(options2, dev)}<\/script>`;
  91. else if (options2.injectRegister === "script")
  92. return `<script id="vite-plugin-pwa:register-sw" src="${options2.base}${FILE_SW_REGISTER}"><\/script>`;
  93. return void 0;
  94. }
  95. function generateRegisterDevSW() {
  96. return `<script id="vite-plugin-pwa:register-dev-sw" type="module">
  97. import registerDevSW from '${DEV_SW_VIRTUAL}';
  98. registerDevSW();
  99. <\/script>`;
  100. }
  101. function generateSWHMR() {
  102. return `
  103. import.meta.hot.on('${DEV_REGISTER_SW_NAME}', ({ inline, inlinePath, registerPath, scope, swType = 'classic' }) => {
  104. if (inline) {
  105. if('serviceWorker' in navigator) {
  106. navigator.serviceWorker.register(inlinePath, { scope, type: swType });
  107. }
  108. }
  109. else {
  110. const registerSW = document.createElement('script');
  111. registerSW.setAttribute('id', 'vite-plugin-pwa:register-sw');
  112. registerSW.setAttribute('src', registerPath);
  113. document.head.appendChild(registerSW);
  114. }
  115. });
  116. function registerDevSW() {
  117. try {
  118. import.meta.hot.send('${DEV_READY_NAME}');
  119. } catch (e) {
  120. console.error('unable to send ${DEV_READY_NAME} message to register service worker in dev mode!', e);
  121. }
  122. }
  123. export default registerDevSW;
  124. `;
  125. }
  126. // src/api.ts
  127. import { resolve as resolve2 } from "path";
  128. import { existsSync } from "fs";
  129. // src/modules.ts
  130. import { dirname, resolve } from "path";
  131. import { promises as fs } from "fs";
  132. import { fileURLToPath } from "url";
  133. // src/log.ts
  134. import { relative } from "path";
  135. // node_modules/.pnpm/kolorist@1.6.0/node_modules/kolorist/dist/esm/index.mjs
  136. var enabled = true;
  137. var globalVar = typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {};
  138. var supportLevel = 0;
  139. if (globalVar.process && globalVar.process.env && globalVar.process.stdout) {
  140. const { FORCE_COLOR, NODE_DISABLE_COLORS, TERM } = globalVar.process.env;
  141. if (NODE_DISABLE_COLORS || FORCE_COLOR === "0") {
  142. enabled = false;
  143. } else if (FORCE_COLOR === "1") {
  144. enabled = true;
  145. } else if (TERM === "dumb") {
  146. enabled = false;
  147. } else if ("CI" in globalVar.process.env && [
  148. "TRAVIS",
  149. "CIRCLECI",
  150. "APPVEYOR",
  151. "GITLAB_CI",
  152. "GITHUB_ACTIONS",
  153. "BUILDKITE",
  154. "DRONE"
  155. ].some((vendor) => vendor in globalVar.process.env)) {
  156. enabled = true;
  157. } else {
  158. enabled = process.stdout.isTTY;
  159. }
  160. if (enabled) {
  161. supportLevel = TERM && TERM.endsWith("-256color") ? 2 : 1;
  162. }
  163. }
  164. var options = {
  165. enabled,
  166. supportLevel
  167. };
  168. function kolorist(start, end, level = 1) {
  169. const open = `\x1B[${start}m`;
  170. const close = `\x1B[${end}m`;
  171. const regex = new RegExp(`\\x1b\\[${end}m`, "g");
  172. return (str) => {
  173. return options.enabled && options.supportLevel >= level ? open + ("" + str).replace(regex, open) + close : "" + str;
  174. };
  175. }
  176. var reset = kolorist(0, 0);
  177. var bold = kolorist(1, 22);
  178. var dim = kolorist(2, 22);
  179. var italic = kolorist(3, 23);
  180. var underline = kolorist(4, 24);
  181. var inverse = kolorist(7, 27);
  182. var hidden = kolorist(8, 28);
  183. var strikethrough = kolorist(9, 29);
  184. var black = kolorist(30, 39);
  185. var red = kolorist(31, 39);
  186. var green = kolorist(32, 39);
  187. var yellow = kolorist(33, 39);
  188. var blue = kolorist(34, 39);
  189. var magenta = kolorist(35, 39);
  190. var cyan = kolorist(36, 39);
  191. var white = kolorist(97, 39);
  192. var gray = kolorist(90, 39);
  193. var lightGray = kolorist(37, 39);
  194. var lightRed = kolorist(91, 39);
  195. var lightGreen = kolorist(92, 39);
  196. var lightYellow = kolorist(93, 39);
  197. var lightBlue = kolorist(94, 39);
  198. var lightMagenta = kolorist(95, 39);
  199. var lightCyan = kolorist(96, 39);
  200. var bgBlack = kolorist(40, 49);
  201. var bgRed = kolorist(41, 49);
  202. var bgGreen = kolorist(42, 49);
  203. var bgYellow = kolorist(43, 49);
  204. var bgBlue = kolorist(44, 49);
  205. var bgMagenta = kolorist(45, 49);
  206. var bgCyan = kolorist(46, 49);
  207. var bgWhite = kolorist(107, 49);
  208. var bgGray = kolorist(100, 49);
  209. var bgLightRed = kolorist(101, 49);
  210. var bgLightGreen = kolorist(102, 49);
  211. var bgLightYellow = kolorist(103, 49);
  212. var bgLightBlue = kolorist(104, 49);
  213. var bgLightMagenta = kolorist(105, 49);
  214. var bgLightCyan = kolorist(106, 49);
  215. var bgLightGray = kolorist(47, 49);
  216. // package.json
  217. var version = "0.14.0";
  218. // src/log.ts
  219. function logWorkboxResult(strategy, buildResult, viteOptions) {
  220. const { root, logLevel = "info" } = viteOptions;
  221. if (logLevel === "silent")
  222. return;
  223. const { count, size, filePaths, warnings } = buildResult;
  224. if (logLevel === "info") {
  225. console.info([
  226. "",
  227. `${cyan(`PWA v${version}`)}`,
  228. `mode ${magenta(strategy)}`,
  229. `precache ${green(`${count} entries`)} ${dim(`(${(size / 1024).toFixed(2)} KiB)`)}`,
  230. "files generated",
  231. ...filePaths.map((p) => ` ${dim(relative(root, p))}`)
  232. ].join("\n"));
  233. }
  234. warnings && warnings.length > 0 && console.warn(yellow([
  235. "warnings",
  236. ...warnings.map((w) => ` ${w}`),
  237. ""
  238. ].join("\n")));
  239. }
  240. // src/modules.ts
  241. var _dirname = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
  242. async function loadWorkboxBuild() {
  243. try {
  244. const workbox = await import("workbox-build");
  245. return workbox.default ?? workbox;
  246. } catch (_) {
  247. return __require("workbox-build");
  248. }
  249. }
  250. async function loadRollupReplacePlugin() {
  251. try {
  252. const { createRequire } = await import("module").then((m) => m.default || m);
  253. const nodeRequire = createRequire(_dirname);
  254. return nodeRequire("@rollup/plugin-replace");
  255. } catch (_) {
  256. return __require("@rollup/plugin-replace");
  257. }
  258. }
  259. async function generateRegisterSW2(options2, mode, source = "register") {
  260. const sw = options2.base + options2.filename;
  261. const scope = options2.scope;
  262. const content = await fs.readFile(resolve(_dirname, `client/${mode}/${source}.mjs`), "utf-8");
  263. return content.replace(/__SW__/g, sw).replace("__SCOPE__", scope).replace("__SW_AUTO_UPDATE__", `${options2.registerType === "autoUpdate"}`).replace("__SW_SELF_DESTROYING__", `${options2.selfDestroying}`).replace("__TYPE__", `${options2.devOptions.enabled ? options2.devOptions.type : "classic"}`);
  264. }
  265. async function generateServiceWorker(options2, viteOptions) {
  266. if (options2.selfDestroying) {
  267. const selfDestroyingSW = `
  268. self.addEventListener('install', function(e) {
  269. self.skipWaiting();
  270. });
  271. self.addEventListener('activate', function(e) {
  272. self.registration.unregister()
  273. .then(function() {
  274. return self.clients.matchAll();
  275. })
  276. .then(function(clients) {
  277. clients.forEach(client => client.navigate(client.url))
  278. });
  279. });
  280. `;
  281. await fs.writeFile(options2.swDest.replace(/\\/g, "/"), selfDestroyingSW, { encoding: "utf8" });
  282. return {
  283. count: 1,
  284. size: selfDestroyingSW.length,
  285. warnings: [],
  286. filePaths: [options2.filename]
  287. };
  288. }
  289. const { generateSW } = await loadWorkboxBuild();
  290. const buildResult = await generateSW(options2.workbox);
  291. logWorkboxResult("generateSW", buildResult, viteOptions);
  292. return buildResult;
  293. }
  294. async function generateInjectManifest(options2, viteOptions) {
  295. const { selfDestroying } = options2;
  296. if (selfDestroying) {
  297. await generateServiceWorker(options2, viteOptions);
  298. return;
  299. }
  300. const vitePlugins = options2.vitePlugins;
  301. const includedPluginNames = [];
  302. if (typeof vitePlugins === "function")
  303. includedPluginNames.push(...vitePlugins(viteOptions.plugins.map((p) => p.name)));
  304. else
  305. includedPluginNames.push(...vitePlugins);
  306. if (includedPluginNames.length === 0)
  307. includedPluginNames.push(...defaultInjectManifestVitePlugins);
  308. const replace = await loadRollupReplacePlugin();
  309. const plugins = [
  310. replace({
  311. "preventAssignment": true,
  312. "process.env.NODE_ENV": JSON.stringify(options2.mode)
  313. }),
  314. ...viteOptions.plugins.filter((p) => includedPluginNames.includes(p.name))
  315. ];
  316. const { rollup } = await import("rollup");
  317. const bundle = await rollup({
  318. input: options2.swSrc,
  319. plugins
  320. });
  321. try {
  322. await bundle.write({
  323. format: options2.rollupFormat,
  324. exports: "none",
  325. inlineDynamicImports: true,
  326. file: options2.injectManifest.swDest,
  327. sourcemap: viteOptions.build.sourcemap
  328. });
  329. } finally {
  330. await bundle.close();
  331. }
  332. if (!options2.injectManifest.injectionPoint)
  333. return;
  334. const injectManifestOptions = {
  335. ...options2.injectManifest,
  336. swSrc: options2.injectManifest.swDest
  337. };
  338. const { injectManifest } = await loadWorkboxBuild();
  339. const buildResult = await injectManifest(injectManifestOptions);
  340. logWorkboxResult("injectManifest", buildResult, viteOptions);
  341. }
  342. // src/assets.ts
  343. import { resolve as resolveFs } from "path";
  344. import fs2 from "fs";
  345. import crypto from "crypto";
  346. import fg from "fast-glob";
  347. function buildManifestEntry(publicDir, url) {
  348. return new Promise((resolve5, reject) => {
  349. const cHash = crypto.createHash("MD5");
  350. const stream = fs2.createReadStream(resolveFs(publicDir, url));
  351. stream.on("error", (err) => {
  352. reject(err);
  353. });
  354. stream.on("data", (chunk) => {
  355. cHash.update(chunk);
  356. });
  357. stream.on("end", () => {
  358. return resolve5({
  359. url,
  360. revision: `${cHash.digest("hex")}`
  361. });
  362. });
  363. });
  364. }
  365. function lookupAdditionalManifestEntries(useInjectManifest, injectManifest, workbox) {
  366. return useInjectManifest ? injectManifest.additionalManifestEntries || [] : workbox.additionalManifestEntries || [];
  367. }
  368. function normalizeIconPath(path) {
  369. return path.startsWith("/") ? path.substring(1) : path;
  370. }
  371. function includeIcons(icons, globs) {
  372. Object.keys(icons).forEach((key) => {
  373. const icon = icons[key];
  374. const src = normalizeIconPath(icon.src);
  375. if (!globs.includes(src))
  376. globs.push(src);
  377. });
  378. }
  379. async function configureStaticAssets(resolvedVitePWAOptions, viteConfig) {
  380. const {
  381. manifest,
  382. strategies,
  383. injectManifest,
  384. workbox,
  385. includeAssets,
  386. includeManifestIcons,
  387. manifestFilename
  388. } = resolvedVitePWAOptions;
  389. const useInjectManifest = strategies === "injectManifest";
  390. const { publicDir } = viteConfig;
  391. const globs = [];
  392. const manifestEntries = lookupAdditionalManifestEntries(
  393. useInjectManifest,
  394. injectManifest,
  395. workbox
  396. );
  397. if (includeAssets) {
  398. if (Array.isArray(includeAssets))
  399. globs.push(...includeAssets.map(normalizeIconPath));
  400. else
  401. globs.push(normalizeIconPath(includeAssets));
  402. }
  403. if (includeManifestIcons && manifest) {
  404. manifest.icons && includeIcons(manifest.icons, globs);
  405. manifest.shortcuts && manifest.shortcuts.forEach((s) => {
  406. s.icons && includeIcons(s.icons, globs);
  407. });
  408. }
  409. if (globs.length > 0) {
  410. let assets = await fg(
  411. globs,
  412. {
  413. cwd: publicDir,
  414. onlyFiles: true,
  415. unique: true
  416. }
  417. );
  418. if (manifestEntries.length > 0) {
  419. const included = manifestEntries.map((me) => {
  420. if (typeof me === "string")
  421. return me;
  422. else
  423. return me.url;
  424. });
  425. assets = assets.filter((a) => !included.includes(a));
  426. }
  427. const assetsEntries = await Promise.all(assets.map((a) => {
  428. return buildManifestEntry(publicDir, a);
  429. }));
  430. manifestEntries.push(...assetsEntries);
  431. }
  432. if (manifest) {
  433. const cHash = crypto.createHash("MD5");
  434. cHash.update(generateWebManifestFile(resolvedVitePWAOptions));
  435. manifestEntries.push({
  436. url: manifestFilename,
  437. revision: `${cHash.digest("hex")}`
  438. });
  439. }
  440. if (manifestEntries.length > 0) {
  441. if (useInjectManifest)
  442. injectManifest.additionalManifestEntries = manifestEntries;
  443. else
  444. workbox.additionalManifestEntries = manifestEntries;
  445. }
  446. }
  447. function generateWebManifestFile(options2) {
  448. return `${JSON.stringify(options2.manifest, null, options2.minify ? 0 : 2)}
  449. `;
  450. }
  451. // src/api.ts
  452. async function _generateSW({ options: options2, viteConfig }) {
  453. if (options2.disable)
  454. return;
  455. if (options2.strategies === "injectManifest")
  456. await generateInjectManifest(options2, viteConfig);
  457. else
  458. await generateServiceWorker(options2, viteConfig);
  459. }
  460. function _generateBundle({ options: options2, viteConfig, useImportRegister }, bundle) {
  461. if (options2.disable || !bundle)
  462. return;
  463. if (options2.manifest) {
  464. bundle[options2.manifestFilename] = {
  465. isAsset: true,
  466. type: "asset",
  467. name: void 0,
  468. source: generateWebManifestFile(options2),
  469. fileName: options2.manifestFilename
  470. };
  471. }
  472. if (options2.injectRegister === "auto")
  473. options2.injectRegister = useImportRegister ? null : "script";
  474. if (options2.injectRegister === "script" && !existsSync(resolve2(viteConfig.publicDir, FILE_SW_REGISTER))) {
  475. bundle[FILE_SW_REGISTER] = {
  476. isAsset: true,
  477. type: "asset",
  478. name: void 0,
  479. source: generateSimpleSWRegister(options2, false),
  480. fileName: FILE_SW_REGISTER
  481. };
  482. }
  483. return bundle;
  484. }
  485. function createAPI(ctx) {
  486. return {
  487. get disabled() {
  488. var _a;
  489. return (_a = ctx == null ? void 0 : ctx.options) == null ? void 0 : _a.disable;
  490. },
  491. get pwaInDevEnvironment() {
  492. return (ctx == null ? void 0 : ctx.devEnvironment) === true;
  493. },
  494. webManifestData() {
  495. const options2 = ctx == null ? void 0 : ctx.options;
  496. if (!options2 || options2.disable || !options2.manifest || ctx.devEnvironment && !ctx.options.devOptions.enabled)
  497. return void 0;
  498. let url = options2.manifestFilename;
  499. let manifest;
  500. if (ctx.devEnvironment && ctx.options.devOptions.enabled === true) {
  501. url = ctx.options.devOptions.webManifestUrl ?? options2.manifestFilename;
  502. manifest = generateWebManifest(options2, true);
  503. } else {
  504. manifest = generateWebManifest(options2, false);
  505. }
  506. return {
  507. href: `${options2.base}${url}`,
  508. useCredentials: ctx.options.useCredentials,
  509. toLinkTag() {
  510. return manifest;
  511. }
  512. };
  513. },
  514. registerSWData() {
  515. const options2 = ctx == null ? void 0 : ctx.options;
  516. if (!options2 || options2.disable || ctx.devEnvironment && !ctx.options.devOptions.enabled)
  517. return void 0;
  518. const mode = options2.injectRegister;
  519. if (!mode || ctx.useImportRegister)
  520. return void 0;
  521. let type = "classic";
  522. let script;
  523. let shouldRegisterSW = options2.injectRegister === "inline" || options2.injectRegister === "script";
  524. if (ctx.devEnvironment && ctx.options.devOptions.enabled === true) {
  525. type = ctx.options.devOptions.type ?? "classic";
  526. script = generateRegisterDevSW();
  527. shouldRegisterSW = true;
  528. } else if (shouldRegisterSW) {
  529. script = generateRegisterSW(options2, false);
  530. }
  531. return {
  532. shouldRegisterSW,
  533. inline: options2.injectRegister === "inline",
  534. scope: options2.scope,
  535. inlinePath: `${options2.base}${ctx.devEnvironment ? DEV_SW_NAME : options2.filename}`,
  536. registerPath: `${options2.base}${FILE_SW_REGISTER}`,
  537. type,
  538. toScriptTag() {
  539. return script;
  540. }
  541. };
  542. },
  543. generateBundle(bundle) {
  544. return _generateBundle(ctx, bundle);
  545. },
  546. async generateSW() {
  547. return await _generateSW(ctx);
  548. },
  549. extendManifestEntries(fn) {
  550. const { options: options2 } = ctx;
  551. if (options2.disable)
  552. return;
  553. const configField = options2.strategies === "generateSW" ? "workbox" : "injectManifest";
  554. const result = fn(options2[configField].additionalManifestEntries || []);
  555. if (result != null)
  556. options2[configField].additionalManifestEntries = result;
  557. }
  558. };
  559. }
  560. // src/plugins/build.ts
  561. function BuildPlugin(ctx) {
  562. var _a, _b;
  563. return {
  564. name: "vite-plugin-pwa:build",
  565. enforce: "post",
  566. apply: "build",
  567. transformIndexHtml: {
  568. enforce: "post",
  569. transform(html) {
  570. const { options: options2, useImportRegister } = ctx;
  571. if (options2.disable)
  572. return html;
  573. if (options2.injectRegister === "auto")
  574. options2.injectRegister = useImportRegister ? null : "script";
  575. return injectServiceWorker(html, options2, false);
  576. }
  577. },
  578. generateBundle(_, bundle) {
  579. return _generateBundle(ctx, bundle);
  580. },
  581. closeBundle: {
  582. sequential: true,
  583. order: (_b = (_a = ctx.userOptions) == null ? void 0 : _a.integration) == null ? void 0 : _b.closeBundleOrder,
  584. async handler() {
  585. if (!ctx.viteConfig.build.ssr && !ctx.options.disable)
  586. await _generateSW(ctx);
  587. }
  588. },
  589. async buildEnd(error) {
  590. if (error)
  591. throw error;
  592. }
  593. };
  594. }
  595. // src/plugins/dev.ts
  596. import { basename, resolve as resolve3 } from "path";
  597. import { existsSync as existsSync2, promises as fs3, mkdirSync } from "fs";
  598. // src/utils.ts
  599. function resolveBathPath(base) {
  600. if (isAbsolute(base))
  601. return base;
  602. return !base.startsWith("/") && !base.startsWith("./") ? `/${base}` : base;
  603. }
  604. function isAbsolute(url) {
  605. return url.match(/^(?:[a-z]+:)?\/\//i);
  606. }
  607. function normalizePath(path) {
  608. return path.replace(/\\/g, "/");
  609. }
  610. // src/plugins/dev.ts
  611. var swDevOptions = {
  612. swUrl: DEV_SW_NAME,
  613. swDevGenerated: false,
  614. workboxPaths: /* @__PURE__ */ new Map()
  615. };
  616. function DevPlugin(ctx) {
  617. return {
  618. name: "vite-plugin-pwa:dev-sw",
  619. apply: "serve",
  620. transformIndexHtml: {
  621. enforce: "post",
  622. async transform(html) {
  623. const { options: options2 } = ctx;
  624. if (options2.disable || !options2.manifest || !options2.devOptions.enabled)
  625. return html;
  626. html = injectServiceWorker(html, options2, true);
  627. return html.replace(
  628. "</body>",
  629. `${generateRegisterDevSW()}
  630. </body>`
  631. );
  632. }
  633. },
  634. configureServer(server) {
  635. ctx.devEnvironment = true;
  636. const { options: options2 } = ctx;
  637. if (!options2.disable && options2.manifest && options2.devOptions.enabled) {
  638. server.ws.on(DEV_READY_NAME, createSWResponseHandler(server, ctx));
  639. const name = options2.devOptions.webManifestUrl ?? `${options2.base}${options2.manifestFilename}`;
  640. server.middlewares.use((req, res, next) => {
  641. if (req.url === name) {
  642. res.statusCode = 200;
  643. res.setHeader("Content-Type", "application/manifest+json");
  644. res.write(generateWebManifestFile(options2), "utf-8");
  645. res.end();
  646. } else {
  647. next();
  648. }
  649. });
  650. }
  651. },
  652. resolveId(id) {
  653. if (id === DEV_SW_VIRTUAL)
  654. return RESOLVED_DEV_SW_VIRTUAL;
  655. const { options: options2 } = ctx;
  656. if (!options2.disable && options2.devOptions.enabled && options2.strategies === "injectManifest" && !options2.selfDestroying) {
  657. const name = id.startsWith("/") ? id.slice(1) : id;
  658. return name === swDevOptions.swUrl || name === options2.injectManifest.swSrc ? options2.injectManifest.swSrc : void 0;
  659. }
  660. return void 0;
  661. },
  662. async load(id) {
  663. if (id === RESOLVED_DEV_SW_VIRTUAL)
  664. return generateSWHMR();
  665. const { options: options2, viteConfig } = ctx;
  666. if (!options2.disable && options2.devOptions.enabled) {
  667. if (options2.strategies === "injectManifest" && !options2.selfDestroying) {
  668. const swSrc = normalizePath(options2.injectManifest.swSrc);
  669. if (id === swSrc) {
  670. let content = await fs3.readFile(options2.injectManifest.swSrc, "utf-8");
  671. const resolvedIP = options2.injectManifest.injectionPoint;
  672. if (resolvedIP) {
  673. const ip = new RegExp(resolvedIP, "g");
  674. const navigateFallback = options2.devOptions.navigateFallback;
  675. if (navigateFallback)
  676. content = content.replace(ip, `[{ url: '${navigateFallback}' }]`);
  677. else
  678. content = content.replace(ip, "[]");
  679. }
  680. return content;
  681. }
  682. if (swDevOptions.workboxPaths.has(id))
  683. return await fs3.readFile(swDevOptions.workboxPaths.get(id), "utf-8");
  684. return void 0;
  685. }
  686. if (id.endsWith(swDevOptions.swUrl)) {
  687. const globDirectory = resolve3(viteConfig.root, "dev-dist");
  688. if (!existsSync2(globDirectory))
  689. mkdirSync(globDirectory);
  690. const swDest = resolve3(globDirectory, "sw.js");
  691. if (!swDevOptions.swDevGenerated || !existsSync2(swDest)) {
  692. const navigateFallback = options2.workbox.navigateFallback;
  693. const { filePaths } = await generateServiceWorker(
  694. Object.assign(
  695. {},
  696. options2,
  697. {
  698. swDest: options2.selfDestroying ? swDest : options2.swDest,
  699. workbox: {
  700. ...options2.workbox,
  701. navigateFallbackAllowlist: options2.devOptions.navigateFallbackAllowlist ?? [/^\/$/],
  702. runtimeCaching: options2.devOptions.disableRuntimeConfig ? void 0 : options2.workbox.runtimeCaching,
  703. additionalManifestEntries: navigateFallback ? [navigateFallback] : void 0,
  704. cleanupOutdatedCaches: true,
  705. globDirectory: globDirectory.replace(/\\/g, "/"),
  706. swDest: swDest.replace(/\\/g, "/")
  707. }
  708. }
  709. ),
  710. viteConfig
  711. );
  712. filePaths.forEach((we) => {
  713. const name = basename(we);
  714. if (name !== "sw.js")
  715. swDevOptions.workboxPaths.set(normalizePath(`${options2.base}${name}`), we);
  716. });
  717. swDevOptions.swDevGenerated = true;
  718. }
  719. return await fs3.readFile(swDest, "utf-8");
  720. }
  721. const key = normalizePath(`${options2.base}${id.startsWith("/") ? id.slice(1) : id}`);
  722. if (swDevOptions.workboxPaths.has(key))
  723. return await fs3.readFile(swDevOptions.workboxPaths.get(key), "utf-8");
  724. }
  725. }
  726. };
  727. }
  728. async function createDevRegisterSW(options2, viteConfig) {
  729. if (options2.injectRegister === "script") {
  730. const devDist = resolve3(viteConfig.root, "dev-dist");
  731. if (!existsSync2(devDist))
  732. mkdirSync(devDist);
  733. const registerSW = resolve3(devDist, FILE_SW_REGISTER);
  734. if (existsSync2(registerSW)) {
  735. if (!swDevOptions.workboxPaths.has(registerSW))
  736. swDevOptions.workboxPaths.set(normalizePath(`${options2.base}${FILE_SW_REGISTER}`), registerSW);
  737. return;
  738. }
  739. await fs3.writeFile(registerSW, generateSimpleSWRegister(options2, true), { encoding: "utf8" });
  740. swDevOptions.workboxPaths.set(normalizePath(`${options2.base}${FILE_SW_REGISTER}`), registerSW);
  741. }
  742. }
  743. function createSWResponseHandler(server, ctx) {
  744. return async () => {
  745. const { options: options2, useImportRegister } = ctx;
  746. const { injectRegister, scope, base } = options2;
  747. if (!useImportRegister && injectRegister) {
  748. if (injectRegister === "auto")
  749. options2.injectRegister = "script";
  750. await createDevRegisterSW(options2, ctx.viteConfig);
  751. server.ws.send({
  752. type: "custom",
  753. event: DEV_REGISTER_SW_NAME,
  754. data: {
  755. inline: options2.injectRegister === "inline",
  756. scope,
  757. inlinePath: `${base}${DEV_SW_NAME}`,
  758. registerPath: `${base}${FILE_SW_REGISTER}`,
  759. swType: options2.devOptions.type
  760. }
  761. });
  762. }
  763. };
  764. }
  765. // src/options.ts
  766. import fs4 from "fs";
  767. import { extname, resolve as resolve4 } from "path";
  768. function resolveSwPaths(injectManifest, root, srcDir, outDir, filename) {
  769. const swSrc = resolve4(root, srcDir, filename);
  770. if (injectManifest && extname(filename) === ".ts" && fs4.existsSync(swSrc)) {
  771. const useFilename = `${filename.substring(0, filename.lastIndexOf("."))}.js`;
  772. return {
  773. swSrc,
  774. swDest: resolve4(root, outDir, useFilename),
  775. useFilename
  776. };
  777. }
  778. return {
  779. swSrc,
  780. swDest: resolve4(root, outDir, filename)
  781. };
  782. }
  783. async function resolveOptions(options2, viteConfig) {
  784. var _a;
  785. const root = viteConfig.root;
  786. const pkg = fs4.existsSync("package.json") ? JSON.parse(fs4.readFileSync("package.json", "utf-8")) : {};
  787. const {
  788. mode = process["env"]["NODE_ENV"] || "production",
  789. srcDir = "public",
  790. outDir = viteConfig.build.outDir || "dist",
  791. injectRegister = "auto",
  792. registerType = "prompt",
  793. filename = "sw.js",
  794. manifestFilename = "manifest.webmanifest",
  795. strategies = "generateSW",
  796. minify = true,
  797. base = viteConfig.base,
  798. includeAssets = void 0,
  799. includeManifestIcons = true,
  800. useCredentials = false,
  801. disable = false,
  802. devOptions = { enabled: false, type: "classic" },
  803. selfDestroying = false,
  804. integration = {}
  805. } = options2;
  806. const basePath = resolveBathPath(base);
  807. const { swSrc, swDest, useFilename } = resolveSwPaths(
  808. strategies === "injectManifest",
  809. root,
  810. srcDir,
  811. outDir,
  812. filename
  813. );
  814. const outDirRoot = resolve4(root, outDir);
  815. const scope = options2.scope || basePath;
  816. const defaultWorkbox = {
  817. swDest,
  818. globDirectory: outDirRoot,
  819. offlineGoogleAnalytics: false,
  820. cleanupOutdatedCaches: true,
  821. dontCacheBustURLsMatching: /[.-][a-f0-9]{8}\./,
  822. mode,
  823. navigateFallback: "index.html"
  824. };
  825. const defaultInjectManifest = {
  826. swSrc,
  827. swDest,
  828. globDirectory: outDirRoot,
  829. dontCacheBustURLsMatching: /[.-][a-f0-9]{8}\./,
  830. injectionPoint: "self.__WB_MANIFEST"
  831. };
  832. const defaultManifest = {
  833. name: pkg.name,
  834. short_name: pkg.name,
  835. start_url: basePath,
  836. display: "standalone",
  837. background_color: "#ffffff",
  838. lang: "en",
  839. scope
  840. };
  841. const workbox = Object.assign({}, defaultWorkbox, options2.workbox || {});
  842. const manifest = typeof options2.manifest === "boolean" && !options2.manifest ? false : Object.assign({}, defaultManifest, options2.manifest || {});
  843. const {
  844. vitePlugins = defaultInjectManifestVitePlugins,
  845. rollupFormat = "es",
  846. ...userInjectManifest
  847. } = options2.injectManifest || {};
  848. const injectManifest = Object.assign({}, defaultInjectManifest, userInjectManifest);
  849. if ((injectRegister === "auto" || injectRegister == null) && registerType === "autoUpdate") {
  850. workbox.skipWaiting = true;
  851. workbox.clientsClaim = true;
  852. }
  853. if (strategies === "generateSW" && workbox.sourcemap === void 0) {
  854. const sourcemap = (_a = viteConfig.build) == null ? void 0 : _a.sourcemap;
  855. workbox.sourcemap = sourcemap === true || sourcemap === "inline" || sourcemap === "hidden";
  856. }
  857. if (devOptions.enabled && viteConfig.command === "serve") {
  858. if (strategies === "generateSW")
  859. devOptions.type = "classic";
  860. } else {
  861. devOptions.enabled = false;
  862. devOptions.type = "classic";
  863. }
  864. const resolvedVitePWAOptions = {
  865. base: basePath,
  866. mode,
  867. swSrc,
  868. swDest,
  869. srcDir,
  870. outDir,
  871. injectRegister,
  872. registerType,
  873. filename: useFilename || filename,
  874. manifestFilename,
  875. strategies,
  876. workbox,
  877. manifest,
  878. useCredentials,
  879. injectManifest,
  880. scope,
  881. minify,
  882. includeAssets,
  883. includeManifestIcons,
  884. disable,
  885. integration,
  886. devOptions,
  887. rollupFormat,
  888. vitePlugins,
  889. selfDestroying
  890. };
  891. await configureStaticAssets(resolvedVitePWAOptions, viteConfig);
  892. return resolvedVitePWAOptions;
  893. }
  894. // src/plugins/main.ts
  895. function MainPlugin(ctx, api) {
  896. return {
  897. name: "vite-plugin-pwa",
  898. enforce: "pre",
  899. config() {
  900. return {
  901. ssr: {
  902. noExternal: ["workbox-window"]
  903. }
  904. };
  905. },
  906. async configResolved(config) {
  907. var _a, _b, _c;
  908. ctx.useImportRegister = false;
  909. ctx.viteConfig = config;
  910. (_c = (_b = (_a = ctx.userOptions) == null ? void 0 : _a.integration) == null ? void 0 : _b.configureOptions) == null ? void 0 : _c.call(_b, config, ctx.userOptions);
  911. ctx.options = await resolveOptions(ctx.userOptions, config);
  912. },
  913. resolveId(id) {
  914. return VIRTUAL_MODULES.includes(id) ? VIRTUAL_MODULES_RESOLVE_PREFIX + id : void 0;
  915. },
  916. load(id) {
  917. if (id.startsWith(VIRTUAL_MODULES_RESOLVE_PREFIX))
  918. id = id.slice(VIRTUAL_MODULES_RESOLVE_PREFIX.length);
  919. else
  920. return;
  921. if (VIRTUAL_MODULES.includes(id)) {
  922. ctx.useImportRegister = true;
  923. if (ctx.viteConfig.command === "serve" && ctx.options.devOptions.enabled) {
  924. return generateRegisterSW2(
  925. { ...ctx.options, filename: swDevOptions.swUrl },
  926. "build",
  927. VIRTUAL_MODULES_MAP[id]
  928. );
  929. } else {
  930. return generateRegisterSW2(
  931. ctx.options,
  932. !ctx.options.disable && ctx.viteConfig.command === "build" ? "build" : "dev",
  933. VIRTUAL_MODULES_MAP[id]
  934. );
  935. }
  936. }
  937. },
  938. api
  939. };
  940. }
  941. // src/plugins/info.ts
  942. function InfoPlugin(ctx, api) {
  943. return {
  944. name: "vite-plugin-pwa:info",
  945. enforce: "post",
  946. resolveId(id) {
  947. if (id === PWA_INFO_VIRTUAL)
  948. return RESOLVED_PWA_INFO_VIRTUAL;
  949. return void 0;
  950. },
  951. load(id) {
  952. if (id === RESOLVED_PWA_INFO_VIRTUAL)
  953. return generatePwaInfo(ctx, api);
  954. }
  955. };
  956. }
  957. function generatePwaInfo(ctx, api) {
  958. const webManifestData = api.webManifestData();
  959. if (!webManifestData)
  960. return "export const pwaInfo = undefined;";
  961. const { href, useCredentials, toLinkTag } = webManifestData;
  962. const registerSWData = api.registerSWData();
  963. const entry = {
  964. pwaInDevEnvironment: api.pwaInDevEnvironment,
  965. webManifest: {
  966. href,
  967. useCredentials,
  968. linkTag: toLinkTag()
  969. }
  970. };
  971. if (registerSWData) {
  972. const scriptTag = registerSWData.toScriptTag();
  973. if (scriptTag) {
  974. const { inline, inlinePath, registerPath, type, scope } = registerSWData;
  975. entry.registerSW = {
  976. inline,
  977. inlinePath,
  978. registerPath,
  979. type,
  980. scope,
  981. scriptTag
  982. };
  983. }
  984. }
  985. return `export const pwaInfo = ${JSON.stringify(entry)};`;
  986. }
  987. // src/cache.ts
  988. var cachePreset = [
  989. {
  990. urlPattern: /^https:\/\/fonts\.(?:googleapis|gstatic)\.com\/.*/i,
  991. handler: "CacheFirst",
  992. options: {
  993. cacheName: "google-fonts",
  994. expiration: {
  995. maxEntries: 4,
  996. maxAgeSeconds: 365 * 24 * 60 * 60
  997. }
  998. }
  999. },
  1000. {
  1001. urlPattern: /\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,
  1002. handler: "StaleWhileRevalidate",
  1003. options: {
  1004. cacheName: "static-font-assets",
  1005. expiration: {
  1006. maxEntries: 4,
  1007. maxAgeSeconds: 7 * 24 * 60 * 60
  1008. }
  1009. }
  1010. },
  1011. {
  1012. urlPattern: /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,
  1013. handler: "StaleWhileRevalidate",
  1014. options: {
  1015. cacheName: "static-image-assets",
  1016. expiration: {
  1017. maxEntries: 64,
  1018. maxAgeSeconds: 24 * 60 * 60
  1019. }
  1020. }
  1021. },
  1022. {
  1023. urlPattern: /\.(?:js)$/i,
  1024. handler: "StaleWhileRevalidate",
  1025. options: {
  1026. cacheName: "static-js-assets",
  1027. expiration: {
  1028. maxEntries: 32,
  1029. maxAgeSeconds: 24 * 60 * 60
  1030. }
  1031. }
  1032. },
  1033. {
  1034. urlPattern: /\.(?:css|less)$/i,
  1035. handler: "StaleWhileRevalidate",
  1036. options: {
  1037. cacheName: "static-style-assets",
  1038. expiration: {
  1039. maxEntries: 32,
  1040. maxAgeSeconds: 24 * 60 * 60
  1041. }
  1042. }
  1043. },
  1044. {
  1045. urlPattern: /\.(?:json|xml|csv)$/i,
  1046. handler: "NetworkFirst",
  1047. options: {
  1048. cacheName: "static-data-assets",
  1049. expiration: {
  1050. maxEntries: 32,
  1051. maxAgeSeconds: 24 * 60 * 60
  1052. }
  1053. }
  1054. },
  1055. {
  1056. urlPattern: /\/api\/.*$/i,
  1057. handler: "NetworkFirst",
  1058. method: "GET",
  1059. options: {
  1060. cacheName: "apis",
  1061. expiration: {
  1062. maxEntries: 16,
  1063. maxAgeSeconds: 24 * 60 * 60
  1064. },
  1065. networkTimeoutSeconds: 10
  1066. }
  1067. },
  1068. {
  1069. urlPattern: /.*/i,
  1070. handler: "NetworkFirst",
  1071. options: {
  1072. cacheName: "others",
  1073. expiration: {
  1074. maxEntries: 32,
  1075. maxAgeSeconds: 24 * 60 * 60
  1076. },
  1077. networkTimeoutSeconds: 10
  1078. }
  1079. }
  1080. ];
  1081. // src/index.ts
  1082. function VitePWA(userOptions = {}) {
  1083. const ctx = createContext(userOptions);
  1084. const api = createAPI(ctx);
  1085. return [
  1086. MainPlugin(ctx, api),
  1087. InfoPlugin(ctx, api),
  1088. BuildPlugin(ctx),
  1089. DevPlugin(ctx)
  1090. ];
  1091. }
  1092. export {
  1093. VitePWA,
  1094. cachePreset,
  1095. defaultInjectManifestVitePlugins
  1096. };