svelte-testing/src/service-worker.ts

109 lines
3.5 KiB
TypeScript

/* eslint-disable no-var */
import { build, files, version } from '$service-worker';
// Code mostly comes from: https://dev.to/100lvlmaster/create-a-pwa-with-sveltekit-svelte-a36
// With some modifications done by someone in the comments
const worker = (self as unknown) as ServiceWorkerGlobalScope;
const STATIC_CACHE_NAME = `cache${version}`
const APP_CACHE_NAME = `cache${version}`;
// Hardcoded bunch of routes to always preemptively cache
const routes = ["/", "/about"];
// Hardcoded list of other assets to always preemptively cache
const custom_assets = [
"https://fonts.googleapis.com/css2?family=Montserrat:wght@200&family=Roboto+Flex:opsz,wght@8..144,300;8..144,400;8..144,500&family=Roboto+Slab&display=swap"
];
// Adds the domain to the file path, making it a full name
const addDomain = (assets: string[]) =>
assets.map((f) => self.location.origin + f);
// build is an array of all files generated by the bundler
// files is an array of all files in the static dir
const ourAssets = addDomain([
...files,
...build,
...routes
]);
const to_cache = [...ourAssets, ...custom_assets]
const staticAssets = new Set(to_cache);
worker.addEventListener("install", (event) => {
event.waitUntil(
caches
.open(STATIC_CACHE_NAME)
.then((cache) => {
return cache.addAll(to_cache)
})
.then(() => {
worker.skipWaiting();
})
);
});
worker.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys()
.then(async (keys) => {
// Delete old caches
for (const key of keys) {
if (key !== STATIC_CACHE_NAME && key !== APP_CACHE_NAME) await caches.delete(key);
}
console.log("old caches deleted")
worker.clients.claim();
})
.then(() => console.log("Activated"))
)
})
// Fetch the asset from the network and store it in the cache
// Falls back to the cache if the user is offline
async function fetchAndCache(request: Request) {
const cache = await caches.open(APP_CACHE_NAME);
try {
const response = await fetch(request);
cache.put(request, response.clone());
return response;
} catch (err) { // if the user is offline, the request will fail
const response = await cache.match(request);
if (response) return response;
// if not in cache, throw error anyway
throw err;
}
}
worker.addEventListener("fetch", (event) => {
if (event.request.method !== "GET") return;
//console.log("fetch event intercepted")
// Firefox has not yet fixed this, so it needs to be excluded from this caching behavior
// https://web.dev/sw-range-requests/
// https://wpt.fyi/results/fetch/range/sw.https.window.html?label=master&label=experimental&aligned
if (navigator.userAgent.includes("Firefox/") && event.request.headers.has("range")) return;
const url = new URL(event.request.url);
// Don't try to cache protocols other than http/https
const isHttp = url.protocol.startsWith("http");
const isStaticAsset = staticAssets.has(url.href);
const skipBecauseUncached = event.request.cache === 'only-if-cached' && !isStaticAsset;
if (isHttp && !skipBecauseUncached) {
event.respondWith(
(async () => {
// always serve static files and bundler-generated assets from cache.
// if your application has other URLs with data that will never change,
// set this variable to true for them, and they will only be fetched once.
const cachedAsset = isStaticAsset && (await caches.match(event.request));
return cachedAsset || fetchAndCache(event.request);
})()
)
}
})