From 6d3601a8f49d795249c9de577a08c9e1efa77006 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Tue, 23 Jun 2026 06:07:23 +0300 Subject: [PATCH] docs: add plan, tasks, research, and ADRs for frontend infrastructure tooling --- .../adr/ADR-017-biome-linter-formatter.md | 67 +++++++++ apps/docs/docs/adr/ADR-018-tanstack-router.md | 72 ++++++++++ .../docs/adr/ADR-019-api-layer-unification.md | 64 +++++++++ apps/docs/docs/adr/index.md | 5 +- .../frontend-infrastructure-tooling/plan.md | 132 ++++++++++++++++++ .../frontend-infrastructure-tooling/tasks.md | 105 ++++++++++++++ .../biome-vs-oxlint.md | 52 +++++++ .../react-router-vs-tanstack-router.md | 57 ++++++++ 8 files changed, 553 insertions(+), 1 deletion(-) create mode 100644 apps/docs/docs/adr/ADR-017-biome-linter-formatter.md create mode 100644 apps/docs/docs/adr/ADR-018-tanstack-router.md create mode 100644 apps/docs/docs/adr/ADR-019-api-layer-unification.md create mode 100644 docs/features/frontend-infrastructure-tooling/plan.md create mode 100644 docs/features/frontend-infrastructure-tooling/tasks.md create mode 100644 docs/research/frontend-infrastructure-tooling/biome-vs-oxlint.md create mode 100644 docs/research/frontend-infrastructure-tooling/react-router-vs-tanstack-router.md diff --git a/apps/docs/docs/adr/ADR-017-biome-linter-formatter.md b/apps/docs/docs/adr/ADR-017-biome-linter-formatter.md new file mode 100644 index 0000000..e1c5be8 --- /dev/null +++ b/apps/docs/docs/adr/ADR-017-biome-linter-formatter.md @@ -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) diff --git a/apps/docs/docs/adr/ADR-018-tanstack-router.md b/apps/docs/docs/adr/ADR-018-tanstack-router.md new file mode 100644 index 0000000..b1855d9 --- /dev/null +++ b/apps/docs/docs/adr/ADR-018-tanstack-router.md @@ -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` + `` +- Не решает проблему типизации +- Нет 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) diff --git a/apps/docs/docs/adr/ADR-019-api-layer-unification.md b/apps/docs/docs/adr/ADR-019-api-layer-unification.md new file mode 100644 index 0000000..075b898 --- /dev/null +++ b/apps/docs/docs/adr/ADR-019-api-layer-unification.md @@ -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) diff --git a/apps/docs/docs/adr/index.md b/apps/docs/docs/adr/index.md index ac259c5..14fdbc6 100644 --- a/apps/docs/docs/adr/index.md +++ b/apps/docs/docs/adr/index.md @@ -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-разделе. diff --git a/docs/features/frontend-infrastructure-tooling/plan.md b/docs/features/frontend-infrastructure-tooling/plan.md new file mode 100644 index 0000000..52b535b --- /dev/null +++ b/docs/features/frontend-infrastructure-tooling/plan.md @@ -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. Заменить `` + `` → `` в `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` зелёными diff --git a/docs/features/frontend-infrastructure-tooling/tasks.md b/docs/features/frontend-infrastructure-tooling/tasks.md new file mode 100644 index 0000000..6b07edb --- /dev/null +++ b/docs/features/frontend-infrastructure-tooling/tasks.md @@ -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) +- [ ] Заменить `` + `` → `` в `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-режиме +- [ ] Проверить навигацию по всем страницам вручную diff --git a/docs/research/frontend-infrastructure-tooling/biome-vs-oxlint.md b/docs/research/frontend-infrastructure-tooling/biome-vs-oxlint.md new file mode 100644 index 0000000..b851f83 --- /dev/null +++ b/docs/research/frontend-infrastructure-tooling/biome-vs-oxlint.md @@ -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 — вопрос стоит пересмотреть. diff --git a/docs/research/frontend-infrastructure-tooling/react-router-vs-tanstack-router.md b/docs/research/frontend-infrastructure-tooling/react-router-vs-tanstack-router.md new file mode 100644 index 0000000..7cf28d5 --- /dev/null +++ b/docs/research/frontend-infrastructure-tooling/react-router-vs-tanstack-router.md @@ -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: ``, ``, `` +- lazy loading через `React.lazy()` + `` — добавляется вручную +- 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`). Каждый переезд — отдельная задача с тестированием.