108 lines
3.7 KiB
TypeScript
108 lines
3.7 KiB
TypeScript
import { Marked } from 'marked';
|
|
import {createDirectives, presetDirectiveConfigs} from 'marked-directive';
|
|
import yaml from 'js-yaml';
|
|
import markedAlert from "marked-alert";
|
|
import markedMermaid from "./mermaid";
|
|
import {gfmHeadingId} from "marked-gfm-heading-id";
|
|
|
|
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(createDirectives([
|
|
...presetDirectiveConfigs,
|
|
{
|
|
marker: '::::',
|
|
level: 'container'
|
|
},
|
|
{
|
|
marker: ':::::',
|
|
level: 'container'
|
|
},
|
|
{
|
|
level: 'inline',
|
|
marker: ':',
|
|
// :[blah] becomes <i class="icon icon-blah"></i>
|
|
renderer(token) {
|
|
if (!token.meta.name) {
|
|
const style = globalIconPrefix ? `style="--icon-src: url('${globalIconPrefix}${token.text}.png')"` : '';
|
|
return `<icon ${style} class="icon-${token.text}"></icon>`;
|
|
}
|
|
return false;
|
|
}
|
|
},
|
|
]), {
|
|
// 自定义代码块渲染器,支持 yaml/tag 格式
|
|
extensions: [{
|
|
name: 'code-block-yaml-tag',
|
|
level: 'block',
|
|
start(src: string) {
|
|
// 检测 ```yaml/tag 开头的代码块
|
|
return src.match(/^```yaml\/tag\s*\n/m)?.index;
|
|
},
|
|
tokenizer(src: string) {
|
|
const rule = /^```yaml\/tag\s*\n([\s\S]*?)\n```/;
|
|
const match = rule.exec(src);
|
|
if (match) {
|
|
const yamlContent = match[1]?.trim() || '';
|
|
const props = yaml.load(yamlContent) as Record<string, unknown> || {};
|
|
|
|
// 提取 tag 名称,默认为 tag-unknown
|
|
const tagName = (props.tag as string) || 'tag-unknown';
|
|
|
|
// 移除 tag 属性,剩下的作为 HTML 属性
|
|
const { tag, ...rest } = props;
|
|
|
|
// 提取 innerText 内容(如果有 body 字段)
|
|
let content = '';
|
|
if ('body' in rest) {
|
|
content = String(rest.body || '');
|
|
delete (rest as Record<string, unknown>).body;
|
|
}
|
|
|
|
// 构建属性字符串
|
|
const propsStr = Object.entries(rest)
|
|
.map(([key, value]) => {
|
|
const strValue = String(value);
|
|
// 如果值包含空格或特殊字符,添加引号
|
|
if (strValue.includes(' ') || strValue.includes('"')) {
|
|
return `${key}="${strValue.replace(/"/g, '"')}"`;
|
|
}
|
|
return `${key}="${strValue}"`;
|
|
})
|
|
.join(' ');
|
|
|
|
return {
|
|
type: 'code-block-yaml-tag',
|
|
raw: match[0],
|
|
tagName,
|
|
props: propsStr,
|
|
content
|
|
};
|
|
}
|
|
},
|
|
renderer(token: any) {
|
|
// 渲染为自定义 HTML 标签
|
|
const propsAttr = token.props ? ` ${token.props}` : '';
|
|
return `<${token.tagName}${propsAttr}>${token.content || ''}</${token.tagName}>\n`;
|
|
}
|
|
}]
|
|
});
|
|
|
|
export function parseMarkdown(content: string, iconPrefix?: string): string {
|
|
using prefix = overrideIconPrefix(iconPrefix);
|
|
return marked.parse(content.trimStart()) as string;
|
|
}
|
|
|
|
export { marked };
|