const fs = require("fs"); const path = require("path"); const { marked } = require("marked"); function escapeHtml(s) { return s .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function stripHtml(s) { return s.replace(/<[^>]*>/g, ""); } function buildToc(items) { if (items.length === 0) return ""; let html = ""; let prevDepth = 0; for (const item of items) { if (item.depth > prevDepth) { for (let i = prevDepth; i < item.depth; i++) html += ""; } html += `
  • ${escapeHtml(item.text)}
  • \n`; prevDepth = item.depth; } for (let i = 0; i < prevDepth; i++) html += ""; return html; } function markdownToHtml(md) { const renderer = new marked.Renderer(); const defaultCode = renderer.code.bind(renderer); renderer.code = function ({ text, lang }) { if (lang === "mermaid") { return `
    ${escapeHtml(text)}
    \n`; } return defaultCode({ text, lang }); }; const tocItems = []; renderer.heading = function ({ text, depth }) { const slug = text .toLowerCase() .replace(/<[^>]*>/g, "") .replace(/[^\w\s-]/g, "") .replace(/\s+/g, "-") .replace(/-+/g, "-") .trim(); tocItems.push({ text: stripHtml(text), depth, slug }); return `${text}\n`; }; marked.setOptions({ renderer, gfm: true, breaks: false }); const bodyHtml = marked.parse(md); const tocHtml = buildToc(tocItems); const docTitle = tocItems.length > 0 ? tocItems[0].text : "Document"; return ` ${escapeHtml(docTitle)}
    ${bodyHtml}
    `; } async function htmlToPdf(html) { const puppeteer = require("puppeteer-core"); const browser = await puppeteer.launch({ executablePath: process.env.CHROME_PATH || "/usr/bin/google-chrome-stable", args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"], }); try { const page = await browser.newPage(); const htmlForPdf = html.replace( /