Emotion 基于 Changesets 的版本发布指南:从提交变更到 npm 发布的全流程

原创2026-09-20 21:10:10851 阅读
文章标签:前端

Emotion 基于 Changesets 的版本发布指南:从提交变更到 npm 发布的全流程

Emotion(CSS-in-JS 高性能样式组合库)以 monorepo 形式在 package.json 中管理 packages/*、site、scripts/*、playgrounds/* 等众多工作区,其版本管理与 npm 发布完全交给 Changesets 工具链驱动:贡献者提交 changeset,维护者统一执行版本号更新与发布命令,Changelog 自动生成。本文将结合仓库源码级配置,完整讲解从 yarn changeset 到 NPM_CONFIG_OTP=xxx yarn release 的发布流程、底层命令原理与 2FA 超时处理方案,读完即可独立完成一次 Emotion 的正式发布。

一、为什么是 Changesets:版本管理的核心思路

Emotion 在 RELEASING.md 中明确说明:使用 Changesets 做版本管理,让发布"非常容易",且 Changelog 是自动生成的。

其核心思路是变更驱动(change-driven)的版本管理:

  • 每次代码改动不直接提交版本号变更,而是先产生一个描述性的 changeset(一段 Markdown + 受影响包列表 + 各自的 semver bump 类型);
  • 发布前统一由 CLI 聚合所有 changeset,自动计算每个包应升级到的版本号,并同步重写各包的 package.json 与 CHANGELOG.md;
  • 随后执行构建与 npm publish 将包真正推送到 npm registry。

在 package.json 的 dependencies 中可以看到完整的工具链支撑:@changesets/cli@^2.27.7 与 @changesets/changelog-github@^0.5.0(第 184-185 行);yarn.lock 中 @changesets/cli@npm:2.27.7 的依赖树包含 @changesets/apply-release-plan、@changesets/assemble-release-plan、@changesets/read、@changesets/write、@changesets/git 等模块,分别负责"汇总变更计划、计算版本、读取/写入 changeset 文件、操作 git"。

这种设计对 Emotion 这种多包仓库的价值在于:一个 PR 往往同时影响 @emotion/css、@emotion/styled、@emotion/react 等多个包,人工维护版本号极易出错,而 changesets 能根据依赖关系自动推导每个包是否需要连带 bump。

二、贡献者视角:先写 changeset,再谈发布

发布流程的起点在贡献者提交 PR 时。按照 CONTRIBUTING.md 中 "Changesets" 一节的说明,贡献者需要在根目录运行:

yarn changeset

该命令对应 package.json 第 25 行的 script:"changeset": "changeset"。运行后会以交互方式引导你完成三件事:

  1. 选择受影响的包(Emotion 的 packages/* 下每个目录都是一个独立包);
  2. 为每个包选择 semver bump 类型:major(破坏性变更)、minor(新增功能)、patch(缺陷修复);
  3. 用 Markdown 编写变更说明,这段文字最终会被自动插入对应包的 CHANGELOG.md。

生成的 changeset 文件会存放在仓库的 .changeset 目录中。这是"贡献者写描述、维护者做发布"的协作分界线:任何人在合并 PR 前都应确保对应变更已经带有 changeset,否则发布时该变更不会触发版本升级。

从仓库现状可以印证 changelog 的组织方式:根目录 CHANGELOG.md 第一段明确写着"所有新的变更现在都记录在各包目录下的 CHANGELOG.md 文件中"。例如 packages/css/CHANGELOG.md、packages/babel-plugin/CHANGELOG.md、packages/react/CHANGELOG.md 等,每个包的发布历史独立成册;依赖 @changesets/changelog-github 则负责把 changeset 中的 Markdown 与 GitHub PR/作者信息关联,生成更丰富的 changelog 条目。

三、完整发布流程:三步走

正式发布由维护者执行,完整流程记录在 RELEASING.md,共三步:

第 1 步:确保依赖与工作区最新

yarn

发布前先运行一次 yarn,确保所有工作区依赖与 lockfile 一致。仓库使用 Yarn Modern(package.json 中 "packageManager": "yarn@3.2.3",且配置了 preinstall 脚本 node ./scripts/ensure-yarn.js 强制校验 Yarn 版本),这一步同时也会触发 postinstall(preconstruct dev && manypkg check),即用 Preconstruct 建立开发态链接,并用 manypkg 校验 monorepo 依赖结构是否符合规范,为后续构建与发布做好准备。

第 2 步:聚合 changeset 并更新版本

yarn version-packages

该命令对应 package.json 第 26 行的脚本:

"version-packages": "changeset version && yarn --mode=\"update-lockfile\""

它由两段组成,分两层作用:

  • changeset version:读取 .changeset 目录下所有未消费的 changeset,聚合出完整的发布计划(release plan),据此更新各包的 package.json 版本号、重写各包的 CHANGELOG.md(插入本次变更说明与版本标题),并删除已消费的 changeset 文件;
  • yarn --mode="update-lockfile":仅更新 yarn.lock 中记录的版本信息,不重新解析依赖树,保证 lockfile 与刚更新的包版本保持一致。

这一步通常在本地执行后作为一个"版本发布提交"(version commit)合入仓库。注意:此时只是本地版本号变更,还没有任何东西发布到 npm。

第 3 步:构建并发布到 npm

NPM_CONFIG_OTP=PUTANOTPCODEHERE yarn release

release 脚本定义在 package.json 第 27 行:

"release": "yarn build && changeset publish"

同样分两层:

  • yarn build:调用 Preconstruct 构建所有包(preconstruct build)。这是发布前的必要前置,CONTRIBUTING.md 的 "Building" 一节明确标注"发布前必须构建"(Required before publishing)。Emotion 各包通过 package.json 的 preconstruct 配置生成 dist 产物(如 packages/styled/base/dist/styled.umd.min.js),changeset publish 发布的正是这些构建产物;
  • changeset publish:读取上一步更新后的版本号,对每个版本变化的包执行 npm publish,自动按依赖拓扑顺序发布,避免出现"被依赖的包尚未发布"的情况。

NPM_CONFIG_OTP 环境变量为 npm 发布提供一次性密码(One-Time Password),即 npm 账户开启 2FA(双因素认证)时的动态验证码。将 PUTANOTPCODEHERE 替换为当前有效的验证码即可。

四、2FA 验证码超时怎么办

npm 的 2FA 验证码通常有约 30 秒的有效期,而 Emotion 有十几个包,整个构建加发布过程可能超过一个验证码的生命周期,因此 RELEASING.md 专门给出了处理方案:

如果 2FA 验证码在发布过程中超时,使用新的验证码重新运行命令即可,只有尚未发布的包会被发布。

NPM_CONFIG_OTP=新验证码 yarn release

这一行为的保障来自 changeset publish 的设计:它会先读取每个包的当前已发布版本并与本地版本比对,只发布本地版本号高于 registry 中已存在版本的包。因此重新运行时,之前已经成功发布的包会被自动跳过,幂等性由工具链保证,无需担心重复发布或版本冲突。

五、发布前后的质量保障

一次可靠的发布不仅依赖上述三步,仓库还提供了一整套配套脚本用于发布前的自检(均定义在 package.json 的 scripts 中):

命令 作用 适用时机
yarn test 运行 jest 测试套件 发布前必跑
yarn test:typescript 对所有工作区运行 TypeScript 类型测试 发布前建议跑
yarn lint:check eslint 全量检查(含 @emotion/pkg-renaming 等自定义规则) 发布前建议跑
yarn test:dist 构建后针对 dist 产物运行测试,验证发布物本身可用 模拟发布产物验证
yarn test:prod 使用生产环境配置(jest.prod.js)运行测试 验证生产构建
yarn size 通过 bundlesize 检查关键包体积阈值(见 package.json 的 bundlesize 字段) 发布前可选

其中 test:dist 与发布关系最直接:它先执行 yarn build 再用 jest.dist.js 对构建产物跑一遍测试,能从发布物角度提前发现构建问题,避免"测试通过、发布出来的包却是坏的"。仓库根目录还配有 jest.config.js、jest-react18.config.js 等测试配置,覆盖 React 16 / React 18 双环境。

六、一次发布的关键时间线小结

把上述内容串起来,一次完整的 Emotion 发布过程如下:

  1. 贡献者:开发功能/修复 bug 后运行 yarn changeset,为影响的包声明 bump 类型并撰写变更说明;
  2. 维护者合并含 changeset 的 PR 后:
    • yarn 同步依赖;
    • yarn test、yarn lint:check 等确认质量;
    • yarn version-packages 聚合 changeset、更新版本号与各包 CHANGELOG.md、同步 lockfile,并提交版本提交;
    • NPM_CONFIG_OTP=<当前验证码> yarn release 构建全部包并按依赖顺序发布;
    • 若发布途中 2FA 超时,换新验证码重跑 yarn release,已发布的包自动跳过。

这套以 Changesets 为核心的流程把"版本号维护"从人工计算中彻底解放出来,contributors 只管提交变更描述,维护者只管执行两条命令,Changelog 与版本联动全部自动化,这也是 RELEASING.md 所说"releasing really easy and changelogs are automatically generated"的完整实现。

登录后查看全文
emotion