From 7749162414f6f7ff282e708ddc7250a4b2cd6bf5 Mon Sep 17 00:00:00 2001 From: Adrian Bonpin Date: Fri, 1 May 2026 09:44:47 +0800 Subject: [PATCH] fix: switch markdown pipeline to remark-rehype for proper HTML rendering --- bun.lock | 11 ++++++----- lib/updates.ts | 8 +++++--- package.json | 3 ++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/bun.lock b/bun.lock index f0bb8fe..a090acd 100644 --- a/bun.lock +++ b/bun.lock @@ -32,8 +32,9 @@ "react-icons": "^5.6.0", "rehype-autolink-headings": "^7.1.0", "rehype-slug": "^6.0.0", + "rehype-stringify": "^10.0.1", "remark": "^15.0.1", - "remark-html": "^16.0.1", + "remark-rehype": "^11.1.2", "resend": "^6.12.2", "web-haptics": "^0.0.6", }, @@ -1079,8 +1080,6 @@ "hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="], - "hast-util-sanitize": ["hast-util-sanitize@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "unist-util-position": "^5.0.0" } }, "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg=="], - "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], "hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="], @@ -1457,12 +1456,14 @@ "rehype-slug": ["rehype-slug@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "github-slugger": "^2.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-to-string": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A=="], + "rehype-stringify": ["rehype-stringify@10.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" } }, "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA=="], + "remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="], - "remark-html": ["remark-html@16.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "hast-util-sanitize": "^5.0.0", "hast-util-to-html": "^9.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0" } }, "sha512-B9JqA5i0qZe0Nsf49q3OXyGvyXuZFDzAP2iOFLEumymuYJITVpiH1IgsTEwTpdptDmZlMDMWeDmSawdaJIGCXQ=="], - "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], "resend": ["resend@6.12.2", "", { "dependencies": { "postal-mime": "2.7.4", "svix": "1.90.0" }, "peerDependencies": { "@react-email/render": "*" }, "optionalPeers": ["@react-email/render"] }, "sha512-xwgmU4b0OqoabJsIoK/x0Whk0Fcs3bpbK4i/DEWPiE5hYJHyHl0TbB6QbI3gIr+bLdLUJ1GYm/fe41aVFuHXgw=="], diff --git a/lib/updates.ts b/lib/updates.ts index e790df6..743997e 100644 --- a/lib/updates.ts +++ b/lib/updates.ts @@ -2,9 +2,10 @@ import fs from "fs" import path from "path" import matter from "gray-matter" import { remark } from "remark" -import remarkHtml from "remark-html" +import remarkRehype from "remark-rehype" import rehypeSlug from "rehype-slug" import rehypeAutolinkHeadings from "rehype-autolink-headings" +import rehypeStringify from "rehype-stringify" export interface UpdateMeta { slug: string @@ -70,15 +71,16 @@ export async function getUpdateBySlug(slug: string): Promise { const { data, content } = matter(fileContents) const processedContent = await remark() - .use(remarkHtml, { sanitize: false }) + .use(remarkRehype) .use(rehypeSlug) .use(rehypeAutolinkHeadings) + .use(rehypeStringify) .process(content) const html = processedContent.toString() // Extract headings from the rendered HTML - const headingRegex = /]*id=["']([^"']+)["'][^>]*>(.*?)<\/h[2-3]>/g + const headingRegex = /]*id=["']([^"']+)["'][^>]*>(.*?)<\/h[1-6]>/g const headings: UpdateHeading[] = [] let match: RegExpExecArray | null while ((match = headingRegex.exec(html)) !== null) { diff --git a/package.json b/package.json index 84e1943..8547851 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,9 @@ "react-icons": "^5.6.0", "rehype-autolink-headings": "^7.1.0", "rehype-slug": "^6.0.0", + "rehype-stringify": "^10.0.1", "remark": "^15.0.1", - "remark-html": "^16.0.1", + "remark-rehype": "^11.1.2", "resend": "^6.12.2", "web-haptics": "^0.0.6" },