Nx 21.5 迁移指南:将 `development` 自定义条件迁移为工作区专属名称
Nx 21.5 迁移指南将development自定义条件迁移为工作区专属名称【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nxTypeScript 的customConditions自定义条件与package.json的exports条件导出是 Nx 基于 TS 工程TS solution setup工作区中实现源码直引、免构建调试的核心机制。Nx 21.5 引入了一条自动迁移将工作区中沿用的通用条件名development替换为与根package.jsonname 绑定的工作区专属名称如my-org/source以避免不同工作区互相消费包时发生条件冲突。本文以 Nx 仓库中的迁移文档与实现源码为主线讲解该迁移的触发条件、改写范围、源码原理与验证方法帮助你判断自己的工作区是否需要执行、以及如何手动落地同样的改造。为什么要把development条件改成工作区专属名称在 Nx 基于 TypeScript 工程的解决方案TS solution setup中工作区会通过tsconfig.base.json的compilerOptions.customConditions声明一组自定义条件同时在各个库的package.json的exports字段里用这些条件映射到 TypeScript 源文件。例如tsconfig.base.json声明customConditions: [development]libs/my-lib/package.json中声明development: ./src/index.ts。这样在本工作区内消费该库时TypeScript 会优先解析到源码实现免编译的增量开发发布后则回落到default指向的dist产物。问题在于development是一个过于通用的条件名。当其他工作区也以development作为自定义条件、或在自身exports中为同一条件指向不同的文件时跨工作区消费包就可能被错误地命中条件解析到不属于你的文件。Nx 给出的解决思路是用根package.json的name生成一个工作区专属的条件名例如工作区名为my-org/source条件即为my-org/source若根package.json没有name则回退到默认值nx/source从命名空间上杜绝条件冲突。这一点从迁移文档开头的说明可以确认也与仓库中nx/js自身的package.json做法完全一致——Nx 仓库根package.json的 name 为nx/nx-source而packages/js/package.json的每个exports入口都使用了nx/nx-source: ./src/...这样的工作区专属条件见 packages/js/package.json。迁移的触发条件全部满足才会执行迁移位于 packages/js/src/migrations/update-21-5-0/migrate-development-custom-condition.ts并通过 packages/js/migrations.json 注册为 Nx 21.5.0-beta.2 的生成器。它并不是无条件执行而是需要同时满足以下前置条件工作区同时存在tsconfig.base.json与tsconfig.json——缺少任何一个都说明仓库并未采用或已偏离标准的 TS solution setup迁移直接返回不做任何改动对应源码isDevelopmentCustomConditionDefined中的 exists 检查。tsconfig.base.json中compilerOptions.customConditions恰好只有一个条件且该条件就是development。源码中的判定逻辑为customConditions必须是数组、长度为 1、且唯一元素等于development。若customConditions未设置、为空数组、包含多个条件如[development, production]、或唯一条件并非development如已是my-org/source迁移都不会执行——这表示仓库已不是最初由 Nx 生成的 TS solution setup 形态或已经完成过该迁移。工作区所有package.json的development条件导出都指向 TypeScript 文件。迁移会通过globAsync(tree, [**/package.json])收集所有package.json排除根目录的package.json递归检查exports对象中每个development键指向的路径必须匹配.ts、.tsx、.mts、.cts之一正则/\.m?ts$|\.tsx$|\.cts$/见 isTypeScriptFile。只要任何一个development条件指向了非 TS 文件例如./dist/index.js整个迁移就会中止。迁移做了什么两处同步改写满足上述全部条件后迁移会执行两次写入并在最后调用formatFiles(tree)统一格式化1. 改写tsconfig.base.json中的自定义条件通过updateJson将customConditions数组中值为development的元素替换为新的条件名见 updateTsconfigBaseCustomCondition{ compilerOptions: { customConditions: [development] } }{ compilerOptions: { customConditions: [my-org/source] } }其中my-org/source来自根package.json的name。新条件名的计算复用自 getCustomConditionName优先读取根package.json的name若不存在则回退到默认值nx/source。迁移调用时传入了{ skipDevelopmentFallback: true }强制跳过development旧名称的回退逻辑确保生成的名称一定是工作区专属的。这一点在测试中也有明确覆盖当根package.json没有name时迁移结果使用nx/source见 migrate-development-custom-condition.spec.ts。2. 改写所有库级package.json的exports条件迁移会遍历工作区中所有非根目录的package.json递归处理exports对象把键为development的条目改名为新的条件名其余键如import、require、types、default、嵌套的子路径导出./sub、./feature等保持不变见 updateExportsRecursively。只有exports结构实际发生变化时才会写入文件并打印Updated exports in path日志。{ name: myorg/my-lib, exports: { .: { development: ./src/index.ts, default: ./dist/index.js } } }{ name: myorg/my-lib, exports: { .: { my-org/source: ./src/index.ts, default: ./dist/index.js } } }嵌套的子路径导出同样会被递归改写例如同时处理.、./sub、./feature等多个入口中的development键测试用例 should update nested exports with development condition 对这一点做了完整验证。不会改动的场景安全边界设计迁移对以下情况采取不动策略避免破坏偏离标准形态的仓库development条件没有指向 TypeScript 文件。无论是指向./dist/index.js这样的 JS 产物还是任意非.ts/.tsx/.mts/.cts路径迁移都不会修改任何配置tsconfig.base.json与所有package.json原样保留。测试 should not run when development exports point to non-TS files 与 should not run when some development exports point to non-TS files 分别覆盖了单个指向 JS与混合指向 JS/TS两种情况——注意后者即使只有一个包不合规整个迁移也会中止。package.json没有exports、exports是字符串如./dist/index.js或 JSON 解析失败。迁移会跳过这些文件继续处理不会抛错中断见测试 should handle package.json files with no exports、should handle package.json files with string exports 与 should handle invalid package.json files gracefully。根目录package.json不参与改写。getPackageJsonFiles会过滤掉package.json本身见 getPackageJsonFiles根package.json只作为条件名的来源被读取其exports不会被迁移触碰见测试 should skip root package.json。如何执行这条迁移该迁移随nx/js包发布已注册在 packages/js/migrations.json版本标记为21.5.0-beta.2。当你将工作区中的nx/js升级到 21.5 及以上版本、并运行 Nx 的迁移命令时它会作为自动迁移被应用nx migrate nx/js21.5.0 nx migrate --run-migrations如果你希望手动确认或复现迁移行为可以直接运行对应的生成器nx g nx/js:migrate-development-custom-condition执行完成后建议检查以下位置以确认结果根目录 tsconfig.base.json 的compilerOptions.customConditions已变成工作区专属名称各库的package.json中exports的development键已被替换项目级tsconfig*.json若通过extends继承基础配置无需额外改动因为customConditions是从tsconfig.base.json继承的。作为参考Nx 仓库自身就是该模式的落地范例根package.json的 name 为nx/nx-source而 packages/js/package.json 的每个exports入口都使用nx/nx-source: ./src/...指向 TS 源文件配合types与default指向dist产物正是迁移完成后工作区应有的最终形态。迁移背后的源码调用链整条迁移的执行流程可以概括为isDevelopmentCustomConditionDefined(tree)校验 TS solution setup 存在且customConditions恰为[development]getPackageJsonFiles(tree)用globAsync收集所有库级package.jsondoDevelopmentExportsPointToTsFiles递归校验所有development条件导出均指向 TS 文件否则中止getCustomConditionName(tree, { skipDevelopmentFallback: true })依据根package.json的name计算新条件名缺省回退nx/sourceupdateTsconfigBaseCustomCondition改写tsconfig.base.jsonupdatePackageJsonExports递归改写各package.json的exports仅在结构变化时写入并记录日志formatFiles(tree)统一格式化所有改动文件。该调用链同时被 migrate-development-custom-condition.spec.ts 中的 20 余个用例覆盖涵盖条件满足时的改写条件不满足时的不动边缘情况无效 JSON、无 exports、字符串 exports、跳过根 package.json三大维度可以作为理解迁移行为的可执行参考。小结Nx 21.5 的development自定义条件迁移本质上是一次命名空间收敛把跨工作区容易撞车的通用条件名收敛为与根package.json名称绑定的专属条件名。它的执行非常克制——只有工作区完整满足 TS solution setup 形态、customConditions恰好为[development]、且所有development条件导出均指向 TS 源码时才会改写任何一项不满足都会安全跳过。对于已经完成该迁移的工作区my-org/source这样的条件名既保留了源码直引的开发体验又消除了包被其他工作区错误命中的隐患。【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考