Skip to content

Commit 6ae3bd4

Browse files
committed
wip
1 parent 83e1eff commit 6ae3bd4

File tree

1 file changed

+71
-0
lines changed

1 file changed

+71
-0
lines changed

src/routes/rss.xml.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { createAPIFileRoute } from '@tanstack/react-start/api'
2+
import { getPublishedPosts, formatAuthors } from '~/utils/blog'
3+
4+
function escapeXml(unsafe: string): string {
5+
return unsafe
6+
.replace(/&/g, '&')
7+
.replace(/</g, '&lt;')
8+
.replace(/>/g, '&gt;')
9+
.replace(/"/g, '&quot;')
10+
.replace(/'/g, '&apos;')
11+
}
12+
13+
function generateRSSFeed() {
14+
const posts = getPublishedPosts().slice(0, 50) // Most recent 50 posts
15+
const siteUrl = 'https://tanstack.com'
16+
const buildDate = new Date().toUTCString()
17+
18+
const rssItems = posts
19+
.map((post) => {
20+
const postUrl = `${siteUrl}/blog/${post.slug}`
21+
const pubDate = new Date(post.published).toUTCString()
22+
const author = formatAuthors(post.authors)
23+
24+
// Use excerpt if available, otherwise try to get first paragraph from content
25+
let description = post.excerpt || ''
26+
if (!description && post.content) {
27+
// Extract first paragraph after frontmatter
28+
const contentWithoutFrontmatter = post.content.replace(/^---[\s\S]*?---/, '').trim()
29+
const firstParagraph = contentWithoutFrontmatter.split('\n\n')[0]
30+
description = firstParagraph.replace(/!\[[^\]]*\]\([^)]*\)/g, '') // Remove images
31+
}
32+
33+
return `
34+
<item>
35+
<title>${escapeXml(post.title)}</title>
36+
<link>${escapeXml(postUrl)}</link>
37+
<guid isPermaLink="true">${escapeXml(postUrl)}</guid>
38+
<pubDate>${pubDate}</pubDate>
39+
<author>${escapeXml(author)}</author>
40+
<description>${escapeXml(description)}</description>
41+
${post.headerImage ? `<enclosure url="${escapeXml(siteUrl + post.headerImage)}" type="image/png" />` : ''}
42+
</item>`
43+
})
44+
.join('')
45+
46+
return `<?xml version="1.0" encoding="UTF-8"?>
47+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
48+
<channel>
49+
<title>TanStack Blog</title>
50+
<link>${siteUrl}/blog</link>
51+
<description>The latest news and updates from TanStack</description>
52+
<language>en-us</language>
53+
<lastBuildDate>${buildDate}</lastBuildDate>
54+
<atom:link href="${siteUrl}/rss.xml" rel="self" type="application/rss+xml" />
55+
${rssItems}
56+
</channel>
57+
</rss>`
58+
}
59+
60+
export const APIRoute = createAPIFileRoute('/rss.xml')({
61+
GET: async () => {
62+
const rss = generateRSSFeed()
63+
64+
return new Response(rss, {
65+
headers: {
66+
'Content-Type': 'application/xml; charset=utf-8',
67+
'Cache-Control': 'public, max-age=300, s-maxage=3600',
68+
},
69+
})
70+
},
71+
})

0 commit comments

Comments
 (0)