Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | import type { ParsedCommit, Tag } from '../git-utils/tags'
import path from 'node:path'
import process from 'node:process'
import chalk from 'chalk'
import { x } from 'tinyexec'
import { gitCommit } from '../git-utils/commit'
import { GitTagParser } from '../git-utils/tags'
import { detectMonorepo } from '../monorepo/detect'
import { findPackages } from '../monorepo/packages'
import { createFile } from '../utils/file'
/* eslint-disable unused-imports/no-unused-vars */
const commitTypes = [
{ value: 'feat', name: '🚀 Features: 新功能', emoji: '🚀' },
{ value: 'perf', name: '🔥 Performance: 性能优化', emoji: '🔥' },
{ value: 'fix', name: '🩹 Fixes: 缺陷修复', emoji: '🩹' },
{ value: 'refactor', name: '💅 Refactors: 代码重构', emoji: '💅' },
{ value: 'docs', name: '📖 Documentation: 文档', emoji: '📖' },
{ value: 'build', name: '📦 Build: 构建工具', emoji: '📦' },
{ value: 'types', name: '🌊 Types: 类型定义', emoji: '🌊' },
{ value: 'chore', name: '🏡 Chore: 简修处理', emoji: '🏡' },
{ value: 'examples', name: '🏀 Examples: 例子展示', emoji: '🏀' },
{ value: 'test', name: '✅ Tests: 测试用例', emoji: '✅' },
{ value: 'style', name: '🎨 Styles: 代码风格', emoji: '🎨' },
{ value: 'ci', name: '🤖 CI: 持续集成', emoji: '🤖' },
{ value: 'init', name: '🎉 Init: 项目初始化', emoji: '🎉' },
]
// 根据提交类型生成 changelog
export function generateChangelog(commits: ParsedCommit[], title = 'Changelog'): string {
// 按时间排序
const sortedCommits = commits.sort((a, b) => (b.date?.getTime() ?? 0) - (a.date?.getTime() ?? 0))
const changelog: string[] = [`## ${title}`]
// 根据类型分类
commitTypes.forEach(({ value, name, emoji }, index) => {
const filteredCommits = sortedCommits.filter(commit => commit.type === value)
if (filteredCommits.length > 0) {
changelog.push(`\n### ${name}\n`)
filteredCommits.forEach((commit) => {
let sha = commit.sha ? `(${commit.sha})` : undefined
sha = sha?.padStart(sha.length + 1, ' ') || ''
let author = commit.author ? `- by @${commit.author}` : undefined
author = author?.padStart(author.length + 1, ' ') || ''
changelog.push(`- ${commit.raw}${author}${sha}`)
})
}
})
changelog.length && changelog.push(`${changelog.pop()}\n`) // 在最后添加换行
return changelog.join('\n')
}
function getAllTags(tag: Tag): Tag[] {
const tags: Tag[] = [tag] // 初始化一个包含当前标签的数组
// 如果有 `pre` 标签,递归调用
if (tag.pre) {
tags.push(...getAllTags(tag.pre)) // 合并结果
}
return tags
}
/**
* 重建整个项目的 changelog
*/
export async function resetChangelog() {
const parser = new GitTagParser()
await parser.fetchTags()
console.log(chalk.blue('Checking monorepo structure...'))
const monorepo = await detectMonorepo()
if (!monorepo) {
console.log(chalk.red('Not a pnpm monorepo project'))
return process.exit(1)
}
const allPackages = await findPackages(monorepo)
const publishable = allPackages.filter(p => !p.private)
for (const pkg of publishable) {
const currTag = `${pkg.name}@${pkg.version}`
const prevTag = await parser.getPreviousTag(currTag, true, true)
if (prevTag) {
const tagsInfo = getAllTags(prevTag).reverse()
const tags = [...tagsInfo.map(item => item.tag), currTag]
const changelogs: string[] = []
for (let index = 0; index < tags.length; index++) {
const currentTag = tags[index]!
const prevTag = tags[index - 1]
const commits = await parser.getCommitsBetweenTags(currentTag, prevTag, true)
changelogs.push(generateChangelog(commits, currentTag))
}
// TODO: 可以选择拆分更新日志至细分文件
const changelogPath = createFile(path.join(pkg.path, 'changelog.md'), changelogs.reverse().join('\n'), { cover: true })
await x('npx', ['eslint', '--fix', changelogPath])
const commitMsg = `chore(changelog): ${pkg.name}@{${parser.extractScopeProjectVersion(tagsInfo[0]!.tag).version}..${pkg.version}}`
await gitCommit([changelogPath], commitMsg)
}
}
}
|