* fix "dev:build" watch mode
* remove cross-env * append build hash to service worker cache names for easy purge
This commit is contained in:
@ -7,7 +7,7 @@ import handleNotificationClick from "./service-worker/notification-click";
|
||||
import handleFetch from "./service-worker/fetch";
|
||||
import handleMessage from "./service-worker/message";
|
||||
|
||||
declare let self: ServiceWorkerGlobalScope;
|
||||
declare const self: ServiceWorkerGlobalScope;
|
||||
|
||||
self.addEventListener("install", (event) => {
|
||||
event.waitUntil(handleInstall(event).then(() => self.skipWaiting()));
|
||||
|
@ -53,7 +53,6 @@ export default function ServiceWorkerUpdateNotifier() {
|
||||
aria-label="An updated version of the app is available. Reload to get the latest version."
|
||||
/>
|
||||
</button>
|
||||
;
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { deleteCaches } from "./cache-utils";
|
||||
|
||||
declare let self: ServiceWorkerGlobalScope;
|
||||
declare const self: ServiceWorkerGlobalScope;
|
||||
|
||||
export default async function handleActivate(event: ExtendableEvent) {
|
||||
console.debug("Service worker activated");
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { json } from "@remix-run/server-runtime";
|
||||
|
||||
export const ASSET_CACHE = "asset-cache";
|
||||
export const DATA_CACHE = "data-cache";
|
||||
export const DOCUMENT_CACHE = "document-cache";
|
||||
declare const ASSET_CACHE: string;
|
||||
declare const DATA_CACHE: string;
|
||||
declare const DOCUMENT_CACHE: string;
|
||||
|
||||
export function isAssetRequest(request: Request) {
|
||||
return ["font", "image", "script", "style"].includes(request.destination);
|
||||
@ -17,7 +17,7 @@ export function isDocumentGetRequest(request: Request) {
|
||||
return request.method.toLowerCase() === "get" && request.mode === "navigate";
|
||||
}
|
||||
|
||||
export function cacheAsset(event: FetchEvent): Promise<Response> {
|
||||
export function fetchAsset(event: FetchEvent): Promise<Response> {
|
||||
// stale-while-revalidate
|
||||
const url = new URL(event.request.url);
|
||||
return caches
|
||||
@ -53,7 +53,7 @@ export function cacheAsset(event: FetchEvent): Promise<Response> {
|
||||
// stores the timestamp for when each URL's cached response has been revalidated
|
||||
const lastTimeRevalidated: Record<string, number> = {};
|
||||
|
||||
export function cacheLoaderData(event: FetchEvent): Promise<Response> {
|
||||
export function fetchLoaderData(event: FetchEvent): Promise<Response> {
|
||||
const url = new URL(event.request.url);
|
||||
const path = url.pathname + url.search;
|
||||
|
||||
@ -145,7 +145,7 @@ async function areResponsesEqual(a: Response, b: Response): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function cacheDocument(event: FetchEvent): Promise<Response> {
|
||||
export function fetchDocument(event: FetchEvent): Promise<Response> {
|
||||
// network-first
|
||||
const url = new URL(event.request.url);
|
||||
console.debug("Serving document from network", url.pathname);
|
||||
@ -170,6 +170,7 @@ export function cacheDocument(event: FetchEvent): Promise<Response> {
|
||||
|
||||
export async function deleteCaches() {
|
||||
const allCaches = await caches.keys();
|
||||
await Promise.all(allCaches.map((cacheName) => caches.delete(cacheName)));
|
||||
const cachesToDelete = allCaches.filter((cacheName) => cacheName !== ASSET_CACHE);
|
||||
await Promise.all(cachesToDelete.map((cacheName) => caches.delete(cacheName)));
|
||||
console.debug("Caches deleted");
|
||||
}
|
||||
|
@ -1,25 +1,25 @@
|
||||
import {
|
||||
cacheAsset,
|
||||
cacheDocument,
|
||||
cacheLoaderData,
|
||||
fetchAsset,
|
||||
fetchDocument,
|
||||
fetchLoaderData,
|
||||
isAssetRequest,
|
||||
isDocumentGetRequest,
|
||||
isLoaderRequest,
|
||||
} from "./cache-utils";
|
||||
|
||||
declare let self: ServiceWorkerGlobalScope;
|
||||
declare const self: ServiceWorkerGlobalScope;
|
||||
|
||||
export default async function handleFetch(event: FetchEvent) {
|
||||
if (isAssetRequest(event.request)) {
|
||||
return cacheAsset(event);
|
||||
return fetchAsset(event);
|
||||
}
|
||||
|
||||
if (isLoaderRequest(event.request)) {
|
||||
return cacheLoaderData(event);
|
||||
return fetchLoaderData(event);
|
||||
}
|
||||
|
||||
if (isDocumentGetRequest(event.request)) {
|
||||
return cacheDocument(event);
|
||||
return fetchDocument(event);
|
||||
}
|
||||
|
||||
return fetch(event.request);
|
||||
|
@ -1,4 +1,4 @@
|
||||
declare let self: ServiceWorkerGlobalScope;
|
||||
declare const self: ServiceWorkerGlobalScope;
|
||||
|
||||
export default async function handleInstall(event: ExtendableEvent) {
|
||||
console.debug("Service worker installed");
|
||||
|
@ -1,8 +1,7 @@
|
||||
import type { AssetsManifest } from "@remix-run/react/entry";
|
||||
|
||||
import { ASSET_CACHE } from "./cache-utils";
|
||||
|
||||
declare let self: ServiceWorkerGlobalScope;
|
||||
declare const ASSET_CACHE: string;
|
||||
declare const self: ServiceWorkerGlobalScope;
|
||||
|
||||
export default async function handleMessage(event: ExtendableMessageEvent) {
|
||||
if (event.data.type === "SYNC_REMIX_MANIFEST") {
|
||||
@ -13,32 +12,31 @@ export default async function handleMessage(event: ExtendableMessageEvent) {
|
||||
async function handleSyncRemixManifest(event: ExtendableMessageEvent) {
|
||||
console.debug("Caching routes modules");
|
||||
|
||||
await cacheStaticAssets(event.data.manifest);
|
||||
}
|
||||
|
||||
async function cacheStaticAssets(manifest: AssetsManifest) {
|
||||
const cachePromises: Map<string, Promise<void>> = new Map();
|
||||
const assetCache = await caches.open(ASSET_CACHE);
|
||||
const manifest: AssetsManifest = event.data.manifest;
|
||||
const routes = [...Object.values(manifest.routes), manifest.entry];
|
||||
|
||||
const assetsToCache: string[] = [];
|
||||
for (const route of routes) {
|
||||
if (!cachePromises.has(route.module)) {
|
||||
cachePromises.set(route.module, cacheAsset(route.module));
|
||||
}
|
||||
assetsToCache.push(route.module);
|
||||
|
||||
if (route.imports) {
|
||||
for (const assetUrl of route.imports) {
|
||||
if (!cachePromises.has(assetUrl)) {
|
||||
cachePromises.set(assetUrl, cacheAsset(assetUrl));
|
||||
}
|
||||
}
|
||||
assetsToCache.push(...route.imports);
|
||||
}
|
||||
}
|
||||
|
||||
await purgeStaticAssets(assetsToCache);
|
||||
await cacheStaticAssets(assetsToCache);
|
||||
}
|
||||
|
||||
async function cacheStaticAssets(assetsToCache: string[]) {
|
||||
const cachePromises: Map<string, Promise<void>> = new Map();
|
||||
const assetCache = await caches.open(ASSET_CACHE);
|
||||
|
||||
assetsToCache.forEach((assetUrl) => cachePromises.set(assetUrl, cacheAsset(assetUrl)));
|
||||
await Promise.all(cachePromises.values());
|
||||
|
||||
async function cacheAsset(assetUrl: string) {
|
||||
if (await assetCache.match(assetUrl)) {
|
||||
// no need to update the asset, it has a unique hash in its name
|
||||
return;
|
||||
}
|
||||
|
||||
@ -48,3 +46,14 @@ async function cacheStaticAssets(manifest: AssetsManifest) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function purgeStaticAssets(assetsToCache: string[]) {
|
||||
const assetCache = await caches.open(ASSET_CACHE);
|
||||
const cachedAssets = await assetCache.keys();
|
||||
const cachesToDelete = cachedAssets.filter((asset) => !assetsToCache.includes(new URL(asset.url).pathname));
|
||||
console.log(
|
||||
"cachesToDelete",
|
||||
cachesToDelete.map((c) => new URL(c.url).pathname),
|
||||
);
|
||||
await Promise.all(cachesToDelete.map((asset) => assetCache.delete(asset)));
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { removeBadge } from "~/utils/pwa.client";
|
||||
|
||||
declare let self: ServiceWorkerGlobalScope;
|
||||
declare const self: ServiceWorkerGlobalScope;
|
||||
|
||||
// noinspection TypeScriptUnresolvedVariable
|
||||
export default async function handleNotificationClick(event: NotificationEvent) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { NotificationPayload } from "~/utils/web-push.server";
|
||||
import { addBadge } from "~/utils/pwa.client";
|
||||
|
||||
declare let self: ServiceWorkerGlobalScope;
|
||||
declare const self: ServiceWorkerGlobalScope;
|
||||
|
||||
const defaultOptions: NotificationOptions = {
|
||||
icon: "/icons/android-chrome-192x192.png",
|
||||
|
Reference in New Issue
Block a user