diff --git a/bun.lockb b/bun.lockb index 24f19bd..6297847 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index c6bc766..3eefbd3 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@tailwindcss/forms": "^0.5.7", "@tailwindcss/typography": "^0.5.13", "@types/bun": "latest", + "fast-xml-parser": "^4.4.0", "kleur": "^4.1.5", "prettier": "^3.3.3", "prettier-plugin-astro": "^0.14.1" @@ -25,6 +26,7 @@ }, "dependencies": { "@astrojs/mdx": "3.1.3", + "@astrojs/rss": "^4.0.7", "@astrojs/sitemap": "^3.1.6", "@astrojs/tailwind": "^5.1.0", "@astrojs/ts-plugin": "1.9.0", diff --git a/src/components/global/head.astro b/src/components/global/head.astro index aaeb3f6..2821520 100644 --- a/src/components/global/head.astro +++ b/src/components/global/head.astro @@ -34,3 +34,10 @@ const { title, description, author, image } = Astro.props; {title !== undefined ? title : "Ladybird"} + + diff --git a/src/pages/posts.xml.ts b/src/pages/posts.xml.ts new file mode 100644 index 0000000..04548d6 --- /dev/null +++ b/src/pages/posts.xml.ts @@ -0,0 +1,20 @@ +import rss from "@astrojs/rss"; +import { getCollection } from "astro:content"; +import type { APIContext } from "astro"; + +export async function GET(context: APIContext) { + const posts = await getCollection("posts"); + return rss({ + title: "Ladybird Browser Posts", + description: "Ladybird is a brand-new browser & web engine", + site: context.site!, + items: posts.map((post) => ({ + title: post.data.title, + description: post.data.description, + author: post.data.author, + pubDate: post.data.date, + link: `/posts/${post.slug}`, + })), + trailingSlash: false, + }); +} diff --git a/tests/rss.test.ts b/tests/rss.test.ts new file mode 100644 index 0000000..fbc7f15 --- /dev/null +++ b/tests/rss.test.ts @@ -0,0 +1,45 @@ +import { test, expect, describe } from "bun:test"; +import { XMLParser } from "fast-xml-parser"; +import fs from "fs"; +import path from "path"; + +const rootDir: string = path.join(__dirname, "../"); + +describe("RSS Feeds", () => { + const srcDir = path.join(rootDir, "/src/content/posts"); + const distDir = path.join(rootDir, "/dist/"); + + const mdFiles = fs + .readdirSync(srcDir) + .filter((file) => file.endsWith(".mdx")); + const xmlFile = fs.readFileSync(path.join(distDir, "posts.xml")); + + const parser = new XMLParser(); + const parsedXML = parser.parse(xmlFile); + + test("RSS Channel includes overall metadata", () => { + expect(parsedXML.rss.channel).toEqual( + expect.objectContaining({ + title: "Ladybird Browser Posts", + description: "Ladybird is a brand-new browser & web engine", + link: "https://ladybird.org", + }) + ); + }); + + test("XML should contain entries for every post", async () => { + expect(parsedXML.rss.channel.item).toBeArrayOfSize(mdFiles.length); + parsedXML.rss.channel.item.forEach((item: any) => { + const itemAttributes = Object.keys(item); + expect(itemAttributes).toEqual( + expect.arrayContaining([ + "title", + "link", + "description", + "pubDate", + "author", + ]) + ); + }); + }); +});