codex/frontend-infrastructure-tooling #39
67
apps/docs/docs/adr/ADR-017-biome-linter-formatter.md
Normal file
67
apps/docs/docs/adr/ADR-017-biome-linter-formatter.md
Normal 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)
|
||||||
72
apps/docs/docs/adr/ADR-018-tanstack-router.md
Normal file
72
apps/docs/docs/adr/ADR-018-tanstack-router.md
Normal 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)
|
||||||
64
apps/docs/docs/adr/ADR-019-api-layer-unification.md
Normal file
64
apps/docs/docs/adr/ADR-019-api-layer-unification.md
Normal 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)
|
||||||
@ -18,5 +18,8 @@
|
|||||||
| [ADR-014](ADR-014-frontend-fsd-market-pages) | Accepted | FSD-миграция market pages и market widgets |
|
| [ADR-014](ADR-014-frontend-fsd-market-pages) | Accepted | FSD-миграция market pages и market widgets |
|
||||||
| [ADR-015](ADR-015-frontend-libraries-modernization) | — | Модернизация инфраструктуры фронтенда |
|
| [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-разделе.
|
Все опубликованные ADR находятся в `apps/docs/docs/adr/` и отображаются в этом Docusaurus-разделе.
|
||||||
|
|||||||
132
docs/features/frontend-infrastructure-tooling/plan.md
Normal file
132
docs/features/frontend-infrastructure-tooling/plan.md
Normal 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` зелёными
|
||||||
105
docs/features/frontend-infrastructure-tooling/tasks.md
Normal file
105
docs/features/frontend-infrastructure-tooling/tasks.md
Normal 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-режиме
|
||||||
|
- [ ] Проверить навигацию по всем страницам вручную
|
||||||
@ -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 — вопрос стоит пересмотреть.
|
||||||
@ -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`). Каждый переезд — отдельная задача с тестированием.
|
||||||
Loading…
x
Reference in New Issue
Block a user