import { readdirSync, writeFileSync } from 'node:fs' import { relative, resolve } from 'node:path' const distDirectory = resolve('dist') function listFiles(directory) { return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { const path = resolve(directory, entry.name) return entry.isDirectory() ? listFiles(path) : [path] }) } const assets = listFiles(distDirectory) .map((path) => `/${relative(distDirectory, path).replaceAll('\\', '/')}`) .filter((path) => path !== '/service-worker.js') .sort() const cacheName = `chronicle-${Date.now()}` const source = `const CACHE_NAME = ${JSON.stringify(cacheName)} const APP_ASSETS = ${JSON.stringify(assets, null, 2)} self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then((cache) => cache.addAll(APP_ASSETS)) .then(() => self.skipWaiting()), ) }) self.addEventListener('activate', (event) => { event.waitUntil( caches.keys() .then((keys) => Promise.all( keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)), )) .then(() => self.clients.claim()), ) }) self.addEventListener('fetch', (event) => { const requestUrl = new URL(event.request.url) if ( event.request.method !== 'GET' || requestUrl.origin !== self.location.origin || requestUrl.pathname.startsWith('/api/') ) { return } event.respondWith( caches.match(event.request).then((cached) => { if (cached) return cached return fetch(event.request).catch(() => ( event.request.mode === 'navigate' ? caches.match('/index.html') : Response.error() )) }), ) }) ` writeFileSync(resolve(distDirectory, 'service-worker.js'), source) console.log(`Offline app shell generated with ${assets.length} files.`)