news 2026/9/10 8:12:23

Nx 迁移编写避坑指南:废弃模式(Deprecated Patterns)识别与现代形态重写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Nx 迁移编写避坑指南:废弃模式(Deprecated Patterns)识别与现代形态重写

Nx 迁移编写避坑指南:废弃模式(Deprecated Patterns)识别与现代形态重写

【免费下载链接】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 中,迁移(migration)是nx migrate升级工作区时自动执行的修复脚本,负责把用户配置与源码从旧形态改写为新形态。而"废弃模式"(deprecated patterns)指两类不该被新代码复制的写法:一类只存在于 git 历史中、只能在阅读旧迁移或第三方插件时遇到;另一类仍存活在仓库中但被明确禁止复制。本文以 .claude/skills/author-migration/deprecated-patterns.md 为核心骨架,结合仓库源码与配套技能文档,讲清每一类废弃模式的特征签名(recognition signature)、为何被废弃,以及对应的现代替代写法,帮助你迁移或移植旧迁移时直接改写为现代形态,而不是照搬。

为什么需要一份"废弃模式"清单

Nx 的迁移体系经历了从 Angular Devkit Schematics 到@nx/devkit的漫长演进,十余年间沉淀了大量历史写法。同一份migrations.json中既有老条目也有新条目,git 历史中更埋藏着 v6–v16 各时代的实现。当开发者需要:

  • 移植某个上游框架自带的迁移到 Nx;
  • 参照一条旧迁移写新迁移;
  • 审查第三方插件中的迁移实现;

最容易犯的错误就是"照着历史抄"。但历史写法往往依赖已被移除的运行路径(例如schematics适配器会丢弃迁移的返回值),或者与当前运行时契约(runtime-contract.md)相冲突。因此 Nx 官方将废弃模式分为两个 registry:

类别含义适用场景
仅存于历史(Historical only)已从仓库删除,运行时会直接失败阅读旧迁移作参考、移植第三方插件时
仍存活但禁止复制(Still live, do not copy)代码还在运行,旧条目仍在用编写新迁移时绝不复制,需用现代替代

无论哪种,规则只有一条:迁移或引用旧迁移时,必须改写为现代形态,绝不原样复现

历史遗留模式:只存在于 git 历史的写法

以下模式已从仓库删除,遇到时需识别并整体替换。

Angular Devkit Schematic Rules(v6–11 时代)

特征签名import { Rule, chain } from '@angular-devkit/schematics',配合updateJsonInTreereadJsonInTreecreateOrUpdate、带Change对象的insert,以及在结尾追加formatFiles()作为 Rule。

现代替代:默认导出的async function (tree: Tree),统一使用@nx/devkit。示例形态见 templates/spec-skeleton.md 中的 spec 骨架——现代迁移直接导入默认导出并调用,而非通过 Schematic 的 collection 机制。

顶层schematics段(2023 年之前)

特征签名migrations.json中条目挂在"schematics"键下而非"generators"

现代替代:一律使用generators段。依据 runtime-contract.md,schematics段在运行时走 Angular Devkit 适配器,且适配器会完全丢弃迁移的返回值——nextStepsagentContext全部失效,这正是该段被废弃的根本原因。

迁移代码内的包版本升级(v6–10 时代)

特征签名:在 .ts 实现里调用addUpdateTask(...)、链式RunSchematicTask,或通过updateJsonInTree('package.json', ...)直接改版本。

现代替代:声明式的packageJsonUpdates组;只有依赖变更依赖工作区状态(条件性变更)时才写 .ts 实现。这一点在 SKILL.md 第 1 节有明确规定:无条件升级永远不写 .ts 实现,条件性依赖变更才使用addDependenciesToPackageJson/removeDependenciesFromPackageJson

workspace.json / angular.json 编辑(v8–11 时代)

特征签名updateWorkspaceInTreegetWorkspace/updateWorkspaceupdateBuilderConfig

现代替代:项目配置用getProjects/updateProjectConfiguration,Nx 级配置用readNxJson/updateNxJson。核心原因在 SKILL.md 第 4 节 Config edits:getProjects直接感知项目图,且updateProjectConfiguration不会像裸updateJson那样静默跳过基于 package.json 的项目。

@nrwl/*导入(约 v15 之前)

特征签名from '@nrwl/workspace'from '@nrwl/devkit',以及readWorkspaceConfiguration/updateWorkspaceConfiguration

现代替代:统一@nx/devkit

SchematicTestRunner 规格测试(v6–13 时代)

特征签名SchematicTestRunnerUnitTestTree、通过 collection 执行runMigration('<name>', ...)

现代替代createTreeWithEmptyWorkspace()+ 直接导入迁移的默认导出。文档特别提醒了一个被丢失的能力:旧 helper 通过 migrations.json 按名字加载迁移,因此会验证"名字 → 实现"的接线是否正确;而直接导入的 spec 不再做这种校验——这正是为什么 pre-PR 检查清单要求打开 manifest 路径背后的真实文件确认实现指向正确(SKILL.md 第 7 节)。

AI 指令包装工厂(2026 年中之前)

特征签名:工厂函数读取files/<name>.md模板,通过tree.write写出tools/ai-migrations/MIGRATE_<THING>.md,并返回string[]

现代替代prompt键直接指向同目录(colocate)的 .md;runner 自身会把托管的工作区副本写到tools/ai-migrations/下。即路径写入职责从迁移代码移交给了 runner(见 runtime-contract.md 的prompt行)。

@nx/devkit/src/*深路径导入(pre-23)

特征签名from '@nx/devkit/src/generators/...'

现代替代@nx/devkit/internal。半公开 helper(如forEachExecutorOptions、target-default helper)从@nx/devkit/internal导入是 SKILL.md 第 4 节 Common canon 的明确要求。

仍存活但禁止复制的模式

以下写法仍存在于仓库旧条目中(运行时也不一定立刻报错),但新条目一律禁止复制。

"cli": "nx"

特征签名:generators 条目内出现"cli"键(旧条目中很普遍)。

规则:死键(dead key)。依据 runtime-contract.md,cli在"按段选择 runner"的改造后就已不再使用,schema 已标注 "No longer used"。新条目直接省略。

factory

特征签名"factory": "./dist/..."

规则:被容忍的别名。运行时implementationfactory等价,且两者同时存在时implementation优先;但新条目只写implementation不要对存量条目做批量改名

packageJsonUpdates 上的x-prompt

特征签名"x-prompt": "Do you want to update..."

规则:仅交互式生效,且已被标记为 Nx v24 移除;改用requires做门控。参见 runtime-contract.md 与 templates/migrations-json.md。

无 slug 或带点号的条目键

特征签名update-22-2-0(只有版本、没有动作 slug)、16.0.0-remove-nrwl-cli(版本段用点号而非连字符)、裸版本目录如21-0-0/

规则:键名应表达动作(无 slug 的键无法区分同一次发布中的两条迁移),版本段之间用连字符而非点号;目录统一为update-<ver>/。键的具体形态遵循该文件的主流约定(SKILL.md 第 3 节)。

updateJson(tree, 'nx.json', ...)

特征签名:直接对 nx.json 调用updateJson

规则:改用readNxJson/updateNxJson,且仅在内容变化时写回(SKILL.md 第 4 节)。

updateJson编辑 project.json

特征签名updateJson(tree, join(root, 'project.json'), ...)

规则:改用updateProjectConfiguration裸编辑会静默跳过基于 package.json 的项目——这是项目图统一抽象下最容易踩的坑。

返回GeneratorCallback

特征签名:返回类型为Promise<GeneratorCallback>,函数返回安装任务。

规则:runner 会静默丢弃 callback(runtime-contract.md Return values)。现代返回值为void | string[] | { nextSteps, agentContext, skipAgentic };安装由 runner 通过 diff package.json 完成,绝不返回安装任务、绝不调用installPackagesTask

console.*或 nx 的output工具

特征签名console.warn(...)import { output } from 'nx/src/utils/output'

规则:改用 devkit 的logger。原因:agentic 运行会捕获 generator 的 logger 输出并喂给验证 agent(<generator_output>),好的警告应点名文件和剩余工作;console.*输出不在该通道内。

静态import * as ts from 'typescript'

特征签名:模块顶部值导入 TypeScript。

规则:顶部用 type-only 导入(import type * as ts from 'typescript'),首次使用时惰性ensureTypescript()(来自@nx/js/internal)或ensurePackage<typeof import('typescript')>('typescript', '*')。这避免在不需要 TypeScript API 的运行中支付加载成本。

深层nx/src/*导入

特征签名:插件迁移里出现from 'nx/src/utils/...'

规则:使用 devkit 导出;跨边界导入在旧代码中被容忍,新代码不允许。例外是packages/nx内部自身使用相对导入。

忽略文件的子串检查

特征签名content.includes(entry)后字符串拼接。

规则:改用addEntryToGitIgnore。其实现位于 packages/nx/src/utils/ignore.ts,基于ignore包解析而非子串匹配,能正确处理已覆盖模式、并自动创建不存在的文件。实际使用范例见 packages/nx/src/migrations/update-23-0-0/add-migrate-runs-to-git-ignore.ts:

export default async function addMigrateRunsToGitIgnore(tree: Tree) { if (!tree.exists('.gitignore')) { return; } // Lerna users that don't use nx.json may not expect .nx directory changes if (tree.exists('lerna.json') && !tree.exists('nx.json')) { return; } addEntryToGitIgnore(tree, '.gitignore', '.nx/migrate-runs'); await formatChangedFiles(tree); }

注意它同时示范了两个通用守卫:无.gitignore直接返回;Lerna 用户未使用 nx.json 时不写.nx目录。对应条目见 packages/nx/migrations.json 的23-0-0-add-migrate-runs-to-git-ignore

非 colocate 的 prompt 文件

特征签名prompt指向某个 generator 的files/目录。

规则:把 .md 与迁移放在同一update-<ver>/目录中。依据 runtime-contract.md,prompt相对路径会被校验必须留在 migrations 目录内,因此 colocate 是硬性前提。

以文档风格编写的 prompt .md

特征签名:作为prompt接入的文件里出现#### Sample Code Changes这类 h4 标题。

规则:prompt 使用 runbook 风格(模板见 templates/prompt-runbook.md),h4 是documentation文件的体裁。一个典型的 prompt-runbook 包含:Overview(含 out of scope)、Pre-Migration Checklist(作用域任务须以 no-op guard 开头)、分步 Before/After、Post-Migration Validation 的循环直到变绿、Nx-Specific Notes。

迁移中的 devkitglob

特征签名:从@nx/devkit导入的glob(

规则:原地弃用(deprecated in place),改用globAsync

从"识别"到"改写":现代迁移的标准形态

识别出废弃模式后,改写时直接落到现代规范。综合 SKILL.md 与 templates/migrations-json.md,现代迁移的标准形态可以概括为三层。

文件布局与 manifest 条目

packages/<plugin>/src/migrations/update-<major>-<minor>-<patch>/<name>.ts packages/<plugin>/src/migrations/update-<major>-<minor>-<patch>/<name>.spec.ts (有实现时才需要) packages/<plugin>/src/migrations/update-<major>-<minor>-<patch>/<name>.md (documentation,与 .ts 同名) packages/<plugin>/src/migrations/update-<major>-<minor>-<patch>/<other-name>.md (prompt,基名必须与任何 .ts 不同)

manifest 中挂generators段,路径使用发布形态(dist 前缀):

"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" }

实现层通则

  • export default async function update(tree: Tree),不接收选项;runner 以(tree, {})调用。
  • 只用 Tree API,绝不使用fs;路径用joinPathFragmentsnode:pathposixhelper 构建。
  • 结尾await formatFiles(tree)(不触碰 JS/TS/JSON 面时可跳过;packages/nx内部用formatChangedFiles)。
  • 失败开放(fail open),绝不 throw——一个抛错的迁移会让整个--run-migrations无法恢复;无法解析的文件跳过并记录进返回的agentContext
  • 幂等构造:重写消耗自身触发条件、写回以updated !== original门控,或显式 already-migrated 守卫(nx repair会无条件重跑 nx-core 迁移)。
  • 版本字符串冻结为文件内本地常量,绝不从插件的utils/versions导入(那些常量随每次发布浮动,用户运行的是编译进目标版本的迁移)。

返回值契约

现代迁移的返回值为void | string[] | { nextSteps, agentContext, skipAgentic }

  • string[]nextSteps的简写;
  • nextSteps:展示在运行结束摘要与失败回顾中,被 Nx Console 持久化,但永不进入 agent prompt
  • agentContext:注入 agent prompt 作为<advisory_context>;在纯人工运行时被丢弃,因此面向人的内容必须同步进nextSteps
  • skipAgentic: true:声明确定性运行已覆盖一切,跳过本会执行的 AI 步骤(hybrid 的 prompt 阶段或 generator-only 迁移后的验证步骤),仅在迁移能证明"无事可做"时返回,绝不与agentContext同返。

新旧形态对照速查

废弃模式特征签名现代替代
Schematic Rules@angular-devkit/schematicsRule/chain默认导出async (tree: Tree)
顶层schematics条目在schematicsgenerators
迁移内升级包addUpdateTaskRunSchematicTask声明式packageJsonUpdates
编辑 workspace.jsonupdateWorkspaceInTreegetProjects/updateProjectConfigurationreadNxJson/updateNxJson
@nrwl/*导入@nrwl/workspace@nrwl/devkit@nx/devkit
SchematicTestRunner spec按名字runMigrationcreateTreeWithEmptyWorkspace+ 直接导入默认导出
AI 指令工厂读模板 +tree.writetools/ai-migrations/prompt键 + runner 自管副本
@nx/devkit/src/*深路径from '@nx/devkit/src/...'@nx/devkit/internal
"cli": "nx"条目内cli省略(死键)
factory"factory": "./dist/..."implementation
x-promptpackageJsonUpdates 上的交互提示requires门控
无 slug / 点号键update-22-2-016.0.0-remove-nrwl-cli动作 slug + 连字符版本段
updateJson(nx.json)直接改 nx.jsonreadNxJson/updateNxJson
updateJson(project.json)join(root, 'project.json')updateProjectConfiguration
GeneratorCallback返回返回安装任务void/string[]/{ nextSteps, agentContext, skipAgentic }
console.*/outputconsole.warnnx/src/utils/outputdevkitlogger
静态 ts 导入顶部import * as tstype-only +ensureTypescript()
nx/src/*导入插件迁移里的nx/src/utils/...devkit 导出
忽略文件子串检查content.includes(entry)addEntryToGitIgnore(packages/nx/src/utils/ignore.ts)
非 colocate promptprompt指向 generator 的files/迁移update-<ver>/目录内 colocate
文档体裁 prompth4#### Sample Code Changesrunbook 体裁(templates/prompt-runbook.md)
devkitglob迁移中的glob(globAsync

验证与收尾:让改写经得起检查

改写完成后,用两层机制验证。机械层由仓库 validator 把关:npx nx run-many -t test,lint -p <plugin>跑根级migrations.spec.tsassertValidMigrationPaths校验每条条目路径能在源码树中解析、无孤儿 .ts/.md)与@nx/nx-plugin-checks(校验 manifest 形态、重复键);pnpm nx-cloud conformance:check中的migration-markdown-assets规则校验发布形态(每个被引用的 .md 确实被打包进构建产物)。

判断层(SKILL.md 第 7 节)是 validator 覆盖不到的"人工残留":确认implementation指向本迁移的文件(validator 只查存在性,不查指向是否正确);确认使用implementation而非factory、无cli键;确认版本是该发布轨(train)的下一个精确预发布版、requires相对落地版本评估且无编码源窗口的上界;确认 spec 覆盖全部必测用例(negative 必测,幂等、畸形输入、多编辑、优先级、列表健全、行为复现按触发条件必测)。核心原则始终如一:从历史与旧条目中识别模式、改写为现代形态,而不是复现它们

【免费下载链接】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:06:51

多模态与视觉大模型开发,OpenCV仍是必修课:实战学习路线全解

2026年了&#xff0c;聊多模态和视觉大模型开发&#xff0c;还有人觉得OpenCV是过时的“传统图像处理”&#xff0c;我每次听到都想把人拉过来坐下聊聊。恰恰是我最近几年做的多模态融合、视觉语言模型项目里&#xff0c;OpenCV几乎每天都在用&#xff0c;从图片清洗、图像预处…

作者头像 李华
网站建设 2026/9/10 7:59:19

从CDN加速到边缘智能:多语言工程的语法实践与框架改造

很多人把CDN理解成“一个缓存加速工具”&#xff0c;但真正在互联网工程里摸爬滚打过的同学都知道&#xff0c;CDN只是第一层&#xff0c;边缘智能才是在加速之上长出价值的那个点。而多语言场景&#xff0c;恰好是能把CDN加速、边缘路由、缓存策略、后端工程化串起来的最典型战…

作者头像 李华
网站建设 2026/9/10 7:58:40

蓝牙网关如何破解多人运动心率监测的接入难题

/* 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 7:56:49

软考高项变更管理全解析:流程、CCB与实战应用

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

作者头像 李华