关于 nx-import
用于指导将代码仓库或文件夹导入 Nx 工作区并保留 Git 历史的技能,帮助开发者选择导入方式并处理后续配置问题。
主要功能
- 01导入代码并保留 Git 历史
- 02选择整仓库或子目录导入
- 03识别应用与库
- 04处理工作区 glob 和依赖配置
- 05排查 TypeScript 与插件配置问题
适用场景
- 01将其他仓库中的项目加入 Nx
- 02导入 monorepo 中选定的包
- 03排查工作区包解析失败
- 04为 Nx 配置导入的 TypeScript 项目
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ nrwl/ nx-ai-agents-config --skill nx-import安装后在 Claude Code 中输入/nx-import确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/ESLINT.mdGRADLE.mdJEST.mdNEXT.mdTURBOREPO.mdVITE.md详细介绍
快速入门
nx import会将代码从源代码仓库或文件夹导入当前工作区,并保留提交历史。- 在 nx
22.6.0及更高版本中,nx import会输出 .ndjson 并提出后续问题。对于更早的版本,始终使用--no-interactive运行,并直接指定所有标志。 - 运行
nx import --help查看可用选项。 - 导入前,请确保目标目录为空。
示例:目标目录中有
libs/utils和libs/models;源目录中有libs/ui和libs/data-access——你不能直接将libs/导入libs/。请分别导入源目录中的每个库。
主要文档:
- https://nx.dev/docs/guides/adopting-nx/import-project
- https://nx.dev/docs/guides/adopting-nx/preserving-git-histories
如果你有相关工具,请阅读 nx 文档。
导入策略
逐个子目录导入(nx import <source> apps --source=apps):
- 建议用于单体仓库源——文件会放在顶层,不会产生冗余配置
- 注意事项:需要运行多条导入命令(每条都会创建单独的合并提交);目标目录不能有冲突目录;不会导入根目录配置(依赖项、插件、targetDefaults)
- 目录冲突:导入到名称不同的目录(例如
imported-apps/),然后重命名
导入整个仓库(nx import <source> imported --source=.):
- 仅用于非单体仓库源(单项目仓库)
- 对于单体仓库,会产生杂乱的嵌套配置(
imported/nx.json、imported/tsconfig.base.json等) - 如果必须这样做:保留导入的
tsconfig.base.json(项目会扩展此配置),为工作区 glob 和 executor 路径添加前缀
目录约定
- **始终优先遵循目标目录现有的约定。**源目录使用
libs/,但目标目录使用packages/?导入到packages/(nx import <source> packages/foo --source=libs/foo)。 - 如果目标目录没有约定(工作区为空),请询问用户。
应用与库的识别
导入前,判断源项目是应用还是库:
- 应用:可部署的最终产品。常见特征:
- 前端:
next.config.*、带有构建入口点的vite.config.*、特定框架的应用脚手架(CRA、Angular CLI 应用等) - 后端(Node.js):Express/Fastify/NestJS 服务入口点,
package.json中没有"exports"字段 - JVM:Maven
pom.xml中包含<packaging>jar</packaging>或<packaging>war</packaging>,且有main类;Gradleapplication插件或mainClass设置 - .NET:
.csproj/.fsproj中包含<OutputType>Exe</OutputType>或<OutputType>WinExe</OutputType> - 通用:Dockerfile、可运行的入口点、没有供其他项目导入的公共 API
- 前端:
- 库:供其他项目使用的可复用包。常见特征:
package.json中的"main"/"exports"、Maven/Gradle 将其打包为库 JAR、.NET<OutputType>Library</OutputType>、旨在供其他包导入的命名导出。
目标目录规则:
- 应用 →
apps/<name>。检查工作区 glob(例如pnpm-workspace.yaml、根package.json中的workspaces),确认是否已有apps/*条目。- 如果没有
apps/*,请在导入前添加:更新工作区 glob 配置并提交(或暂存)更改。 - 示例:
nx import <source> apps/my-app --source=packages/my-app
- 如果没有
- 库 → 遵循目标目录现有的约定(
packages/、libs/等)。
常见问题
pnpm 工作区 Glob(重要)
nx import 会将导入目录本身(例如 apps)添加到 pnpm-workspace.yaml,而不是添加其中包的 glob 模式。跨包导入会失败,并出现 Cannot find module。
修复方法:根据源配置替换为正确的 glob(例如 apps/*、libs/shared/*),然后运行 pnpm install。
根依赖和配置不会被导入(严重)
nx import 不会合并源项目根目录中的以下内容:
package.json中的dependencies/devDependenciesnx.json中的targetDefaults(例如"@nx/esbuild:esbuild": { "dependsOn": ["^build"] }——对构建顺序至关重要)nx.json中的namedInputs(例如用于排除测试文件的production模式)nx.json中的插件配置
修复方法:比较源项目和目标项目的 package.json 与 nx.json。补上缺失的依赖项,并合并相关的 targetDefaults 和 namedInputs。
TypeScript 项目引用
导入后,运行 nx sync --yes。如果没有报告任何内容,但类型检查仍然失败,请先运行 nx reset,然后再次运行 nx sync --yes。
显式执行器路径修正
推断出的目标(通过 Nx 插件)会基于项目根目录解析配置,无需更改。显式执行器目标(例如 @nx/esbuild:esbuild)使用相对于工作区根目录的路径(main、outputPath、tsConfig、assets、sourceRoot),必须在这些路径前加上导入目标目录。
插件检测
- 导入整个仓库:
nx import会检测并提示安装插件。接受提示。 - 导入子目录:不会自动检测插件。请手动使用
npx nx add @nx/PLUGIN添加。检查include/exclude模式——默认模式不适用于其他目录(例如apps-beta/)。 - 更改任何插件配置后,运行
npx nx reset。
多余的根目录文件(仅适用于导入整个仓库)
导入整个仓库会将源项目根目录下的所有文件带入目标子目录。请清理以下文件:
pnpm-lock.yaml——已过时;目标项目有自己的锁文件pnpm-workspace.yaml——源项目的工作区配置;会与目标项目冲突node_modules/——已过时,包含指向源文件系统的符号链接.gitignore——与目标项目根目录下的.gitignore重复nx.json——源项目的 Nx 配置;目标项目有自己的配置README.md——可选;保留或删除均可
不要盲目删除 tsconfig.base.json——导入的项目可能会通过相对路径扩展该文件。
缺少根目录 ESLint 配置(导入子目录)
导入子目录不会带入源项目根目录下的 eslint.config.mjs,但项目配置中引用了 ../../eslint.config.mjs。
修复顺序:
- 先安装 ESLint 依赖:
pnpm add -wD eslint@^9 @nx/eslint-plugin typescript-eslint(以及特定框架所需的插件) - 创建根目录
eslint.config.mjs(从源项目复制,或使用@nx/eslint-plugin基础规则创建) - 然后运行
npx nx add @nx/eslint,在nx.json中注册插件
请显式安装 typescript-eslint——pnpm 的严格提升机制不会自动解析 @nx/eslint-plugin 的这个传递依赖。
固定 ESLint 版本(严重)
将 ESLint 固定为 v9(eslint@^9.0.0)。ESLint 10 会导致 @nx/eslint 和许多插件出错,并产生难以理解的错误,例如 Cannot read properties of undefined (reading 'version')。
@nx/eslint 可能会将 ESLint 8 作为对等依赖,导致解析到错误版本。如果 lint 因 Cannot read properties of undefined (reading 'allow') 失败,请添加 pnpm.overrides:
{ "pnpm": { "overrides": { "eslint": "^9.0.0" } } }
依赖版本冲突
导入后,请比较关键依赖项(typescript、eslint 和特定框架的依赖项)。如果目标项目使用较新版本,请将导入的软件包升级到匹配版本(通常是安全的)。如果源项目使用较新版本,可能需要先升级目标项目。如果需要强制执行单一版本策略,请使用 pnpm.overrides。
模块边界
导入的项目可能缺少 tags。请添加标签,或更新 @nx/enforce-module-boundaries 规则。
项目名称冲突(多次导入)
源项目和目标项目的 package.json 中如果有相同的 name,会导致 MultipleProjectsWithSameNameError。修复方法:重命名冲突项(例如将 @org/api 改为 @org/teama-api),更新所有依赖引用和导入语句,然后运行 pnpm install。每个导入仓库的根目录 package.json 也会成为一个项目——也请重命名其中的名称。
工作区依赖导入顺序
如果尚未导入某个 "workspace:*" 依赖,pnpm install 会在 nx import 期间失败。文件操作仍会成功。修复方法:先导入所有项目,再运行 pnpm install --no-frozen-lockfile。
.gitkeep 阻止子目录导入
TS 预设会创建 packages/.gitkeep。导入前将其删除并提交。
前端 tsconfig 基础设置(关键)
TS 预设的默认设置(module: "nodenext"、moduleResolution: "nodenext"、lib: ["es2022"])与前端框架(React、Next.js、Vue、Vite)不兼容。导入前端项目后,请检查目标根目录的 tsconfig.base.json:
moduleResolution:必须为"bundler"(而非"nodenext")module:必须为"esnext"(而非"nodenext")lib:必须包含"dom"和"dom.iterable"(前端项目需要这些)jsx:仅使用 React 的工作区设为"react-jsx";混合框架则按项目设置
对于子目录导入,目标根目录的 tsconfig 是权威配置——请更新它。对于整个仓库导入,导入的项目可能会继承各自嵌套的 tsconfig.base.json,因此这项设置没那么关键。
如果目标仓库还有需要 nodenext 的后端项目,请使用按项目覆盖设置,而不是修改根目录。
注意事项:TypeScript 不会合并 lib 数组——项目级覆盖设置会完全替换基础数组。项目级 lib 中务必包含所有需要的条目(例如 es2022、dom、dom.iterable)。
库的 @nx/react 类型定义
使用 @nx/react:library 生成的 React 库,会在其 tsconfig 的 types 中引用 @nx/react/typings/cssmodule.d.ts 和 @nx/react/typings/image.d.ts。如果目标工作区未安装 @nx/react,就会出现 Cannot find type definition file 错误。
修复方法:pnpm add -wD @nx/react
缺少 Jest 预设(子目录导入)
Nx 预设会在工作区根目录创建 jest.preset.js,项目的 Jest 配置会引用它(例如 ../../jest.preset.js)。子目录导入不会导入此文件。
修复方法:
- 运行
npx nx add @nx/jest——在nx.json中注册@nx/jest/plugin并更新namedInputs - 在工作区根目录创建
jest.preset.js(内容见references/JEST.md)——nx add只有在运行生成器时才会创建此文件,单独运行nx add不会创建 - 安装测试运行器依赖:
pnpm add -wD jest jest-environment-jsdom ts-jest @types/jest - 按需安装框架专用的测试依赖(见
references/JEST.md)
有关更深入的 Jest 问题(tsconfig.spec.json、Babel 转换、CI 原子化、Jest 与 Vitest 共存),请参阅 references/JEST.md。
目标名称添加前缀(整个仓库导入)
导入含有现有 npm 脚本(build、dev、start、lint)的项目时,Nx 插件会自动为推断出的目标名称添加前缀,以避免冲突:例如 next:build、vite:build、eslint:lint。
修复方法:从导入项目的 package.json 中删除 Nx 重写过的 npm 脚本,然后选择以下方式之一:
- 接受添加前缀后的名称(例如
nx run app:next:build) - 在
nx.json中重命名插件目标,使其使用不带前缀的名称
非 Nx 源代码问题
当源代码是没有 nx.json 的普通 pnpm/npm 工作区时。
npm 脚本重写(关键)
Nx 会在初始化期间重写 package.json 脚本,导致命令失效(例如将 vitest run 改为 nx test run)。修复方法:删除所有被重写的脚本——Nx 插件会根据配置文件推断目标。
noEmit → composite + emitDeclarationOnly(关键)
普通 TS 项目使用 "noEmit": true,这与 Nx 项目引用不兼容。
症状:“typecheck target is disabled because one or more project references set 'noEmit: true'” 或 TS6310。
修复:在所有导入的 tsconfig 中:
- 移除
"noEmit": true。如果它是通过 extends 链继承的,请显式设置"noEmit": false。 - 添加
"composite": true、"emitDeclarationOnly": true、"declarationMap": true - 添加
"outDir": "dist"和"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo" - 如果缺少
"extends": "../../tsconfig.base.json",则添加。移除现在可以从基础配置继承的设置。
过时的 node_modules 和锁文件
nx import 可能会从源项目导入 node_modules/(其中包含指向源文件系统的 pnpm 符号链接)和 pnpm-lock.yaml。两者都已过时。
修复:rm -rf imported/node_modules imported/pnpm-lock.yaml imported/pnpm-workspace.yaml imported/.gitignore,然后运行 pnpm install。
ESLint 配置处理
- 旧版
.eslintrc.json(ESLint 8):删除所有.eslintrc.*,移除 v8 依赖,并创建扁平配置eslint.config.mjs。 - 扁平配置(
eslint.config.js):自包含的配置通常可以保持原样。 - 未使用 ESLint:从头创建根目录和项目级配置。
TypeScript paths 别名
Nx 使用 package.json "exports" 和 pnpm 工作区链接,而不是 tsconfig "paths"。如果软件包配置了正确的 "exports",则不需要 paths。否则,请根据新的目录结构更新 paths。
特定技术的指导
识别源代码仓库中使用的技术,然后阅读并应用对应的参考文件。
可用参考文件:
references/ESLINT.md— ESLint 项目:重复的lint/eslint:lint目标、旧版.eslintrc.*对生成文件的检查、扁平配置.cjs自检查、typescript-eslintv7/v9 对等依赖冲突、同一工作区混用 ESLint v8 和 v9。references/GRADLE.mdreferences/JEST.md— Jest 测试:@nx/jest/plugin配置、jest.preset.js、按框架配置测试依赖、tsconfig.spec.json、Jest 与 Vitest 共存、Babel 转换、CI 原子化。references/NEXT.md— Next.js 项目:@nx/next/plugin目标、withNx、Next.js TS 配置(noEmit、jsx: "preserve")、通过错误的 PM 自动安装依赖、导入非 Nxcreate-next-app项目、Next.js 与 Vite 混用。references/TURBOREPO.mdreferences/VITE.md— Vite 项目(React、Vue 或两者):@nx/vite/plugin类型检查目标、resolve.alias/__dirname修复、框架依赖、Vue 专属配置、React 与 Vue 混用。