使用 feed 生成 RSS Feed - Modern Next.js Blog 系列 #19
- Published on
本文同步發佈於 it 邦幫忙 2022 iThome 鐵人賽
TL;DR
這是「Modern Blog 30 天」系列第 19 篇文章,上一篇我們做完 Sitemap 生成了,這篇接著來做非常相似的 RSS Feed 生成,讓部落格能被 RSS 訂閱。
這篇修改的程式碼如下:
加入 RSS Feed
安裝 feed 套件
已複製!pnpm add feed
修改 src/configs/siteConfigs.ts
,新增 credit 和 email:
已複製!// ... export const siteConfigs = { // ... credit: "Stark Industries", email: "stark@example.com", };
新增 src/lib/generateRSS.ts
:
已複製!import { Feed } from "feed"; import { writeFileSync } from "fs"; import { siteConfigs } from "@/configs/siteConfigs"; import { allPostsNewToOld } from "@/lib/contentLayerAdapter"; import { getPostOGImage } from "@/lib/getPostOGImage"; export default function generateRSS() { const author = { name: siteConfigs.author, email: siteConfigs.email, link: siteConfigs.fqdn, }; const feed = new Feed({ title: siteConfigs.title, description: siteConfigs.description, id: siteConfigs.fqdn, link: siteConfigs.fqdn, image: siteConfigs.logoUrl, favicon: siteConfigs.logoUrl, copyright: `Copyright © 2015 - ${new Date().getFullYear()} ${ siteConfigs.credit }`, feedLinks: { rss2: `${siteConfigs.fqdn}/feed.xml`, json: `${siteConfigs.fqdn}/feed.json`, atom: `${siteConfigs.fqdn}/atom.xml`, }, author: author, }); allPostsNewToOld.forEach((post) => { feed.addItem({ id: siteConfigs.fqdn + post.path, title: post.title, link: siteConfigs.fqdn + post.path, description: post.description, image: getPostOGImage(post.socialImage), author: [author], contributor: [author], date: new Date(post.date), // content: post.body.html, }); }); writeFileSync("./public/feed.xml", feed.rss2()); writeFileSync("./public/atom.xml", feed.atom1()); writeFileSync("./public/feed.json", feed.json1()); }
修改 src/pages/index.tsx
,將上面的 generateRSS()
function 放進 getStaticProps
裡,在 pnpm build 時就會執行它來生成 RSS 檔案:
已複製!// ... // 新增這行 Import import generateRSS from "@/lib/generateRSS"; // ... export const getStaticProps: GetStaticProps<Props> = () => { // ... // 新增下面這行 generateRSS(); return { props: { posts } }; }; // ...
修改 src/pages/_app.tsx
,在全站加入 meta data 標註 RSS Feed 的路徑:
已複製!// ... function MyApp({ Component, pageProps }: AppProps) { return ( <ThemeProvider attribute="class"> <DefaultSeo // ... additionalLinkTags={[ { rel: "icon", href: siteConfigs.logoPath, }, // 加入下面這兩個 link tag { rel: "alternate", type: "application/rss+xml", href: "/feed.xml", }, { rel: "alternate", type: "application/atom+xml", href: "/atom.xml", }, ]} /> <LayoutWrapper> <Component {...pageProps} /> </LayoutWrapper> </ThemeProvider> ); } export default MyApp;
修改 .gitignore
、.eslintignore
、.prettierignore
,忽略生成的 RSS Feed:
已複製!# ... # 加入下面這 3 條規則 # RSS related files (generated by generateRSS.js) /public/atom.xml /public/feed.xml /public/feed.json
成果
完成了!使用 pnpm build
,執行完就會看到 /public 路徑裡多了 atom.xml
、feed.json
和 feed.xml
了,三種不同的 RSS 格式。
生成的內容如下:
public/atom.xml
:
已複製!<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <id>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</id> <title>Next.js Tailwind Contentlayer Blog Starter</title> <updated>2022-10-04T15:38:07.971Z</updated> <generator>https://github.com/jpmonette/feed</generator> <author> <name>Tony Stark</name> <email>stark@example.com</email> <uri>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</uri> </author> <link rel="alternate" href="https://nextjs-tailwind-contentlayer-blog-starter.vercel.app"/> <link rel="self" href="https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/atom.xml"/> <subtitle>Blog starter template with modern frontend technologies like Next.js, Tailwind CSS, Contentlayer, i18Next</subtitle> <logo>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/logo.png</logo> <icon>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/logo.png</icon> <rights>Copyright © 2015 - 2022 Stark Industries</rights> <entry> <title type="html"><![CDATA[Post with images]]></title> <id>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-images</id> <link href="https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-images"/> <updated>2022-09-02T00:00:00.000Z</updated> <summary type="html"><![CDATA[My post with images]]></summary> <author> <name>Tony Stark</name> <email>stark@example.com</email> <uri>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</uri> </author> <contributor> <name>Tony Stark</name> <email>stark@example.com</email> <uri>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</uri> </contributor> </entry> <entry> <title type="html"><![CDATA[Post with code]]></title> <id>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-code</id> <link href="https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-code"/> <updated>2022-09-01T00:00:00.000Z</updated> <summary type="html"><![CDATA[My post with code]]></summary> <author> <name>Tony Stark</name> <email>stark@example.com</email> <uri>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</uri> </author> <contributor> <name>Tony Stark</name> <email>stark@example.com</email> <uri>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</uri> </contributor> </entry> <entry> <title type="html"><![CDATA[Markdown demo]]></title> <id>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/markdown-demo</id> <link href="https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/markdown-demo"/> <updated>2022-08-31T00:00:00.000Z</updated> <summary type="html"><![CDATA[This is a demo of Markdown]]></summary> <author> <name>Tony Stark</name> <email>stark@example.com</email> <uri>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</uri> </author> <contributor> <name>Tony Stark</name> <email>stark@example.com</email> <uri>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</uri> </contributor> </entry> <entry> <title type="html"><![CDATA[Sample post]]></title> <id>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/sample-post</id> <link href="https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/sample-post"/> <updated>2022-08-30T00:00:00.000Z</updated> <summary type="html"><![CDATA[My first post]]></summary> <author> <name>Tony Stark</name> <email>stark@example.com</email> <uri>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</uri> </author> <contributor> <name>Tony Stark</name> <email>stark@example.com</email> <uri>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</uri> </contributor> </entry> </feed>
public/feed.json
:
已複製!{ "version": "https://jsonfeed.org/version/1", "title": "Next.js Tailwind Contentlayer Blog Starter", "home_page_url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app", "feed_url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/feed.json", "description": "Blog starter template with modern frontend technologies like Next.js, Tailwind CSS, Contentlayer, i18Next", "icon": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/logo.png", "author": { "name": "Tony Stark", "url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app" }, "items": [ { "id": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-images", "url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-images", "title": "Post with images", "summary": "My post with images", "image": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/images/anita-chong-unsplash.jpg", "date_modified": "2022-09-02T00:00:00.000Z", "author": { "name": "Tony Stark", "url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app" } }, { "id": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-code", "url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-code", "title": "Post with code", "summary": "My post with code", "image": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/og-image.png", "date_modified": "2022-09-01T00:00:00.000Z", "author": { "name": "Tony Stark", "url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app" } }, { "id": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/markdown-demo", "url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/markdown-demo", "title": "Markdown demo", "summary": "This is a demo of Markdown", "image": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/og-image.png", "date_modified": "2022-08-31T00:00:00.000Z", "author": { "name": "Tony Stark", "url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app" } }, { "id": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/sample-post", "url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/sample-post", "title": "Sample post", "summary": "My first post", "image": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/og-image.png", "date_modified": "2022-08-30T00:00:00.000Z", "author": { "name": "Tony Stark", "url": "https://nextjs-tailwind-contentlayer-blog-starter.vercel.app" } } ] }
public/feed.xml
:
已複製!<?xml version="1.0" encoding="utf-8"?> <rss version="2.0"> <channel> <title>Next.js Tailwind Contentlayer Blog Starter</title> <link>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</link> <description>Blog starter template with modern frontend technologies like Next.js, Tailwind CSS, Contentlayer, i18Next</description> <lastBuildDate>Tue, 04 Oct 2022 15:38:07 GMT</lastBuildDate> <docs>https://validator.w3.org/feed/docs/rss2.html</docs> <generator>https://github.com/jpmonette/feed</generator> <image> <title>Next.js Tailwind Contentlayer Blog Starter</title> <url>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/logo.png</url> <link>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app</link> </image> <copyright>Copyright © 2015 - 2022 Stark Industries</copyright> <item> <title><![CDATA[Post with images]]></title> <link>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-images</link> <guid>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-images</guid> <pubDate>Fri, 02 Sep 2022 00:00:00 GMT</pubDate> <description><![CDATA[My post with images]]></description> <author>stark@example.com (Tony Stark)</author> <enclosure url="https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/images/anita-chong-unsplash.jpg" length="0" type="image/jpg"/> </item> <item> <title><![CDATA[Post with code]]></title> <link>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-code</link> <guid>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/post-with-code</guid> <pubDate>Thu, 01 Sep 2022 00:00:00 GMT</pubDate> <description><![CDATA[My post with code]]></description> <author>stark@example.com (Tony Stark)</author> <enclosure url="https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/og-image.png" length="0" type="image/png"/> </item> <item> <title><![CDATA[Markdown demo]]></title> <link>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/markdown-demo</link> <guid>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/markdown-demo</guid> <pubDate>Wed, 31 Aug 2022 00:00:00 GMT</pubDate> <description><![CDATA[This is a demo of Markdown]]></description> <author>stark@example.com (Tony Stark)</author> <enclosure url="https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/og-image.png" length="0" type="image/png"/> </item> <item> <title><![CDATA[Sample post]]></title> <link>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/sample-post</link> <guid>https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/posts/sample-post</guid> <pubDate>Tue, 30 Aug 2022 00:00:00 GMT</pubDate> <description><![CDATA[My first post]]></description> <author>stark@example.com (Tony Stark)</author> <enclosure url="https://nextjs-tailwind-contentlayer-blog-starter.vercel.app/og-image.png" length="0" type="image/png"/> </item> </channel> </rss>
這篇修改的程式碼如下:
References
- jpmonette/feed: A RSS, Atom and JSON Feed generator for Node.js, making content syndication simple and intuitive! ?
- Generate RSS feeds for your static Next.js blog | Phiilu | Florian Kapfenberger
- 為 Next.js 靜態網站產生 RSS feed - kpman | code
下一篇
恭喜你完成了 RSS Feed 生成!
目前部落格該有的功能都具備了,已經是個可以實際上線的專案了。
但後面還有 11 天,我們當然不會止步於此。後面 11 天我們會繼續加入更多炫砲功能,繼續朝向 Modern Blog 前進。
下一篇我們會修改文章內文 heading 小標題樣式,為每個 heading 加入 anchor 連結!