import path from "node:path/posix"; import ReactMarkdown, { defaultUrlTransform } from "react-markdown"; import rehypeRaw from "rehype-raw"; import rehypeSanitize from "rehype-sanitize"; import remarkGfm from "remark-gfm"; import { encodePath } from "@/lib/format"; /** * Renders GitHub-flavored markdown. Inline HTML is allowed but sanitized with * GitHub's own allowlist, so untrusted READMEs and issue bodies are safe. When * `context` is given, relative links and images resolve against the file's * location in the repository. */ export function Markdown({ children, context, }: { children: string; context?: { base: string; gitRef: string; dir: string }; }) { return (
{ const safe = defaultUrlTransform(url); if (!context || !safe || /^([a-z][a-z0-9+.-]*:|\/\/|#|\/)/i.test(safe)) return safe; const [target, hash] = safe.split("#"); const resolved = path.normalize(path.join(context.dir, target)).replace(/^(\.\.\/)+/, ""); const kind = key === "src" ? "raw" : "blob"; return `${context.base}/${kind}/${encodePath(context.gitRef)}/${encodePath(resolved)}${hash ? `#${hash}` : ""}`; }} > {children}
); }