From c8a8bfbe31a902d367e4350e31091d70bbad3d6d Mon Sep 17 00:00:00 2001 From: Chris Arter Date: Fri, 8 May 2026 12:44:09 -0400 Subject: [PATCH] feat(rss): RSS 2.0 builder Co-Authored-By: Claude Sonnet 4.6 --- src/lib/rss.ts | 39 +++++++++++++++++++++++++++++++++++++++ tests/int/rss.int.spec.ts | 19 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/lib/rss.ts create mode 100644 tests/int/rss.int.spec.ts diff --git a/src/lib/rss.ts b/src/lib/rss.ts new file mode 100644 index 0000000..7c092f5 --- /dev/null +++ b/src/lib/rss.ts @@ -0,0 +1,39 @@ +function esc(s: string) { + return s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') +} + +export type RssItem = { + title: string + url: string + description?: string + publishDate: Date +} + +export function buildRss(opts: { + title: string + description: string + siteUrl: string + items: RssItem[] +}) { + const items = opts.items + .map( + (i) => ` + + ${esc(i.title)} + ${esc(opts.siteUrl + i.url)} + ${esc(opts.siteUrl + i.url)} + ${i.publishDate.toUTCString()} + ${i.description ? `${esc(i.description)}` : ''} + `, + ) + .join('') + return ` + + + ${esc(opts.title)} + ${esc(opts.siteUrl)} + ${esc(opts.description)} + ${items} + +` +} diff --git a/tests/int/rss.int.spec.ts b/tests/int/rss.int.spec.ts new file mode 100644 index 0000000..e056cd0 --- /dev/null +++ b/tests/int/rss.int.spec.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest' +import { buildRss } from '@/lib/rss' + +describe('buildRss', () => { + it('produces a valid-looking RSS 2.0 doc', () => { + const xml = buildRss({ + title: 'T', + description: 'D', + siteUrl: 'https://x.dev', + items: [ + { title: 'A', url: '/a/', description: 'd', publishDate: new Date('2024-01-01') }, + ], + }) + expect(xml).toContain('T') + expect(xml).toContain('https://x.dev/a/') + expect(xml).toContain('Mon, 01 Jan 2024') + }) +}) -- 2.51.2