使用 Nx 空工作区测试本地包:面向 nx 源码开发的 init 生成器与推理插件验证指南
【免费下载链接】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
导读
在 nx 仓库中开发或修改一个包(如@nx/vite、@nx/oxlint)后,你往往希望在发布到 registry 之前,就验证init生成器、推理插件(inference plugins)和nx add流程在真实工作区中的表现。仓库提供了 examples/empty 这样一个"真正的空工作区":它没有项目、没有tsconfig.json、没有任何源码文件,因此生成器或插件对工作区做出的任何改动,就是完整且唯一的 diff。本文基于该空工作区的设计,讲解它的工作原理、本地包接入方式、生成器与推理插件的实测流程、常见陷阱(如根目录被误判为项目)以及重置方法,并给出对应源码依据。
为什么需要"真正的空工作区"
普通示例工作区通常已经包含项目、配置和源码,测试包时难以区分"哪些改动来自我的生成器"。而examples/empty的设计原则是:工作区内没有任何 Nx 项目,凡是生成器写入的内容,都能通过git status完整观察,做到"所见即所得"。
- 没有
tsconfig.json,也没有源码文件,因此init生成器、nx add流程对工作区的全部修改就是一个干净的 diff; - 它本身是一个独立的 pnpm workspace,拥有自己的
pnpm-lock.yaml(见 pnpm-lock.yaml),在其中执行pnpm install永远不会触碰仓库根目录的依赖树; - 本地包通过
link:依赖接入,pnpm 的link:协议天然支持跨 workspace 边界链接(见 pnpm-workspace.yaml 的注释说明)。
从 package.json 可以看到,它声明的devDependencies全部指向../../packages/*:
// examples/empty/package.json "devDependencies": { "@nx/angular": "link:../../packages/angular", "@nx/devkit": "link:../../packages/devkit", "@nx/js": "link:../../packages/js", "@nx/oxlint": "link:../../packages/oxlint", "@nx/playwright": "link:../../packages/playwright", "@nx/react": "link:../../packages/react", "@nx/vite": "link:../../packages/vite", "@nx/vitest": "link:../../packages/vitest", "@nx/vue": "link:../../packages/vue", "@nx/web": "link:../../packages/web", "nx": "link:../../packages/nx", "typescript": "~5.9.2" }也就是说,工作区里跑的nx命令来自packages/nx的工作树代码,而非已发布的 npm 包——这正是"针对工作树里的包而不是发布版本进行测试"这一目标的实现基础。
初始化与构建链接包
安装
pnpm installpackage.json中的postinstall脚本负责在安装完成后构建链接的本地包:
"scripts": { "postinstall": "cd ../.. && pnpm nx run-many -t build -p nx js @nx/oxlint angular react vue playwright vite vitest web --skip-sync" }该脚本回到仓库根目录(cd ../..),用根工作区的nx对nx、js、@nx/oxlint、angular、react、vue、playwright、vite、vitest、web等包执行run-many -t build。由于这些包是以link:方式接入的,postinstall从源码构建它们,确保空工作区运行的始终是你工作树中的最新代码,而不是某个缓存的旧构建产物。
如果你修改了某个包的源码但未构建,可在空工作区里手动重新构建:
cd ../.. && pnpm nx run-many -t build -p <包名>工作区配置文件
nx.json 保持了最小化配置,只声明了输入与遥测相关项:
{ "$schema": "../../node_modules/nx/schemas/nx-schema.json", "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], "sharedGlobals": [] }, "plugins": [], "analytics": false, "neverConnectToCloud": true }其中plugins: []表示默认不加载任何插件,analytics: false与neverConnectToCloud: true则确保测试过程不会产生遥测上报或连接 Nx Cloud。
测试某个包的生成器
链接目标包
无论你要验证的是已有包还是新开发的包,第一步都是把它加进devDependencies,并同步加入postinstall的构建列表:
// examples/empty/package.json "devDependencies": { "@nx/vite": "link:../../packages/vite" }然后在根目录构建该包后重新安装:
cd ../.. && pnpm nx run-many -t build -p vite cd examples/empty && pnpm install直接调用生成器,绕过 registry
生成器通常通过nx add触发,而nx add会从registry解析包——对于尚未发布、或仅在工作树中被修改过的本地包,这一路径并不可行。此时应直接调用生成器:
nx g @nx/oxlint:init该命令会在空工作区中执行@nx/oxlint的init生成器,其产生的所有文件变更(oxlint.json、package.json依赖、.vscode/extensions.json推荐等)就是你测试的"净输出"。
检查生成结果
生成完成后,用 git 观察完整 diff:
git status再查看工作区中被识别的项目:
nx show projects因为初始工作区没有任何项目,nx show projects的输出一开始应为空;生成器每写入一个可被推理插件识别的项目,这里就会多出一行,这正是验证推理插件是否生效的最直接手段。
生成项目以验证推理插件
大多数推理插件会刻意跳过没有匹配文件的项目——空工作区里没有任何源码文件,插件无从推断项目。因此,当你要验证某个推理插件时,需要先在工作区内生成一个真实项目供插件拾取:
nx g @nx/js:lib packages/demo --linter oxlint例如上述命令会在packages/demo下生成一个使用 oxlint 作为 linter 的 JS 库。随后:
nx show projects如果推理插件工作正常,demo会出现在项目列表中。用源码佐证这一点:文件归属推断依赖 find-project-for-path.ts 中的findProjectForPath——它基于{ projectRoot -> projectName }映射,从文件路径逐级向上(dirname)查找最近的已知项目根;对应的单元测试 验证了"给定 src 内任意子路径都能定位到所属项目"的行为。由此可见,若工作区中不存在任何项目,findProjectForPath自然匹配不到项目,推理插件也就不会产出项目节点。
.vscode/extensions.json:模拟真实新建工作区
.vscode/extensions.json是刻意存在的:它镜像了create-nx-workspace生成的内容,因此那些"追加编辑器推荐扩展"的生成器有东西可追加。
// examples/empty/.vscode/extensions.json { "recommendations": ["nrwl.angular-console"] }实现层面可参考 utils.ts 中的addVsCodeRecommendedExtensions:它只在该文件已存在时读取并追加去重后的recommendations,不存在时则以{ recommendations: extensions }新建。也就是说,生成器不会为没有该文件的工作区凭空创建它——空工作区预置这一文件,正是为了测试"追加"路径而非"新建"路径。
关键陷阱:不要给根目录添加项目
这是空工作区最重要的使用约束。根目录的 package.json刻意不包含nx键。原因如下:
- 一旦在根
package.json中加入nx: {},根目录就会成为一个项目(root: '.'); - 此时 findProjectForPath 的向上逐级匹配会命中根目录这个
'.'项目——任何路径(包括工作区之外的路径)都会被判定属于该根项目; - 于是本地
link:依赖会被当作工作区源码处理,Nx 转而加载插件的TypeScript 源码而非构建后的dist; - 由于 TypeScript 源码中的
NodeNext风格的.jsimport 说明符只在构建后才会存在,生成器最终会因为这些不存在的导入而失败。
因此,在空工作区中测试时,请保持根package.json不含nx键,避免把根目录变成项目。
在两次测试之间重置工作区
生成器会不断污染工作区,测试之间需要彻底重置:
git clean -fdx . && git checkout -- . pnpm installgit clean -fdx .删除所有未跟踪文件(包括node_modules、.nx缓存以及生成器写入的新文件);git checkout -- .恢复被修改的已跟踪文件;- 最后重新
pnpm install,触发postinstall重新构建链接包,回到一个干净、可复现的初始状态。
小结:空工作区的完整测试循环
把上面各节串起来,一次典型的本地包验证流程是:
- 在
examples/empty/package.json中加入"@nx/xxx": "link:../../packages/xxx",并把它加进postinstall的构建列表; cd ../.. && pnpm nx run-many -t build -p xxx,再回到examples/empty执行pnpm install;- 直接调用生成器:
nx g @nx/xxx:init; - 观察输出:
git status看 diff,nx show projects看项目推断结果; - 需要验证推理插件时,先
nx g @nx/js:lib packages/demo --linter oxlint生成一个真实项目; - 测试完毕,执行
git clean -fdx . && git checkout -- . && pnpm install重置。
这套基于 examples/empty 的流程,让 nx 的开发者在不发布任何包的前提下,用最小、最可控的工作区验证生成器与推理插件的真实行为,是 nx 源码开发与 CI 回归中极具价值的测试阵地。
【免费下载链接】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),仅供参考