import { readdirSync, readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import sitemap from '@astrojs/sitemap'; import { defineConfig } from 'astro/config'; import rehypeAutolinkHeadings from 'rehype-autolink-headings'; import rehypeSlug from 'rehype-slug'; // Build a lookup of post slugs to their last modification dates so the sitemap // can advertise accurate values to crawlers. astro:content isn't // available inside the config, so we read post frontmatter directly. Our posts // always use single-line scalar `date:` / `updated:` keys, so a small regex // extraction is sufficient and intentional. const postsDir = path.resolve( path.dirname(fileURLToPath(import.meta.url)), 'src/content/posts' ); function extractScalar(frontmatter, key) { const match = frontmatter.match(new RegExp(`^${key}:\\s*(.+?)\\s*$`, 'm')); return match?.[1]?.replace(/^['"]|['"]$/g, ''); } const postLastmodLookup = new Map( readdirSync(postsDir, { withFileTypes: true }) .filter((entry) => entry.isFile() && entry.name.endsWith('.md')) .map((entry) => { const raw = readFileSync(path.join(postsDir, entry.name), 'utf8'); const frontmatter = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1] ?? ''; const rawDate = extractScalar(frontmatter, 'updated') ?? extractScalar(frontmatter, 'date'); const parsed = rawDate ? new Date(rawDate) : null; const valid = parsed && !Number.isNaN(parsed.valueOf()) ? parsed : null; return [entry.name.replace(/\.md$/, ''), valid]; }) .filter(([, date]) => date !== null) ); export default defineConfig({ site: 'https://schmelczer.dev', trailingSlash: 'ignore', build: { inlineStylesheets: 'always' }, redirects: { '/writing/': '/articles/', '/writing/[slug]': '/articles/[slug]', }, integrations: [ sitemap({ filter: (page) => { const path = new URL(page).pathname; return !path.startsWith('/writing/') && path !== '/404/'; }, serialize(item) { const url = new URL(item.url); const match = url.pathname.match(/^\/articles\/([^/]+)\/?$/); let lastmod = item.lastmod; if (match) { const date = postLastmodLookup.get(match[1]); if (date instanceof Date && !Number.isNaN(date.valueOf())) { lastmod = date.toISOString(); } } return { ...item, changefreq: 'monthly', ...(lastmod ? { lastmod } : {}) }; }, }), ], image: { service: { entrypoint: 'astro/assets/services/sharp' }, }, vite: { server: { watch: { // Avoid inotify instance limits in dev containers and mounted volumes. usePolling: true, }, }, }, markdown: { shikiConfig: { themes: { light: 'github-light', dark: 'github-dark', }, defaultColor: false, wrap: false, }, rehypePlugins: [ rehypeSlug, [ rehypeAutolinkHeadings, { behavior: 'append', properties: { className: ['heading-anchor'], ariaLabel: 'Permalink', }, // Glyph rendered via CSS ::before so it doesn't leak into the TOC // when astro:content extracts heading.text from the rendered HTML. content: [], }, ], ], }, });