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;