import { Marked, type MarkedExtension } from "marked"; import { createDirectives, presetDirectiveConfigs } from "marked-directive"; import markedAlert from "marked-alert"; import markedMermaid from "./mermaid"; import markedTable from "./table"; import { gfmHeadingId } from "marked-gfm-heading-id"; import markedColumns from "./columns"; import markedCodeBlockYamlTag from "./code-block-yaml-tag"; let globalIconPrefix: string | undefined = undefined; function overrideIconPrefix(path?: string) { globalIconPrefix = path; return { [Symbol.dispose]() { globalIconPrefix = undefined; }, }; } // 使用 marked-directive 来支持指令语法 const marked = new Marked() .use(gfmHeadingId()) .use(markedAlert()) .use(markedMermaid()) .use(markedTable()) .use(markedCodeBlockYamlTag()) .use( createDirectives([ ...presetDirectiveConfigs, { marker: "::::", level: "container", }, { marker: ":::::", level: "container", }, { level: "inline", marker: ":", // :[icon] 或 :[icon.ext] 语法 // 支持的扩展名: .svg, .png, .gif, .jpg, .jpeg, .webp // 如果不指定扩展名,默认为 .png renderer(token) { if (!token.meta.name) { const iconText = token.text || ""; // 已知支持的图片扩展名 const supportedExtensions = [ "svg", "png", "gif", "jpg", "jpeg", "webp", ]; // 检查是否包含扩展名(查找最后一个点) let iconName = iconText; let extension = "png"; // 默认扩展名 const lastDotIndex = iconName.lastIndexOf("."); if (lastDotIndex > 0) { const potentialExt = iconName .slice(lastDotIndex + 1) .toLowerCase(); if (supportedExtensions.includes(potentialExt)) { extension = potentialExt; iconName = iconName.slice(0, lastDotIndex); } } const label = token.attrs?.label as string | undefined; const inner = label ? `${label}${label}` : ""; const style = globalIconPrefix ? `style="--icon-src: url('${globalIconPrefix}/${iconName}.${extension}')"` : ""; const iconHtml = `${inner}`; const repeat = parseInt(`${token.attrs?.repeat || ""}`); const join = token.attrs?.join || ""; const separator = join ? `<${join}>` : ""; if (isNaN(repeat) || repeat < 1) return iconHtml; return Array(repeat).fill(iconHtml).join(separator); } return false; }, }, ]), { extensions: [...markedColumns()], }, ); export function parseMarkdown(content: string, iconPrefix?: string): string { using prefix = overrideIconPrefix(iconPrefix); return marked.parse(content.trimStart()) as string; } export { marked };