Files
shop-server/docs/superpowers/plans/2026-06-11-split-monorepo.md
T
Kirill 93b1624191 вы
2026-06-11 16:50:47 +05:00

12 KiB

Split Monorepo Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Split monorepo into shop-server and shop-client repos on Gitea (192.168.1.110:3000) with shared/ in server repo.

Architecture: Two independent repos. shared/ lives in shop-server. Client references it via relative alias ../shop-server/shared. Clean init commits, no filter-branch.

Tech Stack: git, bash, Gitea at 192.168.1.110:3000

Prerequisite: Создать пустые репозитории в Gitea через веб-интерфейс:


Task 1: Prepare workspace

Files:

  • Create: /tmp/shop-repos/shop-server/ (empty dir)

  • Create: /tmp/shop-repos/shop-client/ (empty dir)

  • Step 1: Create workspace directories

mkdir -p /tmp/shop-repos/shop-server /tmp/shop-repos/shop-client

Task 2: shop-server — .gitignore

Files:

  • Create: /tmp/shop-repos/shop-server/.gitignore

  • Step 1: Write .gitignore

node_modules
dist
*.log
.env
scripts/deploy.env
server/prisma/dev.db
server/prisma/dev.db-journal
server/uploads/
uploads/.cache/
  • Step 2: Copy server/, shared/, scripts/
mkdir -p /tmp/shop-repos/shop-server/scripts
cp -r /mnt/d/my_projects/shop_deploy/server /tmp/shop-repos/shop-server/server
cp -r /mnt/d/my_projects/shop_deploy/shared /tmp/shop-repos/shop-server/shared
cp /mnt/d/my_projects/shop_deploy/scripts/backup-db.sh /tmp/shop-repos/shop-server/scripts/backup-db.sh
cp /mnt/d/my_projects/shop_deploy/scripts/craftshop-backup.service /tmp/shop-repos/shop-server/scripts/craftshop-backup.service
cp /mnt/d/my_projects/shop_deploy/scripts/craftshop-backup.timer /tmp/shop-repos/shop-server/scripts/craftshop-backup.timer
cp /mnt/d/my_projects/shop_deploy/scripts/SERVER_SETUP.md /tmp/shop-repos/shop-server/scripts/SERVER_SETUP.md
cp /mnt/d/my_projects/shop_deploy/scripts/craftshop-netbird.conf /tmp/shop-repos/shop-server/scripts/craftshop-netbird.conf

Task 3: shop-server — README.md + AGENTS.md

Files:

  • Create: /tmp/shop-repos/shop-server/README.md

  • Create: /tmp/shop-repos/shop-server/AGENTS.md

  • Step 1: Write README.md

# shop-server — бэкенд магазина ручной работы

Fastify + Prisma + SQLite. API, админка, загрузка изображений.

## Запуск

cd server
cp .env.example .env
npm ci
npx prisma migrate dev
npm run dev

Сервер: http://127.0.0.1:3333. Health: GET /health.

## Деплой

См. scripts/SERVER_SETUP.md.
  • Step 2: Write AGENTS.md
# AGENTS.md — shop-server

## Project structure

- `server/` — Fastify + Prisma + SQLite backend
- `shared/constants/` — JS + .d.ts shared with client (order statuses, delivery carriers, payment methods, upload limits)

## Developer commands

| Command | What it does |
|---|---|
| `npm run dev` | node --env-file=.env --watch src/index.js (requires Node 20.6+) |
| `npm run dev:classic` | node --watch src/index.js (loads .env via dotenv) |
| `npm run lint` | ESLint (flat config) |
| `npm run lint:fix` | ESLint with --fix |
| `npm run format` | Prettier write all |
| `npm run format:check` | Prettier check only |
| `npm test` | vitest run |
| `npm run db:reset:test` | Reset SQLite DB + re-run migrations + seed (uses .env) |

## Conventions

- **Language**: Отвечай пользователю на русском.
- **Single quotes**, no semicolons, trailing commas, 120 print width (Prettier + ESLint enforce).
- **Alias**: @shared → shared/ (configured in vitest.config.js for tests).
- **Admin access**: Only users with email matching ADMIN_EMAIL env var can access admin routes. Server auto-creates the admin user on startup.
- **Server helpers**: slugify, parseMaterialsInput, mapProductForApi are decorated on fastify instance, accessed via request.server.*.

## Testing

- Vitest with globals enabled.
- Test files live in __tests__/ directories next to the code they test.

## OAuth

- VK callback: {SERVER_PUBLIC_URL}/api/auth/oauth/vk/callback
- Yandex callback: {SERVER_PUBLIC_URL}/api/auth/oauth/yandex/callback

## Infrastructure (deployment)

- VPS runs Nginx Proxy Manager (NPM), connected via Netbird VPN to the server machine
- Server machine runs the project
- Traffic flow: Browser → Domain → VPS (NPM) → Netbird → Server machine (3333)
- trustProxy: true on Fastify

## Notable quirks

- .env is gitignored. Copy .env.example to .env for local dev.
- db:reset:test runs prisma migrate reset --force, which destroys all data.

Task 4: shop-server — git init, commit, push

Files:

  • None new

  • Step 1: Init git repo and commit

cd /tmp/shop-repos/shop-server
git init
git add -A
git commit -m "initial: server + shared"
  • Step 2: Add remote and push
cd /tmp/shop-repos/shop-server
git remote add origin http://192.168.1.110:3000/admin/shop-server.git
git push -u origin main

Expected: push successful, repo populated on Gitea.


Task 5: shop-client — .gitignore

Files:

  • Create: /tmp/shop-repos/shop-client/.gitignore

  • Step 1: Write .gitignore

node_modules
dist
dist-ssr
*.log
*.local
.env
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
  • Step 2: Copy client/ directory
cp -r /mnt/d/my_projects/shop_deploy/client /tmp/shop-repos/shop-client/client

Task 6: shop-client — fix shared/ alias

Files:

  • Modify: /tmp/shop-repos/shop-client/client/vite.config.ts:6-15

  • Modify: /tmp/shop-repos/shop-client/client/tsconfig.app.json:3-7

  • Step 1: Fix vite.config.ts

Change lines 6-7, 15:

Old:

const rootDir = fileURLToPath(new URL('.', import.meta.url))
const projectRoot = path.resolve(rootDir, '..')
// ...
'@shared': path.resolve(projectRoot, 'shared'),

New:

const rootDir = fileURLToPath(new URL('.', import.meta.url))
const projectRoot = path.resolve(rootDir, '..', '..', 'shop-server')
// ...
'@shared': path.resolve(projectRoot, 'shared'),

Full file after:

import path from 'node:path'
import { fileURLToPath } from 'node:url'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

const rootDir = fileURLToPath(new URL('.', import.meta.url))
const projectRoot = path.resolve(rootDir, '..', '..', 'shop-server')

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(rootDir, 'src'),
      '@shared': path.resolve(projectRoot, 'shared'),
    },
  },
  server: {
    fs: {
      allow: [path.resolve(rootDir, '..'), projectRoot],
    },
    port: 5173,
    proxy: {
      '/api': {
        target: 'http://127.0.0.1:3333',
        changeOrigin: true,
      },
      '/uploads': {
        target: 'http://127.0.0.1:3333',
        changeOrigin: true,
      },
      '/uploads-resized': {
        target: 'http://127.0.0.1:3333',
        changeOrigin: true,
      },
    },
  },
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            if (id.includes('react-router')) return 'vendor-react'
            if (id.includes('react-dom')) return 'vendor-react'
            if (id.includes('node_modules/react/')) return 'vendor-react'
            if (id.includes('@mui')) return 'vendor-mui'
            if (id.includes('@emotion')) return 'vendor-mui'
            if (id.includes('swiper')) return 'vendor-swiper'
            if (id.includes('@tanstack/react-query')) return 'vendor-query'
            if (id.includes('effector')) return 'vendor-effector'
          }
        },
      },
    },
  },
})
  • Step 2: Fix tsconfig.app.json

Change line 6:

Old:

"@shared/*": ["../shared/*"]

New:

"@shared/*": ["../../shop-server/shared/*"]

Full file after:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@shared/*": ["../../shop-server/shared/*"]
    },
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "target": "es2023",
    "lib": ["ES2023", "DOM"],
    "module": "esnext",
    "types": ["vite/client"],
    "skipLibCheck": true,
    "strict": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "noEmit": true,
    "jsx": "react-jsx",

    "ignoreDeprecations": "6.0",

    /* Linting */
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "erasableSyntaxOnly": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"]
}

Task 7: shop-client — README.md + AGENTS.md

Files:

  • Create: /tmp/shop-repos/shop-client/README.md

  • Create: /tmp/shop-repos/shop-client/AGENTS.md

  • Step 1: Write README.md

# shop-client — витрина и админка магазина ручной работы

React + Vite + TypeScript + MUI. FSD-архитектура, @tanstack/react-query.

## Запуск

npm ci
npm run dev

Откройте http://localhost:5173. API проксируется на http://127.0.0.1:3333.

Требуется shop-server рядом (../../shop-server/shared/ для @shared/ alias).

## Команды

| Command | Description |
|---|---|
| npm run dev | Vite dev server |
| npm run build | tsc + vite build |
| npm run lint | ESLint |
| npm run lint:fix | ESLint --fix |
| npm run format | Prettier |
| npm test | vitest run |
  • Step 2: Write AGENTS.md
# AGENTS.md — shop-client

## Project structure

FSD architecture: app/pages/widgets/features/entities/shared

## Developer commands

| Command | What it does |
|---|---|
| `npm run dev` | Vite dev server on :5173, proxies /api and /uploads to 127.0.0.1:3333 |
| `npm run build` | Runs tsc -b first, then vite build |
| `npm run lint` | ESLint (flat config) |
| `npm run lint:fix` | ESLint with --fix |
| `npm run format` | Prettier write all |
| `npm run format:check` | Prettier check only |
| `npm test` | vitest run |
| `npm run test:watch` | vitest watch mode |

## Conventions

- **Language**: Отвечай пользователю на русском.
- **Single quotes**, no semicolons, trailing commas, 120 print width (Prettier + ESLint enforce).
- **FSD import boundaries** enforced by eslint-plugin-boundaries. Lower layers cannot import upper layers.
- **Aliases**: @/ → client/src/, @shared/ → ../../shop-server/shared/
- **API requests**: Use apiClient (axios wrapper from shared/api/) with @tanstack/react-query. Invalidate queries after mutations.
- **UI**: Prefer MUI components over custom HTML/CSS.
- **no-console**: ESLint error; use console.warn/error/info only.

## Testing

- vitest + jsdom + @testing-library/react
- Setup file: src/testing/setup.ts
- Test files live in __tests__/ directories next to the code they test.

## Important

- shop-server must be cloned alongside shop-client for @shared/ alias to resolve.
- Vite dev server relies on backend running at 127.0.0.1:3333. Start server first.

Task 8: shop-client — git init, commit, push

Files:

  • None new

  • Step 1: Init git repo and commit

cd /tmp/shop-repos/shop-client
git init
git add -A
git commit -m "initial: client"
  • Step 2: Add remote and push
cd /tmp/shop-repos/shop-client
git remote add origin http://192.168.1.110:3000/admin/shop-client.git
git push -u origin main

Expected: push successful, repo populated on Gitea.


Task 9: Verify

Files:

  • None

  • Step 1: Clone fresh and verify structure

cd /tmp
git clone http://192.168.1.110:3000/admin/shop-server.git shop-server-test
git clone http://192.168.1.110:3000/admin/shop-client.git shop-client-test
ls shop-server-test/server/
ls shop-server-test/shared/constants/
ls shop-client-test/client/src/

Expected: all directories present.

  • Step 2: Verify client can resolve @shared alias (TypeScript)
cd /tmp/shop-client-test/client
npm ci
npx tsc --noEmit -p tsconfig.app.json 2>&1 | head -20

Expected: no errors related to @shared/ imports. May have unrelated warnings — ignore those.

  • Step 3: Cleanup test dirs
rm -rf /tmp/shop-repos /tmp/shop-server-test /tmp/shop-client-test