diff options
author | João Augusto Costa Branco Marado Torres <torres.dev@disroot.org> | 2025-07-04 15:37:16 -0300 |
---|---|---|
committer | João Augusto Costa Branco Marado Torres <torres.dev@disroot.org> | 2025-07-04 15:37:16 -0300 |
commit | 2598c9ef0b945f13e94dba8f36c5fbb5cba58feb (patch) | |
tree | 39e1c481af223cb2356b994637aa4d1ded267934 /astro.config.ts | |
parent | 0fae4d0b001526a200b2ab1270cf353e4c3b5681 (diff) |
feat: offline support with service workers
Signed-off-by: João Augusto Costa Branco Marado Torres <torres.dev@disroot.org>
Diffstat (limited to 'astro.config.ts')
-rw-r--r-- | astro.config.ts | 140 |
1 files changed, 102 insertions, 38 deletions
diff --git a/astro.config.ts b/astro.config.ts index 174e0ca..c7d4946 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -15,6 +15,9 @@ import remarkToc from "remark-toc"; import { get } from "./src/utils/anonymous.ts"; import { loadEnv } from "vite"; import process from "node:process"; +import { generateSW, injectManifest } from "workbox-build"; +import { NetworkFirst, StaleWhileRevalidate } from "workbox-strategies"; +import { ExpirationPlugin } from "workbox-expiration"; // deno-lint-ignore no-non-null-assertion const { PUBLIC_SITE_URL } = loadEnv(process.env.NODE_ENV!, process.cwd(), ""); @@ -22,56 +25,117 @@ const { PUBLIC_SITE_URL } = loadEnv(process.env.NODE_ENV!, process.cwd(), ""); // https://astro.build/config export default defineConfig({ site: new URL(PUBLIC_SITE_URL).href, - integrations: [sitemap({ - serialize: async (item) => { - const match = item.url.match(/\/blog\/read\/([^/]+)\/$/); - if (match === null) { - return item; - } - const slug = match[1]; - - let frontmatter; - try { - frontmatter = await Deno.readTextFile( - `${Deno.cwd()}/public/blog/${slug}.md`, - ).then(parseFrontmatter).then(get("frontmatter")); - } catch { - return item; - } - - item.lastmod = (frontmatter.dateUpdated ?? frontmatter.dateCreated) - .toISOString(); - for await ( - const { name, isFile } of Deno.readDir(`${Deno.cwd()}/public/blog/`) - ) { - if (!name.endsWith(".md") || !isFile || name === `${slug}.md`) { - continue; + integrations: [ + sitemap({ + serialize: async (item) => { + const match = item.url.match(/\/blog\/read\/([^/]+)\/$/); + if (match === null) { + return item; } + const slug = match[1]; let frontmatter; try { frontmatter = await Deno.readTextFile( - `${Deno.cwd()}/public/blog/${name}`, + `${Deno.cwd()}/public/blog/${slug}.md`, ).then(parseFrontmatter).then(get("frontmatter")); } catch { - continue; + return item; } - if (frontmatter.translationOf !== slug) { - continue; + item.lastmod = (frontmatter.dateUpdated ?? frontmatter.dateCreated) + .toISOString(); + for await ( + const { name, isFile } of Deno.readDir(`${Deno.cwd()}/public/blog/`) + ) { + if (!name.endsWith(".md") || !isFile || name === `${slug}.md`) { + continue; + } + + let frontmatter; + try { + frontmatter = await Deno.readTextFile( + `${Deno.cwd()}/public/blog/${name}`, + ).then(parseFrontmatter).then(get("frontmatter")); + } catch { + continue; + } + + if (frontmatter.translationOf !== slug) { + continue; + } + + item.links ??= []; + item.links.push({ + url: `https://cravodeabril.pt/blog/${name}`, + lang: frontmatter.lang, + hreflang: frontmatter.lang, + }); } + return item; + }, + xslURL: "/sitemap.xsl", + }), + { + name: "service worker", + hooks: { + "astro:build:done": async ({ dir, logger }) => { + // const { warnings, count, size } = await injectManifest({ + // globDirectory: dir.pathname, + // swSrc: "src/sw.ts", + // swDest: new URL("sw.js", dir).pathname, + // }); - item.links ??= []; - item.links.push({ - url: `https://cravodeabril.pt/blog/${name}`, - lang: frontmatter.lang, - hreflang: frontmatter.lang, - }); - } - return item; + const theLatest = /^(\/public)?\/blog/; + const { warnings, count, size } = await generateSW({ + swDest: new URL("sw.js", dir).pathname, + globDirectory: dir.pathname, + globPatterns: ["**/*"], + skipWaiting: true, + clientsClaim: true, + runtimeCaching: [{ + urlPattern({ sameOrigin, url }) { + return sameOrigin && theLatest.test(url.pathname); + }, + handler: new NetworkFirst({ + cacheName: "posts", + networkTimeoutSeconds: 3, + }), + }, { + urlPattern({ sameOrigin, url }) { + return sameOrigin && !theLatest.test(url.pathname); + }, + handler: new StaleWhileRevalidate({ + cacheName: "resources", + plugins: [ + new ExpirationPlugin({ maxAgeSeconds: 60 * 60 * 24 * 30 }), + ], + }), + }], + navigateFallback: undefined, + navigationPreload: undefined, + inlineWorkboxRuntime: true, + cleanupOutdatedCaches: true, + dontCacheBustURLsMatching: /^_astro\/.*\.js$/, + }); + + const log = logger.fork("service worker"); + + if (warnings.length > 0) { + log.warn( + `Warnings encountered while injecting the manifest:\t${ + warnings.join("\n") + }`, + ); + } + + log.info( + `Injected a manifest which will precache ${count} files, totaling ${size} bytes.`, + ); + }, + }, }, - xslURL: "/sitemap.xsl", - })], + ], server: ({ command }) => ({ host: command === "dev", }), |