资讯动态

@commitlint/config-workspace-scopes 使用指南:用 npm/Yarn workspace 名称强制约束 commit 的 scope 字段

发布时间:2026/9/20 13:39:52 来源:尧图企业网站定制
开发工具Lint代码质量【免费下载链接】commitlint Lint commit messages项目地址https://gitcode.com/gh_mirrors/co/commitlint点击查看免费下载commitlint/config-workspace-scopes是 commitlint 官方提供的一个 shareable config可共享配置其核心能力是根据仓库package.json中的workspaces字段自动收集所有子包名称并将其作为 commit message 中scope字段的唯一合法取值从而在 monorepo 中实现提交的 scope 必须是真实存在的包名的硬性约束。读完本文你将掌握该配置的安装方式、典型运行效果、底层解析逻辑workspace glob 匹配、scoped 包名归一化、node_modules 排除以及它在各种 workspace 写法下的实际行为可直接落地到你的 npm/Yarn workspace 仓库中。这个配置解决什么问题在 monoreponpm workspaces / Yarn workspaces仓库中提交信息里的scope字段通常用来标识本次改动影响的子包例如build(api): 修改 api 包的构建配置。但scope是一个自由填写的文本很容易出现拼写错误、使用了不存在的包名或与团队约定不一致的情况。commitlint/config-workspace-scopes的思路是让合法 scope 的集合不是手写维护的静态列表而是从仓库的 workspaces 配置中动态生成。它对外暴露一个scope-enum规则枚举规则每次执行 lint 时都会重新扫描 workspace 包名得到类似[api, app, web]的枚举值凡是 scope 不在其中的提交都会报错。官方对该包的一句话定位是Shareablecommitlintconfig enforcing workspace names as scopes强制以 workspace 名称作为 scope 的可共享 commitlint 配置并且设计为与 commitlint/cli 和 commitlint/prompt-cli 配合使用。快速开始Getting Started1. 安装依赖npm install --save-dev commitlint/config-workspace-scopes commitlint/cli安装完成后仓库根目录的package.json中会出现这两个 devDependencies{ devDependencies: { commitlint/cli: ^21.2.0, commitlint/config-workspace-scopes: ^21.2.0 } }说明该包在仓库中的版本为 21.2.0engines声明要求 Node.js22.12.0见 package.json。如果你的 Node 版本较低请留意兼容性。2. 创建 commitlint 配置文件在仓库根目录创建commitlint.config.js通过extends引入该配置export default { extends: [commitlint/config-workspace-scopes] };如果你的项目使用 CommonJS也可以写成module.exports { extends: [commitlint/config-workspace-scopes] };配置文件只需extends一行——scope的合法枚举值完全由配置包在运行时自行计算无需你手动维护。运行效果示例原文档给出了一个完整的端到端示例。假设仓库结构如下❯ cat package.json { workspaces: [packages/*] } ❯ cat commitlint.config.js { extends: [commitlint/config-workspace-scopes] } ❯ tree packages packages ├── api ├── app └── web此时仓库存在api、app、web三个 workspace 子包。执行 lint❯ echo build(api): change something in apis build | commitlintscope为api属于合法枚举值校验通过无任何输出。❯ echo test(foo): this wont pass | commitlint ⧗ --- input --- test(foo): this wont pass ✖ scope must be one of [api, app, web] [scope-enum] ✖ found 1 problems, 0 warningsscope为foo不在[api, app, web]枚举中校验失败commitlint 明确提示错误规则为scope-enum并给出当前允许的全部 scope 列表。❯ echo ci: do some general maintenance | commitlint该提交没有scope字段scope-enum只约束写了 scope 就必须合法因此同样通过校验。这也意味着像ci、chore、docs这类全局性的、不针对某个子包的提交可以正常提交不会因为缺少 scope 被拦截。底层原理scope 枚举是如何动态生成的这一节我们深入 index.js 的源码看看合法的 scope 列表究竟是怎么算出来的。该包的默认导出结构如下export default { utils: { getPackages }, rules: { scope-enum: (ctx) getPackages(ctx).then((packages) [2, always, packages]), }, };它做了两件事导出一个utils.getPackages工具函数供内部调用将scope-enum注册为一条动态规则规则值不是静态数组而是一个函数返回形如[2, always, packages]的数组——这正好是 commitlint 规则的三元组约定[severity, modifier, value]。结合 index.test.js 中的测试断言可以确认severity恒为2即错误级别1为警告2为错误0为禁用modifier恒为always表示该规则始终生效value则是运行时计算出的包名数组。getPackages 的解析流程function getPackages(context) { return Promise.resolve() .then(() { const ctx context || {}; const cwd ctx.cwd || process.cwd(); const { workspaces } require(Path.join(cwd, package.json)); if (!Array.isArray(workspaces)) { // no workspaces configured, skipping return []; } const wsGlobs workspaces.flatMap((ws) { const path Path.posix.join(ws, package.json); return globSync(path, { cwd, exclude: (p) p.includes(node_modules), }); }); return wsGlobs.sort().map((pJson) require(Path.join(cwd, pJson))); }) .then((packages) { return packages .map((pkg) pkg.name) .filter(Boolean) .map((name) (name.charAt(0) ? name.split(/)[1] : name)); }); }整个流程可以分为五个关键步骤确定工作目录优先使用 lint 上下文ctx.cwd未提供时回退到process.cwd()。这意味着该配置始终以正在被 lint 的项目根目录为基准读取package.json而不是以包自身的位置为基准。读取workspaces字段require(Path.join(cwd, package.json))取出workspaces。如果该字段不存在或不是数组例如单包仓库直接返回[]源码注释为 no workspaces configured, skipping。把每个 workspace glob 拼接成package.json路径并扫描对workspaces数组中的每一项如packages/*、packages/**执行Path.posix.join(ws, package.json)再用globSync在cwd下匹配所有子包的package.json。注意exclude: (p) p.includes(node_modules)——这一步会把node_modules中任何命中的路径全部排除。排序并读取包信息wsGlobs.sort()保证扫描结果按路径字典序排序从而让最终生成的 scope 枚举顺序是确定的、可复现的这也是错误提示中[api, app, web]总是稳定有序的原因。随后逐个require这些package.json。提取并归一化包名取出每个包的name字段filter(Boolean)过滤掉没有name的包最后对 scoped 包名做归一化——凡是开头的包名只取/后的第二段。例如packages/a最终变成acompany/ui变成ui。为什么要排除 node_modules排除node_modules是一个非常关键的防御性设计workspace glob尤其是**形式的通配在展开时可能会扫到依赖安装目录中的第三方包。测试 index.test.js 中有一条断言专门验证了这一点test(returns expected value for workspaces has nested packages, async () { // ... expect(value).toEqual(expect.arrayContaining([nested-a, nested-b])); expect(value).toEqual(expect.not.arrayContaining([dependency-a, dependency-b])); });即嵌套的 workspace 包nested-a、nested-b会被正确识别而出现在node_modules中的依赖包dependency-a、dependency-b绝不会混入 scope 枚举。这正是exclude回调的功劳。各种 workspace 写法下的行为以 fixtures 佐证仓库的 fixtures 目录提供了多组真实测试样例可以直观看到不同workspaces写法下的解析结果workspace 写法fixture 位置解析出的 scope 枚举测试断言[packages/*]fixtures/basic/package.json[a, b]来自packages/a、packages/btoEqual([a, b])[packages/**]含 lerna.jsonfixtures/scoped/package.json[a, b]toEqual([a, b])[packages/**]且子包内还有嵌套包fixtures/nested-workspaces/package.json包含[nested-a, nested-b]arrayContaining(...)未配置 workspacesfixtures/empty/package.json[]toEqual([])几点值得注意的结论通配符层级不限packages/*一级与packages/**多级都会被正确处理**可以命中嵌套更深的子包目录。嵌套 workspace 也能被发现如packages/a/nested-a这类目录层级较深的包只要被 glob 命中其name就会被纳入枚举。scoped 包名统一去掉前缀packages/a的 scope 写作a即可无需也不允许写packages/a。无 workspaces 时返回空数组此时scope-enum的枚举值为[]意味着任何带 scope 的提交都无法匹配到合法值。如果你的项目实际是多包但忘了在根package.json声明workspaces所有带 scope 的提交都会失败——这可以看作一个有效的配置提醒。结果自动排序最终枚举按包路径字典序排列错误提示信息中的 scope 列表始终稳定、可预期。测试中还验证了规则对空上下文fn()不传任何参数也不会抛错回退到process.cwd()以及scope-enum始终返回[2, always, ...]的完整三元组。与 prompt 类工具的配合除commitlint/cli之外原文档还提到可与 commitlint/prompt-cli 配合使用。prompt-cli提供交互式提交引导在你填写 commit message 时scope-enum的动态枚举值会被用于生成 scope 的可选项让开发者直接从[api, app, web]中选择而不是手打从源头避免非法 scope。这一用法与 docs/guides/use-prompt.md 中描述的交互式提交流程一致。与其他 scope 类共享配置的差异仓库中还存在几个定位相近的共享配置commitlint/config-lerna-scopes读取lerna.json、commitlint/config-pnpm-scopes读取pnpm-workspace.yaml、commitlint/config-rush-scopes读取rush.json和 commitlint/config-nx-scopes读取 Nx workspace 图。而commitlint/config-workspace-scopes的适用场景是标准 npm / Yarn workspaces——它只读取根package.json的workspaces字段不依赖任何第三方工具链。如果你的仓库恰好使用 npm 或 Yarn 的 workspaces 能力即 package.json 中声明的关键词npm-workspaces、yarn-workspaces这是最贴合的选择如果使用的是 pnpm、Rush 或 Nx则应选择对应的专用配置。小结commitlint/config-workspace-scopes用运行时动态计算的方式解决了 monorepo 中 scope 校验的维护成本问题无需在配置里写死任何包名只要根package.json正确声明了workspaces所有合法 scope 就会自动生成并保持同步。其底层通过 glob 扫描 workspace 中的package.json、排除node_modules、剥离 scoped 包名前缀最终产出稳定有序的scope-enum枚举值并以错误级别severity 2强制约束。对使用 npm / Yarn workspaces 的仓库而言它是让提交规范与仓库结构保持天然一致的最小成本方案。如需了解scope-enum等规则的完整语义与可用规则清单可查阅仓库内的 规则参考关于 commitlint 的整体接入方式可参考 快速开始指南。赞分享开发工具Lint代码质量【免费下载链接】commitlint Lint commit messages项目地址https://gitcode.com/gh_mirrors/co/commitlint点击查看免费下载相关推荐Nx Monorepo 中链接 Workspace 包npm、pnpm、yarn、bun 四种管理器的 workspace 协议与符号链接机制Nx Monorepo 中链接 Workspace 包npm、pnpm、yarn、bun 四种管理器的 workspace 协议与符号链接机制 本文围绕 Nx开发工具构建工具MonorepoCLINx 仓库实战在 Monorepo 中链接 Workspace 包pnpm / npm / yarn / bunNx 仓库实战在 Monorepo 中链接 Workspace 包pnpm / npm / yarn / bun 本指南面向在 Nx 多包仓库中工作的开发开发工具构建工具MonorepoCLItsParticles Monorepo 工作区包链接全指南pnpm / yarn / npm / bun 的 workspace 依赖实战tsParticles Monorepo 工作区包链接全指南pnpm / yarn / npm / bun 的 workspace 依赖实战 导读 tsPar前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价