┌─ TS ───────────────────────────────────────────────────────────────────────┐
│ // Hand-rolled RSS and sitemap XML - no integration dependency, and the │
│ // builders stay pure and testable. │
│ │
│ export interface FeedItem { │
│ title: string; │
│ url: string; // absolute │
│ published: string; // ISO date │
│ description: string; │
│ } │
│ │
│ export interface SitemapUrl { │
│ loc: string; // absolute │
│ lastmod?: string; // ISO date │
│ } │
│ │
│ const esc = (s: string) => │
│ s │
│ .replace(/&/g, "&") │
│ .replace(/</g, "<") │
│ .replace(/>/g, ">") │
│ .replace(/"/g, """); │
│ │
│ export interface RssOpts { │
│ title: string; │
│ site: string; // absolute site url │
│ description: string; │
│ items: FeedItem[]; │
│ } │
│ │
│ export function buildRss({ title, site, description, items }: RssOpts): string { │
│ const channel = items │
│ .map((it) => │
│ [ │
│ " <item>", │
│ ` <title>${esc(it.title)}</title>`, │
│ ` <link>${esc(it.url)}</link>`, │
│ ` <guid>${esc(it.url)}</guid>`, │
│ ` <pubDate>${new Date(it.published).toUTCString()}</pubDate>`, │
│ ` <description>${esc(it.description)}</description>`, │
│ " </item>", │
│ ].join("\n"), │
│ ) │
│ .join("\n"); │
│ return [ │
│ `<?xml version="1.0" encoding="UTF-8"?>`, │
│ `<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">`, │
│ " <channel>", │
│ ` <title>${esc(title)}</title>`, │
│ ` <link>${esc(site)}</link>`, │
│ ` <atom:link href="${esc(site)}rss.xml" rel="self" type="application/rss+xm │
│ l"/>`, │
│ ` <description>${esc(description)}</description>`, │
│ " <language>en</language>", │
│ channel, │
│ " </channel>", │
│ "</rss>", │
│ "", │
│ ].join("\n"); │
│ } │
│ │
│ export function buildSitemap(urls: SitemapUrl[]): string { │
│ const body = urls │
│ .map((u) => │
│ [ │
│ " <url>", │
│ ` <loc>${esc(u.loc)}</loc>`, │
│ ...(u.lastmod ? [` <lastmod>${esc(u.lastmod)}</lastmod>`] : []), │
│ " </url>", │
│ ].join("\n"), │
│ ) │
│ .join("\n"); │
│ return [ │
│ `<?xml version="1.0" encoding="UTF-8"?>`, │
│ `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`, │
│ body, │
│ "</urlset>", │
│ "", │
│ ].join("\n"); │
│ } │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ TS ─────────────────────────────────┐
│ // Hand-rolled RSS and sitemap XML - no inte │
│ gration dependency, and the │
│ // builders stay pure and testable. │
│ │
│ export interface FeedItem { │
│ title: string; │
│ url: string; // absolute │
│ published: string; // ISO date │
│ description: string; │
│ } │
│ │
│ export interface SitemapUrl { │
│ loc: string; // absolute │
│ lastmod?: string; // ISO date │
│ } │
│ │
│ const esc = (s: string) => │
│ s │
│ .replace(/&/g, "&") │
│ .replace(/</g, "<") │
│ .replace(/>/g, ">") │
│ .replace(/"/g, """); │
│ │
│ export interface RssOpts { │
│ title: string; │
│ site: string; // absolute site url │
│ description: string; │
│ items: FeedItem[]; │
│ } │
│ │
│ export function buildRss({ title, site, desc │
│ ription, items }: RssOpts): string { │
│ const channel = items │
│ .map((it) => │
│ [ │
│ " <item>", │
│ ` <title>${esc(it.title)}</titl │
│ e>`, │
│ ` <link>${esc(it.url)}</link>`, │
│ ` <guid>${esc(it.url)}</guid>`, │
│ ` <pubDate>${new Date(it.publis │
│ hed).toUTCString()}</pubDate>`, │
│ ` <description>${esc(it.descrip │
│ tion)}</description>`, │
│ " </item>", │
│ ].join("\n"), │
│ ) │
│ .join("\n"); │
│ return [ │
│ `<?xml version="1.0" encoding="UTF-8"?>` │
│ , │
│ `<rss version="2.0" xmlns:atom="http://w │
│ ww.w3.org/2005/Atom">`, │
│ " <channel>", │
│ ` <title>${esc(title)}</title>`, │
│ ` <link>${esc(site)}</link>`, │
│ ` <atom:link href="${esc(site)}rss.xm │
│ l" rel="self" type="application/rss+xml"/>`, │
│ ` <description>${esc(description)}</d │
│ escription>`, │
│ " <language>en</language>", │
│ channel, │
│ " </channel>", │
│ "</rss>", │
│ "", │
│ ].join("\n"); │
│ } │
│ │
│ export function buildSitemap(urls: SitemapUr │
│ l[]): string { │
│ const body = urls │
│ .map((u) => │
│ [ │
│ " <url>", │
│ ` <loc>${esc(u.loc)}</loc>`, │
│ ...(u.lastmod ? [` <lastmod>${esc │
│ (u.lastmod)}</lastmod>`] : []), │
│ " </url>", │
│ ].join("\n"), │
│ ) │
│ .join("\n"); │
│ return [ │
│ `<?xml version="1.0" encoding="UTF-8"?>` │
│ , │
│ `<urlset xmlns="http://www.sitemaps.org/ │
│ schemas/sitemap/0.9">`, │
│ body, │
│ "</urlset>", │
│ "", │
│ ].join("\n"); │
│ } │
└──────────────────────────────────────────────┘