- Implemented a service worker (sw.js) that handles installation, activation, and fetch events. - Added caching logic for public assets and a fallback for offline navigation. - Created unit tests (sw.test.ts) to validate service worker behavior, including asset caching, navigation handling, and activation logic. - Ensured that private API routes and third-party requests are excluded from caching.
28 lines
1.8 KiB
TypeScript
28 lines
1.8 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { copyFile, mkdir } from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { manifest } from "../src/pwa/manifest";
|
|
|
|
export async function buildPwa(outdir: string, outputs: readonly { path: string }[]) {
|
|
const directory = path.join(outdir, "pwa");
|
|
await mkdir(directory, { recursive: true });
|
|
for (const icon of manifest.icons) await copyFile(`src/pwa/public/${path.basename(icon.src)}`, path.join(directory, path.basename(icon.src)));
|
|
await Bun.write(path.join(outdir, "manifest.webmanifest"), JSON.stringify(manifest, null, 2));
|
|
// Only browser assets emitted by Bun; index.js is the private server bundle.
|
|
const files = outputs.map(output => path.relative(outdir, output.path)).filter(file =>
|
|
file !== "index.js" && /\.(js|css|png|svg|woff2|webmanifest)$/.test(file));
|
|
files.push(...manifest.icons.map(icon => icon.src.slice(1)), "manifest.webmanifest");
|
|
for await (const file of new Bun.Glob("fonts/browser/*.woff2").scan(outdir)) files.push(file);
|
|
files.sort();
|
|
const template = await Bun.file("src/pwa/sw.js").text();
|
|
const html = await Bun.file(path.join(outdir, "index.html")).text();
|
|
const hash = createHash("sha256").update(template).update(html);
|
|
for (const file of files) hash.update(file).update(await Bun.file(path.join(outdir, file)).arrayBuffer().then(Buffer.from));
|
|
const version = hash.digest("hex").slice(0, 16);
|
|
const shell = `/pwa/shell-${version}.html`;
|
|
await Bun.write(path.join(outdir, shell.slice(1)), html);
|
|
const config = { version, shell, assets: [...files.map(file => `/${file}`), shell] };
|
|
await Bun.write(path.join(directory, "sw.js"), template.replace('"__PWA_CONFIG__"', JSON.stringify(JSON.stringify(config))));
|
|
console.log(` PWA ${version}: ${config.assets.length} public assets`);
|
|
}
|