Skip to content

Commit 1f82031

Browse files
authored
Merge branch 'main' into reteps/docs-move
2 parents 113e0ad + adecff3 commit 1f82031

5 files changed

Lines changed: 98 additions & 1 deletion

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ yarn-error.log
1414
public/build
1515
out
1616

17+
# Other generated files
18+
public/blog/rss.xml
19+
1720
# macOS files
1821
.DS_Store
1922

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"remark-emoji": "^5.0.2",
4141
"remark-gfm": "^4.0.1",
4242
"remark-math": "^6.0.0",
43+
"rss": "^1.2.2",
4344
"slugify": "^1.6.6",
4445
"unified": "^11.0.5",
4546
"unist-util-visit": "^5.0.0",
@@ -49,6 +50,7 @@
4950
"@types/fs-extra": "^11.0.4",
5051
"@types/node": "^24.1.0",
5152
"@types/react": "^19.1.9",
53+
"@types/rss": "^0.0.32",
5254
"@types/unist": "^3.0.3",
5355
"eslint": "^9.32.0",
5456
"eslint-config-next": "^16.1.1",

src/lib/rss.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import fs from "fs/promises";
2+
import path from "path";
3+
import RSS from "rss";
4+
import { BlogPostWithSlug } from "./blog";
5+
6+
const SITE_URL = process.env.VERCEL_PROJECT_PRODUCTION_URL
7+
? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
8+
: "https://www.prairielearn.com";
9+
const BLOG_URL = `${SITE_URL}/about/blog`;
10+
11+
export async function generateRssFeed(
12+
posts: BlogPostWithSlug[],
13+
): Promise<void> {
14+
const feed = new RSS({
15+
title: "PrairieLearn Blog",
16+
description:
17+
"News, updates, teaching strategies, and deep dives from the PrairieLearn team.",
18+
feed_url: `${SITE_URL}/blog/rss.xml`,
19+
site_url: BLOG_URL,
20+
language: "en-us",
21+
});
22+
23+
for (const post of posts) {
24+
feed.item({
25+
title: post.title,
26+
description: post.excerpt || "",
27+
url: `${BLOG_URL}/${post.slug}`,
28+
guid: `${BLOG_URL}/${post.slug}`,
29+
categories: post.tags || [],
30+
author: post.author,
31+
date: new Date(post.date),
32+
});
33+
}
34+
35+
const outputDir = path.join(process.cwd(), "public", "blog");
36+
await fs.mkdir(outputDir, { recursive: true });
37+
await fs.writeFile(
38+
path.join(outputDir, "rss.xml"),
39+
feed.xml({ indent: true }),
40+
);
41+
}

src/pages/about/blog/index.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { Heading } from "../../../components/Heading";
77
import Stack from "../../../components/Stack";
88
import { TagList } from "../../../components/Tag";
99
import { format } from "date-fns";
10-
import { getAllPosts, BlogPost, BlogPostWithSlug } from "../../../lib/blog";
10+
import { getAllPosts, BlogPostWithSlug } from "../../../lib/blog";
11+
import { generateRssFeed } from "../../../lib/rss";
1112

1213
import styles from "./index.module.scss";
1314

@@ -42,6 +43,12 @@ export default function BlogIndex({ posts }: BlogIndexProps) {
4243
name="description"
4344
content="News, updates, teaching strategies, and deep dives from the PrairieLearn team."
4445
/>
46+
<link
47+
rel="alternate"
48+
type="application/rss+xml"
49+
title="PrairieLearn Blog RSS Feed"
50+
href="/blog/rss.xml"
51+
/>
4552
</Head>
4653

4754
<PageBanner
@@ -76,6 +83,8 @@ export default function BlogIndex({ posts }: BlogIndexProps) {
7683
export const getStaticProps: GetStaticProps<BlogIndexProps> = async () => {
7784
const posts = await getAllPosts();
7885

86+
await generateRssFeed(posts);
87+
7988
return {
8089
props: {
8190
posts,

yarn.lock

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,13 @@ __metadata:
12131213
languageName: node
12141214
linkType: hard
12151215

1216+
"@types/rss@npm:^0.0.32":
1217+
version: 0.0.32
1218+
resolution: "@types/rss@npm:0.0.32"
1219+
checksum: 10c0/b8e214cde30f0d31ad468bcdc8f5370ce7a4b0dc6f2cb131dcfa115a90b6dbc765c4f668e551d5638072f8c1f25e0e81048c3c684b046c91803a9e0dec157c72
1220+
languageName: node
1221+
linkType: hard
1222+
12161223
"@types/unist@npm:*, @types/unist@npm:^3.0.0, @types/unist@npm:^3.0.3":
12171224
version: 3.0.3
12181225
resolution: "@types/unist@npm:3.0.3"
@@ -4141,6 +4148,7 @@ __metadata:
41414148
"@types/fs-extra": "npm:^11.0.4"
41424149
"@types/node": "npm:^24.1.0"
41434150
"@types/react": "npm:^19.1.9"
4151+
"@types/rss": "npm:^0.0.32"
41444152
"@types/unist": "npm:^3.0.3"
41454153
bootstrap: "npm:^5.3.7"
41464154
bootstrap-icons: "npm:^1.13.1"
@@ -4171,6 +4179,7 @@ __metadata:
41714179
remark-emoji: "npm:^5.0.2"
41724180
remark-gfm: "npm:^4.0.1"
41734181
remark-math: "npm:^6.0.0"
4182+
rss: "npm:^1.2.2"
41744183
sass: "npm:^1.89.2"
41754184
slugify: "npm:^1.6.6"
41764185
typescript: "npm:^5.8.3"
@@ -4894,6 +4903,22 @@ __metadata:
48944903
languageName: node
48954904
linkType: hard
48964905

4906+
"mime-db@npm:~1.25.0":
4907+
version: 1.25.0
4908+
resolution: "mime-db@npm:1.25.0"
4909+
checksum: 10c0/a90951c80700314d6131e106cbf56ae94718d046e9669a5afd7f91e603248da328aad5b63314865fd9547f4688465ac62d1ace59e1ffdb73688452a28a9c3622
4910+
languageName: node
4911+
linkType: hard
4912+
4913+
"mime-types@npm:2.1.13":
4914+
version: 2.1.13
4915+
resolution: "mime-types@npm:2.1.13"
4916+
dependencies:
4917+
mime-db: "npm:~1.25.0"
4918+
checksum: 10c0/a53a0453f7863fa2d823025ce92978f5142a18528316caa53cbb1d4212be847973e9a5f2466362cbd2351a512e369252fc2c88c9c5bbc85df4bd8b42214f1785
4919+
languageName: node
4920+
linkType: hard
4921+
48974922
"minimatch@npm:^3.1.2":
48984923
version: 3.1.2
48994924
resolution: "minimatch@npm:3.1.2"
@@ -5970,6 +5995,16 @@ __metadata:
59705995
languageName: node
59715996
linkType: hard
59725997

5998+
"rss@npm:^1.2.2":
5999+
version: 1.2.2
6000+
resolution: "rss@npm:1.2.2"
6001+
dependencies:
6002+
mime-types: "npm:2.1.13"
6003+
xml: "npm:1.0.1"
6004+
checksum: 10c0/7e37a6ed1820b69476c76e4fefe09f69dd7505408e9fc7690441a5190aca2e2e2faf8d19575fd1efd66debfb178893374fbff9abf68e5416f5e4d4cccb7ae8b9
6005+
languageName: node
6006+
linkType: hard
6007+
59736008
"run-parallel@npm:^1.1.9":
59746009
version: 1.2.0
59756010
resolution: "run-parallel@npm:1.2.0"
@@ -7161,6 +7196,13 @@ __metadata:
71617196
languageName: node
71627197
linkType: hard
71637198

7199+
"xml@npm:1.0.1":
7200+
version: 1.0.1
7201+
resolution: "xml@npm:1.0.1"
7202+
checksum: 10c0/04bcc9b8b5e7b49392072fbd9c6b0f0958bd8e8f8606fee460318e43991349a68cbc5384038d179ff15aef7d222285f69ca0f067f53d071084eb14c7fdb30411
7203+
languageName: node
7204+
linkType: hard
7205+
71647206
"yallist@npm:^3.0.2":
71657207
version: 3.1.1
71667208
resolution: "yallist@npm:3.1.1"

0 commit comments

Comments
 (0)