# Технологии и библиотеки Базовый стек технологий и библиотек, на который опираются проекты и примеры в документации. ## Что используем обычно - **TypeScript** — типизация всей логики и компонентов. - **FSD (Feature-Sliced Design)** — структура проекта и границы модулей. - **React + Next.js** — основной стек для UI и приложения. - **Mantine UI** — базовые UI-компоненты. - **SWR** — получение данных по GET (кеширование и revalidate). - **Zustand** — глобальное состояние. - **i18next (i18n)** — локализация всех пользовательских текстов. - **Vitest** — тестирование. - **clsx** — конкатенация CSS‑классов. - **PostCSS Modules** — изоляция стилей. - **Mobile First** — подход к адаптивной верстке. # Архитектура Архитектура построена на FSD (Feature‑Sliced Design) и строгих границах модулей. Цель — разделить ответственность, упростить сопровождение и контроль зависимостей. ## Принципы - Разделять UI, бизнес-логику и инфраструктуру. - Держать зависимости однонаправленными. - Открывать наружу только публичный API модулей. - Не допускать циклических зависимостей. ## Слои - **app** — инициализация приложения, роутинг, конфигурации, глобальные провайдеры. - **screens** — экраны и их композиция. - **layouts** — каркас и шаблоны страниц. - **widgets** — крупные блоки интерфейса, собирающие несколько сценариев. - **features** — отдельные пользовательские действия и сценарии. - **entities** — бизнес-сущности и их модель. - **shared** — переиспользуемая инфраструктура, утилиты и базовые UI‑компоненты. ## Правила зависимостей - Допустимые импорты идут сверху вниз: `app → screens → layouts → widgets → features → entities → shared`. - Импорты между слоями — через публичный API. - Внутри одного слоя — относительные импорты. ## Публичный API модулей - Каждый модуль экспортирует наружу только то, что нужно другим слоям. - Внешние импорты идут только через `index`‑файл модуля. - Внутренние файлы не импортируются напрямую извне. ## Границы ответственности - Бизнес‑логика не размещается в UI‑компонентах. - UI‑компоненты должны быть максимально простыми и предсказуемыми. - Связь между независимыми сценариями поднимается на уровень выше. ## Типовые ошибки - Импорт из более высокого слоя в более низкий. - Смешивание логики нескольких слоёв в одном модуле. - Прямые импорты внутренних файлов, минуя публичный API. # Стиль кода Раздел описывает единые правила оформления кода: отступы, переносы, кавычки, порядок импортов и базовую читаемость. ## Отступы - 2 пробела (не табы). ## Длина строк - Ориентироваться на 100 символов, но превышение допустимо, если строка читается легко. - Переносить выражение на новые строки, когда строка становится плохо читаемой. - Не переносить строку внутри строковых литералов без необходимости. **Хорошо** ```ts const config = createRequestConfig( endpoint, { headers: { 'X-Request-Id': requestId, 'X-User-Id': userId, }, params: { page, pageSize, sort: 'createdAt', }, }, timeoutMs, ); ``` **Плохо** ```ts // Плохо: длинная строка с вложенными структурами плохо читается. const config = createRequestConfig(endpoint, { headers: { 'X-Request-Id': requestId, 'X-User-Id': userId }, params: { page, pageSize, sort: 'createdAt' } }, timeoutMs); ``` ## Кавычки - В JavaScript/TypeScript использовать одинарные кавычки. - В JSX/TSX для атрибутов использовать двойные кавычки. - Шаблонные строки использовать только при интерполяции или многострочном тексте. **Хорошо** ```ts const label = 'Сохранить'; const title = `Привет, ${name}`; ``` ```tsx ``` **Плохо** ```ts // Плохо: двойные кавычки в TS и конкатенация вместо шаблонной строки. const label = "Сохранить"; const title = 'Привет, ' + name; ``` ```tsx // Плохо: одинарные кавычки в JSX-атрибутах. ``` ## Точки с запятой и запятые - Допускаются упущения точки с запятой, если код остаётся читаемым и однозначным. - В многострочных массивах, объектах и параметрах функции запятая в конце допускается, но не обязательна. ## Импорты - В именованных импортах использовать пробелы внутри фигурных скобок. - Типы импортировать через `import type`. - `default` импорт и экспорт избегать, использовать именованные. - Избегать импорта всего модуля через `*`. **Хорошо** ```ts import { MyComponent } from 'MyComponent'; import type { User } from '../model/types'; ``` **Плохо** ```ts // Плохо: default импорт и отсутствие пробелов в именованном импорте. import MyComponent from 'MyComponent'; import type {User} from '../model/types'; ``` ## Ранние возвраты (early return) - Использовать ранние возвраты для упрощения чтения. - Избегать `else` после `return`. **Хорошо** ```ts const getName = (user?: { name: string }) => { if (!user) { return 'Гость'; } return user.name; }; ``` **Плохо** ```ts // Плохо: лишний else после return усложняет чтение. const getName = (user?: { name: string }) => { if (user) { return user.name; } else { return 'Гость'; } }; ``` ## Форматирование объектов и массивов - В многострочных объектах каждое свойство на новой строке. - В многострочных массивах каждый элемент на новой строке. - Объекты и массивы можно писать в одну строку, если длина строки не превышает 100 символов. - В однострочных объектах и массивах использовать пробелы после запятых. **Хорошо** ```ts const roles = ['admin', 'editor', 'viewer']; const options = { id: 1, name: 'User' }; const config = { url: '/api/users', method: 'GET', params: { page: 1, pageSize: 20 }, }; ``` **Плохо** ```ts // Плохо: нет пробелов после запятых и объект слишком длинный для одной строки. const roles = ['admin','editor','viewer']; const options = { id: 1,name: 'User' }; const config = { url: '/api/users', method: 'GET', params: { page: 1, pageSize: 20 } }; ``` # Именование Именование должно быть предсказуемым, коротким и отражать смысл сущности. ## Базовые правила | Что | Рекомендуется | | ---------------- | ---------------------- | | Папки | `kebab-case` | | Файлы | `kebab-case` | | Переменные | `camelCase` | | Константы | `SCREAMING_SNAKE_CASE` | | Классы | `PascalCase` | | React-компоненты | `PascalCase` | | Хуки | `useSomething` | | CSS классы | `camelCase` | ## Архитектурный неймспейс Соглашение о суффиксах, которые обозначают слой (уровень абстракции), роль или тип файла. - Суффиксы используются для обозначения слоя, роли или типа файла. - Суффиксы всегда пишутся в единственном числе. - Формат имени: `name..ts` или `name..tsx`. **UI и слои FSD** - `.screen.tsx` — экран - `.layout.tsx` — layout - `.widget.tsx` — виджет - `.feature.tsx` — UI фичи - `.entity.tsx` — UI сущности - `.ui.tsx` — UI‑компонент **Логика и модель** - `.store.ts` — стор - `.service.ts` — сервис **Типы и контракты** - `.type.ts` — типы и интерфейсы - `.interface.ts` — файл с интерфейсами (если нужен отдельный контракт) - `.enum.ts` — enum - `.dto.ts` — внешние DTO - `.schema.ts` — схемы валидации - `.constant.ts` — константы - `.config.ts` — конфигурация **Утилиты и хелперы** - `.util.ts` — утилиты - `.helper.ts` — вспомогательные функции - `.lib.ts` — вспомогательные функции **Тесты** - `.test.ts` / `.test.tsx` - `.mock.ts` **Хорошо** ```text src/ ├── screens/ │ └── main/ │ ├── main.screen.tsx │ └── index.ts ├── features/ │ └── auth-by-email/ │ ├── ui/ │ │ └── login-form.ui.tsx │ ├── auth-by-email.feature.tsx │ └── index.ts └── shared/ └── ui/ └── icon/ ├── icon.ui.tsx └── icon.module.css ``` **Плохо** ```text // Плохо: нет единых правил для слоёв и публичных файлов. src/ ├── screens/ │ └── Main/ │ └── Main.tsx └── features/ └── authByEmail/ └── login-form.tsx ``` ## Булевы значения - Использовать префиксы `is`, `has`, `can`, `should`. **Хорошо** ```ts const isReady = true; const hasAccess = false; const canSubmit = true; const shouldRedirect = false; ``` **Плохо** ```ts // Плохо: неясное булево значение без префикса. const ready = true; const access = false; const submit = true; ``` ## События и обработчики - Обработчики начинать с `handle`. - События и колбэки начинать с `on`. **Хорошо** ```ts const handleSubmit = () => { ... }; const onSubmit = () => { ... }; ``` **Плохо** ```ts // Плохо: неочевидное назначение имени. const submitClick = () => { ... }; ``` ## Коллекции - Для массивов использовать имена во множественном числе. - Для словарей/мап — использовать суффиксы `ById`, `Map`, `Dict`. **Хорошо** ```ts const users = []; const usersById = {} as Record; const userIds = ['u1', 'u2']; const ordersMap = new Map(); const featureFlagsDict = { beta: true, legacy: false } as Record; ``` **Плохо** ```ts // Плохо: имя не отражает, что это коллекция. const user = []; // Плохо: словарь назван как массив. const usersMap = []; // Плохо: по имени непонятно, что это словарь. const users = {} as Record; ``` # Документирование Документирование должно помогать понять назначение сущности, а не дублировать её типы или очевидные детали. ## Правила - Документировать только назначение функций, компонентов, типов, интерфейсов и enum. - Не документировать параметры, возвращаемые значения, типы пропсов и очевидные детали. - В интерфейсах, типах и enum описывать только смысл поля или значения. - Описание должно быть кратким, информативным и завершаться точкой. ## Примеры **Хорошо** ```ts /** * Список задач пользователя. */ export const TodoList = memo(() => { ... }); /** * Интерфейс задачи. */ export interface TodoItem { /** Уникальный идентификатор задачи. */ id: string; /** Текст задачи. */ text: string; /** Статус выполнения задачи. */ completed: boolean; } /** * Перечисление фильтров задач. */ export enum TodoFilter { /** Все задачи. */ All = 'all', /** Только активные задачи. */ Active = 'active', /** Только выполненные задачи. */ Completed = 'completed', } ``` **Плохо** ```ts // Плохо: дублирование параметров и возвращаемых значений. /** * @param id - идентификатор задачи * @returns объект задачи */ // Плохо: описание очевидных деталей. /** * id — идентификатор задачи * text — текст задачи * completed — статус выполнения */ ``` # Типизация Типизация обязательна для всех публичных интерфейсов, функций и компонентов. Цель — предсказуемость, безопасность и автодополнение. ## Общие правила - Указывать типы для параметров компонентов, возвращаемых значений и параметров функций. - Предпочитать `type` для описания сущностей и `interface` для расширяемых контрактов. - Избегать `any` и `unknown` без необходимости. - Не использовать `ts-ignore`, кроме крайних случаев с явным комментарием причины. ## Типы для компонентов - Типизировать параметры и публичный интерфейс компонента. - Дефолтные значения описывать явно в коде. **Хорошо** ```tsx /** * Параметры кнопки. */ interface IOwnProps extends HTMLAttributes { /** Текст кнопки. */ label: string; /** Обработчик клика по кнопке. */ onClick: () => void; } /** * Кнопка с пользовательскими стилями. */ export const Button:FC = ({ className, label, onClick, ...htmlAttr }) => { return (
button
) } ``` **Плохо** ```tsx // Плохо: параметры не типизированы. export const Button = (props) => ( ); ``` ## Функции - Для публичных функций указывать возвращаемый тип. - Не полагаться на неявный вывод для важных API. **Хорошо** ```ts export const formatPrice = (value: number): string => { return `${value} ₽`; }; ``` **Плохо** ```ts // Плохо: нет явного возвращаемого типа. export const formatPrice = (value: number) => { return `${value} ₽`; }; ``` ## Работа с any/unknown - `any` использовать только для временных заглушек. - `unknown` сужать через проверки перед использованием. **Хорошо** ```ts const parse = (value: unknown): string => { if (typeof value === 'string') { return value; } return ''; }; ``` **Плохо** ```ts // Плохо: any отключает проверку типов. const parse = (value: any) => value; ``` # Структура проекта Раздел описывает базовую структуру проекта и принципы организации модулей на уровне папок и файлов. ## Базовая структура проекта **Хорошо** ```text src/ ├── app/ # Инициализация приложения, роутинг, провайдеры │ ├── config/ # Конфигурации и константы уровня приложения │ ├── providers/ # Провайдеры и обёртки приложения │ ├── routing/ # Конфигурация маршрутов │ └── index.ts # Публичный API слоя ├── screens/ # Экраны приложения │ └── Profile/ # Экран профиля │ └── ... # ui/model/index.ts ├── layouts/ # Общие шаблоны и каркасы страниц │ └── MainLayout/ # Основной layout │ └── ... # ui/index.ts ├── widgets/ # Крупные блоки интерфейса │ └── Header/ # Виджет шапки │ └── ... # ui/index.ts ├── features/ # Пользовательские сценарии │ └── auth-by-email/ # Авторизация по email │ └── ... # ui/model/api/index.ts ├── entities/ # Бизнес-сущности │ └── user/ # Сущность пользователя │ └── ... # ui/model/api/lib/index.ts └── shared/ # Общие ресурсы проекта ├── ui/ # Базовые UI-компоненты ├── lib/ # Утилиты и хелперы ├── services/ # Общие сервисы и клиенты ├── config/ # Общие конфигурации ├── styles/ # Глобальные стили и токены └── assets/ # Ресурсы ├── images/ # Изображения ├── icons/ # Иконки ├── fonts/ # Шрифты └── video/ # Видео ``` **Плохо** ```text // Плохо: слои смешаны, нет понятных границ и публичного API. src/ ├── components/ ├── api/ ├── styles/ └── user.ts ``` ## Правила организации - Каждый слой и модуль хранится в собственной папке. - Внутренние реализации разделяются на `ui`, `model`, `lib`, `api`. - Публичный API модуля объявляется в `index.ts`. - Внутренние файлы не импортируются напрямую извне. - Не смешивать ответственность разных слоёв в одном модуле. # Компоненты Раздел описывает правила создания UI‑компонентов. Эти правила обязательны для всех слоёв FSD: `app`, `screens`, `layouts`, `widgets`, `features`, `entities`, `shared`. ## Базовая структура компонента Минимальный набор файлов: компонент, стили, типы и публичный экспорт. ```text container/ ├── styles/ │ └── container.module.scss ├── types/ │ └── container.interface.ts ├── container.ui.tsx └── index.ts ``` ## Пример базового компонента `styles/container.module.scss` ```scss .root {} ``` В CSS Modules использование имени класса **.root** — это общепринятое соглашение (best practice) `types/container.interface.ts` ```ts import type { HTMLAttributes } from 'react' /** * Параметры контейнера. */ export interface ContainerProps extends HTMLAttributes {} ``` Интерфес параметров компонента всегда наследует свойства своего тега: div, button, итд.. `container.ui.tsx` ```tsx import type { FC } from 'react' import { cl } from 'clsx' import type { ContainerProps } from './types/container.interface' import styles from './styles/container.module.scss' /** * Контейнер с адаптивной максимальной шириной. * * Используется для: * - ограничения ширины контента * - центрирования содержимого * - построения адаптивной сетки страницы */ export const Container: FC = ({ className, ...htmlAttr }) => { return (
Container...
) } ``` - Компонент объявляется через `const` и экспортируется именованно. - Пропсы деструктурируются в сигнатуре; если их больше двух — деструктуризацию переносим в тело компонента. - Из пропсов отдельно извлекаются `className` и `...htmlAttr`, чтобы корректно объединять классы и прокидывать остальные атрибуты. - `cl` — короткое имя функции для конкатенации CSS‑классов. - `FC<>` используется для декларации `children`. `index.ts` ```ts export { Container } from './container.ui' ``` ## Вложенные (дочерние) компоненты Если для реализации функционала нужны компоненты, которые используются только внутри текущего компонента, создавайте их как вложенные в папке `ui/`. Такие компоненты не экспортируются наружу и используются только локально. Вложенные компоненты подчиняются тем же правилам по структуре, именованию и стилю (включая папку `styles/` для их стилей).