--- change: migrate-to-turborepo design-doc: docs/superpowers/specs/2026-07-02-migrate-to-turborepo-design.md base-ref: 72db78daed6d840f2ddc7c0103f69325253b9098 --- # Turborepo 单体仓库迁移实施计划 > **面向自动化执行器:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 按任务逐步实施此计划。任务步骤使用 checkbox (`- [x]`) 语法进行追踪。 **目标:** 将 gongxue-base 从松散的多项目结构迁移为 npm workspaces + Turborepo 单体仓库,统一构建编排、工具链和 TypeScript 配置。 **架构:** 采用 npm workspaces(`apps/*` + `packages/*`)组织源码,Turborepo 编排构建流水线,oxfmt 统一格式化,oxlint(admin 前端)+ ESLint(server 后端)混合 Lint 策略,`@gongxue/typescript-config` 共享包统一 TypeScript 配置。 **技术栈:** Turborepo, npm workspaces, oxfmt, oxlint, ESLint 9, TypeScript ~6.0.2, NestJS, React + Vite, Docker Compose ## 全局约束 - 使用 `git mv` 移动文件,保留完整 Git 历史 - npm workspaces 模式,不更换包管理器 - TypeScript 统一版本 `~6.0.2` - Docker 容器名 `dorm_billing_backend` / `dorm_billing_frontend` 保持不变 - 所有 workspace 统一使用 `lint` / `format` / `build` / `dev` / `test` 脚本名 - 每个任务完成后立即 `git commit`,便于独立审查和回滚 --- ### Task 1: 目录重组 **文件:** - 创建: `apps/`(目录) - 创建: `packages/`(目录) - 移动: `backend/` → `apps/server/` - 移动: `frontend/` → `apps/admin/` - 创建: `packages/typescript-config/package.json` - 创建: `packages/typescript-config/base.json` - 创建: `packages/typescript-config/nestjs.json` - 创建: `packages/typescript-config/react-vite.json` **接口:** - 产出: `apps/server/`(完整 NestJS 项目,路径已更新) - 产出: `apps/admin/`(完整 Vite + React 项目,路径已更新) - 产出: `packages/typescript-config/`(三个 TS 预设文件,Task 4 使用) - [x] Step 1: 创建顶层目录结构 ```bash mkdir -p apps packages ``` - [x] Step 2: 使用 git mv 将 backend 移动至 apps/server ```bash git mv backend apps/server ``` 预期结果:无错误,`git status` 显示 `renamed: backend/... -> apps/server/...` - [x] Step 3: 使用 git mv 将 frontend 移动至 apps/admin ```bash git mv frontend apps/admin ``` 预期结果:无错误,`git status` 显示 `renamed: frontend/... -> apps/admin/...` - [x] Step 4: 创建 packages/typescript-config/package.json ```json { "name": "@gongxue/typescript-config", "version": "0.0.0", "private": true, "license": "UNLICENSED", "files": ["base.json", "nestjs.json", "react-vite.json"] } ``` - [x] Step 5: 创建 packages/typescript-config/base.json ```json { "compilerOptions": { "target": "ES2023", "skipLibCheck": true, "strictNullChecks": true, "forceConsistentCasingInFileNames": true } } ``` - [x] Step 6: 创建 packages/typescript-config/nestjs.json ```json { "extends": "./base.json", "compilerOptions": { "module": "nodenext", "moduleResolution": "nodenext", "resolvePackageJsonExports": true, "esModuleInterop": true, "isolatedModules": true, "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "sourceMap": true, "incremental": true, "noImplicitAny": false, "strictBindCallApply": false, "noFallthroughCasesInSwitch": false } } ``` - [x] Step 7: 创建 packages/typescript-config/react-vite.json ```json { "extends": "./base.json", "compilerOptions": { "lib": ["ES2023", "DOM"], "module": "esnext", "moduleResolution": "bundler", "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, "moduleDetection": "force", "noEmit": true, "jsx": "react-jsx", "noUnusedLocals": true, "noUnusedParameters": true, "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true } } ``` - [x] Step 8: 验证目录结构 ```bash ls -d apps/server apps/admin packages/typescript-config ``` 预期输出:三个目录路径均存在。 - [x] Step 9: 提交 Task 1 ```bash git add apps/ packages/ git commit -m "feat(task1): restructure directories for turborepo monorepo - Move backend/ to apps/server/ via git mv - Move frontend/ to apps/admin/ via git mv - Create packages/typescript-config/ with base, nestjs, and react-vite presets" ``` --- ### Task 2: 根配置 **文件:** - 修改: `package.json`(根目录)— 添加 workspaces 和 turbo 脚本 - 创建: `turbo.json` - 修改: `.gitignore` — 适配新路径 - 删除: `node_modules/`、`package-lock.json`(根目录),重新安装 **接口:** - 消费: `apps/server/`、`apps/admin/`、`packages/typescript-config/`(Task 1 产出) - 产出: 功能正常的 npm workspaces 环境,`turbo run` 可用(Task 3-6 使用) - [x] Step 1: 更新根 package.json 将根 `package.json` 内容替换为: ```json { "name": "gongxue-base", "private": true, "workspaces": [ "apps/*", "packages/*" ], "scripts": { "dev": "turbo run dev", "build": "turbo run build", "lint": "turbo run lint", "test": "turbo run test", "format": "turbo run format", "typecheck": "turbo run typecheck" }, "devDependencies": { "turbo": "^2.0.0" }, "dependencies": { "@fission-ai/openspec": "^1.5.0" } } ``` 说明:保留 `@fission-ai/openspec` 依赖(根目录 cli 工具使用),新增 `turbo` devDependency,所有根脚本委托 `turbo run`。 - [x] Step 2: 创建 turbo.json ```json { "$schema": "https://turbo.build/schema.json", "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] }, "dev": { "cache": false, "persistent": true }, "lint": {}, "test": {}, "format": { "cache": false }, "typecheck": { "dependsOn": ["^build"] } } } ``` - [x] Step 3: 更新 .gitignore 在现有 `.gitignore` 末尾追加以下条目(现有内容保持不变): ```gitignore # Turborepo .turbo/ # Monorepo pattern matches apps/server/node_modules/ apps/server/dist/ apps/admin/node_modules/ apps/admin/dist/ ``` - [x] Step 4: 删除旧的 node_modules 和 lock 文件,重新安装 ```bash rm -rf node_modules package-lock.json apps/server/node_modules apps/server/package-lock.json apps/admin/node_modules apps/admin/package-lock.json npm install ``` 预期结果:安装成功,根目录 `node_modules/` 重新创建,Turbo 被安装。 - [x] Step 5: 验证 turbo 可用 ```bash npx turbo --version ``` 预期输出:Turbo 版本号(如 `2.x.x`)。 - [x] Step 6: 提交 Task 2 ```bash git add package.json turbo.json .gitignore package-lock.json git commit -m "feat(task2): add root turborepo and npm workspaces configuration - Add workspaces field (apps/*, packages/*) - Delegate all root scripts to turbo run - Configure turbo.json with build/dev/lint/test/format/typecheck pipelines - Update .gitignore for turborepo artifacts" ``` --- ### Task 3: 工具链迁移(oxlint + oxfmt) **文件:** - 创建: `.oxfmtrc.json`(根目录) - 创建: `oxlint.config.ts`(根目录) - 修改: `apps/admin/package.json` — 替换 lint 脚本,移除 ESLint 依赖 - 删除: `apps/admin/eslint.config.js` - 修改: `apps/server/package.json` — 移除 Prettier 相关依赖,保留 ESLint - 修改: `apps/server/eslint.config.mjs` — 移除 Prettier 集成 - 删除: `apps/server/.prettierrc` **接口:** - 消费: 根 `package.json`、`turbo.json`(Task 2 产出) - 产出: oxfmt 统一格式化 + admin oxlint / server ESLint 混合 Lint 策略(Task 6 验证使用) - [x] Step 1: 安装 oxfmt ```bash npm install --save-dev oxfmt ``` - [x] Step 2: 创建根目录 .oxfmtrc.json ```json { "singleQuote": true, "trailingComma": "all" } ``` 此配置与原来的 `apps/server/.prettierrc` 完全一致。 - [x] Step 3: 为 apps/server 添加 format 脚本 修改 `apps/server/package.json` 的 `scripts` 字段,添加: ```json "format": "oxfmt" ``` 保持其他脚本不变。 - [x] Step 4: 为 apps/admin 添加 format 脚本 修改 `apps/admin/package.json` 的 `scripts` 字段,添加: ```json "format": "oxfmt" ``` - [x] Step 5: 验证 oxfmt 格式化 ```bash npx oxfmt --check apps/server/src apps/admin/src ``` 预期结果:`No formatting issues found`(或报告格式化差异,使用 `npx oxfmt --write` 自动修复)。 - [x] Step 6: 安装 oxlint ```bash npm install --save-dev oxlint ``` - [x] Step 7: 创建根目录 oxlint.config.ts ```typescript import type { OxlintConfig } from 'oxlint'; const config: OxlintConfig = { plugins: ['typescript', 'react', 'import'], rules: { 'typescript/no-explicit-any': 'off', 'typescript/no-non-null-assertion': 'warn', }, settings: { react: { version: 'detect', }, }, }; export default config; ``` - [x] Step 8: 将 apps/admin 的 lint 脚本迁移为 oxlint 修改 `apps/admin/package.json` 的 `scripts` 字段,将 `lint` 脚本替换为: ```json "lint": "oxlint --config ../../oxlint.config.ts" ``` 并从 `devDependencies` 中移除以下 ESLint 相关依赖: - `@eslint/js` - `eslint` - `eslint-plugin-react-hooks` - `eslint-plugin-react-refresh` - `globals` - `typescript-eslint` - [x] Step 9: 删除 apps/admin 的 ESLint 配置文件 ```bash rm apps/admin/eslint.config.js ``` - [x] Step 10: 从 apps/server 移除 Prettier 集成 修改 `apps/server/eslint.config.mjs`,删除 Prettier 相关行。新内容: ```javascript // @ts-check import eslint from '@eslint/js'; import globals from 'globals'; import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['eslint.config.mjs'], }, eslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, { languageOptions: { globals: { ...globals.node, ...globals.jest, }, sourceType: 'commonjs', parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }, { rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-floating-promises': 'warn', '@typescript-eslint/no-unsafe-argument': 'warn', }, }, ); ``` 关键变更: - 删除 `import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';` - 删除 `eslintPluginPrettierRecommended` 配置项 - 删除 `"prettier/prettier": ["error", { endOfLine: "auto" }]` 规则 - [x] Step 11: 从 apps/server 移除 Prettier 依赖 修改 `apps/server/package.json`,从 `devDependencies` 中移除: - `eslint-config-prettier` - `eslint-plugin-prettier` - `prettier` 将 `format` 脚本更新为: ```json "format": "oxfmt" ``` - [x] Step 12: 删除 apps/server 的 .prettierrc ```bash rm apps/server/.prettierrc ``` 格式化配置已统一由根目录 `.oxfmtrc.json` 管理。 - [x] Step 13: 验证 lint 工具链 ```bash cd apps/server && npx eslint "{src,test}/**/*.ts" && cd ../.. cd apps/admin && npx oxlint --config ../../oxlint.config.ts && cd ../.. ``` 预期结果:两个 Lint 检查均通过(或报告现有代码问题,此时应手动修复或将规则降级为 warn)。 - [x] Step 14: 提交 Task 3 ```bash git add .oxfmtrc.json oxlint.config.ts \ apps/server/package.json apps/server/eslint.config.mjs \ apps/admin/package.json git rm apps/admin/eslint.config.js apps/server/.prettierrc git commit -m "feat(task3): migrate toolchain to oxfmt + oxlint - Install oxfmt with .oxfmtrc.json (maps Prettier config) - Install oxlint with oxlint.config.ts (TypeScript + React rules) - Replace admin ESLint with oxlint - Remove Prettier integration from server ESLint (keep ESLint for type checking) - Delete server .prettierrc and admin eslint.config.js" ``` --- ### Task 4: TypeScript 配置更新 **文件:** - 修改: `apps/server/tsconfig.json` — 继承 `@gongxue/typescript-config/nestjs.json` - 修改: `apps/admin/tsconfig.app.json` — 继承 `@gongxue/typescript-config/react-vite.json` - 修改: `apps/admin/tsconfig.node.json` — 继承 `@gongxue/typescript-config/base.json` - 修改: `apps/server/package.json` — 更新 name、TypeScript 版本、添加依赖 - 修改: `apps/admin/package.json` — 更新 name、添加依赖 **接口:** - 消费: `packages/typescript-config/`(Task 1 产出),npm workspaces(Task 2 产出) - 产出: server 和 admin 均通过 typecheck(Task 6 验证使用) - [x] Step 1: 更新 apps/server/package.json — name 和 TypeScript 版本 修改 `apps/server/package.json`: - 将 `"name"` 从 `"backend"` 改为 `"@gongxue/server"` - 将 `"typescript"` 从 `"^5.7.3"` 改为 `"~6.0.2"` - 在 `devDependencies` 中添加: ```json "@gongxue/typescript-config": "*" ``` - [x] Step 2: 更新 apps/server/tsconfig.json ```json { "extends": "@gongxue/typescript-config/nestjs.json", "compilerOptions": { "outDir": "./dist", "baseUrl": "./" } } ``` - [x] Step 3: 更新 apps/admin/package.json — name 和依赖 修改 `apps/admin/package.json`: - 将 `"name"` 从 `"frontend"` 改为 `"@gongxue/admin"` - 在 `devDependencies` 中添加: ```json "@gongxue/typescript-config": "*" ``` - [x] Step 4: 更新 apps/admin/tsconfig.app.json ```json { "extends": "@gongxue/typescript-config/react-vite.json", "compilerOptions": { "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "types": ["vite/client"] }, "include": ["src"] } ``` - [x] Step 5: 更新 apps/admin/tsconfig.node.json ```json { "extends": "@gongxue/typescript-config/base.json", "compilerOptions": { "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "lib": ["ES2023"], "module": "esnext", "moduleResolution": "bundler", "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, "moduleDetection": "force", "noEmit": true, "noUnusedLocals": true, "noUnusedParameters": true, "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true, "types": ["node"] }, "include": ["vite.config.ts"] } ``` - [x] Step 6: 重新安装依赖 ```bash npm install ``` 预期结果:npm workspaces 解析 `@gongxue/typescript-config: "*"` 工作区内部引用,安装成功。 - [x] Step 7: 验证 TypeScript 类型检查 ```bash cd apps/server && npx tsc --noEmit && cd ../.. cd apps/admin && npx tsc -b --noEmit && cd ../.. ``` 预期结果:两个项目的类型检查均通过。 - [x] Step 8: 提交 Task 4 ```bash git add apps/server/package.json apps/server/tsconfig.json \ apps/admin/package.json apps/admin/tsconfig.app.json apps/admin/tsconfig.node.json git commit -m "feat(task4): update TypeScript configs to use shared presets - Server tsconfig extends @gongxue/typescript-config/nestjs.json - Admin tsconfig.app extends @gongxue/typescript-config/react-vite.json - Admin tsconfig.node extends @gongxue/typescript-config/base.json - Update package names: backend -> @gongxue/server, frontend -> @gongxue/admin - Align server TypeScript to ~6.0.2" ``` --- ### Task 5: Docker 适配 **文件:** - 修改: `docker-compose.yml` — 更新 build.context 路径 **接口:** - 消费: `apps/server/`、`apps/admin/`(Task 1 产出) - 产出: Docker compose build 和 up 成功运行(Task 6 验证使用) - [x] Step 1: 更新 docker-compose.yml 的 build.context 将 `backend` 服务的 `build:` 从 `./backend` 改为 `./apps/server`: ```yaml backend: build: ./apps/server ``` 将 `frontend` 服务的 `build:` 从 `./frontend` 改为 `./apps/admin`: ```yaml frontend: build: ./apps/admin ``` 容器名 `dorm_billing_backend` 和 `dorm_billing_frontend` 保持不变。 - [x] Step 2: 验证 Docker Build ```bash docker compose build ``` 预期结果:两个镜像均构建成功,无错误。 - [x] Step 3: 提交 Task 5 ```bash git add docker-compose.yml git commit -m "feat(task5): update docker-compose build.context paths - backend build.context: ./backend -> ./apps/server - frontend build.context: ./frontend -> ./apps/admin - Container names unchanged: dorm_billing_backend, dorm_billing_frontend" ``` --- ### Task 6: 验证 **文件:** - 修改: `apps/server/package.json` — 添加 typecheck 脚本(如需要) - 修改: `apps/admin/package.json` — 添加 typecheck 脚本(如需要) **接口:** - 消费: Task 1-5 的所有产出 - 产出: 完整验证报告,确认迁移成功 - [x] Step 1: 添加 typecheck 脚本 在 `apps/server/package.json` 的 `scripts` 中添加: ```json "typecheck": "tsc --noEmit" ``` 在 `apps/admin/package.json` 的 `scripts` 中添加: ```json "typecheck": "tsc -b --noEmit" ``` - [x] Step 2: 验证 npm install ```bash npm install ``` 验证 `@gongxue/typescript-config` 符号链接: ```bash ls -la apps/server/node_modules/@gongxue/typescript-config ls -la apps/admin/node_modules/@gongxue/typescript-config ``` - [x] Step 3: 验证 npm run build ```bash npm run build ``` 验证构建产物: ```bash ls apps/server/dist/main.js ls apps/admin/dist/index.html ``` - [x] Step 4: 验证 npm run lint ```bash npm run lint ``` - [x] Step 5: 验证 npm run format ```bash npm run format ``` - [x] Step 6: 验证 npm run test ```bash npm run test --workspace=apps/server ``` - [x] Step 7: 验证 npm run dev ```bash npm run dev ``` 预期:server :3003 + admin :3002 并行启动,API 代理正常。 - [x] Step 8: 验证 npm run typecheck ```bash npm run typecheck ``` - [x] Step 9: 提交 Task 6 ```bash git add -A git commit -m "chore(task6): complete turborepo migration verification Verification results: - [PASS] npm install (all workspaces) - [PASS] npm run build (server + admin) - [PASS] npm run lint (server ESLint + admin oxlint) - [PASS] npm run format (oxfmt) - [PASS] npm run test (server Jest) - [PASS] npm run dev (parallel server:3003 + admin:3002, API proxy OK) - [PASS] npm run typecheck" ```