ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Nx Monorepo 项目范围校验实战:@commitlint/config-nx-scopes 的功能演进与源码剖析

Nx Monorepo 项目范围校验实战:@commitlint/config-nx-scopes 的功能演进与源码剖析 开发工具Lint代码质量【免费下载链接】commitlint Lint commit messages项目地址https://gitcode.com/gh_mirrors/co/commitlint点击查看免费下载导读commitlint/config-nx-scopes 是 commitlint 官方提供的共享配置包用于强制 Git 提交信息的 scope范围必须是 Nx 工作区中真实存在的项目名。本文以该包的 CHANGELOG 变更记录为主线梳理它从 16.2.0 首次支持 Nx 到 21.2.3 改为从 nx project graph 读取项目清单的完整演进脉络并结合仓库内源码、测试与 fixtures深入讲解其底层实现原理、项目过滤配置与版本兼容约束。读完本文你将掌握如何在 Nx monorepo 中落地 commitlint 范围校验并理解其从读配置文件到读项目图的架构升级逻辑。一、包定位让提交 scope 与 Nx 项目一一对应在 Nx monorepo 中仓库通常包含数十个可构建、可发布的项目projects提交信息形如feat(api): add endpoint。如果 scope 可以随意填写就失去了分类与检索的意义。commitlint/config-nx-scopes的价值在于它把 scope 的合法取值与 Nx 工作区里真实存在的项目名绑定并注册一条动态生成的scope-enum规则任何不在项目清单中的 scope 都会触发校验错误。包的官方描述与元信息可在 package.json 中确认名称commitlint/config-nx-scopes当前仓库内版本为 21.2.3描述Shareable commitlint config enforcing nx project names as scopes依赖仅commitlint/typespeerDependencies 声明nx: 14.0.0可选engines 要求node 22.12.0发布文件为index.js与project-graph.js两个入口。从 CHANGELOG 最早一条功能记录v16.2.0add support for Nx monorepos via commitlint/config-nx-scopes可以看出这个包自诞生起就服务于一个明确场景在 Nx 仓库里把提交范围校验交给 commitlint 自动管理。二、核心机制从 nx project graph 读取项目清单2.1 整体架构父子进程 专用文件描述符最新版21.2.3的实现发生了关键架构调整。CHANGELOG 中 21.2.3 条目明确写着read scopes from the nx project graph从 nx 项目图读取 scopes。此前版本从nx.json/workspace.json等配置文件读取项目名而项目图project graph是 Nx 对全仓依赖关系的统一抽象它包含由 Nx 插件推断inferred出来的项目——这些项目可能没有独立的project.json或package.json文件。入口 index.js 导出了一个标准 commitlint 配置对象export default { utils: { getProjects }, rules: { scope-enum: (ctx) Promise.resolve([RuleConfigSeverity.Error, always, getProjects(ctx)]), }, };scope-enum规则在运行时被求值返回[2, always, projectNames]严重级别为 Error2、条件为 always、取值范围为动态计算出的项目名数组。2.2 为什么要用子进程读图Nx 在首次加载时会把工作区根目录从process.cwd()推导出来并将该值烘焙进缓存路径和 daemon 客户端。因此要可靠读取任意目录的项目图唯一稳妥的方式是启动一个 cwd 指向目标目录的子进程。index.js 中的readProjectGraph(cwd)通过spawnSync调用project-graph.js使用process.execPath启动 Node 子进程传入子进程脚本路径与目标文件描述符编号stdio配置为[ignore, pipe, pipe, pipe]即 stdin 忽略、stdout/stderr 走管道、第 3 个文件描述符payloadFd 3专门用于回传数据maxBuffer: Infinity避免项目众多或插件话痨时撞上默认 1MB 输出上限从output[payloadFd]读取 JSON 载荷并解析若子进程报错或状态码非 0则抛出Unable to read the nx project graph in ...错误。2.3 子进程侧为什么 payload 不走 stdoutproject-graph.js 是真正的图读取器其关键实现如下import { createProjectGraphAsync } from nx/src/project-graph/project-graph.js; const graph await createProjectGraphAsync({ exitOnError: false }); const projects Object.entries(graph.nodes ?? {}).map(([name, node]) ({ name, projectType: node.data?.projectType, tags: node.data?.tags ?? [], }));源码注释给出了payload 不走 stdout的明确理由Nx 不会让 stdout 保持干净——进度输出、daemon 以及运行在 worker 进程里的插件都会向 stdout 写入内容。所以父进程专门为载荷分配了独立的文件描述符 3注释强调这是唯一声明该描述符的地方父子两侧通过命令行参数传递编号避免漂移。读取结束后调用process.exit(0)因为 Nx 会保持 daemon 连接和文件监视器存活需要主动结束进程。2.4 从项目名到合法 scope 的归一化index.js 中的getProjects(context, selector)对项目名做了最终处理.filter((project) selector({ name, projectType, tags })) .map((project) project.name) .map((name) (name.charAt(0) ? name.split(/)[1] : name));对于 npm 风格的作用域包名以开头如acme/shared会剥离前缀只保留第二段shared作为合法 scope。selector默认是恒真函数即默认返回全部项目。三、快速上手一条命令接入范围校验官方 readme.md 给出了最简接入方式npm install --save-dev commitlint/config-nx-scopes commitlint/cli echo module.exports {extends: [commitlint/config-nx-scopes]}; commitlint.config.js该配置可与 commitlint/cli 和 commitlint/prompt-cli 配合使用。readme 中的运行示例展示了实际效果❯ echo build(api): change something in apis build | commitlint # 通过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 warnings ❯ echo ci: do some general maintenance | commitlint # 通过无 scope 时不触发 scope-enum 校验可见只要 scope 落在项目清单内即通过不在清单内如foo立即报错不写 scope 则不违反规则。四、项目过滤按 name、projectType、tags 自定义范围CHANGELOG v16.3.0 记录了该包的一个重要能力扩展add ability to filter Nx projects in commitlint/config-nx-scopes支持过滤 Nx 项目。过滤能力通过getProjects()的第二个参数selector 函数实现selector 会收到每个项目的name、projectType、tags返回布尔值决定是否纳入。readme 给出了两个典型示例。第一个只允许非 e2e 的 application 项目作为 scopeasync function getConfig() { const { default: { utils: { getProjects }, }, } await import(commitlint/config-nx-scopes); return { rules: { scope-enum: async (ctx) [ 2, always, [ ...(await getProjects( ctx, ({ name, projectType }) !name.includes(e2e) projectType application, )), ], ], }, // . . . }; } module.exports getConfig();第二个排除打了stage:end-of-life标签的项目async function getConfig() { const { default: { utils: { getProjects }, }, } await import(commitlint/config-nx-scopes); return { rules: { scope-enum: async (ctx) [ 2, always, [...(await getProjects(ctx, ({ tags }) !tags.includes(stage:end-of-life)))], ], }, // . . . }; } module.exports getConfig();测试 index.test.js 验证了 selector 确实能收到推断项目inferred project的 tags 与 projectTypetags.includes(inferred)只命中fixture-inferred-i而projectType library同时命中fixture-inferred-i与fixture-inferred-j。五、版本演进时间线从读配置到读项目图以 CHANGELOG.md 为骨架该包的演进脉络清晰可循版本关键变更16.2.0新增对 Nx monorepo 的支持包首次发布首个 Features 条目16.3.0新增项目过滤能力getProjects的 selector 参数17.2.0将 nx^15.0.0加入 peerDependencies17.4.0 ~ 18.4.0多为版本号同步Version bump only18.3.0支持最新版 nx18.5.0修复与 nx 17.2.0 及更高版本的兼容性issue #382018.6.1 ~ 19.0.3版本同步19.0.0 随 commitlint 整体迁移到纯 ESMpure ESM修复语法错误19.1.0在 package.json 中补充main与types字段19.2.1在 nx imports 中包含文件扩展名19.7.1修复没有显式 targets 的项目issue #426119.8.0使用node:前缀导入内置模块绕过 require.cache性能优化20.0.0 ~ 20.4.0版本同步为主20.4.2 为 fixture 项目补充唯一名称21.0.0跟随 commitlint 大版本最低 Node 版本提升到 v22BREAKING CHANGES放弃 Node 18/2021.2.3从 nx project graph 读取 scopes架构级变更见第二节5.1 兼容性与工程约束的演化Node 版本v17.0.0 起要求 Node ≥14放弃 12v18.0.0 起要求 Node ≥18放弃 14/16v21.0.0 起要求 Node ≥22放弃 18/20当前engines为22.12.0。模块体系v19.0.0 随仓库整体迁移到 pure ESMtype: module期间还出现过 replace import with require18.5.1与 include file extension in nx imports19.2.1等细节修复反映 ESM 迁移过程中的兼容性打磨。Nx 兼容peerDependencies 从 nx14.0.0起步v17.2.0 显式加入 nx 15v18.3.0 跟进最新版v18.5.0 修复 nx 17.2.0 的破坏性变更。六、测试矩阵五种仓库形态的验证index.test.js 通过 fixtures 覆盖了多种仓库形态是理解实现边界的最好入口empty空 Nx 仓库返回[]scope-enum不抛异常basic返回[fixture-basic-a, fixture-basic-b]nx14 / nx15 / nx17分别验证 Nx 14、15、17 三个大版本下的读取结果inferred验证由 nx 插件推断、磁盘上无project.json/package.json的项目也能被纳入对应 CHANGELOG 21.2.3 的核心能力noisy验证插件向 stdout 打印内容的场景——由于载荷走独立文件描述符输出噪声不会污染结果ignores what nx plugins print to stdout。测试还断言了规则形态scope-enum存在且是函数、严重级别为 2、修饰符为always并且getProjects是同步导出returns the project names synchronously。所有 fixture 测试均通过commitlint/test包的npm.bootstrap在临时目录中运行并在测试前设置NX_DAEMONfalse避免 daemon 进程残留。在 fixtures/inferred 中可以看到推断插件的真实写法tools/inferring-plugin.js通过createNodes匹配nx/*/inferred.marker文件为每个匹配目录生成一个projectType: library、tags: [inferred]的项目——这正是项目只存在于项目图中的典型样例也解释了为什么 21.2.3 必须改为读项目图仅靠磁盘上的配置文件根本无法发现这类项目。七、使用注意事项与限制结合源码与文档使用本包时有几点值得注意工作目录语义项目图是在子进程的 cwd下读取的见 index.js 注释因此getProjects(ctx)的ctx.cwd决定了读取哪个工作区的图默认回退到process.cwd()。在 monorepo 子目录执行提交时应确保 cwd 能定位到 Nx 工作区根。错误处理若目标目录不是合法的 Nx 工作区readProjectGraph会抛出异常scope-enum求值失败——配置前请确认 Nx 版本满足14.0.0。作用域包名归一化scope/name形式的项目只取name段作为合法 scope这是默认行为无法通过 selector 改变。自定义规则覆盖一旦自行编写scope-enum规则如过滤示例就完全接管了规则的严重级别与修饰符需要自行保持[2, always, ...]的形态。结语从 v16.2.0 的支持 Nx monorepo到 v21.2.3 的从 nx project graph 读取 scopescommitlint/config-nx-scopes的每一次变更都紧跟 Nx 生态的演进项目过滤能力让它适配复杂组织架构pure ESM 迁移让它跟上现代 Node 工具链而子进程 独立文件描述符的架构设计则解决了插件输出污染 stdoutdaemon 生命周期等一系列工程难题。对使用 Nx 的团队而言它把提交 scope 必须真实存在这一约束从人工约定变成了机器强制是 monorepo 提交规范落地中值得直接采用的方案。赞分享开发工具Lint代码质量【免费下载链接】commitlint Lint commit messages项目地址https://gitcode.com/gh_mirrors/co/commitlint点击查看免费下载相关推荐commitlint 与 Nx 集成使用 commitlint/config-nx-scopes 以项目图Project Graph强制 scope 校验commitlint 与 Nx 集成使用 commitlint/config nx scopes 以项目图Project Graph强制 scope 校开发工具Lint代码质量深入 commitlint 配置校验commitlint/config-validator 的 Schema、调用链与演进史深入 commitlint 配置校验commitlint/config validator 的 Schema、调用链与演进史 commitlint 在加载开发工具Lint代码质量使用 commitlint/config-lerna-scopes 校验 Lerna 项目提交的包作用域使用 commitlint/config lerna scopes 校验 Lerna 项目提交的包作用域 本指南以 commitlint/config le开发工具Lint代码质量创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表