schmelczer-dev/astro.config.mjs
Andras Schmelczer e9b6035c58
Some checks failed
Deploy to Pages / build (pull_request) Failing after 1m5s
AI fixes
2026-05-24 10:34:24 +01:00

104 lines
3.3 KiB
JavaScript

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 <lastmod> 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: 'always',
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: [],
},
],
],
},
});