Vectrum
Sign In
JjohannSoAdd wikisHistory
44 lines · 1.7 KB · TypeScript
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";
import { remarkWikiLinks, wikiUrl } from "@/lib/wiki-links";

/**
 * 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. With `wiki` (the wiki's base URL) instead, they
 * resolve to the wiki's pages and files, and [[Wiki Links]] work.
 */
export function Markdown({
  children,
  context,
  wiki,
}: {
  children: string;
  context?: { base: string; gitRef: string; dir: string };
  wiki?: string;
}) {
  return (
    <div className="markdown">
      <ReactMarkdown
        remarkPlugins={wiki ? [remarkGfm, [remarkWikiLinks, { base: wiki }]] : [remarkGfm]}
        rehypePlugins={[rehypeRaw, rehypeSanitize]}
        urlTransform={(url, key) => {
          const safe = defaultUrlTransform(url);
          if ((!context && !wiki) || !safe || /^([a-z][a-z0-9+.-]*:|\/\/|#|\/)/i.test(safe)) return safe;
          if (wiki) return wikiUrl(wiki, safe, key);
          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}
      </ReactMarkdown>
    </div>
  );
}