From ed52fa9c24bb261a2b3448d2a6dac824b1202159 Mon Sep 17 00:00:00 2001 From: osmarks Date: Tue, 30 Apr 2024 16:30:27 +0100 Subject: [PATCH] Service-worker-based prefetch --- src/common.js | 8 ++++++++ src/index.js | 15 ++++++++++++++- src/page.js | 34 +++++++++++++++++++++++++++++----- {assets => src}/sw.js | 14 +++----------- 4 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 src/common.js rename {assets => src}/sw.js (94%) diff --git a/src/common.js b/src/common.js new file mode 100644 index 0000000..802de99 --- /dev/null +++ b/src/common.js @@ -0,0 +1,8 @@ +export const ignorePaths = [ + "/isso", + "/infipage", + "/wsthing", + "/random-stuff", + "/radio", + "/ystat" +] \ No newline at end of file diff --git a/src/index.js b/src/index.js index 0eb6ab1..5279bdf 100644 --- a/src/index.js +++ b/src/index.js @@ -374,6 +374,19 @@ const compilePageJSTask = async () => { }) } +const compileServiceWorkerJSTask = async () => { + await esbuild.build({ + entryPoints: [ path.join(srcDir, "sw.js") ], + bundle: true, + outfile: path.join(outDir, "sw.js"), + minify: true, + sourcemap: true, + define: { + "siteVersion": JSON.stringify(globalData.buildID) + } + }) +} + const genServiceWorker = async () => { const serviceWorker = mustache.render(await readFile(path.join(assetsDir, "sw.js")), globalData) await minifyJSFile(serviceWorker, "sw.js", path.join(outDir, "sw.js")) @@ -440,7 +453,7 @@ const tasks = { manifest: { deps: ["assetsDir"], fn: genManifest }, minifyJS: { deps: ["assetsDir"], fn: minifyJSTask }, compilePageJS: { deps: ["assetsDir"], fn: compilePageJSTask }, - serviceWorker: { deps: [], fn: genServiceWorker }, + serviceWorker: { deps: [], fn: compileServiceWorkerJSTask }, images: { deps: ["assetsDir"], fn: doImages }, offlinePage: { deps: ["assetsDir", "pagedeps"], fn: () => applyTemplate(globalData.templates.experiment, path.join(assetsDir, "offline.html"), () => path.join(outAssets, "offline.html"), {}) }, assets: { deps: ["manifest", "minifyJS", "serviceWorker", "images", "compilePageJS"] }, diff --git a/src/page.js b/src/page.js index 62115c8..daee58a 100644 --- a/src/page.js +++ b/src/page.js @@ -1,22 +1,46 @@ const idb = require("idb") const { solve } = require("yalps") +const { ignorePaths } = require("./common") + +const prefetchHook = () => { + let lastFetch = 0 + const prefetch = event => { + if (Date.now() - lastFetch >= 200) { + const href = new URL(event.target.getAttribute("href"), window.location) + if (href.origin === window.location.origin) { + for (const ignorePath of ignorePaths) { + if (href.pathname.startsWith(ignorePath)) return + } + console.log("prefetch", href.href) + fetch(href) + lastFetch = Date.now() + } + } + } + + for (const node of document.querySelectorAll("a[href]")) { + node.addEventListener("touchstart", prefetch) + node.addEventListener("mouseover", prefetch) + } +} // attempt to register service worker if ("serviceWorker" in navigator) { navigator.serviceWorker.register("/sw.js", { scope: "/" }).then(reg => { if (reg.installing) { - console.log("Service worker installing"); + console.log("Service worker installing") } else if (reg.waiting) { - console.log("Service worker installed"); + console.log("Service worker installed") } else if (reg.active) { - console.log("Service worker active"); + console.log("Service worker active") + prefetchHook() } }).catch(error => { // registration failed - console.log("Registration failed with " + error); + console.log("Registration failed with " + error) }); } else { - console.log("Service workers are not supported."); + console.log("Service workers are not supported.") } // https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript diff --git a/assets/sw.js b/src/sw.js similarity index 94% rename from assets/sw.js rename to src/sw.js index 88a6222..1627ac1 100644 --- a/assets/sw.js +++ b/src/sw.js @@ -1,4 +1,5 @@ -const siteVersion = "{{buildID}}" +import { ignorePaths } from "./common" + const offlinePage = "/assets/offline.html" const cacheName = `${siteVersion}-v1` const precache = [ @@ -33,15 +34,6 @@ self.addEventListener("activate", event => { ) }) -const ignorePaths = [ - "/isso", - "/infipage", - "/wsthing", - "/random-stuff", - "/radio", - "/ystat" -] - const shouldRespond = req => { if (req.method !== "GET") { return false } // do not respond to non-GET requests if (!req.url.startsWith(self.location.origin)) { return false } // do not respond to cross-origin requests @@ -89,4 +81,4 @@ self.addEventListener("fetch", event => { if (shouldRespond(event.request)) { event.respondWith(getResponse(event.request)) } -}) +}) \ No newline at end of file