66 lines
3.2 KiB
Markdown
66 lines
3.2 KiB
Markdown
---
|
||
paths:
|
||
- frontend/**/*.{ts,tsx,jsx,js}
|
||
- tradein-mvp/frontend/**/*.{ts,tsx,jsx,js}
|
||
---
|
||
|
||
# Frontend conventions — Next.js 15 / React 19 / TypeScript 5
|
||
|
||
## Critical
|
||
|
||
- **App router only** — НЕ `pages/` router
|
||
- **TanStack Query** для data-fetching — никакого `useEffect` для HTTP запросов
|
||
- **TS strict** — никаких `any`; для unknown shape → `unknown` + narrow
|
||
- TanStack Query keys — массивы: `["scrape-logs", scraperType, runIdFilter]`
|
||
- Tailwind 4 утилитарные классы; inline styles только для динамических значений
|
||
- Server / Client components разделены явно — `"use client"` в начале client'а
|
||
|
||
## XSS prevention (recurring bug class)
|
||
|
||
При рендере markdown / user-supplied content:
|
||
|
||
- **URL validation**: allowlist schemes перед инъекцией в `href=` / `src=`:
|
||
- ✅ `https://`, `http://`, `/`, `#`, `mailto:`
|
||
- ❌ `javascript:`, `data:`, `vbscript:`, `file:` → fallback `#`
|
||
- Pattern: helper `safeUrl(url: string): string` перед записью в attr
|
||
|
||
Reference: vault `Bug_RenderMarkdown_JavascriptUrl_May14` (`renderMarkdown.ts:safeUrl`).
|
||
|
||
## After backend schema change
|
||
|
||
```
|
||
cd frontend && npm run codegen
|
||
```
|
||
|
||
Обновляет `src/lib/api-types.ts` из live OpenAPI (`npm run codegen` = `openapi-typescript … -o src/lib/api-types.ts`). Без этого frontend build упадёт на missing types.
|
||
|
||
## package.json + lockfile sync (CRITICAL — deploy aborts on mismatch)
|
||
|
||
Если меняешь `frontend/package.json` (add/remove/bump dep) — **обязан** также:
|
||
|
||
```bash
|
||
cd frontend && npm install --legacy-peer-deps --no-audit --no-fund
|
||
```
|
||
|
||
И закоммитить обновлённый `frontend/package-lock.json` в той же ветке.
|
||
|
||
**Why:** `frontend/Dockerfile` использует `npm ci --legacy-peer-deps` — этот режим **требует** точного match между `package.json` и `package-lock.json`. Mismatch → `npm error Missing: <pkg>@<version> from lock file` → builder fails → `docker compose --abort-on-container-exit` → **весь deploy aborts** (frontend + backend rollback).
|
||
|
||
- Pre-push check: `git diff main..HEAD -- frontend/package.json frontend/package-lock.json` — если только один из двух тронут → STOP, regen lock.
|
||
- Imports без deps entry (TypeScript авто-resolve через transitive) — **latent bomb** до first `npm ci`.
|
||
- Reference incident: PR #344 (2026-05-17) добавил `lucide-react` без regen lockfile → deploy #135 fail → P0 hotfix PR #345 (commit `6ee20294f2`).
|
||
|
||
## Prettier / lint
|
||
|
||
- 2 spaces, trailing comma, double quotes (config)
|
||
- `prettier` hook auto-rewrites файл → нужен повторный `git add`
|
||
- ESLint в pre-commit: no unused imports, no console.log, exhaustive deps
|
||
|
||
## Запреты
|
||
|
||
- ❌ `any` в TypeScript — `unknown` + type guard
|
||
- ❌ `useEffect` для HTTP — TanStack Query
|
||
- ❌ `pages/` router — только `app/`
|
||
- ❌ `console.log` в prod-коде
|
||
- ❌ Inline в `href`/`src` без validation (XSS vector)
|
||
- ❌ Hardcoded URLs — `process.env.NEXT_PUBLIC_API_URL`
|