From 34726cc46d416a311d60808b16f5aefa658b4701 Mon Sep 17 00:00:00 2001 From: wangziqi Date: Thu, 2 Jul 2026 15:53:08 +0800 Subject: [PATCH] chore: check off all plan tasks --- .../plans/2026-07-02-migrate-to-turborepo.md | 728 ++++++++++++++++++ 1 file changed, 728 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-02-migrate-to-turborepo.md diff --git a/docs/superpowers/plans/2026-07-02-migrate-to-turborepo.md b/docs/superpowers/plans/2026-07-02-migrate-to-turborepo.md new file mode 100644 index 0000000..cf55350 --- /dev/null +++ b/docs/superpowers/plans/2026-07-02-migrate-to-turborepo.md @@ -0,0 +1,728 @@ +--- +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" +```