sync
This commit is contained in:
@@ -31,9 +31,9 @@
|
|||||||
- Конфигурация VitePress — `.vitepress/config.ts`.
|
- Конфигурация VitePress — `.vitepress/config.ts`.
|
||||||
- `README.md` — описание проекта и не связан с `parts/`.
|
- `README.md` — описание проекта и не связан с `parts/`.
|
||||||
|
|
||||||
## Генерация .cursorrules
|
## Генерация RULES.md
|
||||||
|
|
||||||
- Файл `.cursorrules` собирается из содержимого `parts/`.
|
- Файл `RULES.md` собирается из содержимого `parts/`.
|
||||||
- Команда:
|
- Команда:
|
||||||
```bash
|
```bash
|
||||||
npm run docs
|
npm run docs
|
||||||
|
|||||||
630
RULES.md
Normal file
630
RULES.md
Normal file
@@ -0,0 +1,630 @@
|
|||||||
|
|
||||||
|
<a name="1-tech-stackmd"></a>
|
||||||
|
|
||||||
|
# Технологии и библиотеки
|
||||||
|
|
||||||
|
Базовый стек технологий и библиотек, на который опираются проекты и примеры в документации.
|
||||||
|
|
||||||
|
## Что используем обычно
|
||||||
|
|
||||||
|
- **TypeScript** — типизация всей логики и компонентов.
|
||||||
|
- **FSD (Feature-Sliced Design)** — структура проекта и границы модулей.
|
||||||
|
- **React + Next.js** — основной стек для UI и приложения.
|
||||||
|
- **Mantine UI** — базовые UI-компоненты.
|
||||||
|
- **SWR** — получение данных по GET (кеширование и revalidate).
|
||||||
|
- **Zustand** — глобальное состояние.
|
||||||
|
- **i18next (i18n)** — локализация всех пользовательских текстов.
|
||||||
|
- **Vitest** — тестирование.
|
||||||
|
- **PostCSS Modules** — изоляция стилей.
|
||||||
|
- **Mobile First** — подход к адаптивной верстке.
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2-architecturemd"></a>
|
||||||
|
|
||||||
|
# Архитектура
|
||||||
|
|
||||||
|
Архитектура построена на 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.
|
||||||
|
|
||||||
|
|
||||||
|
<a name="3-code-stylemd"></a>
|
||||||
|
|
||||||
|
# Стиль кода
|
||||||
|
|
||||||
|
Раздел описывает единые правила оформления кода: отступы, переносы, кавычки, порядок импортов и базовую читаемость.
|
||||||
|
|
||||||
|
## Отступы
|
||||||
|
|
||||||
|
- 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
|
||||||
|
<input type="text" placeholder="Введите имя" />
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
```ts
|
||||||
|
// Плохо: двойные кавычки в TS и конкатенация вместо шаблонной строки.
|
||||||
|
const label = "Сохранить";
|
||||||
|
const title = 'Привет, ' + name;
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Плохо: одинарные кавычки в JSX-атрибутах.
|
||||||
|
<input type='text' placeholder='Введите имя' />
|
||||||
|
```
|
||||||
|
|
||||||
|
## Точки с запятой и запятые
|
||||||
|
|
||||||
|
- Допускаются упущения точки с запятой, если код остаётся читаемым и однозначным.
|
||||||
|
- В многострочных массивах, объектах и параметрах функции запятая в конце допускается, но не обязательна.
|
||||||
|
|
||||||
|
## Импорты
|
||||||
|
|
||||||
|
- В именованных импортах использовать пробелы внутри фигурных скобок.
|
||||||
|
- Типы импортировать через `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 } };
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<a name="4-namingmd"></a>
|
||||||
|
|
||||||
|
# Именование
|
||||||
|
|
||||||
|
Именование должно быть предсказуемым, коротким и отражать смысл сущности.
|
||||||
|
|
||||||
|
## Базовые правила
|
||||||
|
|
||||||
|
| Что | Рекомендуется |
|
||||||
|
| ---------------- | ---------------------- |
|
||||||
|
| Папки | `kebab-case` |
|
||||||
|
| Файлы | `kebab-case` |
|
||||||
|
| Переменные | `camelCase` |
|
||||||
|
| Константы | `SCREAMING_SNAKE_CASE` |
|
||||||
|
| Классы | `PascalCase` |
|
||||||
|
| React-компоненты | `PascalCase` |
|
||||||
|
| Хуки | `useSomething` |
|
||||||
|
| CSS классы | `camelCase` |
|
||||||
|
|
||||||
|
|
||||||
|
## Архитектурный неймспейс
|
||||||
|
|
||||||
|
Соглашение о суффиксах, которые обозначают слой (уровень абстракции), роль или тип файла.
|
||||||
|
|
||||||
|
- Суффиксы используются для обозначения слоя, роли или типа файла.
|
||||||
|
- Суффиксы всегда пишутся в единственном числе.
|
||||||
|
- Формат имени: `name.<suffix>.ts` или `name.<suffix>.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<string, User>;
|
||||||
|
const userIds = ['u1', 'u2'];
|
||||||
|
const ordersMap = new Map<string, Order>();
|
||||||
|
const featureFlagsDict = { beta: true, legacy: false } as Record<string, boolean>;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
```ts
|
||||||
|
// Плохо: имя не отражает, что это коллекция.
|
||||||
|
const user = [];
|
||||||
|
// Плохо: словарь назван как массив.
|
||||||
|
const usersMap = [];
|
||||||
|
// Плохо: по имени непонятно, что это словарь.
|
||||||
|
const users = {} as Record<string, User>;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<a name="5-documentationmd"></a>
|
||||||
|
|
||||||
|
# Документирование
|
||||||
|
|
||||||
|
Документирование должно помогать понять назначение сущности, а не дублировать её типы или очевидные детали.
|
||||||
|
|
||||||
|
## Правила
|
||||||
|
|
||||||
|
- Документировать только назначение функций, компонентов, типов, интерфейсов и 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 — статус выполнения
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<a name="6-typingmd"></a>
|
||||||
|
|
||||||
|
# Типизация
|
||||||
|
|
||||||
|
Типизация обязательна для всех публичных интерфейсов, функций и компонентов.
|
||||||
|
Цель — предсказуемость, безопасность и автодополнение.
|
||||||
|
|
||||||
|
## Общие правила
|
||||||
|
|
||||||
|
- Указывать типы для параметров компонентов, возвращаемых значений и параметров функций.
|
||||||
|
- Предпочитать `type` для описания сущностей и `interface` для расширяемых контрактов.
|
||||||
|
- Избегать `any` и `unknown` без необходимости.
|
||||||
|
- Не использовать `ts-ignore`, кроме крайних случаев с явным комментарием причины.
|
||||||
|
|
||||||
|
## Типы для компонентов
|
||||||
|
|
||||||
|
- Типизировать параметры и публичный интерфейс компонента.
|
||||||
|
- Дефолтные значения описывать явно в коде.
|
||||||
|
|
||||||
|
**Хорошо**
|
||||||
|
```tsx
|
||||||
|
/**
|
||||||
|
* Параметры кнопки.
|
||||||
|
*/
|
||||||
|
interface IOwnProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
|
/** Текст кнопки. */
|
||||||
|
label: string;
|
||||||
|
/** Обработчик клика по кнопке. */
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Кнопка с пользовательскими стилями.
|
||||||
|
*/
|
||||||
|
export const Button:FC<IOwnProps> = ({ className, label, onClick, ...htmlAttr }) => {
|
||||||
|
return (
|
||||||
|
<div {...htmlAttr} className={cl(styles.root, className)}>
|
||||||
|
button
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
```tsx
|
||||||
|
// Плохо: параметры не типизированы.
|
||||||
|
export const Button = (props) => (
|
||||||
|
<button type="button" onClick={props.onClick}>
|
||||||
|
{props.label}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Функции
|
||||||
|
|
||||||
|
- Для публичных функций указывать возвращаемый тип.
|
||||||
|
- Не полагаться на неявный вывод для важных 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;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<a name="7-project-structuremd"></a>
|
||||||
|
|
||||||
|
# Структура проекта
|
||||||
|
|
||||||
|
Раздел описывает базовую структуру проекта и принципы организации модулей на уровне папок и файлов.
|
||||||
|
|
||||||
|
## Базовая структура проекта
|
||||||
|
|
||||||
|
**Хорошо**
|
||||||
|
```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`.
|
||||||
|
- Внутренние файлы не импортируются напрямую извне.
|
||||||
|
- Не смешивать ответственность разных слоёв в одном модуле.
|
||||||
|
|
||||||
|
|
||||||
|
<a name="8-componentsmd"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="9-stylesmd"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10-images-spritesmd"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="11-videomd"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="12-apimd"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="13-storesmd"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="14-hooksmd"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="15-fontsmd"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="16-localizationmd"></a>
|
||||||
|
|
||||||
@@ -16,8 +16,8 @@ const resultMd = concatMdSync("./parts", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Записываем результат в файл .cursorrules в корне проекта
|
// Записываем результат в файл RULES.md в корне проекта
|
||||||
const outputPath = path.join("./", ".cursorrules");
|
const outputPath = path.join("./", "RULES.md");
|
||||||
fs.writeFileSync(outputPath, resultMd, "utf8");
|
fs.writeFileSync(outputPath, resultMd, "utf8");
|
||||||
|
|
||||||
console.log(`Файл .cursorrules успешно создан: ${outputPath}`);
|
console.log(`Файл RULES.md успешно создан: ${outputPath}`);
|
||||||
|
|||||||
6
index.md
6
index.md
@@ -4,7 +4,11 @@
|
|||||||
|
|
||||||
## Как пользоваться
|
## Как пользоваться
|
||||||
|
|
||||||
- Начните со «Технологии и библиотеки», чтобы понимать базовые допущения.
|
- Начните с «Технологии и библиотеки», чтобы понимать базовые допущения.
|
||||||
- Затем прочитайте разделы из «Базовых правил».
|
- Затем прочитайте разделы из «Базовых правил».
|
||||||
- При реализации используйте соответствующие прикладные разделы.
|
- При реализации используйте соответствующие прикладные разделы.
|
||||||
|
|
||||||
|
## Для ассистентов
|
||||||
|
|
||||||
|
Для передачи ассистенту используйте сконкатенированную версию документации:
|
||||||
|
https://gromlab.ru/docs/react-ts.
|
||||||
|
|||||||
@@ -6,29 +6,9 @@ title: Стиль кода
|
|||||||
|
|
||||||
Раздел описывает единые правила оформления кода: отступы, переносы, кавычки, порядок импортов и базовую читаемость.
|
Раздел описывает единые правила оформления кода: отступы, переносы, кавычки, порядок импортов и базовую читаемость.
|
||||||
|
|
||||||
## Отступы и переносы
|
## Отступы
|
||||||
|
|
||||||
- Использовать 2 пробела для отступов.
|
- 2 пробела (не табы).
|
||||||
- Не использовать табы.
|
|
||||||
- Одна инструкция — одна строка.
|
|
||||||
- Между логическими блоками оставлять пустую строку по необходимости, если это улучшает читаемость.
|
|
||||||
- В многострочных конструкциях выравнивать закрывающую скобку по началу выражения.
|
|
||||||
|
|
||||||
**Хорошо**
|
|
||||||
```ts
|
|
||||||
const payload = {
|
|
||||||
id: 1,
|
|
||||||
name: 'User',
|
|
||||||
meta: {
|
|
||||||
role: 'admin',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Плохо**
|
|
||||||
```ts
|
|
||||||
const payload = { id: 1, name: 'User', meta: { role: 'admin' } };
|
|
||||||
```
|
|
||||||
|
|
||||||
## Длина строк
|
## Длина строк
|
||||||
|
|
||||||
@@ -57,6 +37,7 @@ const config = createRequestConfig(
|
|||||||
|
|
||||||
**Плохо**
|
**Плохо**
|
||||||
```ts
|
```ts
|
||||||
|
// Плохо: длинная строка с вложенными структурами плохо читается.
|
||||||
const config = createRequestConfig(endpoint, { headers: { 'X-Request-Id': requestId, 'X-User-Id': userId }, params: { page, pageSize, sort: 'createdAt' } }, timeoutMs);
|
const config = createRequestConfig(endpoint, { headers: { 'X-Request-Id': requestId, 'X-User-Id': userId }, params: { page, pageSize, sort: 'createdAt' } }, timeoutMs);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -78,11 +59,13 @@ const title = `Привет, ${name}`;
|
|||||||
|
|
||||||
**Плохо**
|
**Плохо**
|
||||||
```ts
|
```ts
|
||||||
|
// Плохо: двойные кавычки в TS и конкатенация вместо шаблонной строки.
|
||||||
const label = "Сохранить";
|
const label = "Сохранить";
|
||||||
const title = 'Привет, ' + name;
|
const title = 'Привет, ' + name;
|
||||||
```
|
```
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
|
// Плохо: одинарные кавычки в JSX-атрибутах.
|
||||||
<input type='text' placeholder='Введите имя' />
|
<input type='text' placeholder='Введите имя' />
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -106,6 +89,7 @@ import type { User } from '../model/types';
|
|||||||
|
|
||||||
**Плохо**
|
**Плохо**
|
||||||
```ts
|
```ts
|
||||||
|
// Плохо: default импорт и отсутствие пробелов в именованном импорте.
|
||||||
import MyComponent from 'MyComponent';
|
import MyComponent from 'MyComponent';
|
||||||
import type {User} from '../model/types';
|
import type {User} from '../model/types';
|
||||||
```
|
```
|
||||||
@@ -128,6 +112,7 @@ const getName = (user?: { name: string }) => {
|
|||||||
|
|
||||||
**Плохо**
|
**Плохо**
|
||||||
```ts
|
```ts
|
||||||
|
// Плохо: лишний else после return усложняет чтение.
|
||||||
const getName = (user?: { name: string }) => {
|
const getName = (user?: { name: string }) => {
|
||||||
if (user) {
|
if (user) {
|
||||||
return user.name;
|
return user.name;
|
||||||
@@ -146,7 +131,7 @@ const getName = (user?: { name: string }) => {
|
|||||||
|
|
||||||
**Хорошо**
|
**Хорошо**
|
||||||
```ts
|
```ts
|
||||||
const roles = ['admin', 'editor'];
|
const roles = ['admin', 'editor', 'viewer'];
|
||||||
const options = { id: 1, name: 'User' };
|
const options = { id: 1, name: 'User' };
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
@@ -158,7 +143,8 @@ const config = {
|
|||||||
|
|
||||||
**Плохо**
|
**Плохо**
|
||||||
```ts
|
```ts
|
||||||
const roles = ['admin','editor'];
|
// Плохо: нет пробелов после запятых и объект слишком длинный для одной строки.
|
||||||
|
const roles = ['admin','editor','viewer'];
|
||||||
const options = { id: 1,name: 'User' };
|
const options = { id: 1,name: 'User' };
|
||||||
const config = { url: '/api/users', method: 'GET', params: { page: 1, pageSize: 20 } };
|
const config = { url: '/api/users', method: 'GET', params: { page: 1, pageSize: 20 } };
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
---
|
||||||
|
title: Именование
|
||||||
|
---
|
||||||
|
|
||||||
|
# Именование
|
||||||
|
|
||||||
|
Именование должно быть предсказуемым, коротким и отражать смысл сущности.
|
||||||
|
|
||||||
|
## Базовые правила
|
||||||
|
|
||||||
|
| Что | Рекомендуется |
|
||||||
|
| ---------------- | ---------------------- |
|
||||||
|
| Папки | `kebab-case` |
|
||||||
|
| Файлы | `kebab-case` |
|
||||||
|
| Переменные | `camelCase` |
|
||||||
|
| Константы | `SCREAMING_SNAKE_CASE` |
|
||||||
|
| Классы | `PascalCase` |
|
||||||
|
| React-компоненты | `PascalCase` |
|
||||||
|
| Хуки | `useSomething` |
|
||||||
|
| CSS классы | `camelCase` |
|
||||||
|
|
||||||
|
|
||||||
|
## Архитектурный неймспейс
|
||||||
|
|
||||||
|
Соглашение о суффиксах, которые обозначают слой (уровень абстракции), роль или тип файла.
|
||||||
|
|
||||||
|
- Суффиксы используются для обозначения слоя, роли или типа файла.
|
||||||
|
- Суффиксы всегда пишутся в единственном числе.
|
||||||
|
- Формат имени: `name.<suffix>.ts` или `name.<suffix>.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<string, User>;
|
||||||
|
const userIds = ['u1', 'u2'];
|
||||||
|
const ordersMap = new Map<string, Order>();
|
||||||
|
const featureFlagsDict = { beta: true, legacy: false } as Record<string, boolean>;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
```ts
|
||||||
|
// Плохо: имя не отражает, что это коллекция.
|
||||||
|
const user = [];
|
||||||
|
// Плохо: словарь назван как массив.
|
||||||
|
const usersMap = [];
|
||||||
|
// Плохо: по имени непонятно, что это словарь.
|
||||||
|
const users = {} as Record<string, User>;
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
title: Документирование
|
||||||
|
---
|
||||||
|
|
||||||
|
# Документирование
|
||||||
|
|
||||||
|
Документирование должно помогать понять назначение сущности, а не дублировать её типы или очевидные детали.
|
||||||
|
|
||||||
|
## Правила
|
||||||
|
|
||||||
|
- Документировать только назначение функций, компонентов, типов, интерфейсов и 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 — статус выполнения
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
title: Типизация
|
||||||
|
---
|
||||||
|
|
||||||
|
# Типизация
|
||||||
|
|
||||||
|
Типизация обязательна для всех публичных интерфейсов, функций и компонентов.
|
||||||
|
Цель — предсказуемость, безопасность и автодополнение.
|
||||||
|
|
||||||
|
## Общие правила
|
||||||
|
|
||||||
|
- Указывать типы для параметров компонентов, возвращаемых значений и параметров функций.
|
||||||
|
- Предпочитать `type` для описания сущностей и `interface` для расширяемых контрактов.
|
||||||
|
- Избегать `any` и `unknown` без необходимости.
|
||||||
|
- Не использовать `ts-ignore`, кроме крайних случаев с явным комментарием причины.
|
||||||
|
|
||||||
|
## Типы для компонентов
|
||||||
|
|
||||||
|
- Типизировать параметры и публичный интерфейс компонента.
|
||||||
|
- Дефолтные значения описывать явно в коде.
|
||||||
|
|
||||||
|
**Хорошо**
|
||||||
|
```tsx
|
||||||
|
/**
|
||||||
|
* Параметры кнопки.
|
||||||
|
*/
|
||||||
|
interface IOwnProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
|
/** Текст кнопки. */
|
||||||
|
label: string;
|
||||||
|
/** Обработчик клика по кнопке. */
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Кнопка с пользовательскими стилями.
|
||||||
|
*/
|
||||||
|
export const Button:FC<IOwnProps> = ({ className, label, onClick, ...htmlAttr }) => {
|
||||||
|
return (
|
||||||
|
<div {...htmlAttr} className={cl(styles.root, className)}>
|
||||||
|
button
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
```tsx
|
||||||
|
// Плохо: параметры не типизированы.
|
||||||
|
export const Button = (props) => (
|
||||||
|
<button type="button" onClick={props.onClick}>
|
||||||
|
{props.label}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Функции
|
||||||
|
|
||||||
|
- Для публичных функций указывать возвращаемый тип.
|
||||||
|
- Не полагаться на неявный вывод для важных 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;
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
title: Структура проекта
|
||||||
|
---
|
||||||
|
|
||||||
|
# Структура проекта
|
||||||
|
|
||||||
|
Раздел описывает базовую структуру проекта и принципы организации модулей на уровне папок и файлов.
|
||||||
|
|
||||||
|
## Базовая структура проекта
|
||||||
|
|
||||||
|
**Хорошо**
|
||||||
|
```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`.
|
||||||
|
- Внутренние файлы не импортируются напрямую извне.
|
||||||
|
- Не смешивать ответственность разных слоёв в одном модуле.
|
||||||
|
|||||||
Reference in New Issue
Block a user