Some checks failed
Deploy to Pages / build (pull_request) Failing after 1m5s
81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import rss from '@astrojs/rss';
|
|
import type { APIRoute } from 'astro';
|
|
import ogDefault from '../assets/og-default.jpg';
|
|
import {
|
|
absoluteUrl,
|
|
articlePath,
|
|
entrySlug,
|
|
getPublishedPosts,
|
|
optimizeOgImage,
|
|
site,
|
|
} from '../lib/site';
|
|
|
|
// Escape characters that would otherwise break XML parsing inside text nodes
|
|
// (the `customData` strings are inserted as-is by @astrojs/rss).
|
|
function escapeXml(value: string) {
|
|
return value
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
// Format a Date as `YYYY-MM-DD` in UTC for use inside tag: URIs.
|
|
function isoDate(date: Date) {
|
|
return date.toISOString().slice(0, 10);
|
|
}
|
|
|
|
export const GET: APIRoute = async (context) => {
|
|
const posts = await getPublishedPosts();
|
|
const feedUrl = absoluteUrl('/rss.xml');
|
|
const channelImage = await optimizeOgImage(ogDefault);
|
|
const channelImageUrl = absoluteUrl(channelImage.src);
|
|
const creator = escapeXml(site.name);
|
|
|
|
return rss({
|
|
title: site.name,
|
|
description: site.description,
|
|
site: context.site ?? site.url,
|
|
xmlns: {
|
|
atom: 'http://www.w3.org/2005/Atom',
|
|
content: 'http://purl.org/rss/1.0/modules/content/',
|
|
dc: 'http://purl.org/dc/elements/1.1/',
|
|
},
|
|
customData: [
|
|
'<language>en-us</language>',
|
|
`<lastBuildDate>${new Date().toUTCString()}</lastBuildDate>`,
|
|
`<atom:link href="${feedUrl}" rel="self" type="application/rss+xml" />`,
|
|
'<image>',
|
|
` <url>${channelImageUrl}</url>`,
|
|
` <title>${escapeXml(site.name)}</title>`,
|
|
` <link>${site.url}</link>`,
|
|
'</image>',
|
|
].join('\n'),
|
|
items: posts.map((post) => {
|
|
const url = absoluteUrl(articlePath(post));
|
|
// Stable tag: URI keeps the GUID constant across path renames
|
|
// (e.g. the `/writing/` → `/articles/` migration). The date is the
|
|
// original publish date so re-publishing won't change the GUID.
|
|
const guid = `tag:schmelczer.dev,${isoDate(post.data.date)}:posts/${entrySlug(post)}`;
|
|
const updated = post.data.updated
|
|
? `<atom:updated>${post.data.updated.toISOString()}</atom:updated>`
|
|
: '';
|
|
return {
|
|
title: post.data.title,
|
|
description: post.data.description,
|
|
pubDate: post.data.date,
|
|
link: url,
|
|
author: `${site.email} (${site.name})`,
|
|
categories: [...post.data.tags],
|
|
customData: [
|
|
`<guid isPermaLink="false">${escapeXml(guid)}</guid>`,
|
|
`<dc:creator>${creator}</dc:creator>`,
|
|
updated,
|
|
]
|
|
.filter(Boolean)
|
|
.join('\n'),
|
|
};
|
|
}),
|
|
});
|
|
};
|