import { render } from "pele"; import { decodeEntities } from "#lib/markup/highlight.ts"; const BLOCK = /([^<]*)<\/pre>/g; // pele is synchronous, these keep a hostile readme from stalling the event loop const MAX_SOURCE_CHARS = 20_000; const MAX_SVG_CHARS = 500_000; const MAX_DIAGRAMS = 10; const MAX_TOTAL_SOURCE_CHARS = 60_000; const MAX_TOTAL_SVG_CHARS = 1_500_000; const drawDiagram = (source: string): string | null => { try { return render(source, { limit: MAX_SOURCE_CHARS, outputLimit: MAX_SVG_CHARS, linkRel: "nofollow noopener noreferrer" }).svg; } catch { return null; } }; // anything left as a pre is still tried by the client export const renderMermaidBlocks = (html: string): string => { let diagrams = 0; let sourceChars = 0; let svgChars = 0; return html.replace(BLOCK, (raw, attrs: string, escaped: string) => { const source = decodeEntities(escaped); if ( diagrams >= MAX_DIAGRAMS || source.length === 0 || sourceChars + source.length > MAX_TOTAL_SOURCE_CHARS ) return raw; diagrams++; sourceChars += source.length; const svg = drawDiagram(source); if (svg === null || svgChars + svg.length > MAX_TOTAL_SVG_CHARS) return raw; svgChars += svg.length; return `${svg}`; }); };