docs: add plan, tasks, research, and ADRs for frontend infrastructure tooling

This commit is contained in:
Sergey Krylov 2026-06-23 06:07:23 +03:00
parent 203b7cbf20
commit 6d3601a8f4
8 changed files with 553 additions and 1 deletions

View File

@ -0,0 +1,67 @@
# ADR-017: Biome как единый инструмент линтинга и форматирования
**Дата:** 2026-06-23
**Статус:** Принято
**Автор:** AI Agent (codex/frontend-infrastructure-tooling)
## Контекст
Проект использует **ESLint v8** + **Prettier** для линтинга и форматирования. ESLint 8 устарел: ESLint 9 имеет полностью изменённый конфиг, миграция потребует переписывания `.eslintrc`. Оба инструмента написаны на JavaScript и работают медленно на больших кодовых базах.
Появились современные Rust-альтернативы, объединяющие линтинг и форматирование в одном CLI со значительным приростом производительности.
## Рассмотренные варианты
### Biome v2.5
- Linter + Formatter в одном CLI
- 500+ правил (ESLint + TypeScript ESLint + others)
- 97% совместимость форматтера с Prettier
- ~35x быстрее Prettier, ~35x быстрее ESLint
- Поддержка: JS, TS, JSX, TSX, JSON, HTML, CSS, GraphQL
- Стабильный LTS, продакшн у AWS, Google, Vercel
- Встроенная миграция: `biome migrate eslint`
### Oxlint + Oxfmt (Oxc Project)
- Linter отдельно (800+ правил, 50-100x быстрее ESLint)
- Formatter отдельно (Oxfmt, beta, 3x быстрее Biome, 30x быстрее Prettier)
- Type-aware linting через tsgo
- Два отдельных инструмента с разными конфигами
- Oxfmt в статусе beta
### Оставить ESLint + Prettier
- Знакомый стек
- Медленная производительность
- Необходимость мигрировать на ESLint 9 в любом случае
- Два набора конфигов
## Решение
Перейти на **Biome** как единый инструмент для линтинга и форматирования.
Причины:
1. **Один инструмент вместо двух** — меньше конфигов, один CI-степ, одна команда
2. **Production-ready** — стабильный LTS, используется крупными компаниями
3. **Плавная миграция**`biome migrate eslint` переносит правила автоматически
4. **Производительность** — ~35x быстрее как линтинг, так и форматирование
5. **Встроенный import sorting** — замена `eslint-plugin-import`
### Ограничения
FSD-правила из `@conarti/eslint-plugin-feature-sliced` не имеют аналога в Biome. Если не удаётся портировать через plugin system — сохраняется минимальный `.eslintrc.cjs` только для FSD layer boundaries.
## Последствия
### Положительные
- Единый конфиг `biome.json` вместо `.eslintrc.cjs` + `.prettierrc`
- Ускорение CI (lint + format за один проход)
- Автоматический import sorting
- Меньше зависимостей в `package.json`
### Риски
- FSD-правила могут не портироваться — потребуется костыль с минимальным ESLint
- Biome может не поддерживать какое-то редкое правило ESLint — потребуется адаптация
- Команде нужно привыкнуть к новому CLI
## Связанные документы
- `docs/research/frontend-infrastructure-tooling/biome-vs-oxlint.md`
- `docs/features/frontend-infrastructure-tooling/plan.md` (Phase 2)

View File

@ -0,0 +1,72 @@
# ADR-018: TanStack Router как основной роутер
**Дата:** 2026-06-23
**Статус:** Принято
**Автор:** AI Agent (codex/frontend-infrastructure-tooling)
## Контекст
Проект использует **react-router-dom v6** для клиентской маршрутизации. Текущая реализация:
- Все страницы импортируются статически в `AppRoutes.tsx`
- Нет lazy loading (code splitting) — каждая навигация грузит весь бандл
- Параметры роутов (`useParams`) и search params (`useSearchParams`) не типизированы
- Нет встроенной валидации search params
- Проект уже использует TanStack Query — потенциальная синергия с TanStack Router
Требуется:
- Route-level code splitting для оптимизации бандла
- Типобезопасность параметров и search params
- Интеграция с существующим TanStack Query (prefetching через loaders)
## Рассмотренные варианты
### react-router-dom v6 + React.lazy
- Минимальные изменения — обернуть каждый импорт в `React.lazy` + `<Suspense>`
- Не решает проблему типизации
- Нет prefetching / loaders
- React.lazy boilerplate на каждый роут
### TanStack Router
- Полная типобезопасность через генерацию RouteTree
- Search params с Zod-схемами
- Code splitting built-in — каждый роут ленивый по умолчанию
- Loaders для prefetching + интеграция с TanStack Query
- Pending/Error/NotFound boundaries на уровне роута
- Размер: ~3-4KB gzip (меньше react-router-dom)
- Требует переписывания всех роутов и навигации
## Решение
Мигрировать на **TanStack Router**.
Причины:
1. **Типобезопасность** — RouteTree generation исключает опечатки в путях и невалидные search params
2. **Code splitting без boilerplate** — built-in lazy, не нужен `React.lazy`
3. **Синергия с TanStack Query** — уже используется в проекте; loaders дают prefetching данных до рендера компонента
4. **Zod** — уже используется для валидации форм; Router использует Zod для search params
5. **Search params** — типизированная валидация вместо строковых `useSearchParams`
6. **Меньший размер** — 3-4KB vs 8KB react-router-dom
## Последствия
### Положительные
- Каждая страница — отдельный chunk, грузится по требованию
- Search params валидируются Zod-схемами (screener, broker-operations)
- Loaders предзагружают данные, уменьшая время до первого контента
- Guard'ы (ProtectedRoute) реализуются через `beforeLoad`, единый подход
### Риски
- Переписывание всех роутов, компонентов навигации (`Link`, `useNavigate`) и тестов
- `MemoryRouter` в тестах заменяется на `createMemoryRouter` из TanStack Router
- Файловая структура роутов меняется — `src/app/routes/` с Route Tree generation
- Learning curve для команды
### Миграция
- Каждый роут переносится по одному
- Старый `AppRoutes.tsx` сохраняется до полного прохождения тестов
- `react-router-dom` удаляется только после верификации
## Связанные документы
- `docs/research/frontend-infrastructure-tooling/react-router-vs-tanstack-router.md`
- `docs/features/frontend-infrastructure-tooling/plan.md` (Phase 6)

View File

@ -0,0 +1,64 @@
# ADR-019: Унификация API-слоя: ky + codegen как единый источник типов
**Дата:** 2026-06-23
**Статус:** Принято
**Автор:** AI Agent (codex/frontend-infrastructure-tooling)
## Контекст
В проекте сложилась ситуация расхождения между принятыми ADR и фактической реализацией:
- **ADR-005** решил использовать `openapi-typescript` + `openapi-fetch` для кодогенерации типов
- **ADR-015** решил использовать `ky` как HTTP-клиент
- Фактически: используется кастомный `client.ts` с нативным fetch, создан `kyClient.ts` (но не используется), рукописный `responses.ts` дублирует сгенерированный `types.ts`
Проблемы:
- Дублирование типов — рукописные расходятся с codegen
- Два HTTP-клиента (один мёртвый) — путаница
- fetch-реализация без удобных интерцепторов (retry, timeout, hooks)
## Решение
### 1. ky как единый HTTP-клиент
- Активировать `kyClient.ts` с доработанными hooks (normalizeEnvelope в afterResponse)
- Перевести все entity-API файлы с `request()` на `kyApi`
- Удалить `shared/api/client.ts`
- ADR-015 исполняется полностью
### 2. OpenAPI codegen как единый источник типов
- Удалить рукописный `shared/api/responses.ts`
- Все entity-API файлы используют типы из сгенерированного `shared/api/types.ts`
- Типы пишутся только через `npm run codegen`
- ADR-005 приводится к фактическому исполнению (без `openapi-fetch`, с кастомным клиентом)
### 3. MSW Browser Mode (расширение существующей инфраструктуры)
- MSW уже используется в тестах (`shared/lib/test/server.ts`)
- Добавить browser entry (`setupWorker`) для dev-режима
- Включается переменной `VITE_API_MOCK=true`
- Переиспользует существующие handlers
### 4. Валидация переменных окружения
- Zod-схема для `VITE_*` переменных
- Валидация при старте приложения
## Последствия
### Положительные
- Единый HTTP-клиент с интерцепторами (auth, retry, normalize)
- Нет дублирования типов — все через codegen
- Возможность разрабатывать UI без бэкенда (MSW browser)
- Раннее обнаружение ошибок конфигурации (env validation)
### Риски
- Миграция entity API требует регрессионного тестирования каждого модуля
- MSW browser handlers могут отличаться от реального API — нужно синхронизировать
- При изменении OpenAPI spec нужно запускать codegen вручную
## Связанные документы
- ADR-005: OpenAPI codegen через openapi-typescript
- ADR-015: Модернизация инфраструктуры фронтенда
- `docs/features/frontend-infrastructure-tooling/plan.md` (Phase 1, 3, 4, 5)

View File

@ -17,6 +17,9 @@
| [ADR-013](ADR-013-frontend-fsd-broker-pilot) | Accepted | Пилотная FSD-миграция broker-домена |
| [ADR-014](ADR-014-frontend-fsd-market-pages) | Accepted | FSD-миграция market pages и market widgets |
| [ADR-015](ADR-015-frontend-libraries-modernization) | — | Модернизация инфраструктуры фронтенда |
| [ADR-016](ADR-016-design-system) | Accepted | Дизайн-система — гибридный подход |
| [ADR-016](ADR-016-design-system) | Accepted | Дизайн-система — гибридный подход |
| [ADR-017](ADR-017-biome-linter-formatter) | Accepted | Biome как единый инструмент линтинга и форматирования |
| [ADR-018](ADR-018-tanstack-router) | Accepted | TanStack Router как основной роутер |
| [ADR-019](ADR-019-api-layer-unification) | Accepted | Унификация API-слоя: ky + codegen как единый источник типов |
Все опубликованные ADR находятся в `apps/docs/docs/adr/` и отображаются в этом Docusaurus-разделе.

View File

@ -0,0 +1,132 @@
# Frontend Infrastructure Tooling — Implementation Plan
## Architecture Decisions
### ADR references
- ADR-005: Biome вместо ESLint + Prettier
- ADR-006: TanStack Router вместо react-router-dom
- ADR-007: OpenAPI codegen как единый источник типов
### Non-ADR decisions
- ky как единый HTTP-клиент — выбор библиотеки, не меняющий архитектуры
- MSW browser mode — расширение существующей инфраструктуры тестов
- Zod для env validation — утилитарное улучшение
## Phases
Выполнять последовательно для минимизации конфликтов.
### Phase 1 — ky Migration
*Низкий риск, обратно совместим*
1. Доработать `kyClient.ts`:
- Добавить нормализацию конверта (normalizeEnvelope) в afterResponse hook
- Экспортировать `kyApi` и `configureKyAuth`
2. Поочерёдно перевести entity API на `kyApi`:
- session, stock, bond, search, portfolio, broker-*
3. Удалить `shared/api/client.ts`
4. Заменить `configureAuth``configureKyAuth` в точке входа
5. Прогнать тесты — поведение не должно измениться
### Phase 2 — Biome
*Средний риск — изменения в коде при авто-миграции*
1. Установить `@biomejs/biome`
2. `npx @biomejs/biome migrate eslint --write`
3. Создать `biome.json`, донастроить:
- Отключить несовместимые правила
- Настроить `files.ignore`, `linter.rules`
4. Проверить FSD-правила: @conarti/feature-sliced не портируются в Biome → оставить минимальный `.eslintrc.cjs` только для FSD
5. Удалить `eslint`, `prettier`, `.eslintrc.cjs` (если FSD не нужен)
6. Обновить `package.json`: `lint` скрипт → `biome check`
7. Обновить CI в `.gitea/workflows/ci.yml`
8. Обновить pre-commit hook (lint-staged → biome)
9. Прогнать `biome check --write`, закоммитить
10. Прогнать тесты
### Phase 3 — Unify API Types
*Низкий риск*
1. Убедиться, что все entity API импортируют из `types.ts` (codegen)
2. Удалить `shared/api/responses.ts`
3. Перенести normalizeEnvelope (ky-версию) в `shared/api/kyClient.ts`
4. Прогнать `npm run build`
### Phase 4 — MSW Browser
*Низкий риск, handlers готовы*
1. Создать `shared/lib/test/browser.ts` (setupWorker)
2. Прокинуть в `public/mockServiceWorker.js` через `npx msw init public/`
3. Создать `shared/config/env.ts` с чтением `VITE_API_MOCK`
4. Подключить MSW browser в `main.tsx` по условию
5. Проверить `npm run dev VITE_API_MOCK=true` без бэкенда
### Phase 5 — Env Validation
*Низкий риск*
1. Расширить `shared/config/env.ts` — Zod-схема для всех VITE_*
2. Вызвать валидацию в `main.tsx` до рендера
### Phase 6 — TanStack Router
*Крупный, высокий риск*
1. Установить зависимости:
- `@tanstack/react-router`
- `@tanstack/router-devtools` (devDependency)
- `@tanstack/router-plugin` (vite plugin)
2. Настроить Vite plugin в `vite.config.ts`
3. Создать файловую структуру роутов:
```
src/app/routes/
__root.tsx — AppLayout + ErrorBoundary
index.tsx — HomePage
stocks.$secid.tsx — StockPage
bonds.$secid.tsx — BondPage
screener.tsx — ScreenerPage + Zod search params
login.tsx — LoginPage
register.tsx — RegisterPage
profile.tsx — ProfilePage (guard: beforeLoad)
portfolios.tsx — PortfoliosListPage (guard)
portfolios.$id.tsx — PortfolioDetailPage (guard)
broker/
index.tsx — BrokerAccountsPage (guard)
$accountId/
index.tsx — BrokerAccountOverviewPage (guard)
shares.tsx — BrokerPositionsPage (guard)
bonds.tsx — BrokerPositionsPage (guard)
operations.tsx — BrokerOperationsPage + Zod search params (guard)
events.tsx — BrokerEventsPage (guard)
```
4. Перенести каждый роут из `AppRoutes.tsx` — каждый файл создаёт lazy route
5. Создать роутер в `app/routing/router.ts`:
- `createRouter()` с Route Tree
- `beforeLoad` для guard'ов
- Loaders для предзагрузки (TanStack Query integration)
6. Заменить `<BrowserRouter>` + `<Routes>``<RouterProvider>` в `App.tsx`
7. Заменить все импорты `react-router-dom` по всему проекту:
- `Link``Link` из `@tanstack/react-router`
- `useNavigate``useNavigate`
- `useParams``useParams`
- `useSearchParams``useSearch` + `useNavigate`
- `useLocation``useLocation`
8. Заменить `MemoryRouter` в тестах на `createMemoryRouter` из TanStack Router
9. Настроить router-devtools в dev-режиме
10. Прогнать тесты
## Dependencies
```
Phase 1 (ky) → независим
Phase 2 (Biome) → независим
Phase 3 (Types) → после Phase 1 (ky меняет нормализацию)
Phase 4 (MSW) → после Phase 5 (env нужен для VITE_API_MOCK)
Phase 5 (Env) → независим
Phase 6 (Router) → после Phase 3 (типы API стабильны)
```
## Rollback Strategy
- Каждый phase — отдельный коммит, можно revert по одному
- Biome: `.eslintrc.cjs` сохраняется как `.eslintrc.cjs.bak` до верификации
- Router: старый `AppRoutes.tsx` и `react-router-dom` не удаляются до полного прохождения тестов
- Каждый phase должен оставлять `npm run build` и `npm run test` зелёными

View File

@ -0,0 +1,105 @@
# Frontend Infrastructure Tooling — Tasks
## Phase 1: ky Migration
- [ ] Доработать `shared/api/kyClient.ts`: добавить normalizeEnvelope в afterResponse hook
- [ ] Экспортировать `kyApi` (create экземпляр) и `configureKyAuth` из kyClient
- [ ] Перевести `entities/session/api/sessionApi.ts` на kyApi
- [ ] Перевести `entities/stock/api/stockApi.ts` на kyApi
- [ ] Перевести `entities/bond/api/bondApi.ts` на kyApi
- [ ] Перевести `entities/search/api/searchApi.ts` на kyApi
- [ ] Перевести `entities/portfolio/api/portfolioApi.ts` на kyApi
- [ ] Перевести `entities/broker-account/api/brokerAccountApi.ts` на kyApi
- [ ] Перевести `entities/broker-position/api/brokerPositionApi.ts` на kyApi
- [ ] Перевести `entities/broker-operation/api/brokerOperationApi.ts` на kyApi
- [ ] Перевести `entities/broker-event/api/brokerEventApi.ts` на kyApi
- [ ] Перевести `features/screener/api/screenerApi.ts` на kyApi
- [ ] Удалить `shared/api/client.ts`
- [ ] Заменить `configureAuth()` на `configureKyAuth()` в точке входа (AppProviders)
- [ ] `npm run test` — все тесты проходят
- [ ] `npm run build` — сборка проходит
## Phase 2: Biome Migration
- [ ] Research: проверить Biome plugin system на поддержку FSD/import-no-restricted-paths
- [ ] Установить `@biomejs/biome` (devDependency)
- [ ] Запустить `npx @biomejs/biome migrate eslint --write`
- [ ] Создать `biome.json` с донастройкой под проект
- [ ] Если FSD-правила не портируются — создать минимальный `.eslintrc.cjs` только для FSD
- [ ] Удалить зависимости: eslint, prettier, @typescript-eslint/*, eslint-plugin-*
- [ ] Удалить `.eslintrc.cjs` (если FSD не нужен)
- [ ] Обновить `package.json`: `lint``biome check src/`
- [ ] Обновить `.gitea/workflows/ci.yml`: заменить eslint на biome
- [ ] Обновить pre-commit hook (lint-staged → biome)
- [ ] Прогнать `biome check --write src/`
- [ ] `npm run test` — все тесты проходят
## Phase 3: Unify API Types
- [ ] Проверить все импорты в entity API — должны быть из `types.ts` (codegen), не из `responses.ts`
- [ ] Если кто-то импортирует из `responses.ts` — переключить на `types.ts`
- [ ] Удалить `shared/api/responses.ts`
- [ ] Перенести normalizeEnvelope (ky-версия) в `shared/api/kyClient.ts`
- [ ] `npm run build` — сборка проходит
## Phase 4: MSW Browser
- [ ] Создать `shared/lib/test/browser.ts` (setupWorker из msw/browser)
- [ ] Установить и прокинуть mockServiceWorker.js: `npx msw init public/`
- [ ] Создать `shared/config/env.ts` с чтением и экспортом VITE_API_MOCK
- [ ] В `main.tsx`: при `VITE_API_MOCK === 'true'` запускать `worker.start()`
- [ ] Проверить: `VITE_API_MOCK=true npm run dev` без бэкенда — приложение работает
- [ ] Проверить: `VITE_API_MOCK=false npm run dev` — запросы идут на бэкенд
## Phase 5: Env Validation
- [ ] Разработать Zod-схему в `shared/config/env.ts` для всех VITE_* переменных
- [ ] Вызвать `validateEnv()` в `main.tsx` до `ReactDOM.createRoot`
- [ ] Проверить: при отсутствии обязательной переменной — понятная ошибка
## Phase 6: TanStack Router
### Setup
- [ ] Установить `@tanstack/react-router`, `@tanstack/router-devtools`, `@tanstack/router-plugin`
- [ ] Настроить Vite plugin для генерации RouteTree в `vite.config.ts`
### Route files
- [ ] Создать `src/app/routes/__root.tsx` — AppLayout + ErrorBoundary
- [ ] Создать `src/app/routes/index.tsx` — HomePage
- [ ] Создать `src/app/routes/stocks.$secid.tsx` — StockPage
- [ ] Создать `src/app/routes/bonds.$secid.tsx` — BondPage
- [ ] Создать `src/app/routes/screener.tsx` — ScreenerPage + Zod search params
- [ ] Создать `src/app/routes/login.tsx` — LoginPage
- [ ] Создать `src/app/routes/register.tsx` — RegisterPage
- [ ] Создать `src/app/routes/profile.tsx` — ProfilePage (guard: beforeLoad)
- [ ] Создать `src/app/routes/portfolios.tsx` — PortfoliosListPage (guard)
- [ ] Создать `src/app/routes/portfolios.$id.tsx` — PortfolioDetailPage (guard)
- [ ] Создать `src/app/routes/broker/index.tsx` — BrokerAccountsPage (guard)
- [ ] Создать `src/app/routes/broker.$accountId/index.tsx` — BrokerAccountOverviewPage (guard)
- [ ] Создать `src/app/routes/broker.$accountId/shares.tsx` — BrokerPositionsPage (guard)
- [ ] Создать `src/app/routes/broker.$accountId/bonds.tsx` — BrokerPositionsPage (guard)
- [ ] Создать `src/app/routes/broker.$accountId/operations.tsx` — BrokerOperationsPage (guard)
- [ ] Создать `src/app/routes/broker.$accountId/events.tsx` — BrokerEventsPage (guard)
### Router integration
- [ ] Создать `app/routing/router.ts`: `createRouter()` с Route Tree
- [ ] Настроить `beforeLoad` для guard'ов
- [ ] Настроить loaders для предзагрузки (TanStack Query)
- [ ] Заменить `<BrowserRouter>` + `<Routes>``<RouterProvider>` в `App.tsx`
### Replace imports
- [ ] Заменить `Link``@tanstack/react-router` Link по всему проекту
- [ ] Заменить `useNavigate``@tanstack/react-router`
- [ ] Заменить `useParams``@tanstack/react-router`
- [ ] Заменить `useSearchParams``useSearch` + `useNavigate`
- [ ] Заменить `useLocation``@tanstack/react-router`
### Tests
- [ ] Заменить `MemoryRouter` в тестах на `createMemoryRouter` из TanStack Router
- [ ] Обновить тестовые утилиты (`test-utils.tsx`)
- [ ] `npm run test` — все тесты проходят
- [ ] `npm run build` — сборка проходит
### Devtools
- [ ] Настроить `@tanstack/router-devtools` в dev-режиме
- [ ] Проверить навигацию по всем страницам вручную

View File

@ -0,0 +1,52 @@
# Biome vs Oxlint: Comparison for Frontend Tooling
## Context
Проект использует **ESLint v8** + **Prettier** для линтинга и форматирования. ESLint 8 устарел (ESLint 9 имеет полностью изменённый конфиг). Рассматриваем замену на современную Rust-тулзу, совмещающую линтинг и форматирование.
## Candidates
### Biome (v2.5.0)
- **Linter + Formatter** в одном CLI (`biome check --write`)
- 500+ правил (ESLint + TypeScript ESLint + другие источники)
- Форматтер: **97% совместимость с Prettier**, ~35x быстрее
- Поддержка: JS, TS, JSX, TSX, JSON, HTML, CSS, GraphQL
- Единый конфиг `biome.json` вместо `.eslintrc` + `.prettierrc`
- Встроенный import sorting (замена `eslint-plugin-import`)
- Production usage: AWS, Google, Vercel, Microsoft, Discord, Cloudflare
- Стабильный LTS-релиз, зрелое сообщество
### Oxlint + Oxfmt (Oxc Project)
- **Oxlint**: 50-100x быстрее ESLint, 800+ правил
- **Oxfmt**: ~3x быстрее Biome formatter, 30x быстрее Prettier
- Type-aware linting через `tsgo`
- ESLint JS Plugin Support (alpha)
- **Два отдельных инструмента** с разными конфигами
- Oxfmt в статусе **beta**
- Под капотом: самый быстрый parser (3x SWC), resolver, minifier (alpha)
- Бэкд: VoidZero (авторы Vite, Rolldown, Rspack)
## Comparison
| Критерий | Biome | Oxlint + Oxfmt |
|----------|-------|----------------|
| Замена ESLint | ✅ 500+ правил | ✅ 800+ правил |
| Замена Prettier | ✅ 97%, stable | ⚠️ Oxfmt beta |
| Один CLI вместо двух | ✅ | ❌ два инструмента |
| Production-ready | ✅ LTS | ⚠️ formatter beta |
| Конфигурация | 1 файл | 2 файла |
| Скорость линтинга | ~35x vs ESLint | ~50-100x vs ESLint |
| Скорость форматирования | ~35x vs Prettier | ~3x vs Biome |
| Сообщество | mature | growing |
## Verdict: Biome
**Рекомендуется Biome** по трём причинам:
1. **Одна тулза** вместо двух — меньше конфигов, меньше CI-степов, меньше точек отказа
2. **Production-ready** — formatter стабилен, LTS-релизы, тысячи проектов в продакшене
3. **Плавная миграция** — встроенная команда `biome migrate eslint --write` переносит правила автоматически; `biome format` совместим с Prettier на 97%, не требуется массовых изменений кода
Oxlint + Oxfmt перспективны (быстрее, больше правил), но Oxfmt ещё beta, и два инструмента усложняют конфигурацию. Если Oxfmt выйдет в stable — вопрос стоит пересмотреть.

View File

@ -0,0 +1,57 @@
# react-router-dom vs TanStack Router
## Context
Проект использует **react-router-dom v6** для клиентской маршрутизации. Требуется route-level code splitting (lazy loading). Также стоит вопрос о типобезопасности роутов и интеграции с уже используемым TanStack Query.
## Candidates
### react-router-dom v6 (текущий)
- Стабильный стандарт де-факто
- Декларативный API: `<Routes>`, `<Route>`, `<Link>`
- lazy loading через `React.lazy()` + `<Suspense>` — добавляется вручную
- Search params: `useSearchParams()` — без типизации, строками
- Параметры: `useParams()` — без типизации
- Нет встроенных loaders / prefetching
- Нет генерации типов
- Размер: ~8KB gzip
- Нет нативной интеграции с TanStack Query
### TanStack Router
- **Генерация RouteTree**: полная типобезопасность путей, параметров, search params
- **Search params с Zod**: декларативная валидация и парсинг (Zod уже используется в проекте)
- **Route loaders**: prefetching данных ДО рендера компонента, кеширование
- **Code splitting built-in**: каждый роут ленивый по умолчанию, без `React.lazy` boilerplate
- **Pending/Error/NotFound boundaries** на уровне роута
- **Интеграция с TanStack Query**: loaders могут вызывать `queryClient.fetchQuery()` напрямую
- **File-based routing**: чище организация кода (опционально)
- Файл роута = route + component + loader + error/loading states
- Размер: ~3-4KB gzip
- 1.2B+ total downloads, 20M+ weekly
## Comparison
| Критерий | react-router-dom v6 | TanStack Router |
|----------|---------------------|-----------------|
| Типизация путей | ❌ строки | ✅ генерация |
| Типизация params | ❌ `useParams()` без типа | ✅ autocomplete |
| Search params typing | ❌ `useSearchParams()` строки | ✅ Zod-схемы |
| Lazy loading | ⚠️ React.lazy + Suspense | ✅ built-in |
| Loaders / prefetch | ❌ нет | ✅ |
| TanStack Query synergy | ❌ | ✅ native |
| Bundle size | ~8KB gzip | ~3-4KB gzip |
| Migration effort | — | средняя |
| Learning curve | низкая | средняя |
## Verdict: TanStack Router
**Рекомендуется TanStack Router**:
1. **Типобезопасность** — генерация типов исключает класс багов (опечатки в путях, невалидные search params)
2. **Code splitting без boilerplate** — каждый роут грузится лениво автоматически, не нужно `React.lazy`
3. **Синергия с TanStack Query** — уже используется в проекте; loaders дают prefetching до рендера
4. **Zod** — уже используется в проекте для валидации форм; Router использует Zod для search params
Минусы: требуется переписывание всех роутов, компонентов навигации (`Link`, `useNavigate`) и тестов (`MemoryRouter``createMemoryRouter`). Каждый переезд — отдельная задача с тестированием.