From 0387754a63ebe7e36b1640a17375ea646256d3c5 Mon Sep 17 00:00:00 2001 From: azu Date: Wed, 15 Mar 2023 23:23:25 +0900 Subject: [PATCH 1/2] feat: support content on RSS Feeds --- package-lock.json | 67 +++++++++++++++++++++ package.json | 3 + scripts/next-data/generatePreBuildFiles.mjs | 38 ++++++++++-- 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9134d8ff7142b..c53adda13404e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@mdx-js/mdx": "^2.3.0", "@mdx-js/react": "^2.3.0", "classnames": "^2.3.2", "feed": "^4.2.2", @@ -24,6 +25,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-intl": "^6.2.10", + "remark-frontmatter": "^4.0.1", + "remark-gfm": "^3.0.1", "sass": "^1.59.2", "semver": "^7.3.8", "strftime": "^0.10.1", @@ -3381,6 +3384,18 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/feed": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", @@ -3468,6 +3483,14 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -5115,6 +5138,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-frontmatter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-1.0.1.tgz", + "integrity": "sha512-JjA2OjxRqAa8wEG8hloD0uTU0kdn8kbtOWpPP94NBkfAlbxn4S8gCGf/9DwFtEeGPXrDcNXdiDjVaRdUFqYokw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0", + "micromark-extension-frontmatter": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-gfm": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz", @@ -6072,6 +6109,21 @@ "uvu": "^0.5.0" } }, + "node_modules/micromark-extension-frontmatter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-1.0.1.tgz", + "integrity": "sha512-9OJhCXkrpj8qIXW5AAgRZGvS8Q4GTMdH5+Ljt98kV4XQVflRGeEhNRYp6O/zCvf8c8lZ+wc4uwmbly27pS/s4Q==", + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/micromark-extension-gfm": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-2.0.1.tgz", @@ -8243,6 +8295,21 @@ "shiki": "*" } }, + "node_modules/remark-frontmatter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-4.0.1.tgz", + "integrity": "sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-frontmatter": "^1.0.0", + "micromark-extension-frontmatter": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-gfm": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", diff --git a/package.json b/package.json index 8b2436a0c1903..eab8b72e536b4 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "scripts:release-post": "node scripts/release-post/index.mjs" }, "dependencies": { + "@mdx-js/mdx": "^2.3.0", "@mdx-js/react": "^2.3.0", "classnames": "^2.3.2", "feed": "^4.2.2", @@ -43,6 +44,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-intl": "^6.2.10", + "remark-frontmatter": "^4.0.1", + "remark-gfm": "^3.0.1", "sass": "^1.59.2", "semver": "^7.3.8", "strftime": "^0.10.1", diff --git a/scripts/next-data/generatePreBuildFiles.mjs b/scripts/next-data/generatePreBuildFiles.mjs index d012c8b4571fc..026ff6e281fcb 100644 --- a/scripts/next-data/generatePreBuildFiles.mjs +++ b/scripts/next-data/generatePreBuildFiles.mjs @@ -1,8 +1,13 @@ // @TODO: This is a temporary hack until we migrate to the `nodejs/nodejs.dev` codebase -import { writeFile } from 'fs/promises'; +import { writeFile, readFile} from 'fs/promises'; import { join } from 'path'; import { Feed } from 'feed'; - +import { evaluate } from '@mdx-js/mdx' +import { createElement } from 'react' +import { renderToString } from 'react-dom/server' +import remarkGfm from 'remark-gfm' +import remarkFrontmatter from 'remark-frontmatter' +import * as runtime from 'react/jsx-runtime' import { getRelativePath } from './_helpers.mjs'; // imports the global site config @@ -29,6 +34,25 @@ export const generateBlogYearPages = cachedBlogData => .then(data => [...new Set(data)]) .then(data => data.forEach(createBlogYearFile)); +/** + * Render markdown to html via mdx + * @param {string} body + * @returns {Promise} + * @see {https://github.com/mdx-js/mdx/discussions/1985} + */ +export const renderMarkdown = async body => { + const { default: mdx } = await evaluate(body, { + ...runtime, + format: "md", + development: false, + // format: 'md' — treat file as plain vanilla markdown + // Need to add the following remark plugins to support GFM and frontmatter + // https://github.com/remarkjs/remark-gfm + // https://github.com/remarkjs/remark-frontmatter + remarkPlugins: [remarkGfm, remarkFrontmatter], + }) + return renderToString(createElement(mdx)) +} export const generateWebsiteFeeds = cachedBlogData => siteConfig.rssFeeds.forEach(metadata => { const feed = new Feed({ @@ -43,16 +67,20 @@ export const generateWebsiteFeeds = cachedBlogData => ? `/en/blog/${metadata.blogCategory}` : '/en/blog'; - const mapBlogPostToFeed = post => + const mapBlogPostToFeed = async post => { + const markdownContent = await readFile(join(blogPath, post.category, post.file), 'utf8'); + const htmlContent = await renderMarkdown(markdownContent) feed.addItem({ title: post.title, + content: htmlContent, id: `https://nodejs.org/en${post.slug}`, link: `https://nodejs.org/en${post.slug}`, author: post.author, date: new Date(post.date), - }); + }) + }; cachedBlogData(blogCategoryOrAll) - .then(({ blogData }) => blogData.posts.forEach(mapBlogPostToFeed)) + .then(({ blogData }) => Promise.all(blogData.posts.map(mapBlogPostToFeed))) .then(() => writeFile(join(publicFeedPath, metadata.file), feed.rss2())); }); From 9c4b4e6523ddefeb78ce7772322283fbdfd41e15 Mon Sep 17 00:00:00 2001 From: azu Date: Wed, 15 Mar 2023 23:37:33 +0900 Subject: [PATCH 2/2] fix: fix order of feed items --- scripts/next-data/generatePreBuildFiles.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/next-data/generatePreBuildFiles.mjs b/scripts/next-data/generatePreBuildFiles.mjs index 026ff6e281fcb..f2ce6262e6e10 100644 --- a/scripts/next-data/generatePreBuildFiles.mjs +++ b/scripts/next-data/generatePreBuildFiles.mjs @@ -70,17 +70,18 @@ export const generateWebsiteFeeds = cachedBlogData => const mapBlogPostToFeed = async post => { const markdownContent = await readFile(join(blogPath, post.category, post.file), 'utf8'); const htmlContent = await renderMarkdown(markdownContent) - feed.addItem({ + return { title: post.title, content: htmlContent, id: `https://nodejs.org/en${post.slug}`, link: `https://nodejs.org/en${post.slug}`, author: post.author, date: new Date(post.date), - }) + }; }; cachedBlogData(blogCategoryOrAll) .then(({ blogData }) => Promise.all(blogData.posts.map(mapBlogPostToFeed))) + .then((feedItems) => feedItems.forEach(feedItem => feed.addItem(feedItem))) .then(() => writeFile(join(publicFeedPath, metadata.file), feed.rss2())); });