my blog https://overreacted.io

add atom/rss feed (#798)

* add atom/rss feed

* generate atom/rss feed via route handler

* fix indent

* add author to rss/atom feed

* add feed links to rss/atom feed

authored by

Christian Mund and committed by
GitHub
833facb9 a074900d

+93 -1
+8
app/atom.xml/route.js
··· 1 + import { getPosts, metadata } from "../page"; 2 + import { generateFeed } from "../feed"; 3 + 4 + export async function GET() { 5 + const posts = await getPosts(); 6 + const feed = generateFeed(posts, metadata); 7 + return new Response(feed.atom1()); 8 + }
+36
app/feed.js
··· 1 + import { Feed } from "feed"; 2 + 3 + export function generateFeed(posts, metadata) { 4 + const site_url = "https://overreacted.io/"; 5 + 6 + const feedOptions = { 7 + author: { 8 + name: "Dan Abramov", 9 + email: "dan.abramov@gmail.com", 10 + link: site_url 11 + }, 12 + description: metadata.description, 13 + favicon: `${site_url}/icon.png`, 14 + feedLinks: { atom: `${site_url}atom.xml`, rss: `${site_url}rss.xml` }, 15 + generator: "Feed for Node.js", 16 + id: site_url, 17 + image: 18 + "https://pbs.twimg.com/profile_images/1545194945161707520/rqkwPViA_400x400.jpg", 19 + link: site_url, 20 + title: metadata.title, 21 + }; 22 + 23 + const feed = new Feed(feedOptions); 24 + 25 + for (const post of posts) { 26 + feed.addItem({ 27 + date: new Date(post.date), 28 + description: post.spoiler, 29 + id: `${site_url}${post.slug}/`, 30 + link: `${site_url}${post.slug}/`, 31 + title: post.title, 32 + }); 33 + } 34 + 35 + return feed 36 + }
+12 -1
app/page.js
··· 7 7 export const metadata = { 8 8 title: "overreacted — A blog by Dan Abramov", 9 9 description: "A personal blog by Dan Abramov", 10 + alternates: { 11 + types: { 12 + "application/atom+xml": "https://overreacted.io/atom.xml", 13 + "application/rss+xml": "https://overreacted.io/rss.xml", 14 + }, 15 + }, 10 16 }; 11 17 12 - export default async function Home() { 18 + export async function getPosts() { 13 19 const entries = await readdir("./public/", { withFileTypes: true }); 14 20 const dirs = entries 15 21 .filter((entry) => entry.isDirectory()) ··· 25 31 posts.sort((a, b) => { 26 32 return Date.parse(a.date) < Date.parse(b.date) ? 1 : -1; 27 33 }); 34 + return posts; 35 + } 36 + 37 + export default async function Home() { 38 + const posts = await getPosts() 28 39 return ( 29 40 <div className="relative -top-[10px] flex flex-col gap-8"> 30 41 {posts.map((post) => (
+8
app/rss.xml/route.js
··· 1 + import { getPosts, metadata } from "../page"; 2 + import { generateFeed } from "../feed"; 3 + 4 + export async function GET() { 5 + const posts = await getPosts(); 6 + const feed = generateFeed(posts, metadata); 7 + return new Response(feed.rss2()); 8 + }
+28
package-lock.json
··· 10 10 "dependencies": { 11 11 "chokidar": "^3.5.3", 12 12 "colorjs.io": "^0.4.5", 13 + "feed": "^4.2.2", 13 14 "gray-matter": "^4.0.3", 14 15 "next": "^14.0.1-canary.2", 15 16 "next-mdx-remote": "^4.4.1", ··· 2601 2602 "dev": true, 2602 2603 "dependencies": { 2603 2604 "reusify": "^1.0.4" 2605 + } 2606 + }, 2607 + "node_modules/feed": { 2608 + "version": "4.2.2", 2609 + "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", 2610 + "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", 2611 + "dependencies": { 2612 + "xml-js": "^1.6.11" 2613 + }, 2614 + "engines": { 2615 + "node": ">=0.4.0" 2604 2616 } 2605 2617 }, 2606 2618 "node_modules/file-entry-cache": { ··· 8002 8014 "url": "https://github.com/sponsors/ljharb" 8003 8015 } 8004 8016 }, 8017 + "node_modules/sax": { 8018 + "version": "1.3.0", 8019 + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", 8020 + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" 8021 + }, 8005 8022 "node_modules/scheduler": { 8006 8023 "version": "0.23.0", 8007 8024 "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", ··· 9055 9072 "utf-8-validate": { 9056 9073 "optional": true 9057 9074 } 9075 + } 9076 + }, 9077 + "node_modules/xml-js": { 9078 + "version": "1.6.11", 9079 + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", 9080 + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", 9081 + "dependencies": { 9082 + "sax": "^1.2.4" 9083 + }, 9084 + "bin": { 9085 + "xml-js": "bin/cli.js" 9058 9086 } 9059 9087 }, 9060 9088 "node_modules/yallist": {
+1
package.json
··· 13 13 "dependencies": { 14 14 "chokidar": "^3.5.3", 15 15 "colorjs.io": "^0.4.5", 16 + "feed": "^4.2.2", 16 17 "gray-matter": "^4.0.3", 17 18 "next": "^14.0.1-canary.2", 18 19 "next-mdx-remote": "^4.4.1",