on master help sign in
meta: generate rss feed of all blog posts, events, updates
574c4efMatilde Park
.gitignore+2
@@ -5,6 +5,8 @@
5/.pnp
5/.pnp
6.pnp.js
6.pnp.js
7
7
8+/public
9+
8# testing
10# testing
9/coverage
11/coverage
10
12
components/Meta.js+6
@@ -20,6 +20,12 @@ export default function Meta(post) {
20 <meta name="description" content={description} />
20 <meta name="description" content={description} />
21 <meta name="author" content={author} key="author" />
21 <meta name="author" content={author} key="author" />
22 <meta name="twitter:image" content={image} key="image" />
22 <meta name="twitter:image" content={image} key="image" />
23+ <link
24+ rel="alternative"
25+ type="application/rss+xml"
26+ title="RSS"
27+ href="/rss.xml"
28+ />
23 </>
29 </>
24 );
30 );
25}
31}
lib/rss.jsnew file+97
@@ -0,0 +1,97 @@
1+const fs = require("fs");
2+const path = require("path");
3+const matter = require("gray-matter");
4+const toml = require("@iarna/toml");
5+const Feed = require("feed").Feed;
6+
7+const options = {
8+ engines: {
9+ toml: toml.parse.bind(toml),
10+ },
11+ language: "toml",
12+ delimiters: "+++",
13+};
14+
15+const generateRSSFeed = () => {
16+ let posts = [];
17+
18+ const getAllChildren = (dir) => {
19+ const children = fs.readdirSync(dir, { withFileTypes: true });
20+ return children
21+ .filter((f) => f.isFile() && f.name !== "_index.md")
22+ .map((post) => {
23+ const { data: pageData, content: pageContent } = matter(
24+ fs.readFileSync(path.join(dir, post.name)),
25+ options
26+ );
27+ return {
28+ title: pageData.title,
29+ descripton: pageData.description || "",
30+ author: pageData.extra.author || "",
31+ date: pageData.date,
32+ link: path.join(
33+ "https://urbit.org/",
34+ dir.substr(dir.indexOf("content") + 7),
35+ "/",
36+ post.name.replace(/.md$/, "")
37+ ),
38+ content: pageContent,
39+ };
40+ });
41+ };
42+ posts.push(
43+ ...getAllChildren(path.join(process.cwd(), "content/blog")),
44+ ...getAllChildren(path.join(process.cwd(), "content/updates")),
45+ ...getAllChildren(path.join(process.cwd(), "content/events"))
46+ );
47+ posts = posts.sort((a, b) => (a.date > b.date ? -1 : 1));
48+
49+ const baseUrl = "https://urbit.org";
50+ const author = {
51+ name: "Urbit.org",
52+ email: "support@urbit.org",
53+ link: "https://urbit.org",
54+ };
55+
56+ // Construct a new Feed object
57+ const feed = new Feed({
58+ title: "Urbit",
59+ description: "Urbit is a personal server built from scratch",
60+ id: baseUrl,
61+ link: baseUrl,
62+ language: "en",
63+ feedLinks: {
64+ rss2: `${baseUrl}/rss.xml`,
65+ },
66+ author,
67+ });
68+
69+ // Add each article to the feed
70+ posts.forEach((post) => {
71+ const { title, description, author, date, link, content } = post;
72+ feed.addItem({
73+ title,
74+ id: link,
75+ link: link,
76+ description,
77+ content,
78+ author: [
79+ {
80+ name: author,
81+ email: "",
82+ link: "",
83+ },
84+ ],
85+ date: new Date(date),
86+ });
87+ });
88+ try {
89+ fs.readdirSync("public");
90+ } catch (err) {
91+ fs.mkdirSync("public");
92+ }
93+
94+ fs.writeFileSync("public/rss.xml", feed.rss2());
95+};
96+
97+generateRSSFeed();
package-lock.json+16
@@ -2897,6 +2897,14 @@
2897 "reusify": "^1.0.4"
2897 "reusify": "^1.0.4"
2898 }
2898 }
2899 },
2899 },
2900+ "feed": {
2901+ "version": "4.2.2",
2902+ "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz",
2903+ "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==",
2904+ "requires": {
2905+ "xml-js": "^1.6.11"
2906+ }
2907+ },
2900 "file-type": {
2908 "file-type": {
2901 "version": "10.11.0",
2909 "version": "10.11.0",
2902 "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz",
2910 "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz",
@@ -6828,6 +6836,14 @@
6828 "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.2.tgz",
6836 "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.2.tgz",
6829 "integrity": "sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ=="
6837 "integrity": "sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ=="
6830 },
6838 },
6839+ "xml-js": {
6840+ "version": "1.6.11",
6841+ "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
6842+ "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
6843+ "requires": {
6844+ "sax": "^1.2.4"
6845+ }
6846+ },
6831 "xml-name-validator": {
6847 "xml-name-validator": {
6832 "version": "3.0.0",
6848 "version": "3.0.0",
6833 "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
6849 "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
package.json+5−3
@@ -3,12 +3,13 @@
3 "version": "0.1.0",
3 "version": "0.1.0",
4 "private": true,
4 "private": true,
5 "scripts": {
5 "scripts": {
6− "dev": "npm run cache-posts && next dev",
6+ "dev": "npm run cache-posts && npm run rss && next dev",
7− "build": "npm run cache-posts && next build",
7+ "build": "npm run cache-posts && npm run rss && next build",
8− "export": "npm run cache-posts && next build && next export",
8+ "export": "npm run cache-posts && npm run rss && next build && next export",
9 "start": "next start",
9 "start": "next start",
10 "prepare": "husky install",
10 "prepare": "husky install",
11 "cache-posts": "node lib/cache.js",
11 "cache-posts": "node lib/cache.js",
12+ "rss": "node lib/rss.js",
12 "postinstall": "patch-package"
13 "postinstall": "patch-package"
13 },
14 },
14 "dependencies": {
15 "dependencies": {
@@ -17,6 +18,7 @@
17 "autoprefixer": "^10.2.6",
18 "autoprefixer": "^10.2.6",
18 "classnames": "^2.3.1",
19 "classnames": "^2.3.1",
19 "critters": "0.0.10",
20 "critters": "0.0.10",
21+ "feed": "^4.2.2",
20 "fuzzy-search": "^3.2.1",
22 "fuzzy-search": "^3.2.1",
21 "gray-matter": "^4.0.3",
23 "gray-matter": "^4.0.3",
22 "html-entities": "^2.3.2",
24 "html-entities": "^2.3.2",