- 开发工具
- CLI
- 文档
【免费下载链接】conventional-changelog
Generate changelogs and release notes from a project's commit messages and metadata.
本文以 packages/conventional-changelog-writer/CHANGELOG.md 为主线,结合
conventional-changelog-writer包的源码(writers.ts、options.ts、commit.ts、context.ts 等),系统梳理该包从独立仓库时期(v1.x,2016 年)到 monorepo 化(v2.0.0,2017 年),再到 TypeScript 重写(v8.0.0,2024 年)与渲染函数替换(v9.0.0,2026 年)的完整演进史,并逐项讲解当前版本的 API、CLI 用法与全部配置项。
一、包定位:conventional-changelog-writer 在工具链中的角色
conventional-changelog-writer是 conventional-changelog 生态中负责"把结构化 commit 数据渲染成 Markdown 变更日志"的核心渲染层。它的上游是conventional-commits-parser(把原始 commit message 解析成结构化对象),下游则是直接生成CHANGELOG.md文本。README 对其功能的描述只有一句话——"Write logs based on conventional commits and templates"(基于 Conventional Commits 与模板编写日志),但整个包的能力远不止于此:
- 提供流式、异步迭代器、整串三种使用形态(见 writers.ts);
- 内置 commit 分组、排序、日期格式化、revert 过滤、上下文推导等默认逻辑;
- 支持通过
transform、generateOn、template、各 partial 渲染函数实现完全定制化输出; - 附带一个可直接消费行分隔 JSON(LDJSON)的 CLI 工具。
二、版本演进主线:从模板字符串到渲染函数
CHANGELOG.md 记录了从 v0.4(2015 年)到 v9.2.1(2026 年)的完整历史。以下是决定架构走向的几个关键节点:
2.1 独立仓库时期(v1.x,2016 年)
- v1.0.0(2016-02-05):首个正式发布。此前的 v0.x 阶段奠定了核心概念:
doFlush、generateOn、notes、transform等选项已具雏形。 - v1.1.0:
generate时把originalCommits作为最后一个参数传入。 - v1.4.0/v1.4.1:context 在
repoUrl存在时自动回退并使用它做引用链接(auto link references)。
2.2 并入 monorepo 与 2.0.0 大重构(2017 年)
v2.0.0 是第一个"里程碑式"破坏性版本,CHANGELOG 中列出的变更揭示了当时的设计决策:
context.host不再能改变context.linkReferences的默认值——如果 host 未知,context.host为undefined,所有链接将直接使用context.repository;closes更名为references;notes对象从键值对象改为数组,每个 note 形如{ title: 'BREAKING AMEND', text: 'some breaking change' };options.replacements更名为options.map,且可以接受函数;commitGroupsCompareFn→commitGroupsSort、commitsCompareFn→commitsSort、noteGroupsCompareFn→noteGroupsSort、notesCompareFn→notesSort;version不再是必需字段,移入context对象,若最后一个 commit 中带版本号会覆盖它;- 默认排序函数从按字典序改为
localeCompare; options.hashLength、options.maxSubjectLength、options.map被废弃,统一收进options.transform;context暴露finalizeContext,允许在最后阶段修改 context。
2.3 统一版本节奏期(v3-v7,2018-2023 年)
- v3.0.0(2018-01-29):重构 release 标题生成逻辑,所有标题层级统一为
##(h2),patch 版本标题用<small>包裹以保持视觉层级,目的是更好地兼容屏幕阅读器与 Markdown 解析器(对应 issue #214)。 - v4.0.0(2018-05-29):从 header 模板中移除锚点标签,并明确建议消费者使用版本对应的完整 release 页面 URL(permalink),而非依赖可能不存在的锚点。
- v5.0.0(2020-12-30):排序时不再支持嵌套对象属性(nested object properties),并移除
compare-func依赖,使排序结果在不同 Node 版本间保持一致。 - v6.0.0(2023-06-06):要求 Node >= 14,并尽可能从依赖中移除 lodash。
- v7.0.0(2023-08-26):要求 Node >= 16;使用
Intl.DateTimeFormat替代dateformat;统一各 preset 的接口(preset 均导出配置工厂函数);transform异步处理器得到修复。
2.4 TypeScript 重写与 ESM 化(v8.0.0,2024 年)
v8.0.0 是近年来影响最大的一次破坏性发布:
- 重写为 TypeScript(PR #1150),
conventional-changelog-writer与conventional-commits-filter(PR #1178)同步 TS 化; - 除
gulp-conventional-changelog外,所有包均为 ESM-only(PR #1144,从 CommonJS 迁移); - 要求Node >= 18;
- 新增
formatDate选项(PR #1189,关闭 issue #1186)与timeZone选项(PR #1162); - 修复了 Date 对象防修改逻辑(PR #1285):
preventModifications的 Proxy 在 getter 中遇到Date实例时直接返回原值,避免把 Date 包进不可变代理导致格式化失败(见 commit.ts); - 8.1.0 起,
transformCommit方法与相关 utils 被加入导出(PR #1350); - 8.2.0 新增
skip选项,可在写 changelog 时跳过指定 commit(PR #1346,关闭 issue #1179 与 #342); - 8.3.0 统一各 preset 的换行格式,并从
simple-libs引入工具函数; - 8.4.0 把 hbs 模板内联为代码字符串。
2.5 v9.0.0:渲染函数取代 Handlebars(2026 年)
v9.0.0 是当前最新的大版本,两项破坏性变更直接重塑了定制方式:
- Handlebars 模板字符串与 partial 文件被替换为渲染函数(render functions)(PR #1477):
template、headerPartial、commitPartial、footerPartial、preamblePartial不再是 hbs 字符串,而是接收 context 并返回字符串(或 Promise )的函数,见 types/options.ts 中TemplateFunction的定义。 - 要求 Node.js 22 或更新版本(PR de5e136)。
v9.1.0 支持 changelog 前言的 partial(preamblePartial,PR #1491);v9.2.0 把 CLI 参数解析从meow换成argue-cli(PR #1505);v9.2.1 修复了 host URL 路径拼接问题(PR #1534,关闭 issue #986)。
三、当前版本 API:三种调用形态
conventional-changelog-writer对外暴露三种写入方式(实现于 writers.ts):
3.1writeChangelogString:最直接的整串输出
README 给出的最小示例即此形态。输入是conventional-commits-parser解析后的 commit 数组,输出是完整 changelog 字符串:
import { writeChangelogString } from 'conventional-changelog-writer' // commits parsed by conventional-commits-parser const commits = [/* ... */] const context = { version: '1.0.0', host: 'https://github.com', owner: 'conventional-changelog', repository: 'conventional-changelog' } console.log(await writeChangelogString(commits, context)) /* ## 1.0.0 (2015-05-29) ### Features * **ng-list:** Allow custom separator ([13f3160](https://github.com/...)) ... */其实现(writers.ts)只是对异步迭代器形态做拼接累加,底层仍是writeChangelog。
3.2writeChangelog:异步生成器(async generator)
返回一个(commits) => AsyncGenerator<string>函数,逐个 yield 每个版本区块的 changelog 文本。第三个参数includeDetails为true时,yield 的不再是纯字符串,而是Details<Commit>对象:
export interface Details<Commit extends CommitKnownProps = CommitKnownProps> { log: string keyCommit: Commit | null }(见 types/index.ts)——log是渲染结果,keyCommit是触发本区块生成的"关键 commit"(通常是携带版本号的提交)。
3.3writeChangelogStream:Transform 流
Transform.from(writeChangelog(...))一行代码把生成器包装成 Node.js Transform 流(writers.ts),便于与pipeline、stdin/stdout等流式场景组合。
3.4 核心工作流
从 writers.ts 的生成器实现可以还原完整处理流水线:
getFinalOptions(options)合并默认选项(见下节),getFinalContext(context, finalOptions)推导最终 context;- 对每个 commit 调用
transformCommit(chunk, transform, finalContext, finalOptions)——先经preventModifications包裹为不可变代理,再交给 transform 函数,返回值(patch)与原始 commit 合并,并把raw指向原始 commit(commit.ts); - 若
skip?.(keyCommit)返回 true,则跳过该 commit; generateOn(keyCommit, commitsGroup)判定是否该生成一个 changelog 区块;默认实现是"commit.version 是合法 semver 时生成"(见 options.ts);- 命中的 commit 累积到
commitsGroup,由createTemplateRenderer渲染出文本块,最终按doFlush/reverse语义决定是否 yield。
注意reverse语义:正常顺序是"时间倒序"(最新在前),reverse: true则按时间正序处理——对应 types 注释"normal order means reverse chronological order"。
四、配置项全解析(Options Reference)
当前版本的完整配置项定义在 types/options.ts,默认值在getFinalOptions(options.ts)中集中给出。下表是全部可配置项:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
groupBy | keyof Commit | 'type' | 按哪个字段对 commit 分组;设为 falsy 则不分组 |
commitsSort | 字段名 | 字段名数组 | 比较函数 | 'header' | commit 组内排序;falsy 则不排序 |
commitGroupsSort | 同上 | 无 | 分组之间的排序 |
notesSort | 同上 | 'text' | note 的排序 |
noteGroupsSort | 同上 | 'title' | note 分组的排序 |
ignoreReverted | boolean | true | 是否忽略被 revert 的 commit(借助conventional-commits-filter的filterRevertedCommitsSync,见 context.ts) |
reverse | boolean | false | true 时按时间正序(chronological)处理 |
doFlush | boolean | true | 是否把最后一段(可能为空的)commit 冲刷输出;从 v0.5.0 引入 |
transform | 函数 | defaultCommitTransform | 变换 commit,返回 patch 对象与原始 commit 合并;返回 falsy 值则该 commit 被忽略 |
generateOn | 函数 | 字段名 |null | commit => Boolean(semverValid(commit.version)) | 判定何时生成 changelog 区块;字符串形式表示"该字段存在即生成",非函数非字符串则永不生成(见 options.ts) |
template | 渲染函数 | 内置 template | 把准备好的 context 渲染成文本(v9 起为函数而非 hbs 字符串) |
headerPartial | 渲染函数 | 内置 | 渲染 release 标题 |
preamblePartial | 渲染函数 | 内置 | 渲染 release 标题之后的引言文本(v9.1.0 新增支持) |
commitPartial | 渲染函数 | 内置 | 渲染单条 commit 条目 |
footerPartial | 渲染函数 | 内置 | 渲染 release 底部 notes |
finalizeContext | 函数 | 恒等函数 | 渲染前最后一次修改 context 的机会,接收(context, options, filteredCommits, keyCommit, commits) |
debug | (message) => void | noop | 输出调试信息,默认会打印最终 context(Your final context is: ...,见 context.ts) |
formatDate | (date) => string | yyyy-mm-dd格式 | v8.0.0 新增;默认实现取toISOString().slice(0, 10)(见 utils.ts) |
skip | (commit) => boolean | 无 | v8.2.0 新增;返回 true 则跳过该 commit 的写入 |
几个容易忽略的实现细节:
- 排序统一走
createComparator:字符串字段名会被编译成(a[key] || '').localeCompare(b[key] || ''),字段名数组则逐字段拼接后比较,也可直接传自定义比较函数(utils.ts)。这正是 v5.0.0 起"不再支持嵌套对象属性"的原因。 - 默认 transform 的裁剪行为:
defaultCommitTransform会把 hash 截断为前 7 位、header 截断为前 100 个字符,并用formatDate格式化committerDate(注意用的是 committerDate 而非 authorDate,这一约定自 v2.0.0 起确立),见 options.ts。 - linkReferences 的自动推导:只要
linkReferences不是显式 boolean、且同时存在repository/repoUrl与commit/issue,就会自动置为 true(context.ts),这是 v0.4.1/v1.4.x 时期"linkReferences 与 host 无关"这一破坏性变更的延续。 isPatch推断:若 context.version 是合法 semver,会据此推导isPatch(semver.patch(version) !== 0),见 context.ts。- 版本号非必需:
version不是必需字段(v2.0.0 起移入 context),若 keyCommit 上带版本号会覆盖 context 中的版本——这与generateOn的默认 semver 判定共同支撑"一个 commit 对应一个 release 区块"的模型。
五、CLI 使用指南
包内自带 CLI 入口(src/cli/index.ts),可直接消费行分隔 JSON 文件或 stdin:
Usage conventional-changelog-writer <path> [<path> ...] cat <path> | conventional-changelog-writer Example conventional-changelog-writer commits.ldjson cat commits.ldjson | conventional-changelog-writer Options -c, --context A filepath of a json that is used to define template variables -o, --options A filepath of a javascript object that is used to define options参数解析由argue-cli完成(v9.2.0 起替换原meow):
-c, --context:指向一个 JSON 文件,内容作为模板变量(context);-o, --options:指向一个 JavaScript 对象文件(.json或可导入的模块,loadDataFile依据扩展名决定JSON.parse还是动态import,见 cli/utils.ts);- 位置参数为 commit 文件列表,每个文件按
JSON.parse解析(单条 commit 对象);若未提供且 stdin 非 TTY,则从 stdin 按 LDJSON 流式读取(parseJsonStream)。
内部实现通过pipeline(inputStream, writeChangelog(context, options), process.stdout)把解析流、生成器与 stdout 串起来,任何错误都会打印并process.exit(1)。测试夹具中的 commits.ldjson 与 context.json 可直接作为 CLI 输入的参考样例。
六、配套测试与验证
包的测试覆盖了以上全部行为,可在仓库中直接查阅:
- writers.spec.ts:验证三种 API 的输出、
includeDetails、doFlush、reverse等生成语义; - commit.spec.ts:验证 transform 的异步处理、falsy 返回值忽略 commit、不可变代理(包括 Date 特殊处理)等;
- context.spec.ts 与 options.spec.ts(见 utils.spec.ts):验证分组、排序、日期格式化、比较器编译;
- template.spec.ts:验证渲染函数模板与 partial 的组合;
- CLI 相关测试见 cli/index.spec.ts。
七、迁移要点与实战建议
针对 CHANGELOG 中列出的各破坏性变更,升级到 v9 时需注意:
- Node 版本:v9 要求 Node >= 22,v8 要求 Node >= 18,v7 要求 Node >= 16,按需选择匹配版本;
- ESM-only:v8 起包只能通过
import使用,CommonJS 项目需改用动态import()或升级构建; - 模板改写:v9 起把 hbs 字符串模板(含 partial 文件)改写为渲染函数——这是迁移工作量最大的部分,函数签名均为
(context) => string | Promise<string>; - 排序与选项名:v5 起排序不再支持嵌套属性、按
localeCompare比较;v2 起的commitsSort/commitGroupsSort/notesSort/noteGroupsSort命名沿用至今,不要使用旧的*CompareFn命名; - 定制入口收敛:
hashLength/maxSubjectLength/map等旧选项早已废弃,统一在transform内实现;需要按 commit 粒度过滤请用skip(v8.2.0+),需要整体跳过 revert 提交请保持ignoreReverted: true。
从 v0.4 到 v9.2.1 的十年演进中,conventional-changelog-writer经历了"模板字符串 → 内联 hbs → 渲染函数"的模板机制迭代、"CJS → ESM"的模块体系迁移、"JS → TypeScript"的类型化改造,以及依赖面的持续瘦身(移除 lodash、compare-func、meow)。理解这份 CHANGELOG,等于同时掌握了该包全部配置项的来历、默认值与最佳实践。
- 开发工具
- CLI
- 文档
【免费下载链接】conventional-changelog
Generate changelogs and release notes from a project's commit messages and metadata.
相关推荐
从 v1 到 v6:next-forge 版本演进全解析(Changelog 深度导读)
从 v1 到 v6:next forge 版本演进全解析(Changelog 深度导读) 本篇以 next forge 仓库的 CHANGELOG.md htt
前端后端示例工程CLIhighlight.js 版本演进全解析:从 CHANGES.md 解读 v9 到 v11 的架构变迁与升级路径
highlight.js 版本演进全解析:从 CHANGES.md 解读 v9 到 v11 的架构变迁与升级路径 本文以开源仓库 highlight.js ht
前端Ionic Framework @ionic/core 版本演进全解:从 v6 到 v9 的 CHANGELOG 深度导读
Ionic Framework @ionic/core 版本演进全解:从 v6 到 v9 的 CHANGELOG 深度导读 本篇技术指南以开源仓库 gh_mir
前端移动开发跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考