summaryrefslogtreecommitdiff
path: root/astro.config.ts
diff options
context:
space:
mode:
authorJoão Augusto Costa Branco Marado Torres <torres.dev@disroot.org>2025-07-04 15:37:16 -0300
committerJoão Augusto Costa Branco Marado Torres <torres.dev@disroot.org>2025-07-04 15:37:16 -0300
commit2598c9ef0b945f13e94dba8f36c5fbb5cba58feb (patch)
tree39e1c481af223cb2356b994637aa4d1ded267934 /astro.config.ts
parent0fae4d0b001526a200b2ab1270cf353e4c3b5681 (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.ts140
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",
}),