import fs from "fs"; import { CopyrightAnnouncement, LatestPostCountInHomePage, WebsiteURL } from "@/consts/consts"; import { Config } from "@/data/config"; import { Feed } from "feed"; import { JSDOM } from "jsdom"; import { MDXRemote } from "next-mdx-remote"; import { serialize } from "next-mdx-remote/serialize"; import { renderToString } from "react-dom/server"; import rehypeAutolinkHeadings from "rehype-autolink-headings"; import rehypeMathJax from "rehype-mathjax/svg"; import rehypePresetMinify from "rehype-preset-minify"; import rehypeRaw from "rehype-raw"; import rehypeSlug from "rehype-slug"; import externalLinks from "remark-external-links"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; import remarkPrism from "remark-prism"; import { getPostFileContent, sortedPosts } from "./post-process"; const NoticeForRSSReaders = (postId: string) => ` --- **NOTE:** Different RSS reader may have deficient even no support for svg formulations rendering. If it happens, [please read the origin web page](https://${Config.SiteDomain}/blog/${postId}) to have better experience `; function minifyHTMLCode(htmlString: string): string { const dom = new JSDOM(htmlString); const document = dom.window.document; const elements = document.querySelectorAll("*"); const unusedElements = document.querySelectorAll("script, style"); // Remove all class attributes. elements.forEach((element) => { element.removeAttribute("class"); }); // Remove all script and style tags. unusedElements.forEach((element) => { element.parentElement?.removeChild(element); }); return dom.serialize(); } /** * Generate the RSS Feed File in `./public` so it could be visited by https://domain/rss.xml */ export const generateRSSFeed = async () => { const feed = new Feed({ title: Config.SiteTitle, description: Config.Sentence, id: Config.SiteDomain, link: WebsiteURL, image: Config.PageCovers.websiteCoverURL, favicon: `https://${Config.SiteDomain}/favcion.ico`, copyright: CopyrightAnnouncement, generator: "Node.js Feed", author: { name: Config.AuthorName, email: Config.SocialLinks.email, link: WebsiteURL, }, }); for (let i = 0; i < Math.min(LatestPostCountInHomePage, sortedPosts.allPostList.length); i++) { const post = sortedPosts.allPostList[i]; const postFileContent = `${getPostFileContent(post.id)}${NoticeForRSSReaders(post.id)}`; const dateNumber = post.frontMatter.time.split("-").map((num: string) => Number.parseInt(num)); const mdxSource = await serialize(postFileContent ?? "", { parseFrontmatter: true, mdxOptions: { remarkPlugins: [remarkPrism, externalLinks, remarkMath, remarkGfm], rehypePlugins: [rehypeMathJax, rehypeAutolinkHeadings, rehypeSlug, rehypePresetMinify as any, rehypeRaw], format: "md", }, }); const htmlContent = minifyHTMLCode(renderToString()); feed.addItem({ title: post.frontMatter.title, id: post.id, link: `https://${Config.SiteDomain}/blog/${post.id}`, description: post.frontMatter.summary ?? undefined, content: htmlContent, author: [ { name: Config.AuthorName, email: Config.SocialLinks.email, link: `https://${Config.SiteDomain}/about`, }, ], category: post.frontMatter.tags?.map((tagname: string) => ({ name: tagname, })), date: new Date(dateNumber[0], dateNumber[1], dateNumber[2]), image: post.frontMatter.coverURL ?? undefined, }); } fs.writeFile("./public/rss.xml", feed.rss2(), "utf-8", (err) => {}); };