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}>${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 };