import { escapeHtml } from "../inline/render.js"; import type { BlockHandler } from "./handler.type.js"; export const codeBlockHandler = { render(raw: string, attrs?: Record): string { const lang = attrs?.language; const filepath = attrs?.filepath; const codeAttrs: string[] = []; if (lang) codeAttrs.push(`class="language-${escapeHtml(lang)}"`); if (filepath) { codeAttrs.push(`data-filepath="${escapeHtml(filepath)}"`); } const attrStr = codeAttrs.length > 0 ? ` ${codeAttrs.join(" ")}` : ""; const preAttrs: string[] = []; if (filepath) preAttrs.push(`data-filepath="${escapeHtml(filepath)}"`); const preAttrStr = preAttrs.length > 0 ? ` ${preAttrs.join(" ")}` : ""; const escaped = escapeHtml(raw); const body = raw === "" || raw.endsWith("\n") ? escaped : `${escaped}\n`; return `${body}`; }, serialize(raw: string, attrs?: Record): string { const parts: string[] = []; if (attrs?.language) parts.push(attrs.language); for (const [k, v] of Object.entries(attrs ?? {})) { if (k === "language") continue; const needsQuote = v.includes(" "); parts.push(needsQuote ? `${k}="${v}"` : `${k}=${v}`); } // Widen the fence past any backtick run in the content, otherwise a code // sample containing ``` closes its own block and the document splits // into three on the next parse. let longest = 0; for (const run of raw.match(/`+/g) ?? []) { longest = Math.max(longest, run.length); } const fence = "`".repeat(Math.max(3, longest + 1)); return `${fence}${parts.join(" ")}\n${raw}\n${fence}`; }, } satisfies BlockHandler;