import type MarkdownIt from "markdown-it" /** 匹配以 --- 开头/结尾的 YAML front matter(全文开头) */ const FRONT_MATTER_RE = /^---[\t ]*\r?\n([\s\S]*?)\r?\n---[\t ]*(?:\r?\n|$)([\s\S]*)$/ export function splitFrontMatter(markdown: string): { front: string | null; body: string } { const s = markdown.replace(/^\uFEFF/, "") const m = FRONT_MATTER_RE.exec(s) if (!m) { return { front: null, body: markdown } } const frontBlock = m[1] ?? "" const body = m[2] ?? "" return { front: frontBlock.trim(), body } } /** 正文渲染用:去掉 front matter,避免把元数据块排进 HTML */ export function stripFrontMatter(markdown: string): string { return splitFrontMatter(markdown).body } function pickYamlScalar(block: string, key: string): string { const lines = block.split(/\r?\n/) const prefix = `${key}:` for (const line of lines) { const t = line.trim() if (!t.startsWith(prefix)) { continue } let v = t.slice(prefix.length).trim() if (!v) { return "" } if ( (v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'")) ) { return v.slice(1, -1) } return v } return "" } /** 列表摘要:优先 `desc`,其次 `description`(简单标量行) */ export function extractFrontMatterDesc(markdown: string): string { const { front } = splitFrontMatter(markdown) if (!front) { return "" } const d = pickYamlScalar(front, "desc") || pickYamlScalar(front, "description") return d.trim() } export function listCardSummaryFromPostBody(bodyMarkdown: string, dbExcerpt: string): string { const fromFm = extractFrontMatterDesc(bodyMarkdown) if (fromFm) { return fromFm } return (dbExcerpt ?? "").trim() } /** * md-editor-v3 的 `markdownItConfig`:在 core 管线中去掉 front matter,预览与站点渲染一致。 * `ruleName` 须唯一(多编辑器实例时用 useId 等生成)。 */ export function attachMarkdownItStripFrontMatter(md: MarkdownIt, ruleName: string): void { md.core.ruler.after("normalize", ruleName, (state) => { state.src = stripFrontMatter(state.src) }) }