/** * 从 frontmatter 文本里读一个顶层标量(整块 metadata 已由调用方从 --- … --- 切好)。 * 多行块(description: > / |):从下一行起收到「下一个顶层键」或 metadata 结束为止。 */ function escapeRegExp(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') } /** 下一行若是顶层键 `foo:`(行首无缩进),则当前块结束。 */ function isTopLevelKeyLine(line) { const t = line.trimStart() if (!t || t.startsWith('#')) return false return /^[A-Za-z_][\w-]*\s*:\s/.test(t) } function minIndentOfNonEmpty(lines) { let min = Infinity for (const l of lines) { if (!l.trim()) continue const n = l.match(/^(\s*)/)[1].length if (n < min) min = n } return min === Infinity ? 0 : min } function dedent(lines) { const min = minIndentOfNonEmpty(lines) return lines.map((l) => (l.length === 0 ? '' : l.slice(min))) } function parseFrontmatterScalarKey(fmBlock, key) { if (!fmBlock || !key) return null const lines = fmBlock.split(/\r?\n/) const keyRe = new RegExp(`^${escapeRegExp(key)}:\\s*(.*)$`) for (let i = 0; i < lines.length; i++) { const m = lines[i].match(keyRe) if (!m) continue let after = m[1].replace(/\s+$/, '') const blockHead = after.match(/^([>|])([-+]?)\s*(.*)$/) if (blockHead) { const mode = blockHead[1] const sameLine = (blockHead[3] || '').trimEnd() const raw = [] if (sameLine) raw.push(sameLine) for (let j = i + 1; j < lines.length; j++) { const line = lines[j] if (line.length > 0 && !/^\s/.test(line) && isTopLevelKeyLine(line)) break raw.push(line) } const body = dedent(raw) if (mode === '>') { return body .map((l) => l.trim()) .filter(Boolean) .join(' ') .trim() } return body.join('\n').trim() } const t = after.trim() if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) { return t.slice(1, -1) } if (t) return t.replace(/^["']|["']$/g, '') return '' } return null } module.exports = { parseFrontmatterScalarKey }