news 2026/9/10 8:55:11

Nx 迁移清单 migrations.json 编写指南:从 generators 条目到 packageJsonUpdates 分组

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Nx 迁移清单 migrations.json 编写指南:从 generators 条目到 packageJsonUpdates 分组

Nx 迁移清单 migrations.json 编写指南:从 generators 条目到 packageJsonUpdates 分组

【免费下载链接】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 monorepo 仓库内的迁移清单模板文档为主体,系统讲解migrations.json的完整编写规范:从generators段落的 generator-only、prompt-only、hybrid 三类迁移条目,到packageJsonUpdates分组的版本门控与依赖升级策略,再到插件首个迁移所需的package.jsonassets.json等配套接线。读完你将掌握为 Nx 插件编写可被nx migrate正确收集、门控与执行的迁移清单的技能,并理解版本字段、requires门控与 dist 路径形状背后的运行时契约。

migrations.json是 Nx 迁移体系的注册中心:每个 Nx 插件通过它宣告"当工作区升级到某个版本时,需要运行哪些迁移、升级哪些依赖"。本文模板文档位于 .claude/skills/author-migration/templates/migrations-json.md,配套的完整编写守则见 .claude/skills/author-migration/SKILL.md,运行时契约见 .claude/skills/author-migration/runtime-contract.md。仓库中真实迁移清单可参考 packages/eslint/migrations.json、packages/storybook/migrations.json 等 30 余个插件的同名文件。

migrations.json 的整体结构

所有迁移条目挂在该文件顶层的generators段落(schematics是 Angular Devkit 适配器的遗留段,新条目一律写入generators);packageJsonUpdates分组则挂在同级的packageJsonUpdates段落。文件底部给出了完整的骨架形状:

{ "$schema": "../../node_modules/nx/schemas/migrations-schema.json", "generators": {}, "packageJsonUpdates": {} }

$schema指向仓库 packages/nx/schemas/migrations-schema.json,其中定义了generatorsschematicspackageJsonUpdates三个顶层键及每个条目的合法字段。类型层面,MigrationsJsonEntryPackageJsonUpdates定义在 packages/nx/src/config/misc-interfaces.ts 中,其中MigrationsJsonEntry包含versiondescriptionimplementation/factorypromptrequiresdocumentation等可选字段,并明确documentation是"补充性"的参考文档,绝不代替implementation/prompt本身。

版本字段与门控:version 是闸门而非标签

模板文档开篇即强调:JSON 块中的条目键、版本值与包名都是示意性的,真正构成契约的是键集合与路径形状。版本值遵循 SKILL.md 第 2 节的 target-train(目标发布线)规则。在 runtime-contract.md 中这一规则被进一步精确化:

  • 条目在gt(version, installed) && lte(version, target)时被收集,采用 semver 预发布排序(beta.N < rc.N < stable);
  • 已安装一侧是严格大于(gt):用户恰好处于该预发布版本时永远不会再运行它;
  • 因此绝不能写裸的正式版本号(预发布用户会跳过它),也绝不能回填旧预发布版本(已越过该预发布的用户会被静默跳过);
  • 同一次变更中的所有条目统一使用所选发布线确切的下一个预发布版本,即使是在批量关联迁移时也是如此。

实际运行逻辑可以在 packages/nx/src/command-line/migrate/migrate.ts 中看到,requires求值统一使用includePrerelease: true。版本字段只决定"何时触发迁移",不决定"哪个发布线承载这段代码"——后者由分支决定,破坏性迁移必须等待发布线切换后再合并。

路径形状:dist 前缀与 rootDir 映射

模板文档明确指出:implementationpromptdocumentation的路径带dist前缀,因为它们要在已安装的包内部解析。运行时通过require.resolve以已安装包的migrations.json所在目录为基准解析这些路径,所以路径必须匹配发布后的布局

  • rootDir: "."(主导形状):发布路径为./dist/src/migrations/...,模板中的示例正是这种形状;
  • rootDir: "src":发布时不含src段,路径为./dist/migrations/...,例如 packages/dotnet/migrations.json 中的"implementation": "./dist/migrations/update-23-0-0/update-plugin-path"

编写建议:从同插件的兄弟条目复制形状;若是插件首个条目,则从该包的tsconfig.lib.jsonrootDir推导。migration-markdown-assets一致性规则会把每个已发布的.md路径通过构建时的rootDir/outDir反映射回源码,路径形状错误(在rootDir: "src"的包中写./dist/src/...)会直接失败;而对未声明rootDir/outDir组合的包该规则不检查,需要手工对照构建出的dist/确认路径。

另外,模板文档提醒:prompt文件(AI 提示词 runbook)的文件名必须与 implementation 的 basename 不同——documentation.md拥有该名称(SKILL.md 第 4 节)。这是为了避免 prompt 文档与实现文档同名而互相覆盖语义。

Generator-only 条目:确定性源码变换

最基础的条目类型,适用于删除选项、键重命名、默认值翻转等"可静态检测、可确定性变换"的变更:

"update-23-2-0-remove-foo-option": { "version": "23.2.0-beta.3", "description": "Removes the deprecated `foo` option from the @nx/bar:build executor options", "implementation": "./dist/src/migrations/update-23-2-0/remove-foo-option", "documentation": "./dist/src/migrations/update-23-2-0/remove-foo-option.md" }

要点:

  • implementation指向编译后的默认导出迁移函数,运行时以await fn(tree, {})调用;#symbol语法可选中命名导出;
  • 每条 generator 条目都必须配套同名的documentation.md(SKILL.md 第 6 节),渲染在插件迁移文档页,并作为 agentic 流程中 agent 的参考材料;
  • description同时喂给 agentic prompt 与公开文档页,应描述具体动作,例如"从 @nx/bar:build executor options 中移除已废弃的foo选项"。

当迁移只在上游主版本升级之后才适用时,追加requires

"requires": { "bar": ">=4.0.0" }

requires的语义值得特别注意(详见 runtime-contract.md):

  • 它针对本次运行中该包将要落地的版本求值(先取待执行的 packageJsonUpdates,回退到已安装版本),包在两者中都不存在则门控失败;
  • 只在收集阶段求值一次,执行阶段绝不重查——所以用上界编码"源窗口"(如>=9 <10)会在同一次运行把包升过上限时失败且迁移永不执行(storybook 曾因该 bug 被修复为去掉上界);
  • 只有迁移在等于或高于某版本时确实不适用,上界才正确(如 packages/next/migrations.json 中落地在 next 16 的工作区对 next 15 指引无用的next >=15.0.0 <16.0.0);
  • requires是跨包 AND 语义;互斥条件需要拆成多个条目;无法用requires表达的 OR 条件(如 umbrella 与 scoped 两种依赖名)要在迁移函数内部用getDeclaredPackageVersion+ semver 做代码级门控——eslint 的hasTypescriptEslintV8(见 packages/eslint/src/migrations/update-23-1-0/remove-removed-typescript-eslint-extension-rules.ts)正是由此而来:曾因单名门控静默跳过了只声明另一名字的工作区。

Prompt-only 条目:由 AI 驱动的判断型迁移

当变更需要 AST 变换无法做出的判断时,使用 prompt-only 条目——只有promptdocumentation,没有implementation

"update-23-2-0-migrate-bar-config-format": { "version": "23.2.0-beta.3", "requires": { "bar": ">=4.0.0" }, "description": "AI-assisted migration: rewrites bar config files to the v4 format, whose options do not map 1:1, so it is driven by an AI prompt rather than a deterministic generator", "prompt": "./dist/src/migrations/update-23-2-0/migrate-bar-config-format.md", "documentation": "./dist/src/migrations/update-23-2-0/upgrade-to-bar-v4.md" }

仓库真实示例:packages/eslint/migrations.json 中的update-23-1-0-migrate-ban-types-rule——@typescript-eslint/ban-types规则迁移到 v8 后继规则时选项无法 1:1 映射,因此由 AI prompt 驱动而非确定性 codemod。prompt运行时语义:生成阶段内容被提取到tools/ai-migrations/<package>/<targetVersion>/<basename>.md,且仅在 agentic 流程下执行;在普通运行中它们只作为"下一步建议"呈现。因此必须发生的变更绝不能放进 prompt——prompt-only 是最后手段,用于完全没有可安全自动化子集的变更;只要存在机械性子集,就应该写 hybrid。

Hybrid 条目:确定性预扫描加 AI 收尾

一个条目同时携带implementationprompt两个键:

"update-23-2-0-convert-bar-config": { "version": "23.2.0-beta.3", "requires": { "bar": ">=4.0.0" }, "description": "Converts bar configuration to the v4 format; mechanically safe conversions are applied by a generator and the remainder is completed by an AI prompt", "implementation": "./dist/src/migrations/update-23-2-0/convert-bar-config", "prompt": "./dist/src/migrations/update-23-2-0/finish-bar-config-conversion.md", "documentation": "./dist/src/migrations/update-23-2-0/convert-bar-config.md" }

规则与语义:

  • prompt 文件名必须与 implementation basename 不同(documentation.md拥有该名称);eslint 的convert-to-flat-config因共享 basename 早于该命名规则,模板文档明确提示不要照抄这一点;
  • hybrid 的生成器半边始终运行;当该半边返回skipAgentic: true时跳过 prompt 半边——这是告诉运行器"确定性运行已覆盖一切,不需要 AI 步骤"的显式信号,通常来自迁移自身的 no-op 守卫;
  • .ts只做机械安全编辑,把每个无法处理的形状累积成人类可读描述,通过返回{ nextSteps, agentContext }分发给不同消费者:agentContext只喂给 agent(并在外层 agent 驱动nx migrate时输出到 stdout),nextSteps展示给人类用户且绝不进入 agent prompt;
  • 返回skipAgentic: true绝不能同时返回agentContext(该上下文正是要喂给被放弃的 AI 步骤的)。

packageJsonUpdates:声明式依赖升级

普通升级

针对目标发布线的普通依赖升级,不需要写任何.ts实现:

"23.2.0": { "version": "23.2.0-beta.3", "packages": { "bar": { "version": "^4.1.0", "alwaysAddToPackageJson": false } } }
  • alwaysAddToPackageJson: false只在包已安装的位置升级它,这是受管依赖的常态;true(或字符串"dependencies"/"devDependencies")在缺失时也会把它加入 package.json;
  • 分组应用条件为installed <= group.version <= target(含下界,与迁移条目不同);
  • 只有已在 dependencies/devDependencies 中的包会被触及,除非设置了addToPackageJson/alwaysAddToPackageJson;跨分组按包取最高版本,写回时过滤降级。

跨主版本升级:源主版本窗口门控

每个受支持的源主版本一个分组,按最旧在前排序:

"23.2.0-bar-v4": { "version": "23.2.0-beta.3", "requires": { "bar": ">=3.0.0 <4.0.0" }, "packages": { "bar": { "version": "^4.1.0", "alwaysAddToPackageJson": false } } }

与迁移条目不同,packageJsonUpdates分组按源主版本窗口门控(">=N.0.0 <N+1.0.0"),且必须同时携带上下界——分组把"源范围"翻译为"目标版本",阶梯依赖每个窗口闭合。分组按键序求值,每个被接受的分组写入后续门控检查可读取的待更新集合;被门控卡住的分组不会丢弃,首轮之后持续重估直到没有更多分组适用(包括另一插件稍后才满足门控的分组),每个分组至多应用一次。这正是多主版本链式升级得以串联的机制——SKILL.md 中以 packages/rspack/migrations.json 未门控的 http-proxy-middleware v2→v3 升级与 packages/react/migrations.json 中同一提交后来被门控的升级对比,强调"老主版本仍受支持时跨主版本升级必须门控源窗口"。

分组键是用户可见的(交互式提示页脚的文档锚点):X.Y.ZX.Y.Z-<topic>(单独门控的第三方升级)。packages/eslint/migrations.json 中的成对分组21.2.0-typescript-eslint/21.2.0-@typescript-eslint正是用"每名字一个分组"来表达 OR 条件(umbrella 与 scoped 依赖名)。注意<version>--PackageGroup键由运行时从插件packageGroup合成,绝不手工编写。

升级自带迁移的包而不触发它们

"packages": { "some-cli": { "version": "~5.0.0", "alwaysAddToPackageJson": false, "ignorePackageGroup": true, "ignoreMigrations": true } }

ignorePackageGroup: true+ignoreMigrations: true使升级只改版本,不把该包的整个 packageGroup 拉进来,也不触发它自带的迁移(@angular/cli模式,见 runtime-contract.md)。此外:不要使用x-prompt(已废弃,Nx v24 移除,schema 中标注为 "Surfaced vianx migrate --interactive... will be removed in Nx v24");ifPackageInstalled是运行时门控,没有任何一方插件使用它,一律改用requires

插件首个迁移:package.json 接线

当插件首次引入迁移时,除了migrations.json本身,还需要在 packages/eslint/package.json 之类的插件package.json中声明:

"nx-migrations": { "migrations": "./migrations.json", "supportsOptionalMigrations": true }

同时确认以下配套项(SKILL.md 第 3 节,缺一不可):

  • migrations.json"$schema": "../../node_modules/nx/schemas/migrations-schema.json"开头(仅新文件,不回填旧文件);
  • assets.jsonsrc/migrations/**/*.md复制进 dist,使每个.md落在构建出的实现旁边(rootDir: "src"的包用{ "glob": "migrations/**/*.md", "input": "packages/<plugin>/src" },见 packages/maven/assets.json)——缺少该 glob 会把.md从发布包中剔除,破坏prompt/documentation解析与文档站,migration-markdown-assets一致性规则会对任何未被 assets 配置产出的.md引用报错;
  • 存在根级migrations.spec.ts,调用@nx/devkit/internal-testing-utilsassertValidMigrationPaths解析每个条目的 implementation/prompt/documentation 路径并标记孤立文件;
  • 插件 eslint 配置把@nx/nx-plugin-checks应用到含./migrations.jsonfiles数组(校验清单形状与重复键);
  • 全新@nx/*插件必须加入 packages/nx/package.json 的nx-migrations.packageGroup,否则nx migrate永远不会升级它(由nx-package-group一致性规则强制)。

模板文档还提醒:不要只依赖@nx/plugin:migration生成器——它只搭出空壳、默认键名取裸文件名,且从不写requires.md文件、prompt 条目或按包的packageJsonUpdates细节。应从模板文档手工编写。

避坑清单与验证

结合 .claude/skills/author-migration/deprecated-patterns.md 与 SKILL.md 第 7 节,常见的正确性陷阱包括:

  • 路径对但文件错:没有校验器能检查路径是否指向"正确的那个"迁移文件——一个存在但错误的 implementation 路径能通过全部校验并在运行时执行(packages/nx曾真实出现过该 bug),务必打开文件人工确认;
  • 不要编写cli(schema 标注 "No longer used")与schema(schema 有记录但运行时从不读取);
  • 不要为@nx/core之外的迁移编写x-repair-skipnx repair会无条件重跑所有 nx-core 迁移,因此 nx-core 迁移必须幂等;
  • 迁移函数签名固定为export default async function update(tree: Tree),只返回void | string[] | { nextSteps, agentContext, skipAgentic },任何GeneratorCallback都会被静默丢弃;
  • 修复已发布迁移时:就地修改实现把条目版本提升到当前下一个预发布,使已运行过坏版本的工作区能够重跑(packages/storybook/migrations.json 展示了从21.1.023.2.0-test-runner的完整分组演化)。

发布前验证:npx nx run-many -t test,lint -p <plugin>跑根级migrations.spec.ts与 lint;npx nx build workspace-plugin && pnpm nx-cloud conformance:checkmigration-markdown-assetsmigration-groups(同一分组内所有@typescript-eslint/*必须一起升级)与nx-package-group规则;最后在真实仓库中通过本地 registry(pnpm local-registry+pnpm nx-release <next-prerelease> --local)或离线 tarball 方式端到端验证一次npx nx migrate

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 8:54:49

SQLite+FTS5+BM25构建智能体本地上下文管理引擎

1. “context-mode”到底是什么&#xff1f;别被术语唬住&#xff0c;它其实是智能体系统里最实在的“上下文管家” 最近在多个技术社区和开发者群里&#xff0c;“context-mode”这个词突然高频出现&#xff0c;尤其和MCP、SQLite、FTS5、BM25这些词绑在一起刷屏。很多人第一反…

作者头像 李华
网站建设 2026/9/10 8:52:17

2026随身WiFi怎么选?从信号原理到品牌差异的实用选购指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 8:51:11

昇腾GE AIPP缩放参数设置

aclmdlSetAIPPScfParams 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、Te…

作者头像 李华
网站建设 2026/9/10 8:49:34

工业PLC与伺服系统中MLCC选型完全指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 8:48:06

类型化消息驱动的生成式 UI:Vercel AI SDK 的边界在哪里

一条 part 分支&#xff0c;给出生成式 UI 的设计前提 消息流里出现 tool-generateImage 时&#xff0c;UI 层要决定渲染什么。官方示例的做法是分支&#xff1a; case text:return <div key{index}>{part.text}</div>; case tool-generateImage:return <ImageG…

作者头像 李华
网站建设 2026/9/10 8:46:38

高光谱选型核心:按分子特征选波段而非堆参数

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华