// @ts-check import { defineConfig } from "astro/config"; import sitemap from "@astrojs/sitemap"; import { parseFrontmatter } from "@astrojs/markdown-remark"; import remarkGfm from "remark-gfm"; import remarkSmartypants from "remark-smartypants"; import rehypeExternalLinks from "rehype-external-links"; import type { Root } from "mdast"; import type { VFile } from "vfile"; import type { Plugin } from "unified"; import type { Options } from "retext-smartypants"; import { visit } from "unist-util-visit"; import rehypeSanitize from "rehype-sanitize"; import remarkToc from "remark-toc"; import { get } from "./src/utils/anonymous.ts"; import { loadEnv } from "vite"; import process from "node:process"; import { generateSW } from "workbox-build"; // deno-lint-ignore no-non-null-assertion 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; } 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 generateSW({ swDest: new URL("sw.js", dir).pathname, globDirectory: dir.pathname, globPatterns: [ "**/*.{html,css,webmanifest,svg,txt,xml}", "_astro/*.js", ], globIgnores: ["blog/**/*", "keys/**/*"], globStrict: true, skipWaiting: false, clientsClaim: true, runtimeCaching: [{ urlPattern: ({ sameOrigin, url }) => sameOrigin && /^\/blog\/read\/[a-z0-9-]+(?:\/|\.md|\.md\.sig)?$/.test( url.pathname, ), handler: "NetworkFirst", options: { cacheName: "posts", networkTimeoutSeconds: 3, }, }, { urlPattern: ({ sameOrigin, url }) => sameOrigin && /^\/blog(\/(20\d{2}(\/\d{2}(\/\d{2})?)?)?|\/keywords(\/[\w-]+)?)?\/?$/ .test(url.pathname), handler: "NetworkFirst", options: { cacheName: "indexes", networkTimeoutSeconds: 3, }, }, { urlPattern: ({ sameOrigin, url }) => sameOrigin && /^(?!\/blog).*/.test(url.pathname), handler: "StaleWhileRevalidate", options: { cacheName: "resources", expiration: { maxAgeSeconds: 60 * 60 * 24 * 30, }, }, }], navigateFallback: undefined, navigationPreload: true, 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.`, ); }, }, }, ], server: ({ command }) => ({ host: command === "dev", }), prefetch: true, markdown: { remarkPlugins: [ remarkGfm, remarkSmartypants as Plugin<[(Options | undefined)?], Root>, [remarkToc, { ordered: true }], () => (tree: Root, _file: VFile): void => { visit(tree, function (node) { if (node.type === "heading") { node.depth++; } }); }, ], rehypePlugins: [ [ rehypeExternalLinks, { target: "_blank", }, ], rehypeSanitize, ], remarkRehype: { clobberPrefix: "" }, }, });