---
change: migrate-to-turborepo
design-doc: docs/superpowers/specs/2026-07-02-migrate-to-turborepo-design.md
base-ref: 72db78daed6d840f2ddc7c0103f69325253b9098
archived-with: 2026-07-02-migrate-to-turborepo
---

# 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`，便于独立审查和回滚

archived-with: 2026-07-02-migrate-to-turborepo
---

### 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"
```

archived-with: 2026-07-02-migrate-to-turborepo
---

### 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"
```

archived-with: 2026-07-02-migrate-to-turborepo
---

### 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"
```

archived-with: 2026-07-02-migrate-to-turborepo
---

### 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"
```

archived-with: 2026-07-02-migrate-to-turborepo
---

### 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"
```

archived-with: 2026-07-02-migrate-to-turborepo
---

### 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"
```
