#!/usr/bin/env node /** * extract-release-notes.mjs * * 从 CHANGELOG.md 中提取指定版本的发版说明。 * * 用法:node scripts/extract-release-notes.mjs 0.2.0 * 输出:该版本的 Markdown 正文(输出到 stdout) */ import { readFileSync } from 'fs' import { resolve, dirname } from 'path' import { fileURLToPath } from 'url' const __dirname = dirname(fileURLToPath(import.meta.url)) const root = resolve(__dirname, '..') const version = process.argv[2] if (!version) { console.error('用法: node scripts/extract-release-notes.mjs ') process.exit(1) } const changelog = readFileSync(resolve(root, 'CHANGELOG.md'), 'utf8') const lines = changelog.split('\n') // 找到 ## [version] 开头的行(转义版本号中所有特殊正则字符) const escapedVersion = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') const startPattern = new RegExp(`^## \\[${escapedVersion}\\]`) const sectionPattern = /^## \[/ let inSection = false const result = [] for (const line of lines) { if (startPattern.test(line)) { inSection = true continue // 跳过标题行本身 } if (inSection && sectionPattern.test(line)) { break // 遇到下一个版本节,停止 } if (inSection) { result.push(line) } } if (result.length === 0) { console.error(`CHANGELOG.md 中找不到版本 ${version} 的内容`) process.exit(1) } // 去掉首尾空行,并过滤掉内容全是占位符 "-" 的空小节 function removeEmptySections(body) { const sectionRegex = /(^|\n)(###[^\n]*\n)([\s\S]*?)(?=\n###|\n##|$)/g return body.replace(sectionRegex, (_, pre, heading, content) => { const hasRealContent = content.split('\n').some(l => l.trim() && l.trim() !== '-') return hasRealContent ? `${pre}${heading}${content}` : '' }).replace(/\n{3,}/g, '\n\n').trim() } const notes = removeEmptySections(result.join('\n').trim()) process.stdout.write(notes + '\n')