Some checks failed
Deploy to Pages / build (pull_request) Failing after 1m3s
103 lines
3.4 KiB
JavaScript
103 lines
3.4 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. We parse the markdown
|
|
// frontmatter ourselves rather than importing `astro:content` (a virtual module
|
|
// that may not be available inside the config). Failures are non-fatal —
|
|
// sitemap entries simply fall back to no lastmod.
|
|
const postLastmodLookup = new Map();
|
|
try {
|
|
const postsDir = path.resolve(
|
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
'src/content/posts'
|
|
);
|
|
for (const entry of readdirSync(postsDir, { withFileTypes: true })) {
|
|
if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
|
|
const slug = entry.name.replace(/\.md$/, '');
|
|
const raw = readFileSync(path.join(postsDir, entry.name), 'utf8');
|
|
const frontmatterMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
if (!frontmatterMatch) continue;
|
|
const frontmatter = frontmatterMatch[1];
|
|
const updatedMatch = frontmatter.match(/^updated:\s*(.+?)\s*$/m);
|
|
const dateMatch = frontmatter.match(/^date:\s*(.+?)\s*$/m);
|
|
const rawDate = (updatedMatch ?? dateMatch)?.[1]?.replace(/^['"]|['"]$/g, '');
|
|
if (!rawDate) continue;
|
|
const parsed = new Date(rawDate);
|
|
if (!Number.isNaN(parsed.valueOf())) postLastmodLookup.set(slug, parsed);
|
|
}
|
|
} catch {
|
|
// Directory missing or unreadable; sitemap will fall back to no lastmod.
|
|
}
|
|
|
|
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: [],
|
|
},
|
|
],
|
|
],
|
|
},
|
|
});
|