Compare commits
2 Commits
7965ec2a96
...
v2
| Author | SHA1 | Date | |
|---|---|---|---|
| 47658cdbb9 | |||
| 4aeb1dd6b2 |
@@ -73,7 +73,7 @@ jobs:
|
|||||||
ssh -i ~/.ssh/deploy_key root@188.225.47.78 bash -s <<'SCRIPT'
|
ssh -i ~/.ssh/deploy_key root@188.225.47.78 bash -s <<'SCRIPT'
|
||||||
set -e
|
set -e
|
||||||
IMAGE="${{ env.REGISTRY_IMAGE }}:latest"
|
IMAGE="${{ env.REGISTRY_IMAGE }}:latest"
|
||||||
CONTAINER="nextjs-style-guide"
|
CONTAINER="frontend-style-guide"
|
||||||
|
|
||||||
# Логин в реестр
|
# Логин в реестр
|
||||||
echo '${{ secrets.CR_TOKEN }}' | docker login ${{ env.DOCKER_REGISTRY }} -u '${{ secrets.CR_USER }}' --password-stdin
|
echo '${{ secrets.CR_TOKEN }}' | docker login ${{ env.DOCKER_REGISTRY }} -u '${{ secrets.CR_USER }}' --password-stdin
|
||||||
|
|||||||
11
AGENTS.md
11
AGENTS.md
@@ -3,6 +3,11 @@
|
|||||||
При работе с документацией следовать правилам из CONTRIBUTING.md.
|
При работе с документацией следовать правилам из CONTRIBUTING.md.
|
||||||
|
|
||||||
- Язык документации и коммитов — русский.
|
- Язык документации и коммитов — русский.
|
||||||
- После изменений в `.md`-файлах — запустить `npm run docs` для обновления RULES.md.
|
- Исходники документации — в `src/`, только `.md` файлы.
|
||||||
- При добавлении нового раздела — обновить сайдбар (`.vitepress/config.ts`)
|
- Скрипты и манифесты сборки — в `scripts/`.
|
||||||
и порядок файлов (`concat-md.js`).
|
- Общие правила — в `src/base/`.
|
||||||
|
- Фреймворк-специфичные — в `src/{framework}/`.
|
||||||
|
- Точки входа (`DEVELOP.md`, `REVIEW.md`) — в `src/{framework}/`.
|
||||||
|
- После изменений в `.md`-файлах — запустить `npm run build:ai` для пересборки `dist/ai/`.
|
||||||
|
- При добавлении нового раздела — добавить файл в `src/` и путь в манифест `scripts/{fw}.build.js`.
|
||||||
|
- Frontmatter каждого `.md`-файла содержит поля `title`, `scope`, `keywords`, `when`.
|
||||||
|
|||||||
250
CONTRIBUTING.md
250
CONTRIBUTING.md
@@ -1,201 +1,129 @@
|
|||||||
# Правила работы над документацией
|
# Правила написания документации
|
||||||
|
|
||||||
Мета-документ: как устроен проект, как писать и редактировать разделы.
|
Как писать и редактировать разделы стайлгайда.
|
||||||
|
|
||||||
## О проекте
|
## Типы разделов
|
||||||
|
|
||||||
Документационный сайт с правилами и стандартами фронтенд-разработки на Next.js + TypeScript.
|
### Базовые правила (`basics/`)
|
||||||
|
|
||||||
- Движок: VitePress
|
|
||||||
- Языки: русский (основной), английский
|
|
||||||
- Русская версия: `docs/ru/`
|
|
||||||
- Английская версия: `docs/en/`
|
|
||||||
|
|
||||||
## Команды
|
|
||||||
|
|
||||||
| Команда | Что делает |
|
|
||||||
|---------|-----------|
|
|
||||||
| `npm run dev` | Локальный сервер разработки |
|
|
||||||
| `npm run build` | Сборка статического сайта |
|
|
||||||
| `npm run docs` | Генерация `generated/{lang}/RULES.md` — единый файл для AI-ассистентов |
|
|
||||||
|
|
||||||
## Структура файлов
|
|
||||||
|
|
||||||
```
|
|
||||||
docs/
|
|
||||||
├── ru/ # Русская версия (основная)
|
|
||||||
│ ├── index.md # Главная страница
|
|
||||||
│ ├── basics/ # Базовые правила
|
|
||||||
│ │ ├── tech-stack.md
|
|
||||||
│ │ ├── architecture.md
|
|
||||||
│ │ ├── code-style.md
|
|
||||||
│ │ ├── naming.md
|
|
||||||
│ │ ├── documentation.md
|
|
||||||
│ │ └── typing.md
|
|
||||||
│ └── applied/ # Прикладные разделы
|
|
||||||
│ ├── vscode.md
|
|
||||||
│ ├── project-structure.md
|
|
||||||
│ ├── components.md
|
|
||||||
│ ├── page-level.md
|
|
||||||
│ ├── templates-generation.md
|
|
||||||
│ ├── styles.md
|
|
||||||
│ ├── images-sprites.md
|
|
||||||
│ ├── svg-sprites.md
|
|
||||||
│ ├── video.md
|
|
||||||
│ ├── api.md
|
|
||||||
│ ├── stores.md
|
|
||||||
│ ├── hooks.md
|
|
||||||
│ ├── fonts.md
|
|
||||||
│ └── localization.md
|
|
||||||
├── en/ # Английская версия (зеркало ru/)
|
|
||||||
.vitepress/
|
|
||||||
├── config.ts # Конфигурация VitePress, сайдбары, локали
|
|
||||||
generated/
|
|
||||||
├── ru/RULES.md # Сгенерированный единый файл (ru)
|
|
||||||
└── en/RULES.md # Сгенерированный единый файл (en)
|
|
||||||
concat-md.js # Скрипт генерации RULES.md
|
|
||||||
```
|
|
||||||
|
|
||||||
### Добавление нового раздела
|
|
||||||
|
|
||||||
1. Создать `.md`-файл в нужной папке (`basics/` или `applied/`).
|
|
||||||
2. Добавить пункт в сайдбар — `.vitepress/config.ts` (оба языка, если есть перевод).
|
|
||||||
3. Добавить файл в массив `fileOrder` — `concat-md.js` (для генерации RULES.md).
|
|
||||||
|
|
||||||
## Два типа документации
|
|
||||||
|
|
||||||
### Базовые правила
|
|
||||||
|
|
||||||
**Отвечает на вопрос:** «Каким должен быть любой код?»
|
**Отвечает на вопрос:** «Каким должен быть любой код?»
|
||||||
|
|
||||||
Универсальные стандарты, **не привязанные к конкретной области**.
|
Универсальные стандарты, не привязанные к конкретной области.
|
||||||
Правило базовое, если оно применимо ко всему коду одинаково: именование переменных, оформление импортов, когда использовать `type` vs `interface`.
|
Правило базовое, если оно применимо ко всему коду одинаково.
|
||||||
|
|
||||||
Примеры в базовых правилах допускаются, но служат иллюстрацией принципа, а не инструкцией по конкретной области.
|
**Граница:** если правило касается только одной области — оно прикладное.
|
||||||
|
|
||||||
**Граница:** если правило касается только одной области (только стили, только компоненты, только API) — оно живёт в прикладном разделе, не в базовых.
|
### Прикладные разделы (`applied/`)
|
||||||
|
|
||||||
### Прикладные разделы
|
|
||||||
|
|
||||||
**Отвечает на вопрос:** «Как работать с X?»
|
**Отвечает на вопрос:** «Как работать с X?»
|
||||||
|
|
||||||
Полное описание конкретной области: структура файлов, правила, именование, типизация, примеры.
|
Полное описание конкретной области: структура файлов, правила, именование, типизация, примеры.
|
||||||
|
|
||||||
**Граница:** прикладной раздел не дублирует базовые правила.
|
**Граница:** не дублирует базовые правила. Если правило уже описано в базовых — ссылается, но не повторяет.
|
||||||
Если правило уже описано в базовых — прикладной раздел ссылается на него, но не повторяет.
|
|
||||||
|
|
||||||
## Структура прикладного раздела
|
### Триггеры (`triggers/`)
|
||||||
|
|
||||||
Шаблон ниже описывает все допустимые секции. Раздел включает только те секции, которые для него релевантны — пустые секции не создаются.
|
**Отвечает на вопрос:** «Как выполнить задачу X?»
|
||||||
|
|
||||||
```markdown
|
Конкретная инструкция: какие разделы прочитать, какие шаги выполнить. Триггер ссылается на basics/ и applied/, но не дублирует их. Группируются по роли: `triggers/develop/`, `triggers/review/`, `triggers/architect/`.
|
||||||
# {Название}
|
|
||||||
|
|
||||||
Краткое описание: о чём раздел и какие аспекты работы с областью он охватывает.
|
Структура триггера:
|
||||||
|
|
||||||
## Что нужно знать
|
- **Заголовок** — глагол + объект ("Создать компонент", "Добавить иконку")
|
||||||
|
- **Описание** — одно предложение: что делает триггер
|
||||||
|
- **Прочитай перед началом** — ссылки на basics/ и applied/
|
||||||
|
- **Шаги** — нумерованный список действий со ссылками
|
||||||
|
- **Смежные триггеры** — ссылки на связанные задачи
|
||||||
|
- **Проверь себя** — чеклист из 2-5 пунктов для самопроверки
|
||||||
|
|
||||||
Неочевидная информация, которую читатель должен знать перед чтением раздела.
|
### Фреймворк-специфичные (`{framework}/`)
|
||||||
Если для раздела нет такой вводной — секция не создаётся.
|
|
||||||
|
|
||||||
## Структура
|
Разделы и триггеры, которые применимы только к конкретному фреймворку. Те же категории — `applied/` и `triggers/`.
|
||||||
|
|
||||||
Файловая организация: какие файлы создавать и куда класть.
|
**Граница:** если правило одинаково для всех фреймворков — оно в `base/`.
|
||||||
Обязательно — дерево файлов через code-block.
|
|
||||||
|
|
||||||
## Правила
|
## Frontmatter
|
||||||
|
|
||||||
Конкретные требования, специфичные для области. Делятся на две подсекции:
|
|
||||||
|
|
||||||
### Реализация
|
|
||||||
|
|
||||||
Как написан код внутри файла: синтаксис, паттерны, API.
|
|
||||||
Отвечает на вопрос: «Как писать код?»
|
|
||||||
|
|
||||||
Примеры: объявление через `const`, деструктуризация пропсов, формат вызова `cl()`, способ подключения стилей, структура хука.
|
|
||||||
|
|
||||||
### Организация
|
|
||||||
|
|
||||||
Как компонент/модуль встроен в проект: файловые границы, зоны ответственности, экспорт.
|
|
||||||
Отвечает на вопрос: «Где что лежит и за что отвечает?»
|
|
||||||
|
|
||||||
Примеры: один компонент — один файл, вложенные компоненты в `ui/`, логика выносится в `model/`.
|
|
||||||
|
|
||||||
Формат обеих подсекций — маркированный список.
|
|
||||||
Для неочевидных случаев — блоки «Хорошо / Плохо».
|
|
||||||
Если в области нет правил одной из категорий — подсекция не создаётся.
|
|
||||||
|
|
||||||
## Именование
|
|
||||||
|
|
||||||
Соглашения по именам, специфичные для этой области.
|
|
||||||
Только то, что НЕ покрыто в базовом разделе «Именование».
|
|
||||||
|
|
||||||
## Типизация
|
|
||||||
|
|
||||||
Правила типизации, специфичные для этой области.
|
|
||||||
Только то, что НЕ покрыто в базовом разделе «Типизация».
|
|
||||||
|
|
||||||
## Документирование
|
|
||||||
|
|
||||||
Что и как документировать в этой области.
|
|
||||||
Только то, что НЕ покрыто в базовом разделе «Документирование».
|
|
||||||
|
|
||||||
## Примеры
|
|
||||||
|
|
||||||
Полноценные примеры кода.
|
|
||||||
Каждый пример с путём к файлу и пояснениями.
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### Порядок секций
|
|
||||||
|
|
||||||
Порядок фиксированный: контекст → структура → правила → специализации базовых правил → примеры.
|
|
||||||
|
|
||||||
Логика: читатель сначала понимает «что это», потом «где это лежит», потом «как это делать», и в конце видит полный пример.
|
|
||||||
|
|
||||||
### Секции-расширения базовых правил
|
|
||||||
|
|
||||||
«Именование», «Типизация», «Документирование» в прикладном разделе — это **точки расширения** базовых правил.
|
|
||||||
|
|
||||||
- В базовых описано общее: `camelCase` для переменных, `type` vs `interface`, формат JSDoc.
|
|
||||||
- В прикладном разделе описано специфичное: как именовать CSS-классы (стили), как типизировать пропсы компонентов (компоненты), как документировать хуки (хуки).
|
|
||||||
|
|
||||||
Если для области нет специфики по именованию, типизации или документированию — секция не создаётся.
|
|
||||||
|
|
||||||
## Конвенции оформления
|
|
||||||
|
|
||||||
### Frontmatter
|
|
||||||
|
|
||||||
Каждый `.md`-файл начинается с YAML frontmatter:
|
Каждый `.md`-файл начинается с YAML frontmatter:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
title: Название раздела
|
title: Название раздела
|
||||||
|
scope: basics | applied | triggers
|
||||||
|
keywords: [ключевое слово 1, ключевое слово 2]
|
||||||
|
when: "Когда агенту читать этот раздел"
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
Значение `title` совпадает с текстом `h1`-заголовка в файле.
|
| Поле | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `title` | Название раздела. Совпадает с `h1` в файле |
|
||||||
|
| `scope` | Тип: `basics`, `applied` или `triggers` |
|
||||||
|
| `keywords` | Ключевые слова для поиска агентом |
|
||||||
|
| `when` | Описание ситуации, когда раздел релевантен |
|
||||||
|
|
||||||
|
## Структура прикладного раздела
|
||||||
|
|
||||||
|
Раздел включает только релевантные секции — пустые не создаются.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# {Название}
|
||||||
|
|
||||||
|
Краткое описание: о чём раздел.
|
||||||
|
|
||||||
|
## Что нужно знать
|
||||||
|
|
||||||
|
Неочевидная вводная информация (если есть).
|
||||||
|
|
||||||
|
## Структура
|
||||||
|
|
||||||
|
Файловая организация. Обязательно — дерево файлов.
|
||||||
|
|
||||||
|
## Правила
|
||||||
|
|
||||||
|
### Реализация
|
||||||
|
|
||||||
|
Как писать код: синтаксис, паттерны, API.
|
||||||
|
|
||||||
|
### Организация
|
||||||
|
|
||||||
|
Где что лежит: файловые границы, зоны ответственности, экспорт.
|
||||||
|
|
||||||
|
## Именование
|
||||||
|
|
||||||
|
Специфичные для области соглашения (не покрытые в basics/naming).
|
||||||
|
|
||||||
|
## Типизация
|
||||||
|
|
||||||
|
Специфичные для области правила (не покрытые в basics/typing).
|
||||||
|
|
||||||
|
## Документирование
|
||||||
|
|
||||||
|
Специфичные для области правила (не покрытые в basics/documentation).
|
||||||
|
|
||||||
|
## Примеры
|
||||||
|
|
||||||
|
Полноценные примеры кода с путями к файлам.
|
||||||
|
```
|
||||||
|
|
||||||
|
Порядок фиксированный: контекст -> структура -> правила -> специализации базовых -> примеры.
|
||||||
|
|
||||||
|
## Конвенции оформления
|
||||||
|
|
||||||
### Заголовки
|
### Заголовки
|
||||||
|
|
||||||
- Один `h1` на файл — совпадает с `title` из frontmatter.
|
- Один `h1` на файл — совпадает с `title` из frontmatter.
|
||||||
- Сразу после `h1` — вводный абзац (одно-два предложения).
|
- Сразу после `h1` — вводный абзац.
|
||||||
- Основные секции — `h2`.
|
- Основные секции — `h2`. Подсекции — `h3`. `h4` не используется.
|
||||||
- Подсекции внутри `h2` — `h3`.
|
|
||||||
- `h4` не используется.
|
|
||||||
|
|
||||||
### Примеры кода
|
### Примеры кода
|
||||||
|
|
||||||
- Блоки кода с указанием языка: ` ```tsx `, ` ```css `, ` ```bash `, ` ```text `.
|
- Блоки кода с указанием языка: ` ```tsx `, ` ```css `, ` ```bash `, ` ```text `.
|
||||||
- Путь к файлу указывается перед блоком кода текстом или комментарием внутри блока.
|
- Путь к файлу — перед блоком кода или комментарием внутри.
|
||||||
- Дерево файлов — ` ```text ` с символами `├──`, `└──`, `│`.
|
- Дерево файлов — ` ```text ` с символами `├──`, `└──`, `│`.
|
||||||
|
|
||||||
### Блоки «Хорошо / Плохо»
|
### Блоки «Хорошо / Плохо»
|
||||||
|
|
||||||
Используются для контрастного сравнения правильного и неправильного подхода.
|
|
||||||
|
|
||||||
Формат:
|
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
**Хорошо:**
|
**Хорошо:**
|
||||||
|
|
||||||
@@ -212,16 +140,16 @@ title: Название раздела
|
|||||||
|
|
||||||
### Таблицы
|
### Таблицы
|
||||||
|
|
||||||
Используются для структурированных перечислений: настройки, команды, соответствия.
|
|
||||||
Формат — стандартный Markdown: `| Ключ | Описание |`.
|
Формат — стандартный Markdown: `| Ключ | Описание |`.
|
||||||
|
|
||||||
### Ссылки между разделами
|
### Ссылки между разделами
|
||||||
|
|
||||||
Прикладной раздел может ссылаться на другие разделы, но не дублирует их содержимое.
|
Ссылаться можно, дублировать содержимое — нет.
|
||||||
|
|
||||||
## Принципы
|
## Принципы
|
||||||
|
|
||||||
1. **Не дублировать.** Одна мысль живёт в одном месте. Остальные ссылаются.
|
1. **Не дублировать.** Одна мысль — одно место. Остальные ссылаются.
|
||||||
2. **Базовое vs прикладное.** Если правило применимо ко всему коду — оно базовое. Если только к одной области — прикладное.
|
2. **Базовое vs прикладное.** Применимо ко всему коду — базовое. К одной области — прикладное.
|
||||||
3. **Пустые секции не создавать.** Если для раздела нет специфики по именованию — секции «Именование» в нём нет.
|
3. **Общее vs специфичное.** Одинаково для всех фреймворков — в `base/`. Для одного — в `{framework}/`.
|
||||||
4. **Примеры обязательны.** Прикладной раздел без примеров кода — незавершён.
|
4. **Пустые секции не создавать.**
|
||||||
|
5. **Примеры обязательны.** Прикладной раздел без примеров — незавершён.
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
# Ассистент
|
|
||||||
|
|
||||||
## Для ассистента
|
|
||||||
|
|
||||||
- Всегда используй Русский язык для общения и генерации документации/комментариев/коммитов.
|
|
||||||
- Всегда следуй этим правилам при генерации кода и ответах.
|
|
||||||
- Всегда пиши план действий перед генерацией кода.
|
|
||||||
- Всегда спрашивай разрешения у пользователя перед генерацией кода.
|
|
||||||
- Всегда проверяй, что код соответствует линтингу и форматированию.
|
|
||||||
- Всегда сверяйся с чек-листом при генерации кода.
|
|
||||||
- Не предлагай решения, которые противоречат этим правилам этого файла.
|
|
||||||
- Если не уверен — уточни у пользователя, не гадай, не придумывай.
|
|
||||||
|
|
||||||
## Обязательность чек-листов
|
|
||||||
|
|
||||||
- Все чек-листы, приведённые в правилах, обязательны к исполнению.
|
|
||||||
- Ассистент обязан сверяться с чек-листом при выполнении любой задачи, связанной с кодом.
|
|
||||||
- Нельзя сокращать, игнорировать или опускать пункты чек-листа — каждый пункт должен быть выполнен или явно отмечен как невыполнимый с объяснением причины.
|
|
||||||
- В каждом ответе, связанном с генерацией или изменением кода, ассистент обязан ссылаться на соответствующий чек-лист и подтверждать его выполнение.
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
---
|
|
||||||
title: Stores
|
|
||||||
---
|
|
||||||
|
|
||||||
# Stores
|
|
||||||
|
|
||||||
## Сторы (Stores)
|
|
||||||
|
|
||||||
> В этом разделе собраны основные правила и рекомендации по созданию и оформлению сторов. Следуйте этим принципам, чтобы обеспечить чистую архитектуру, удобство поддержки и единый стиль работы с состоянием в проекте.
|
|
||||||
> В проекте для организации состояния используется только библиотека Zustand.
|
|
||||||
|
|
||||||
### Структура
|
|
||||||
- Store размещается в файле `<store-name>.store.ts` в папке `stores/` на своём уровне абстракции согласно архитектуре проекта.
|
|
||||||
- Интерфейс состояния описывается в этом же файле с суффиксом `State` (PascalCase).
|
|
||||||
- Для каждого store создаётся отдельный хук доступа (например, `useTodoStore`).
|
|
||||||
- Для глобальных сторов используйте только `shared/store`.
|
|
||||||
|
|
||||||
### Именование
|
|
||||||
- Соблюдайте [правила именования файлов и папок](#правила-именования-файлов-и-папок):
|
|
||||||
- Файл store — `<store-name>.store.ts` (kebab-case).
|
|
||||||
- Имя интерфейса состояния — PascalCase с суффиксом `State`.
|
|
||||||
- Имя хука — camelCase с префиксом `use`.
|
|
||||||
|
|
||||||
### Требования
|
|
||||||
- В store допускается только хранение состояния и методы управления им, без бизнес-логики, асинхронных операций и side-effects (см. раздел "Правила организации и использовалья Store").
|
|
||||||
- Для методов, изменяющих состояние через set, если используется функция — тело функции в фигурных скобках, return с новой строки после стрелки.
|
|
||||||
- Не дублируйте логику между сторами.
|
|
||||||
|
|
||||||
### Типизация
|
|
||||||
- Всегда указывайте типы для всех полей состояния и методов.
|
|
||||||
- Не используйте неявное приведение типов и не полагайтесь на автоматический вывод, если это может снизить читаемость или безопасность.
|
|
||||||
|
|
||||||
### Документирование
|
|
||||||
- Документируйте только назначение store и смысл полей, строго по [правилам документирования кода](#правило-для-документирования-кода).
|
|
||||||
|
|
||||||
### Экспорт
|
|
||||||
- Экспортируйте хук доступа к store и интерфейс состояния через `index.ts` слоя/компонента.
|
|
||||||
|
|
||||||
### Примеры
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { create } from 'zustand';
|
|
||||||
import { TodoItem } from './types/todo-item.interface';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Состояние хранилища задач.
|
|
||||||
*/
|
|
||||||
export interface TodoStoreState {
|
|
||||||
/** Массив задач. */
|
|
||||||
items: TodoItem[];
|
|
||||||
/** Добавить задачу. */
|
|
||||||
addTodo: (item: TodoItem) => void;
|
|
||||||
/** Удалить задачу. */
|
|
||||||
removeTodo: (id: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Хук для доступа к хранилищу задач.
|
|
||||||
*/
|
|
||||||
export const useTodoStore = create<TodoStoreState>((set) => ({
|
|
||||||
items: [],
|
|
||||||
addTodo: (item) => set((state) => {
|
|
||||||
return {
|
|
||||||
items: [...state.items, item],
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
removeTodo: (id) => set((state) => {
|
|
||||||
return {
|
|
||||||
items: state.items.filter((t) => t.id !== id),
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
```
|
|
||||||
|
|
||||||
### Чек-лист
|
|
||||||
|
|
||||||
- [ ] Store размещён в `stores/<store-name>.store.ts` на своём уровне абстракции согласно архитектуре проекта.
|
|
||||||
- [ ] Именование файлов и сущностей соответствует [правилам именования файлов и папок](#правила-именования-файлов-и-папок).
|
|
||||||
- [ ] Все поля и методы строго типизированы (см. [общие правила типизации](#общие-правила-типизации)).
|
|
||||||
- [ ] В store только состояние и методы управления им, без бизнес-логики и side-effects.
|
|
||||||
- [ ] Для методов, изменяющих состояние через set, используется функция с return с новой строки.
|
|
||||||
- [ ] Документировано только назначение store и смысл полей (см. [правила документирования кода](#правило-для-документирования-кода)).
|
|
||||||
- [ ] Нет неиспользуемого или невалидного кода.
|
|
||||||
- [ ] Экспорт через индексный файл.
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
---
|
|
||||||
title: CSS
|
|
||||||
---
|
|
||||||
|
|
||||||
# CSS
|
|
||||||
|
|
||||||
## Правила оформления и стилизации CSS-кода
|
|
||||||
|
|
||||||
- **Препроцессоры**
|
|
||||||
Используй PostCSS и модули для стилизации.
|
|
||||||
|
|
||||||
- **Архитектура написания стилей**
|
|
||||||
Используй подход **Mobile First**
|
|
||||||
Используй CSS переменные для стилизации.
|
|
||||||
Используй Custom Media Queries для адаптивных стилей.
|
|
||||||
Используй BEM для именования классов.
|
|
||||||
Между каждым CSS-правилом (селектором) должен быть один пустой сброс строки, пример:
|
|
||||||
```css
|
|
||||||
.todo-list {
|
|
||||||
max-width: 600px;
|
|
||||||
padding: var(--space-3);
|
|
||||||
|
|
||||||
@media (--md) {
|
|
||||||
max-width: 800px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-list__text {
|
|
||||||
font-size: 18px;
|
|
||||||
|
|
||||||
@media (--md) {
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Запрещено писать правила подряд без пустой строки:
|
|
||||||
```css
|
|
||||||
/* Так делать нельзя! */
|
|
||||||
.todo-list { ... }
|
|
||||||
.todo-list__text { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Методология именования классов**
|
|
||||||
Использовать методологию **BEM** для именования классов.
|
|
||||||
- Блок: kebab-case, пример `.user-bar { }`
|
|
||||||
- Елемент: kebab-case, соеденный с блоком двойным нижним подчеркиванием, пример `.user-bar__slide { }`
|
|
||||||
- Модификатор: kebab-case, отдельный самостоятельный класс, **не соединяется** с блоком/елементом, имя модификатора всегда начинается с нижнего подчеркивания, пример: `._red { }`
|
|
||||||
|
|
||||||
- **Единицы измерения**
|
|
||||||
Используй `px` как основная единица измирения, так-же допускается использовать остальные единицы измерения если того требует реализуемый дизайн.
|
|
||||||
|
|
||||||
- **Импорт стилей**
|
|
||||||
Стили компонента должны импортироваться только внутри соответствующего компонента.
|
|
||||||
Запрещено импортировать стили одного компонента в другой.
|
|
||||||
Запрещено импортировать `css переменные` в файлы стилей, они доступны глобально.
|
|
||||||
Запрещено импортировать `custom media` в файлы стилей, они доступны глобально.
|
|
||||||
|
|
||||||
- **Переменные**
|
|
||||||
Все значения переменных нужно писать в `/shared/styles` или в Mantine ThemeProvider.
|
|
||||||
Все что не является цветами, брекпоинтами, отступами, скруглением допускаются использоваться в компонентах.
|
|
||||||
Обязательное создавай CSS перменные для:
|
|
||||||
- "Цветов", пример: `--color-danger: red;`.
|
|
||||||
- "Брекпоинты", описываем в (Сustom media) пример: `@custom-media --md (min-width: 62em);`.
|
|
||||||
- "Отспупы (--space)", , пример: `--space-1: 4px;`, `--space-2: 8px;`, `--space-3: 12px;` итд..
|
|
||||||
- "Скругление углов (--radius)", пример: `--radius-1: 4px;`,`--radius-2: 8px;`,`--radius-3: 12px;` итд..
|
|
||||||
|
|
||||||
- **Вложенность селекторов**
|
|
||||||
Запрещено использовать вложенность селекторов.
|
|
||||||
Разрешено использовать вложенность только для:
|
|
||||||
- Псевдо-классов `:hover`, `:active` итд..
|
|
||||||
- Псевдо-елементов `::before`, `::after`
|
|
||||||
- Медиа запросов `@media`
|
|
||||||
- Классы **модификаторы** по методологии BEM
|
|
||||||
Каждый вложенный селектор отделяется 1 пустой строкой.
|
|
||||||
|
|
||||||
- **Медиа запросы**
|
|
||||||
Строго запрещено использовать `@media` без вложения в селектор.
|
|
||||||
Строго запрещено использовать в теле `@media` любые селекторы.
|
|
||||||
Разрешено использовать только Custom Media Queries (например, `@media (--md) {}`).
|
|
||||||
Запрещено использовать любые произвольные значения breakpoints (например, max-width: 768px).
|
|
||||||
**Пример как правильно писать @media**
|
|
||||||
```css
|
|
||||||
.todo-list {
|
|
||||||
max-width: 600px;
|
|
||||||
padding: 24px;
|
|
||||||
|
|
||||||
@media (--md) {
|
|
||||||
max-width: 800px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-list__text {
|
|
||||||
font-size: 18px;
|
|
||||||
|
|
||||||
@media (--md) {
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
**Пример как неправильно писать @media**
|
|
||||||
```css
|
|
||||||
// Медиа запрос не вложен в селектор
|
|
||||||
@media (--md) {
|
|
||||||
.todo-list {
|
|
||||||
max-width: 600px;
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
.todo-list__text {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Используется стандартный `min-width: 992px` вмето Custom Media Queries
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
// Внутри @media запроса используются селекторы
|
|
||||||
.todo-list {
|
|
||||||
max-width: 600px;
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
.todo-list__text {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Глобальные стили и сбросы**
|
|
||||||
Все глобальные стили (например, сбросы) должны располагаться в отдельном файле, например, `src/app/styles/global.css`.
|
|
||||||
|
|
||||||
- **Использование Mantine и PostCSS**
|
|
||||||
Для стандартных визуальных компонентов (кнопки, инпуты, layout, grid, notifications и т.д.) использовать только Mantine и его ThemeProvider.
|
|
||||||
Запрещено использовать в Mantine компонентах его props/styling, вмето этого нужно добавлять кастомные стили PostCSS.
|
|
||||||
Кастомные стили допускаются только в случае, если требуемый дизайн невозможно реализовать средствами Mantine.
|
|
||||||
При написании кастомных стилей стараться использовать переменные и токены Mantine, если это возможно.
|
|
||||||
|
|
||||||
- **Порядок CSS-свойств**
|
|
||||||
В стилях рекомендуется придерживаться логического порядка свойств:
|
|
||||||
1. Позиционирование (position, top, left, z-index и т.д.)
|
|
||||||
2. Блочная модель (display, width, height, margin, padding и т.д.)
|
|
||||||
3. Оформление (background, border, box-shadow и т.д.)
|
|
||||||
4. Текст (font, color, text-align и т.д.)
|
|
||||||
5. Прочее (transition, animation и т.д.)
|
|
||||||
|
|
||||||
- **Комментарии**
|
|
||||||
В стилях запрещено использовать комментарии.
|
|
||||||
|
|
||||||
- **Дублирования**
|
|
||||||
Не дублировать стили между компонентами. Общие стили выносить в shared/styles или использовать переменные.
|
|
||||||
|
|
||||||
- **Примеры кода стилей**
|
|
||||||
Пример как хорошо:
|
|
||||||
```css
|
|
||||||
/* Блок BEM */
|
|
||||||
.user-bar {
|
|
||||||
display: none;
|
|
||||||
color: black;
|
|
||||||
|
|
||||||
/* Медиа запрос custom media и отделяется 1 пустой строкой */
|
|
||||||
@media (--md) {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Елемент BEM отделяется 1 пустой строкой*/
|
|
||||||
.user-bar__button-next {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
|
|
||||||
/* Псевдо-класс отделяется 1 пустой строкой*/
|
|
||||||
&:hover {
|
|
||||||
background-color: #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Модификатор BEM отделяется 1 пустой строкой*/
|
|
||||||
&._blue {
|
|
||||||
background-color: #2b2bbe;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Модификатор BEM отделяется 1 пустой строкой*/
|
|
||||||
&._green {
|
|
||||||
background-color: #29c53d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Пример как плохо писать:
|
|
||||||
```css
|
|
||||||
.user-bar {
|
|
||||||
display: none;
|
|
||||||
color: black;
|
|
||||||
&__button {
|
|
||||||
&_next {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
&:hover {
|
|
||||||
background-color: #e0e0e0;
|
|
||||||
}
|
|
||||||
&._blue {
|
|
||||||
background-color: #2b2bbe;
|
|
||||||
}
|
|
||||||
&._green {
|
|
||||||
background-color: #29c53d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
.user-bar {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Чек лист для проверки стилизации.**
|
|
||||||
- [ ] Используется PostCSS и CSS-модули для стилизации.
|
|
||||||
- [ ] Применён подход Mobile First.
|
|
||||||
- [ ] Именование классов строго по BEM:
|
|
||||||
- [ ] Модификатор — отдельный класс, начинается с нижнего подчёркивания (например, `._red`, `._active`)
|
|
||||||
- [ ] Все CSS-переменные (цвета, брейкпоинты, отступы, скругления) определены только в `/shared/styles` или через Mantine ThemeProvider.
|
|
||||||
- [ ] Для медиа-запросов используются только custom media переменные из `/shared/styles/media.css`.
|
|
||||||
- [ ] Соблюдается правила вложености селекторов.
|
|
||||||
- [ ] Соблюдается правила отступов селекторов.
|
|
||||||
- [ ] Глобальные стили (reset) вынесены в отдельный файл, остальные стили — модульные.
|
|
||||||
- [ ] Для стандартных UI-элементов используются только компоненты Mantine, кастомные стили — только при необходимости.
|
|
||||||
- [ ] В Mantine-компонентах не используются props/styling для стилизации, только PostCSS.
|
|
||||||
- [ ] Кастомные стили используют переменные и токены Mantine, если это возможно.
|
|
||||||
- [ ] В стилях нет комментариев.
|
|
||||||
- [ ] Стили компонента импортируются только внутри соответствующего компонента.
|
|
||||||
- [ ] Нет импорта стилей одного компонента в другой.
|
|
||||||
- [ ] Нет импорта файлов переменных и custom media — они доступны глобально.
|
|
||||||
- [ ] Нет дублирования стилей между компонентами, общие стили вынесены в shared/styles или используются переменные.
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
---
|
|
||||||
title: Компоненты
|
|
||||||
---
|
|
||||||
|
|
||||||
# Компоненты
|
|
||||||
|
|
||||||
## Правила создания и работы с компонентами.
|
|
||||||
|
|
||||||
### 1. Структура компонента
|
|
||||||
Ассистент при создании/рефакторинге компонента должен **строго** придерживаться следующей структуры файлов и папок:
|
|
||||||
|
|
||||||
```
|
|
||||||
component-name/
|
|
||||||
index.ts
|
|
||||||
component-name.tsx
|
|
||||||
styles/
|
|
||||||
component-name.module.css
|
|
||||||
locales/
|
|
||||||
ru.json
|
|
||||||
en.json
|
|
||||||
types/
|
|
||||||
component-name.interface.ts
|
|
||||||
component-name.type.ts
|
|
||||||
component-name.enum.ts
|
|
||||||
schemas/
|
|
||||||
schema-name.schema.ts
|
|
||||||
utils/
|
|
||||||
util-name.util.ts
|
|
||||||
hooks/
|
|
||||||
use-hook-name.hook.ts
|
|
||||||
stores/
|
|
||||||
store-name.store.ts
|
|
||||||
ui/
|
|
||||||
... # вложенные компоненты для component-name
|
|
||||||
```
|
|
||||||
|
|
||||||
Пояснения к структуре компонента:
|
|
||||||
**Обязательные файлы** обязательны для всех компонентов, даже если они пустые.
|
|
||||||
- component-name/: Папка компонента корень для всего компонента.
|
|
||||||
- index.ts: экспортирует главный компонент, интерфейс и всё, что может быть переиспользовано.
|
|
||||||
- component-name.tsx: главный компонент.
|
|
||||||
- styles/component-name.module.css: стили компонента.
|
|
||||||
- locales/ru.json: локализация на русском языке.
|
|
||||||
- locales/en.json: локализация на английском языке.
|
|
||||||
- types/component-name.interface.ts: интерфейс пропсов компонента.
|
|
||||||
**Не обязательные файлы** добавляются только при необходимости
|
|
||||||
- types/component-name.type.ts: типы компонента.
|
|
||||||
- types/component-name.enum.ts: enum компонента.
|
|
||||||
- schemas/schema-name.schema.ts: схемы валидации.
|
|
||||||
- utils/util-name.util.ts: утилиты компонента.
|
|
||||||
- hooks/use-hook-name.hook.ts: хуки компонента.
|
|
||||||
- stores/store-name.store.ts: хранилища состояния компонента.
|
|
||||||
- ui/: Папка для вложенных компонентов.
|
|
||||||
|
|
||||||
### Требования к компоненту
|
|
||||||
- Использовать `memo()` для всех компонентов, которые принимают пропсы.
|
|
||||||
- Использовать `useMemo` для всех вычислений, которые передаются в пропсы других компонентов.
|
|
||||||
- Использовать `useCallback` для всех функций/методов, которые передаются в пропсы других компонентов.
|
|
||||||
|
|
||||||
### Требования к вложенным компонентам
|
|
||||||
- Вложенный компонент — это полноценный компонент, который обязан полностью соблюдать все правила, описанные для компонентов (структура, именование, документация, типизация, стилизация и т.д.).
|
|
||||||
- Все вложенные компоненты размещаются только в папке ui/ основного компонента.
|
|
||||||
|
|
||||||
**Пояснение**
|
|
||||||
Нет необходимости повторять структуру и требования — вложенный компонент подчиняется тем же правилам, что и любой другой компонент, только располагается в папке ui/ родительского компонента.
|
|
||||||
|
|
||||||
### Требования к локализации
|
|
||||||
- Все добавленные локализации обязательно подключать в экземпляр `app/i18n` (чтобы новые namespace были доступны для i18next).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Чек-лист для создания нового компонента
|
|
||||||
- [ ] Главный компонент размещён в корне и назван по правилу PascalCase.
|
|
||||||
- [ ] Создан файл стилей в папке `styles/`, имя в kebab-case, используется BEM.
|
|
||||||
- [ ] Все классы применяются через `className={styles['component-name']}`.
|
|
||||||
- [ ] Создана папка `locales/` с файлами `ru.json` и `en.json`.
|
|
||||||
- [ ] Создан файл интерфейса пропсов в папке `types/`, даже если интерфейс пустой.
|
|
||||||
- [ ] Создан файл `index.ts` с экспортом главного компонента и интерфейса.
|
|
||||||
- [ ] Внутренние компоненты (если есть) размещены в папке `ui/`.
|
|
||||||
- [ ] Все важные части кода документированы по TSDoc (см. раздел 16).
|
|
||||||
- [ ] Остальные файлы (schemas, дополнительные типы, enum) добавлены только при необходимости.
|
|
||||||
- [ ] Именование файлов и папок соответствует правилам (см. выше).
|
|
||||||
- [ ] Нет неиспользуемого или невалидного кода.
|
|
||||||
- [ ] Для компонентов с пропсами используется `React.memo`.
|
|
||||||
- [ ] Для вычислений, передаваемых в пропсы, используется `useMemo`.
|
|
||||||
- [ ] Для функций, передаваемых в пропсы, используется `useCallback`.
|
|
||||||
- [ ] Все тексты вынесены в локализационные файлы и используются через i18n.
|
|
||||||
- [ ] Новые namespace подключены в экземпляр i18n.
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
# Хуки (React Hooks)
|
|
||||||
|
|
||||||
> В проекте для создания пользовательских хуков используется только React (функциональные компоненты и хуки).
|
|
||||||
> В этом разделе собраны основные правила и рекомендации по созданию и оформлению хуков. Следуйте этим принципам, чтобы обеспечить чистую архитектуру, переиспользуемость и единый стиль работы с хуками в проекте.
|
|
||||||
|
|
||||||
## Рекомендации по использованию сторонних хуков
|
|
||||||
- Если есть возможность, используйте хуки Mantine в компонентах и кастомных хуках для работы с состоянием, темизацией, медиа-запросами и другими возможностями библиотеки.
|
|
||||||
- Не дублируйте функциональность, уже реализованную в Mantine.
|
|
||||||
|
|
||||||
## Структура
|
|
||||||
- Каждый хук размещается в отдельном файле с именем `use-<hook-name>.hook.ts` в папке `hooks/` на своём уровне абстракции согласно архитектуре проекта.
|
|
||||||
- Имя хука — в стиле camelCase с префиксом `use` (например, `useTodoFilter`).
|
|
||||||
- Для сложных возвращаемых структур использовать отдельные типы или интерфейсы, размещая их в папке `types/` на своём уровне абстракции.
|
|
||||||
|
|
||||||
## Именование
|
|
||||||
- Соблюдайте [правила именования файлов и папок](#правила-именования-файлов-и-папок):
|
|
||||||
- Файл хука — `use-<hook-name>.hook.ts` (kebab-case).
|
|
||||||
- Имя хука — camelCase с префиксом `use`.
|
|
||||||
|
|
||||||
## Требования
|
|
||||||
- Хук должен быть строго типизирован: все параметры, возвращаемые значения и внутренние переменные должны иметь явные типы.
|
|
||||||
- Не хранить бизнес-логику, связанную с несколькими слоями — хук должен быть изолирован в рамках своего слоя/feature.
|
|
||||||
- Не дублировать логику между хуками — общие части выносить в shared.
|
|
||||||
- Не использовать side-effects вне useEffect/useLayoutEffect.
|
|
||||||
- Для мемоизации возвращаемых значений и функций использовать useMemo и useCallback.
|
|
||||||
- Не использовать устаревшие или неразрешённые паттерны React.
|
|
||||||
|
|
||||||
## Типизация
|
|
||||||
- Всегда явно указывать типы для всех параметров, возвращаемых значений и состояния внутри хука.
|
|
||||||
- Не используйте неявное приведение типов и не полагайтесь на автоматический вывод, если это может снизить читаемость или безопасность.
|
|
||||||
|
|
||||||
## Документирование
|
|
||||||
- Документируйте только назначение хука (описание), строго по [правилам документирования кода](#правило-для-документирования-кода).
|
|
||||||
|
|
||||||
## Экспорт
|
|
||||||
- Экспортируйте хук только именованным экспортом через `index.ts` слоя/компонента.
|
|
||||||
|
|
||||||
## Примеры
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { TodoItem } from '../types/todo-item.interface';
|
|
||||||
import { TodoStatus } from '../types/todo-status.enum';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Хук фильтрации задач по статусу.
|
|
||||||
*/
|
|
||||||
export const useTodoFilter = (items: TodoItem[], filter: TodoStatus): TodoItem[] => {
|
|
||||||
return useMemo(() => {
|
|
||||||
if (filter === TodoStatus.ALL) return items;
|
|
||||||
if (filter === TodoStatus.ACTIVE) return items.filter((t) => !t.completed);
|
|
||||||
return items.filter((t) => t.completed);
|
|
||||||
}, [items, filter]);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Чек-лист
|
|
||||||
|
|
||||||
- [ ] Хук размещён в файле `use-<hook-name>.hook.ts` в папке `hooks/` на своём уровне абстракции согласно архитектуре проекта.
|
|
||||||
- [ ] Именование файлов и сущностей соответствует [правилам именования файлов и папок](#правила-именования-файлов-и-папок).
|
|
||||||
- [ ] Все параметры, возвращаемые значения и внутренние переменные строго типизированы.
|
|
||||||
- [ ] Вся бизнес-логика изолирована в рамках слоя/feature.
|
|
||||||
- [ ] Нет дублирования логики между хуками.
|
|
||||||
- [ ] Для мемоизации используется useMemo/useCallback.
|
|
||||||
- [ ] Не используются side-effects вне useEffect/useLayoutEffect.
|
|
||||||
- [ ] Документировано только назначение хука (см. [правила документирования кода](#правило-для-документирования-кода)).
|
|
||||||
- [ ] Нет неиспользуемого или невалидного кода.
|
|
||||||
- [ ] Экспорт только именованный через индексный файл.
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
# Хуки API (React Hooks)
|
|
||||||
|
|
||||||
> В проекте для работы с API-хуками используется только React и библиотека SWR для получения данных (GET-запросы).
|
|
||||||
> В этом разделе собраны основные правила и рекомендации по созданию и оформлению хуков для работы с API. Следуйте этим принципам, чтобы обеспечить чистую архитектуру, переиспользуемость и единый стиль работы с API-хуками в проекте.
|
|
||||||
|
|
||||||
## Описание и назначение API-хуков
|
|
||||||
|
|
||||||
API-хуки предназначены для получения данных с сервера (GET-запросы) и используются в компонентах или других хуках.
|
|
||||||
В проекте для этого применяется библиотека SWR, которая обеспечивает кэширование, автоматическое обновление и удобную работу с асинхронными запросами.
|
|
||||||
|
|
||||||
**Fetcher** — это функция, которую использует SWR для выполнения запроса к API. В проекте fetcher обычно экспортируется из файла клиента (например, `backendFetcher` из `shared/api/backend/client.ts`) и инкапсулирует логику обращения к конкретному API-клиенту.
|
|
||||||
|
|
||||||
**API-клиент** — это отдельный модуль (папка), отвечающий за взаимодействие с конкретным внешним или внутренним API.
|
|
||||||
API-клиент включает:
|
|
||||||
- инициализацию экземпляра HTTP-клиента (например, Axios),
|
|
||||||
- настройку базового URL, интерцепторов и общих обработчиков ошибок,
|
|
||||||
- организацию всех сущностей и методов для работы с этим API (например, users, auth, orders и т.д.),
|
|
||||||
- экспорт всех функций, типов и fetcher через индексные файлы.
|
|
||||||
|
|
||||||
Каждый API-клиент размещается в папке `src/shared/api/<client-name>/` и имеет собственную структуру согласно архитектуре проекта.
|
|
||||||
|
|
||||||
## Структура
|
|
||||||
- Каждый API-хук размещается в отдельном файле с именем `use-<method-name>.hook-api.ts` в папке `hooks/api/<client-name>/` на своём уровне абстракции согласно архитектуре проекта.
|
|
||||||
- Имя хука — в стиле camelCase с префиксом `use` (например, `useGetUser`).
|
|
||||||
- Для сложных возвращаемых структур используйте отдельные типы или интерфейсы, размещая их в папке `types/` на своём уровне абстракции.
|
|
||||||
|
|
||||||
## Именование
|
|
||||||
- Соблюдайте [правила именования файлов и папок](#правила-именования-файлов-и-папок):
|
|
||||||
- Файл хука — `use-<method-name>.hook-api.ts` (kebab-case).
|
|
||||||
- Имя хука — camelCase с префиксом `use`.
|
|
||||||
|
|
||||||
## Требования
|
|
||||||
- Хук должен быть строго типизирован: все параметры, возвращаемые значения и внутренние переменные должны иметь явные типы.
|
|
||||||
- Для получения данных используйте только SWR.
|
|
||||||
- Не дублируйте логику между хуками — общие части выносите в shared.
|
|
||||||
- Не используйте side-effects вне useEffect/useLayoutEffect.
|
|
||||||
|
|
||||||
## Типизация
|
|
||||||
- Всегда явно указывайте типы для всех параметров, возвращаемых значений и состояния внутри хука.
|
|
||||||
- Придерживайтесь [общих правил типизации проекта](#общие-правила-типизации).
|
|
||||||
- Не используйте неявное приведение типов и не полагайтесь на автоматический вывод, если это может снизить читаемость или безопасность.
|
|
||||||
|
|
||||||
## Документирование
|
|
||||||
- Документируйте только назначение хука (описание), строго по [правилам документирования кода](#правило-для-документирования-кода).
|
|
||||||
|
|
||||||
## Экспорт
|
|
||||||
- Экспортируйте хук только именованным экспортом через `index.ts` слоя/компонента.
|
|
||||||
|
|
||||||
## Пример API хука
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// use-get-me.hook-api.ts
|
|
||||||
import useSWR from 'swr';
|
|
||||||
import { backendFetcher } from 'shared/api/backend/client';
|
|
||||||
import { UserDto } from 'shared/api/backend/entities/users/get-me.api';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Хук для получения информации о текущем пользователе.
|
|
||||||
*/
|
|
||||||
export const useGetMe = () => {
|
|
||||||
const { data, error, isLoading } = useSWR<UserDto>('/users/me', backendFetcher);
|
|
||||||
|
|
||||||
return {
|
|
||||||
data,
|
|
||||||
error,
|
|
||||||
isLoading,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Пример использования хука в компоненте
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import React from 'react';
|
|
||||||
import { useGetMe } from 'shared/hooks/api/backend/use-get-me.hook-api';
|
|
||||||
|
|
||||||
export const UserInfo: React.FC = () => {
|
|
||||||
const { data, error, isLoading } = useGetMe();
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <div>Загрузка...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return <div>Ошибка загрузки данных</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return <div>Нет данных о пользователе</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div>Имя: {data.name}</div>
|
|
||||||
<div>Email: {data.email}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Чек-лист для создания API-хука
|
|
||||||
|
|
||||||
- [ ] Для каждого GET-запроса создан отдельный хук.
|
|
||||||
- [ ] Хук размещён в `hooks/api/<client-name>/use-<method-name>.hook-api.ts` на своём уровне абстракции согласно архитектуре проекта.
|
|
||||||
- [ ] Именование файлов и сущностей соответствует [правилам именования файлов и папок](#правила-именования-файлов-и-папок).
|
|
||||||
- [ ] Используется SWR для получения данных.
|
|
||||||
- [ ] Все параметры, возвращаемые значения и внутренние переменные строго типизированы.
|
|
||||||
- [ ] Нет дублирования логики между хуками.
|
|
||||||
- [ ] Не используются side-effects вне useEffect/useLayoutEffect.
|
|
||||||
- [ ] Документировано только назначение хука (см. [правила документирования кода](#правило-для-документирования-кода)).
|
|
||||||
- [ ] Нет неиспользуемого или невалидного кода.
|
|
||||||
- [ ] Экспорт только именованный через индексный файл.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Чек-лист для использования API-хука
|
|
||||||
|
|
||||||
- [ ] Импортируется только нужный хук через публичные экспорты (`index.ts`).
|
|
||||||
- [ ] Использование хука строго по назначению (только для получения данных).
|
|
||||||
- [ ] Если требуется получить данные через GET-запрос в компоненте — обязательно используется соответствующий API-хук.
|
|
||||||
**Запрещено вызывать GET-методы API напрямую в компонентах, только через хуки.**
|
|
||||||
- [ ] Обработка состояний загрузки, ошибки и данных реализована корректно.
|
|
||||||
- [ ] Не происходит дублирования логики, связанной с получением данных.
|
|
||||||
- [ ] Нет неиспользуемого или невалидного кода.
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
# API
|
|
||||||
|
|
||||||
> В этом разделе собраны основные правила и рекомендации по созданию, оформлению и использованию API-клиентов и функций для работы с сервером. Следуйте этим принципам, чтобы обеспечить единый стиль, безопасность и удобство поддержки API-слоя в проекте.
|
|
||||||
|
|
||||||
## Описание и назначение API-клиента
|
|
||||||
|
|
||||||
API-клиент — это модуль (папка), отвечающий за взаимодействие с конкретным внешним или внутренним API.
|
|
||||||
В проекте для HTTP-запросов используется только Axios.
|
|
||||||
API-клиент инкапсулирует:
|
|
||||||
- инициализацию экземпляра Axios,
|
|
||||||
- настройку базового URL, интерцепторов, обработчиков ошибок,
|
|
||||||
- организацию всех сущностей и методов для работы с этим API (например, users, auth, orders и т.д.),
|
|
||||||
- экспорт всех функций, типов и fetcher через индексные файлы.
|
|
||||||
|
|
||||||
Каждый API-клиент размещается в папке `src/shared/api/<client-name>/` и имеет собственную структуру согласно архитектуре проекта.
|
|
||||||
|
|
||||||
|
|
||||||
## Использование методов API
|
|
||||||
|
|
||||||
- Все методы API должны использоваться строго внутри блока `try...catch`.
|
|
||||||
- При вызове методов API всегда используйте полный путь, например:
|
|
||||||
`await api.backend.createUser({ email, password });`
|
|
||||||
- Запрещено вызывать методы API вне блока `try...catch` даже в тестах, утилитах и других вспомогательных функциях.
|
|
||||||
|
|
||||||
|
|
||||||
## Структура клиента
|
|
||||||
```text
|
|
||||||
src/shared/api/backend/
|
|
||||||
│
|
|
||||||
├── client.ts
|
|
||||||
├── index.ts
|
|
||||||
└── entities/
|
|
||||||
├── users/
|
|
||||||
│ ├── get-me.api.ts
|
|
||||||
│ ├── create-user.api.ts
|
|
||||||
│ ├── update-user.api.ts
|
|
||||||
│ └── index.ts
|
|
||||||
├── auth/
|
|
||||||
│ ├── login.api.ts
|
|
||||||
│ ├── register.api.ts
|
|
||||||
│ └── index.ts
|
|
||||||
└── index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Описание ключевых элементов
|
|
||||||
|
|
||||||
- **client.ts**
|
|
||||||
Экземпляр Axios с настройками, интерцепторами, экспортом fetcher для SWR.
|
|
||||||
|
|
||||||
- **index.ts**
|
|
||||||
Главная точка экспорта: экспортирует client, fetcher, все сущности и их методы.
|
|
||||||
|
|
||||||
- **entities/**
|
|
||||||
Папка для бизнес-сущностей (например, users, auth, orders и т.д.).
|
|
||||||
|
|
||||||
- **`<entity>/`**
|
|
||||||
Папка для отдельной сущности. Имя — в kebab-case, отражает бизнес-область (например, users, auth).
|
|
||||||
|
|
||||||
- **`<operation>.api.ts`**
|
|
||||||
Файл для каждой операции (CRUD, спец. действия).
|
|
||||||
Внутри:
|
|
||||||
- DTO (интерфейсы запроса/ответа)
|
|
||||||
- Функция, реализующая запрос через client
|
|
||||||
|
|
||||||
- **index.ts (внутри `<entity>`/)**
|
|
||||||
Экспортирует все методы и типы этой сущности.
|
|
||||||
|
|
||||||
- **index.ts (внутри entities/)**
|
|
||||||
Экспортирует все сущности (users, auth и т.д.).
|
|
||||||
|
|
||||||
|
|
||||||
## Именование
|
|
||||||
|
|
||||||
- Соблюдайте [правила именования файлов и папок](#правила-именования-файлов-и-папок):
|
|
||||||
- Файл клиента — `client.ts`.
|
|
||||||
- Файл функции — `<operation>-<entity>.api.ts` (например, `create-user.api.ts`).
|
|
||||||
- DTO — в папке `dto/` (например, `create-user.dto.ts`).
|
|
||||||
- Все функции и типы экспортируются через индексные файлы на каждом уровне (сущность, entities, клиент).
|
|
||||||
|
|
||||||
|
|
||||||
## Требования
|
|
||||||
|
|
||||||
- Для каждого действия (CRUD, спец. действия) — отдельная функция и файл.
|
|
||||||
- Все функции используют общий экземпляр Axios из `client.ts`.
|
|
||||||
- Все функции строго типизированы (используются DTO).
|
|
||||||
- DTO объявляется в отдельном файле в папке `dto/` перед функцией, которая его использует.
|
|
||||||
- Для каждого GET метода обязательно должен быть создан API-хук.
|
|
||||||
- Все API-хуки должны создаваться строго по [документации раздела "Хуки для API"](#хуки-для-api-api-hooks).
|
|
||||||
|
|
||||||
|
|
||||||
## Типизация
|
|
||||||
|
|
||||||
- Все функции и DTO строго типизированы.
|
|
||||||
- Все интерфейсы, типы и enum размещены в папке `types/` на своём уровне абстракции.
|
|
||||||
- Все DTO размещены в папке `dto/` на своём уровне абстракции.
|
|
||||||
- Придерживайтесь [общих правил типизации проекта](#общие-правила-типизации).
|
|
||||||
|
|
||||||
|
|
||||||
## Документирование
|
|
||||||
|
|
||||||
- Документируйте только назначение функций и DTO.
|
|
||||||
- В описании указывается только смысл функции/типа.
|
|
||||||
|
|
||||||
|
|
||||||
## Экспорт
|
|
||||||
|
|
||||||
- Все функции и типы экспортируются через индексные файлы на каждом уровне (сущность, entities, клиент).
|
|
||||||
|
|
||||||
|
|
||||||
## Примеры
|
|
||||||
|
|
||||||
### Пример клиента API
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// client.ts
|
|
||||||
import axios, { AxiosInstance } from "axios";
|
|
||||||
export { AxiosError, isAxiosError } from 'axios';
|
|
||||||
export type { AxiosResponse } from 'axios';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Экземпляр HTTP-клиента для работы с backend API.
|
|
||||||
*/
|
|
||||||
export const backendHttpClient: AxiosInstance = axios.create({
|
|
||||||
baseURL: '/api',
|
|
||||||
timeout: 10000,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Интерцептор запроса
|
|
||||||
backendHttpClient.interceptors.request.use(
|
|
||||||
(config) => {
|
|
||||||
// Здесь можно добавить авторизационные заголовки или другую логику
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
(error) => Promise.reject(error)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Интерцептор ответа
|
|
||||||
backendHttpClient.interceptors.response.use(
|
|
||||||
(response) => response,
|
|
||||||
(error) => {
|
|
||||||
// Здесь можно обработать ошибки (например, показать уведомление)
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Пример DTO
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// dto/create-user.dto.ts
|
|
||||||
/**
|
|
||||||
* DTO для создания пользователя.
|
|
||||||
*/
|
|
||||||
export interface CreateUserDto {
|
|
||||||
/** Email пользователя. */
|
|
||||||
email: string;
|
|
||||||
/** Пароль пользователя. */
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Пример API-функции
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// create-user.api.ts
|
|
||||||
import { backendHttpClient } from '../client';
|
|
||||||
import { CreateUserDto } from './dto/create-user.dto';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Создать пользователя.
|
|
||||||
*/
|
|
||||||
export const createUser = (data: CreateUserDto) => backendHttpClient.post('/users', data);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Пример index.ts (в папке сущности)
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export * from './create-user.api';
|
|
||||||
export * from './get-user.api';
|
|
||||||
```
|
|
||||||
|
|
||||||
### Пример использования API-функции в компоненте
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
import { api } from 'shared/api';
|
|
||||||
|
|
||||||
export const CreateUserForm: React.FC = () => {
|
|
||||||
const [email, setEmail] = useState('');
|
|
||||||
const [password, setPassword] = useState('');
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
try {
|
|
||||||
await api.backend.createUser({ email, password });
|
|
||||||
console.log('Пользователь создан!');
|
|
||||||
} catch {
|
|
||||||
console.log('Ошибка создания пользователя');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
value={email}
|
|
||||||
onChange={e => setEmail(e.target.value)}
|
|
||||||
placeholder="Email"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
value={password}
|
|
||||||
onChange={e => setPassword(e.target.value)}
|
|
||||||
placeholder="Пароль"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<button type="submit">Создать пользователя</button>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Чек-лист для создания клиента
|
|
||||||
- [ ] Новый клиент размещён в `src/shared/api/<client-name>/`.
|
|
||||||
- [ ] В корне клиента есть client.ts (экземпляр Axios) и index.ts (главный экспорт).
|
|
||||||
- [ ] Все бизнес-сущности размещены в entities/, каждая — в отдельной папке.
|
|
||||||
- [ ] Для каждой операции создан отдельный файл `<operation>`.api.ts с DTO и функцией.
|
|
||||||
- [ ] DTO объявлен непосредственно перед функцией.
|
|
||||||
- [ ] В каждой папке сущности есть свой index.ts для экспорта методов и типов.
|
|
||||||
- [ ] В папке entities/ есть общий index.ts для экспорта всех сущностей.
|
|
||||||
- [ ] Все экспорты организованы через индексные файлы.
|
|
||||||
- [ ] Для каждого GET-метода создан отдельный SWR-хук (см. правила API-хуков).
|
|
||||||
- [ ] Нет дублирования кода и неиспользуемых файлов.
|
|
||||||
|
|
||||||
## Чек-лист для использования API
|
|
||||||
- [ ] Импортируется только нужный метод через публичные экспорты (index.ts).
|
|
||||||
- [ ] Все вызовы API обёрнуты в try...catch.
|
|
||||||
- [ ] Используются только строго типизированные методы.
|
|
||||||
- [ ] Не происходит обращения к Axios напрямую — только через client.
|
|
||||||
- [ ] Нет дублирования логики и неиспользуемого кода.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
---
|
|
||||||
title: Общие принципы
|
|
||||||
---
|
|
||||||
|
|
||||||
# Общие принципы
|
|
||||||
|
|
||||||
## Стек технологий и библиотеки
|
|
||||||
- Использовать **TypeScript** для всех файлов логики и компонентов.
|
|
||||||
- Использовать **FSD (Feature-Sliced Design)**: разделять код на features, entities, processes, widgets, shared.
|
|
||||||
- Использовать **React** (функциональные компоненты, хуки).
|
|
||||||
- Использовать **Mantine UI** для UI-компонентов.
|
|
||||||
- Использовать **Axios** в качестве клиента для работы с API.
|
|
||||||
- Использовать **SWR** для data fetching (GET-запросы).
|
|
||||||
- Использовать **Zustand** для глобального состояния.
|
|
||||||
- Использовать **i18n** для локализации.
|
|
||||||
- Использовать **Vitest** для тестирования.
|
|
||||||
- Использовать **PostCSS модули** для стилизации.
|
|
||||||
- Использовать **BEM** для именований классов в стилях
|
|
||||||
- Использовать **Mobile First** подход для написания стилей.
|
|
||||||
- Использовать **Context7** примеров использования библиотек.
|
|
||||||
- Использовать **i18n** (i18next) для локализации всех пользовательских текстов.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
title: Архитектура
|
|
||||||
---
|
|
||||||
|
|
||||||
# Архитектура
|
|
||||||
|
|
||||||
## Архитектура проекта
|
|
||||||
В проекте используется FSD (Feature-Sliced Design) архитектура.
|
|
||||||
|
|
||||||
- **FSD-границы**
|
|
||||||
- Не нарушать границы слоёв (например, feature не может импортировать из widgets).
|
|
||||||
- Бизнес-логика должна быть вынесена в хуки или сервисы.
|
|
||||||
- **Импорты**
|
|
||||||
- Внутри слоя — относительные импорты.
|
|
||||||
- Между слоями — абсолютные импорты.
|
|
||||||
- **Требования**
|
|
||||||
- Не смешивать логику разных слоёв.
|
|
||||||
- Не хранить бизнес-логику в UI-компонентах.
|
|
||||||
- **Именование**
|
|
||||||
- Файлы и папки kebab-case.
|
|
||||||
|
|
||||||
---
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
---
|
|
||||||
title: Стиль кода
|
|
||||||
---
|
|
||||||
|
|
||||||
# Стиль кода
|
|
||||||
|
|
||||||
## Отступы
|
|
||||||
|
|
||||||
Используем 2 пробела для отступов во всём проекте. Не используем табы.
|
|
||||||
|
|
||||||
|
|
||||||
## Кавычки
|
|
||||||
|
|
||||||
Используем **одинарные кавычки** для строк в JavaScript/TypeScript, и **двойные кавычки** для атрибутов в JSX/TSX.
|
|
||||||
|
|
||||||
**Пример:**
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// JavaScript/TypeScript
|
|
||||||
const message = 'Привет, мир!';
|
|
||||||
const name = 'ProjectName';
|
|
||||||
```
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
// JSX/TSX
|
|
||||||
<input type="text" placeholder="Введите имя" />
|
|
||||||
<button title="Сохранить">Сохранить</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Строгая типизация
|
|
||||||
|
|
||||||
всегда указывать типы для пропсов, возвращаемых значений, параметров функций.
|
|
||||||
|
|
||||||
## Ранние возвраты
|
|
||||||
|
|
||||||
(early return) для повышения читаемости.
|
|
||||||
|
|
||||||
## Мемоизация
|
|
||||||
|
|
||||||
Старайся оптимизировать код если это возможно.
|
|
||||||
|
|
||||||
## Документирование
|
|
||||||
|
|
||||||
Документируем ТОЛЬКО ОПИСАНИЕ (функций, компонентов, типов и их полей).
|
|
||||||
|
|
||||||
## any, unknown
|
|
||||||
|
|
||||||
запрещено использовать без крайней необходимости.
|
|
||||||
|
|
||||||
## Классы в TSX
|
|
||||||
|
|
||||||
Для стилизации компонентов используем CSS-модули и методологию BEM. Классы подключаются через объект стилей, импортированный из соответствующего `.module.css` файла.
|
|
||||||
|
|
||||||
> Объект стилей всегда импортируется с именем `s` (сокращённо от style), а не `styles`.
|
|
||||||
|
|
||||||
**Пример:**
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import s from './my-component.module.css';
|
|
||||||
|
|
||||||
export const MyComponent = () => (
|
|
||||||
<div className={s['my-component']}>
|
|
||||||
<button className={s['my-component__button']}>Кнопка</button>
|
|
||||||
<span className={s['my-component__text']}>Текст</span>
|
|
||||||
<button className={s['my-component__button'] + ' ' + s._active}>
|
|
||||||
Активная кнопка
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
- Имя класса всегда берётся из объекта `s`.
|
|
||||||
- Для модификаторов используется отдельный класс с нижним подчёркиванием (например, `s._active`).
|
|
||||||
- Не используйте строковые литералы с классами напрямую — только через объект `s`.
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
title: Именование
|
|
||||||
---
|
|
||||||
|
|
||||||
# Именование
|
|
||||||
|
|
||||||
## Именование файлов и папок
|
|
||||||
- Папка компонента: kebab-case, совпадает с названием компонента, пример: `component-name`.
|
|
||||||
- React-компонент: kebab-case, совпадает с названием компонента, пример: `component-name.tsx`.
|
|
||||||
- Стили: kebab-case, шаблон: `<style-name>.module.css`, пример: `style-name.module.css`.
|
|
||||||
- Интерфейсы: kebab-case, шаблон: `<interface-name>.interface.ts`, пример: `interface-name.interface.ts`.
|
|
||||||
- Типы: kebab-case, шаблон: `<type-name>.type.ts`, пример: `type-name.type.ts`.
|
|
||||||
- Enum: kebab-case, шаблон: `<enum-name>.enum.ts`, пример: `enum-name.enum.ts`.
|
|
||||||
- Схемы: kebab-case, шаблон: `<schema-name>.schema.ts`, пример: `schema-name.schema.ts`.
|
|
||||||
- Локализация: kebab-case, пример: `ru.json`, `en.json`.
|
|
||||||
- Утилиты: kebab-case, шаблон: `<util-name>.util.ts`, пример: `util-name.util.ts`
|
|
||||||
- React Hooks: kebab-case, шаблон: `use-<hook-name>.hook.ts`, пример: `use-hook-name.hook.ts`
|
|
||||||
- Хранилища состояния компонента: kebab-case, шаблон: `<store-name>.store.ts`, пример: `store-name.store.ts`
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
---
|
|
||||||
title: Документирование
|
|
||||||
---
|
|
||||||
|
|
||||||
# Документирование
|
|
||||||
|
|
||||||
## Правило для документирования кода
|
|
||||||
|
|
||||||
- Документировать разрешено только описание (назначение) функций, компонентов, типов, интерфейсов, enum и их полей.
|
|
||||||
- Строго запрещено документировать параметры, возвращаемые значения, типы пропсов, аргументы, возвращаемые значения функций, компоненты, хуки и т.д.
|
|
||||||
- В интерфейсах, типах и enum разрешено документировать только смысл (описание) каждого поля или значения.
|
|
||||||
- В React-компонентах, функциях, хранилищах, схемах, утилитах разрешено документировать только назначение (описание), без детализации параметров и возвращаемых значений.
|
|
||||||
- Описание должно быть кратким, информативным и реально помогать понять структуру и бизнес-логику.
|
|
||||||
- Не допускается избыточная или дублирующая очевидное документация.
|
|
||||||
- В конце описания всегда ставить точку.
|
|
||||||
|
|
||||||
**Примеры правильного документирования**
|
|
||||||
```tsx
|
|
||||||
/**
|
|
||||||
* Список задач пользователя.
|
|
||||||
*/
|
|
||||||
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 объект задачи
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ❌ Не нужно:/
|
|
||||||
/**
|
|
||||||
* @param props - пропсы компонента
|
|
||||||
* @returns JSX.Element
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ❌ Не нужно:/
|
|
||||||
/**
|
|
||||||
* id — идентификатор задачи
|
|
||||||
* text — текст задачи
|
|
||||||
* completed — статус выполнения
|
|
||||||
*/
|
|
||||||
```
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
---
|
|
||||||
title: Типизация
|
|
||||||
---
|
|
||||||
|
|
||||||
# Типизация
|
|
||||||
|
|
||||||
## Общие правила типизации
|
|
||||||
|
|
||||||
> Данный раздел определяет единые требования к типизации для всего проекта. Соблюдение этих правил обеспечивает читаемость, предсказуемость и безопасность кода.
|
|
||||||
|
|
||||||
- Использовать только строгую типизацию TypeScript для всех файлов логики, компонентов, хуков, API, сторов и утилит.
|
|
||||||
- Всегда явно указывать типы для:
|
|
||||||
- Пропсов компонентов
|
|
||||||
- Параметров функций и методов
|
|
||||||
- Возвращаемых значений функций и методов
|
|
||||||
- Всех переменных состояния (в том числе в store)
|
|
||||||
- Всех значимых переменных и констант, если их тип не очевиден из присваивания
|
|
||||||
- Не использовать `any` и `unknown` без крайней необходимости. Если использование неизбежно — обязательно добавить комментарий с обоснованием.
|
|
||||||
- Все интерфейсы, типы и enum всегда размещать в папке `types/` на своём уровне абстракции (например, `features/todo/types/`).
|
|
||||||
- Для DTO всегда использовать отдельную папку `dto/` на уровне сущности или слоя.
|
|
||||||
- Для сложных структур использовать отдельные интерфейсы или типы, размещая их в соответствующих файлах в папке `types/`.
|
|
||||||
- Для DTO, enum, схем и других сущностей — всегда создавать отдельные типы/интерфейсы с осмысленными именами.
|
|
||||||
- Ключи enum всегда писать ЗАГЛАВНЫМИ_БУКВАМИ (SCREAMING_SNAKE_CASE).
|
|
||||||
- Не использовать неявное приведение типов и не полагаться на автоматический вывод, если это может снизить читаемость или безопасность.
|
|
||||||
- Для массивов и объектов всегда указывать тип элементов/ключей.
|
|
||||||
- Для возвращаемых значений асинхронных функций всегда указывать тип Promise.
|
|
||||||
- Типизацию коллбеков и функций, передаваемых в пропсы, указывать инлайн, не выносить в отдельные типы.
|
|
||||||
- Для типизации внешних библиотек использовать официальные типы или создавать собственные декларации при необходимости.
|
|
||||||
- Не использовать устаревшие или не рекомендуемые паттерны типизации (например, `Function`, `Object`, `{}`).
|
|
||||||
---
|
|
||||||
|
|
||||||
### Примеры
|
|
||||||
|
|
||||||
#### Интерфейс и типы для сущностей (всегда в папке types/)
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// features/todo/types/todo-item.interface.ts
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Интерфейс задачи.
|
|
||||||
*/
|
|
||||||
export interface TodoItem {
|
|
||||||
/** Уникальный идентификатор задачи. */
|
|
||||||
id: string;
|
|
||||||
/** Текст задачи. */
|
|
||||||
text: string;
|
|
||||||
/** Статус выполнения задачи. */
|
|
||||||
completed: boolean;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Типизация enum (всегда в папке types/)
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// features/todo/types/todo-status.enum.ts
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Перечисление статусов задачи.
|
|
||||||
*/
|
|
||||||
export enum TodoStatus {
|
|
||||||
/** Активная задача. */
|
|
||||||
ACTIVE = 'active',
|
|
||||||
/** Выполненная задача. */
|
|
||||||
COMPLETED = 'completed',
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Типизация пропсов компонента
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { FC, memo } from 'react';
|
|
||||||
import { TodoItem } from './types/todo-item.interface';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Список задач.
|
|
||||||
*/
|
|
||||||
export interface TodoListProps {
|
|
||||||
/** Массив задач. */
|
|
||||||
items: TodoItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TodoList: FC<TodoListProps> = memo(({ items }) => (
|
|
||||||
<ul>
|
|
||||||
{items.map((item) => (
|
|
||||||
<li key={item.id}>{item.text}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
));
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Типизация функций и коллбеков (инлайн)
|
|
||||||
|
|
||||||
```ts
|
|
||||||
/**
|
|
||||||
* Функция фильтрации задач.
|
|
||||||
*/
|
|
||||||
export const getCompletedTodos = (items: TodoItem[]): TodoItem[] => {
|
|
||||||
return items.filter((t) => t.completed);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Колбэк для обработки клика (инлайн).
|
|
||||||
*/
|
|
||||||
const handleClick = (id: string): void => {
|
|
||||||
console.log('Clicked:', id);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Типизация асинхронных функций
|
|
||||||
|
|
||||||
```ts
|
|
||||||
/**
|
|
||||||
* Получить задачи с сервера.
|
|
||||||
*/
|
|
||||||
export const fetchTodos = async (): Promise<TodoItem[]> => {
|
|
||||||
const response = await fetch('/api/todos');
|
|
||||||
return response.json();
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Типизация состояния в store (интерфейс в types/)
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// features/todo/types/todo-store.interface.ts
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Состояние хранилища задач.
|
|
||||||
*/
|
|
||||||
export interface TodoStoreState {
|
|
||||||
/** Массив задач. */
|
|
||||||
items: TodoItem[];
|
|
||||||
/** Добавить задачу. */
|
|
||||||
addTodo: (item: TodoItem) => void;
|
|
||||||
/** Удалить задачу. */
|
|
||||||
removeTodo: (id: string) => void;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Типизация DTO (всегда в папке dto/)
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// features/todo/dto/create-todo.dto.ts
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DTO для создания задачи.
|
|
||||||
*/
|
|
||||||
export interface CreateTodoDto {
|
|
||||||
/** Текст задачи. */
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// features/todo/dto/todo-response.dto.ts
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DTO ответа сервера.
|
|
||||||
*/
|
|
||||||
export interface TodoResponseDto {
|
|
||||||
/** Созданная задача. */
|
|
||||||
todo: TodoItem;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Типизация внешних библиотек
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import type { AxiosResponse } from 'axios';
|
|
||||||
|
|
||||||
export const getData = async (): Promise<AxiosResponse<TodoItem[]>> => {
|
|
||||||
// ...
|
|
||||||
};
|
|
||||||
```
|
|
||||||
### Чек-лист проверки типизации
|
|
||||||
|
|
||||||
- [ ] Все пропсы компонентов явно типизированы через интерфейс или тип в папке `types/`.
|
|
||||||
- [ ] Все параметры и возвращаемые значения функций и методов явно типизированы.
|
|
||||||
- [ ] Все переменные состояния (в том числе в store) имеют явные типы.
|
|
||||||
- [ ] Все интерфейсы, типы и enum размещены в папке `types/` на своём уровне абстракции.
|
|
||||||
- [ ] Ключи всех enum написаны ЗАГЛАВНЫМИ_БУКВАМИ (SCREAMING_SNAKE_CASE).
|
|
||||||
- [ ] Все DTO размещены в папке `dto/` на своём уровне абстракции.
|
|
||||||
- [ ] Не используется `any` и `unknown` без крайней необходимости и поясняющего комментария.
|
|
||||||
- [ ] Для сложных структур используются отдельные интерфейсы или типы.
|
|
||||||
- [ ] Для массивов и объектов указан тип элементов/ключей.
|
|
||||||
- [ ] Для асинхронных функций указан тип Promise с конкретным типом результата.
|
|
||||||
- [ ] Типы коллбеков и функций, передаваемых в пропсы, указаны инлайн.
|
|
||||||
- [ ] Не используются устаревшие типы (`Function`, `Object`, `{}`).
|
|
||||||
- [ ] Для внешних библиотек используются официальные типы или собственные декларации.
|
|
||||||
- [ ] Нет неявного приведения типов, все типы читаемы и прозрачны.
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
title: Локализация
|
|
||||||
---
|
|
||||||
|
|
||||||
# Локализация
|
|
||||||
|
|
||||||
## Правила использования локализации
|
|
||||||
|
|
||||||
- Все пользовательские тексты должны быть вынесены в локализационные файлы.
|
|
||||||
- Для каждого компонента создавать папку `locales/` с файлами `ru.json`, `en.json` и т.д.
|
|
||||||
- Новые namespace обязательно регистрировать в экземпляре i18n (см. `app/i18n.ts`).
|
|
||||||
- В коде использовать только функцию перевода из i18n, не использовать "жёстко" прописанные строки.
|
|
||||||
84
README.md
84
README.md
@@ -1,57 +1,53 @@
|
|||||||
# NextJS Style Guide
|
# Style Guide
|
||||||
|
|
||||||
Rules and standards for NextJS and TypeScript development: architecture, typing, styles, components, API, and infrastructure.
|
Репозиторий с правилами и стандартами фронтенд-разработки. Исходники документации собираются в разные форматы под разные фреймворки.
|
||||||
|
|
||||||
## Documentation Structure
|
## Структура
|
||||||
|
|
||||||
### Processes
|
```text
|
||||||
|
src/ # Исходники — только .md файлы
|
||||||
|
├── base/ # Общие правила (не поставляется отдельно)
|
||||||
|
│ ├── basics/ # Базовые: стиль кода, именование, типизация
|
||||||
|
│ ├── applied/ # Прикладные: компоненты, стили, хуки, API
|
||||||
|
│ └── triggers/ # Триггеры: создание компонента, стилизация и т.д.
|
||||||
|
│
|
||||||
|
└── nextjs/ # Next.js — самостоятельная единица
|
||||||
|
├── applied/ # Next.js-специфичные: page-level, project-structure
|
||||||
|
├── triggers/ # Next.js-специфичные триггеры: create-page, create-layout
|
||||||
|
├── DEVELOP.md # Точка входа для агента-разработчика
|
||||||
|
└── REVIEW.md # Точка входа для агента-ревьювера
|
||||||
|
|
||||||
**What to do** in a specific situation — step-by-step instructions.
|
scripts/ # Скрипты и манифесты сборки
|
||||||
|
├── build-ai.js # Скрипт сборки
|
||||||
|
└── nextjs.build.js # Манифест: какие файлы, куда, как называются
|
||||||
|
|
||||||
| Section | Answers the question |
|
dist/ # Собранные версии (gitignore)
|
||||||
|---------|---------------------|
|
├── ai/{framework}/ # Для AI-агентов
|
||||||
| Getting Started | What tools to install before starting development? |
|
└── vitepress/{framework}/ # Для людей (планируется)
|
||||||
| Creating an App | How to create a new project, where to get a template? |
|
```
|
||||||
| Creating Pages | How to add a page: routing and screen? |
|
|
||||||
| Creating Components | How to generate components using templates? |
|
|
||||||
| Styling | What to use: Mantine, tokens, or PostCSS? |
|
|
||||||
| Data Fetching | How to fetch data: SWR, codegen, sockets? |
|
|
||||||
| State Management | When and how to create a store (Zustand)? |
|
|
||||||
| Localization | How to add translations and work with i18next? |
|
|
||||||
|
|
||||||
### Basic Rules
|
## Сборка
|
||||||
|
|
||||||
**What the code should look like** — standards not tied to a specific technology.
|
```bash
|
||||||
|
npm run build:ai # Собрать все фреймворки
|
||||||
|
```
|
||||||
|
|
||||||
| Section | Answers the question |
|
## Манифест
|
||||||
|---------|---------------------|
|
|
||||||
| Tech Stack | What stack do we use? |
|
|
||||||
| Architecture | How are FSD layers, dependencies, and public API structured? |
|
|
||||||
| Code Style | How to format code: indentation, quotes, imports, early return? |
|
|
||||||
| Naming | How to name files, variables, components, hooks? |
|
|
||||||
| Documentation | How to write JSDoc: what to document and what not? |
|
|
||||||
| Typing | How to type: type vs interface, any/unknown? |
|
|
||||||
|
|
||||||
### Applied Sections
|
Каждый фреймворк имеет манифест `scripts/{framework}.build.js`. Ключ — путь в выходной папке, значение — путь исходника в `src/`.
|
||||||
|
|
||||||
**How a specific area works** — rules, structure, and code examples for specific technologies and tools.
|
Скрипт только копирует файлы по манифесту. Никакой генерации.
|
||||||
|
|
||||||
| Section | Answers the question |
|
## Добавление раздела
|
||||||
|---------|---------------------|
|
|
||||||
| Project Structure | How are folders and files organized by FSD? |
|
|
||||||
| Components | How is a component structured: files, props, clsx? |
|
|
||||||
| Page-level Components | How to define layout, page, loading, error, not-found? |
|
|
||||||
| Templates & Code Generation | How do templates work: syntax, variables, modifiers? |
|
|
||||||
| Styles | How to write CSS: PostCSS Modules, nesting, media, tokens? |
|
|
||||||
| Images | _(not filled)_ |
|
|
||||||
| SVG Sprites | _(not filled)_ |
|
|
||||||
| Video | _(not filled)_ |
|
|
||||||
| API | _(not filled)_ |
|
|
||||||
| Stores | _(not filled)_ |
|
|
||||||
| Hooks | _(not filled)_ |
|
|
||||||
| Fonts | _(not filled)_ |
|
|
||||||
| Localization | _(not filled)_ |
|
|
||||||
|
|
||||||
## For Assistants
|
1. Создать `.md` в `src/base/` (общий) или `src/{framework}/` (специфичный).
|
||||||
|
2. Добавить frontmatter: `title`, `scope`, `keywords`, `when`.
|
||||||
|
3. Добавить путь в манифест `scripts/{framework}.build.js`.
|
||||||
|
4. Обновить точку входа (`DEVELOP.md` и/или `REVIEW.md`).
|
||||||
|
5. `npm run build:ai`.
|
||||||
|
|
||||||
Full documentation in a single MD file: https://gromlab.ru/docs/frontend-style-guide/raw/branch/main/generated/en/RULES.md
|
## Добавление фреймворка
|
||||||
|
|
||||||
|
1. Создать `src/{framework}/` с `.md` файлами и точками входа.
|
||||||
|
2. Создать `scripts/{framework}.build.js`.
|
||||||
|
3. `npm run build:ai`.
|
||||||
|
|||||||
60
README_RU.md
60
README_RU.md
@@ -1,60 +0,0 @@
|
|||||||
# NextJS Style Guide
|
|
||||||
|
|
||||||
Правила и стандарты разработки на NextJS и TypeScript: архитектура, типизация, стили, компоненты, API и инфраструктурные разделы.
|
|
||||||
|
|
||||||
## Для ассистентов
|
|
||||||
|
|
||||||
Полная документация в одном MD файле: https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/RULES.md
|
|
||||||
|
|
||||||
## Структура документации
|
|
||||||
|
|
||||||
### Workflow
|
|
||||||
|
|
||||||
**Что делать и в каком порядке** — пошаговые инструкции.
|
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
|
||||||
|--------|-------------------|
|
|
||||||
| Начало работы | Что нужно знать перед началом разработки? |
|
|
||||||
| Создание проекта | Как начать новый проект? |
|
|
||||||
| Генерация кода | Какие модули должны генерироваться из шаблонов? |
|
|
||||||
| Добавление страницы | Как добавить новую страницу в проект? |
|
|
||||||
| Добавление UI-модуля | Как создать компонент, фичу, виджет, сущность или layout? |
|
|
||||||
| Стилизация | Как стилизовать компоненты в проекте? |
|
|
||||||
| Получение данных | Как получать данные с сервера? |
|
|
||||||
| Управление состоянием | Как работать с состоянием? |
|
|
||||||
| Локализация | Как добавлять переводы и подключать локализацию? |
|
|
||||||
|
|
||||||
### Базовые правила
|
|
||||||
|
|
||||||
**Каким должен быть код** — стандарты, не привязанные к конкретной технологии.
|
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
|
||||||
|--------|-------------------|
|
|
||||||
| Технологии и библиотеки | Какой стек используем? |
|
|
||||||
| Архитектура | Как устроены слои FSD, зависимости, публичный API? |
|
|
||||||
| Стиль кода | Как оформлять код: отступы, кавычки, импорты, early return? |
|
|
||||||
| Именование | Как называть файлы, переменные, компоненты, хуки? |
|
|
||||||
| Документирование | Как писать JSDoc: что документировать, а что нет? |
|
|
||||||
| Типизация | Как типизировать: type vs interface, any/unknown? |
|
|
||||||
|
|
||||||
### Прикладные разделы
|
|
||||||
|
|
||||||
**Как это настроить и использовать** — конфигурация, структура и примеры кода для конкретных областей.
|
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
|
||||||
|--------|-------------------|
|
|
||||||
| Настройка VS Code | Как настроить редактор для проекта? |
|
|
||||||
| Структура проекта | Как организованы папки и файлы по FSD? |
|
|
||||||
| Компоненты | Как устроен компонент: файлы, пропсы, clsx? |
|
|
||||||
| Page-level компоненты | Как описывать layout, page, loading, error, not-found? |
|
|
||||||
| Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? |
|
|
||||||
| Стили | Как писать CSS: PostCSS Modules, вложенность, медиа, токены? |
|
|
||||||
| Изображения | _(не заполнен)_ |
|
|
||||||
| SVG-спрайты | _(не заполнен)_ |
|
|
||||||
| Видео | _(не заполнен)_ |
|
|
||||||
| API | _(не заполнен)_ |
|
|
||||||
| Stores | _(не заполнен)_ |
|
|
||||||
| Хуки | _(не заполнен)_ |
|
|
||||||
| Шрифты | _(не заполнен)_ |
|
|
||||||
| Локализация | _(не заполнен)_ |
|
|
||||||
|
|
||||||
107
concat-md.js
107
concat-md.js
@@ -1,107 +0,0 @@
|
|||||||
import path from "path";
|
|
||||||
import fs from "fs";
|
|
||||||
|
|
||||||
// Явный порядок файлов внутри каждого языка
|
|
||||||
const fileOrder = [
|
|
||||||
// index
|
|
||||||
"index.md",
|
|
||||||
// workflow
|
|
||||||
"workflow.md",
|
|
||||||
// basics
|
|
||||||
"basics/tech-stack.md",
|
|
||||||
"basics/naming.md",
|
|
||||||
"basics/architecture.md",
|
|
||||||
"basics/code-style.md",
|
|
||||||
"basics/documentation.md",
|
|
||||||
"basics/typing.md",
|
|
||||||
// applied
|
|
||||||
"applied/project-structure.md",
|
|
||||||
"applied/components.md",
|
|
||||||
"applied/page-level.md",
|
|
||||||
"applied/templates-generation.md",
|
|
||||||
"applied/styles.md",
|
|
||||||
"applied/images-sprites.md",
|
|
||||||
"applied/svg-sprites.md",
|
|
||||||
"applied/video.md",
|
|
||||||
"applied/api.md",
|
|
||||||
"applied/stores.md",
|
|
||||||
"applied/hooks.md",
|
|
||||||
"applied/fonts.md",
|
|
||||||
"applied/localization.md",
|
|
||||||
"applied/vscode.md",
|
|
||||||
];
|
|
||||||
|
|
||||||
// Удалить frontmatter из содержимого md-файла
|
|
||||||
const stripFrontmatter = (content) =>
|
|
||||||
content.replace(/^---[\s\S]*?---\n*/m, "");
|
|
||||||
|
|
||||||
// Сдвинуть уровень заголовков на 1 вниз (h1→h2, h2→h3, ...)
|
|
||||||
// Не трогает заголовки внутри блоков кода
|
|
||||||
const shiftHeadings = (content) => {
|
|
||||||
const lines = content.split("\n");
|
|
||||||
let inCodeBlock = false;
|
|
||||||
|
|
||||||
return lines
|
|
||||||
.map((line) => {
|
|
||||||
if (line.startsWith("```")) inCodeBlock = !inCodeBlock;
|
|
||||||
if (inCodeBlock) return line;
|
|
||||||
if (/^#{1,5}\s/.test(line)) return "#" + line;
|
|
||||||
return line;
|
|
||||||
})
|
|
||||||
.join("\n");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Собрать RULES.md с мета-якорями для каждого файла
|
|
||||||
const buildRules = (lang) => {
|
|
||||||
const srcDir = `./docs/${lang}`;
|
|
||||||
const outDir = `./generated/${lang}`;
|
|
||||||
const outFile = path.join(outDir, "RULES.md");
|
|
||||||
|
|
||||||
if (!fs.existsSync(srcDir)) {
|
|
||||||
console.log(`Пропуск ${lang}: папка ${srcDir} не найдена`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.mkdirSync(outDir, { recursive: true });
|
|
||||||
|
|
||||||
const parts = [];
|
|
||||||
|
|
||||||
for (const file of fileOrder) {
|
|
||||||
const filePath = path.join(srcDir, file);
|
|
||||||
if (!fs.existsSync(filePath)) continue;
|
|
||||||
|
|
||||||
const raw = fs.readFileSync(filePath, "utf8");
|
|
||||||
const content = stripFrontmatter(raw).trim();
|
|
||||||
if (!content) continue;
|
|
||||||
|
|
||||||
// Мета-якорь: путь VitePress без расширения
|
|
||||||
const route = "/" + file.replace(/\.md$/, "");
|
|
||||||
// index.md остаётся без сдвига (его h1 — главный заголовок документа)
|
|
||||||
const processed = file === "index.md" ? content : shiftHeadings(content);
|
|
||||||
parts.push(`<!-- ${route} -->\n${processed}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(outFile, parts.join("\n\n"), "utf8");
|
|
||||||
console.log(`RULES.md (${lang}) создан: ${outFile}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Собираем RULES.md для обоих языков
|
|
||||||
buildRules("ru");
|
|
||||||
buildRules("en");
|
|
||||||
|
|
||||||
// Генерируем README из index.md
|
|
||||||
const buildReadme = (lang, outFile) => {
|
|
||||||
const indexPath = `./docs/${lang}/index.md`;
|
|
||||||
|
|
||||||
if (!fs.existsSync(indexPath)) {
|
|
||||||
console.log(`Пропуск README (${lang}): ${indexPath} не найден`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = stripFrontmatter(fs.readFileSync(indexPath, "utf8"));
|
|
||||||
fs.writeFileSync(outFile, content, "utf8");
|
|
||||||
console.log(`${outFile} создан из ${indexPath}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
buildReadme("en", "./README.md");
|
|
||||||
buildReadme("ru", "./README_RU.md");
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: API
|
|
||||||
---
|
|
||||||
|
|
||||||
# API
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Components
|
|
||||||
---
|
|
||||||
|
|
||||||
# Components
|
|
||||||
|
|
||||||
Rules for creating UI components across all FSD layers.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Fonts
|
|
||||||
---
|
|
||||||
|
|
||||||
# Fonts
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Hooks
|
|
||||||
---
|
|
||||||
|
|
||||||
# Hooks
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Images
|
|
||||||
---
|
|
||||||
|
|
||||||
# Images
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Localization
|
|
||||||
---
|
|
||||||
|
|
||||||
# Localization
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Page-level Components
|
|
||||||
---
|
|
||||||
|
|
||||||
# Page-level Components
|
|
||||||
|
|
||||||
Next.js App Router special files used by the framework by convention: `layout.tsx`, `page.tsx`, `loading.tsx`, `error.tsx`, `not-found.tsx`, `template.tsx`.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Project Structure
|
|
||||||
---
|
|
||||||
|
|
||||||
# Project Structure
|
|
||||||
|
|
||||||
Base project structure and principles of module organization at folder and file level.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Stores
|
|
||||||
---
|
|
||||||
|
|
||||||
# Stores
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Styles
|
|
||||||
---
|
|
||||||
|
|
||||||
# Styles
|
|
||||||
|
|
||||||
CSS writing rules: PostCSS Modules, nesting, media queries, variables, formatting.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: SVG Sprites
|
|
||||||
---
|
|
||||||
|
|
||||||
# SVG Sprites
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Templates & Code Generation
|
|
||||||
---
|
|
||||||
|
|
||||||
# Templates & Code Generation
|
|
||||||
|
|
||||||
Template tools, syntax, and examples for code generation.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Video
|
|
||||||
---
|
|
||||||
|
|
||||||
# Video
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Architecture
|
|
||||||
---
|
|
||||||
|
|
||||||
# Architecture
|
|
||||||
|
|
||||||
Architecture based on FSD (Feature-Sliced Design) and strict module boundaries.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Code Style
|
|
||||||
---
|
|
||||||
|
|
||||||
# Code Style
|
|
||||||
|
|
||||||
Unified code formatting rules: indentation, line breaks, quotes, import order, and readability.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Documentation
|
|
||||||
---
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
|
|
||||||
Documentation should help understand the purpose of an entity, not duplicate its types or obvious details.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Naming
|
|
||||||
---
|
|
||||||
|
|
||||||
# Naming
|
|
||||||
|
|
||||||
Naming should be predictable, concise, and reflect the meaning of the entity.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Tech Stack
|
|
||||||
---
|
|
||||||
|
|
||||||
# Tech Stack
|
|
||||||
|
|
||||||
Base technology stack and libraries used in projects.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Typing
|
|
||||||
---
|
|
||||||
|
|
||||||
# Typing
|
|
||||||
|
|
||||||
Typing is required for all public interfaces, functions, and components.
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
# NextJS Style Guide
|
|
||||||
|
|
||||||
Rules and standards for NextJS and TypeScript development: architecture, typing, styles, components, API, and infrastructure.
|
|
||||||
|
|
||||||
## Documentation Structure
|
|
||||||
|
|
||||||
### Processes
|
|
||||||
|
|
||||||
**What to do** in a specific situation — step-by-step instructions.
|
|
||||||
|
|
||||||
| Section | Answers the question |
|
|
||||||
|---------|---------------------|
|
|
||||||
| Getting Started | What tools to install before starting development? |
|
|
||||||
| Creating an App | How to create a new project, where to get a template? |
|
|
||||||
| Creating Pages | How to add a page: routing and screen? |
|
|
||||||
| Creating Components | How to generate components using templates? |
|
|
||||||
| Styling | What to use: Mantine, tokens, or PostCSS? |
|
|
||||||
| Data Fetching | How to fetch data: SWR, codegen, sockets? |
|
|
||||||
| State Management | When and how to create a store (Zustand)? |
|
|
||||||
| Localization | How to add translations and work with i18next? |
|
|
||||||
|
|
||||||
### Basic Rules
|
|
||||||
|
|
||||||
**What the code should look like** — standards not tied to a specific technology.
|
|
||||||
|
|
||||||
| Section | Answers the question |
|
|
||||||
|---------|---------------------|
|
|
||||||
| Tech Stack | What stack do we use? |
|
|
||||||
| Architecture | How are FSD layers, dependencies, and public API structured? |
|
|
||||||
| Code Style | How to format code: indentation, quotes, imports, early return? |
|
|
||||||
| Naming | How to name files, variables, components, hooks? |
|
|
||||||
| Documentation | How to write JSDoc: what to document and what not? |
|
|
||||||
| Typing | How to type: type vs interface, any/unknown? |
|
|
||||||
|
|
||||||
### Applied Sections
|
|
||||||
|
|
||||||
**How a specific area works** — rules, structure, and code examples for specific technologies and tools.
|
|
||||||
|
|
||||||
| Section | Answers the question |
|
|
||||||
|---------|---------------------|
|
|
||||||
| Project Structure | How are folders and files organized by FSD? |
|
|
||||||
| Components | How is a component structured: files, props, clsx? |
|
|
||||||
| Page-level Components | How to define layout, page, loading, error, not-found? |
|
|
||||||
| Templates & Code Generation | How do templates work: syntax, variables, modifiers? |
|
|
||||||
| Styles | How to write CSS: PostCSS Modules, nesting, media, tokens? |
|
|
||||||
| Images | _(not filled)_ |
|
|
||||||
| SVG Sprites | _(not filled)_ |
|
|
||||||
| Video | _(not filled)_ |
|
|
||||||
| API | _(not filled)_ |
|
|
||||||
| Stores | _(not filled)_ |
|
|
||||||
| Hooks | _(not filled)_ |
|
|
||||||
| Fonts | _(not filled)_ |
|
|
||||||
| Localization | _(not filled)_ |
|
|
||||||
|
|
||||||
## For Assistants
|
|
||||||
|
|
||||||
Full documentation in a single MD file: https://gromlab.ru/docs/frontend-style-guide/raw/branch/main/generated/en/RULES.md
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Creating an App
|
|
||||||
---
|
|
||||||
|
|
||||||
# Creating an App
|
|
||||||
|
|
||||||
How to create a new application: choosing a project template and initialization.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Creating Components
|
|
||||||
---
|
|
||||||
|
|
||||||
# Creating Components
|
|
||||||
|
|
||||||
Generating components using templates, working with child components.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Creating Pages
|
|
||||||
---
|
|
||||||
|
|
||||||
# Creating Pages
|
|
||||||
|
|
||||||
Page creation pattern: routing (page.tsx) and screen.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Data Fetching
|
|
||||||
---
|
|
||||||
|
|
||||||
# Data Fetching
|
|
||||||
|
|
||||||
How to fetch data: SWR, API client codegen, sockets.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Getting Started
|
|
||||||
---
|
|
||||||
|
|
||||||
# Getting Started
|
|
||||||
|
|
||||||
Setting up the environment and installing tools before starting development.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Localization
|
|
||||||
---
|
|
||||||
|
|
||||||
# Localization
|
|
||||||
|
|
||||||
How to add translations and work with i18next.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: State Management
|
|
||||||
---
|
|
||||||
|
|
||||||
# State Management
|
|
||||||
|
|
||||||
When and how to create a store (Zustand), what to store locally vs globally.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Styling
|
|
||||||
---
|
|
||||||
|
|
||||||
# Styling
|
|
||||||
|
|
||||||
Styling tools priority and rules for their application.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: SVG-спрайты
|
|
||||||
---
|
|
||||||
|
|
||||||
# SVG-спрайты
|
|
||||||
@@ -1,471 +0,0 @@
|
|||||||
---
|
|
||||||
title: Архитектура
|
|
||||||
---
|
|
||||||
|
|
||||||
# Архитектура
|
|
||||||
|
|
||||||
Раздел описывает архитектуру проекта: из каких слоёв состоит приложение,
|
|
||||||
как организован код внутри слоёв и какие правила управляют зависимостями.
|
|
||||||
|
|
||||||
## Что нужно знать
|
|
||||||
|
|
||||||
SLM Design (Scoped Layered Module Design) — архитектурный подход
|
|
||||||
к проектированию фронтенд-приложений, предложенный Громовым Сергеем в 2026 г.
|
|
||||||
|
|
||||||
Вырос на основе:
|
|
||||||
|
|
||||||
- [Feature-Sliced Design](https://feature-sliced.design) — слои и направление зависимостей
|
|
||||||
- Screaming Architecture — структура говорит сама за себя
|
|
||||||
- Colocation Principle — код рядом с местом использования
|
|
||||||
|
|
||||||
Переосмыслив эти подходы, SLM Design отличается от FSD в трёх аспектах:
|
|
||||||
где живёт код (колокация), как он организован (модули)
|
|
||||||
и как масштабируется (подъём при переиспользовании).
|
|
||||||
|
|
||||||
## Терминология
|
|
||||||
|
|
||||||
Архитектура оперирует четырьмя ключевыми понятиями:
|
|
||||||
|
|
||||||
- **Слой** — содержит модули
|
|
||||||
- **Модуль** — содержит сегменты
|
|
||||||
- **Компонент** — содержит сегменты
|
|
||||||
- **Сегмент** — папка внутри модуля или компонента, группирующая код по назначению: UI-элементы (`ui/`), хуки (`hooks/`), типы (`types/`), стили (`styles/`) и другие
|
|
||||||
|
|
||||||
Модуль и компонент устроены одинаково — оба имеют сегменты. Разница в том, где они живут и обязателен ли UI.
|
|
||||||
|
|
||||||
```text
|
|
||||||
Слой
|
|
||||||
└── Модуль
|
|
||||||
├── Сегменты (hooks/, stores/, types/, styles/, lib/...)
|
|
||||||
└── ui/
|
|
||||||
└── Компонент
|
|
||||||
├── Сегменты (hooks/, stores/, types/, styles/, lib/...)
|
|
||||||
└── ui/
|
|
||||||
└── Компонент → ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### Слой
|
|
||||||
|
|
||||||
Архитектурный уровень. Содержит только модули. Определяет назначение кода и правила зависимостей.
|
|
||||||
|
|
||||||
### Модуль
|
|
||||||
|
|
||||||
Единица первого уровня слоя, объединённая по смыслу. Может содержать компонент, логику, типы, стили — или любую комбинацию. Имеет публичный API (`index.ts`) и внутреннюю структуру из сегментов.
|
|
||||||
|
|
||||||
Модуль — не обязательно UI. Feature `analytics` может быть только стором и сервисом. Entity `session` может быть только типами и хуком.
|
|
||||||
|
|
||||||
Модуль не может содержать вложенных модулей. Вложенные единицы с UI размещаются в сегменте `ui/` как компоненты.
|
|
||||||
|
|
||||||
### Компонент
|
|
||||||
|
|
||||||
Вложенная единица внутри сегмента `ui/` модуля (или другого компонента). Публичный `.tsx` файл обязателен. Именуется без суффикса слоя.
|
|
||||||
|
|
||||||
Компонент может иметь собственные сегменты (`hooks/`, `styles/`, `types/` и т.д.), `index.ts` и свой `ui/` с ещё более вложенными компонентами.
|
|
||||||
|
|
||||||
Отличия от модуля:
|
|
||||||
|
|
||||||
| | Модуль | Компонент |
|
|
||||||
|--|--------|-----------|
|
|
||||||
| Где живёт | В корне слоя | В `ui/` модуля или другого компонента |
|
|
||||||
| Публичный `.tsx` | С суффиксом слоя, опционален | Без суффикса, обязателен |
|
|
||||||
| Может не иметь UI | Да | Нет |
|
|
||||||
|
|
||||||
Пример:
|
|
||||||
|
|
||||||
```text
|
|
||||||
features/auth-by-email/ # модуль
|
|
||||||
├── auth-by-email.feature.tsx # публичный .tsx модуля (с суффиксом, опционален)
|
|
||||||
├── ui/ # сегмент: компоненты
|
|
||||||
│ ├── login-form/ # компонент
|
|
||||||
│ │ ├── login-form.tsx # публичный .tsx компонента (без суффикса, обязателен)
|
|
||||||
│ │ ├── ui/ # вложенные компоненты
|
|
||||||
│ │ │ └── password-field/
|
|
||||||
│ │ │ └── password-field.tsx
|
|
||||||
│ │ ├── hooks/
|
|
||||||
│ │ │ └── use-validation.hook.ts
|
|
||||||
│ │ ├── styles/
|
|
||||||
│ │ │ └── login-form.module.css
|
|
||||||
│ │ └── index.ts
|
|
||||||
│ └── reset-password/ # компонент
|
|
||||||
│ ├── reset-password.tsx
|
|
||||||
│ └── index.ts
|
|
||||||
├── hooks/
|
|
||||||
│ └── use-auth.hook.ts
|
|
||||||
├── stores/
|
|
||||||
│ └── auth.store.ts
|
|
||||||
└── index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Сегмент
|
|
||||||
|
|
||||||
Техническая папка внутри модуля или компонента, группирующая код по назначению. Набор не фиксирован — включаются только те сегменты, которые нужны.
|
|
||||||
|
|
||||||
| Сегмент | Назначение |
|
|
||||||
|---------|-----------|
|
|
||||||
| `ui/` | Вложенные компоненты |
|
|
||||||
| `hooks/` | React-хуки |
|
|
||||||
| `stores/` | Сторы состояния |
|
|
||||||
| `types/` | Интерфейсы, типы, enums, DTO |
|
|
||||||
| `styles/` | Стили |
|
|
||||||
| `lib/` | Утилиты |
|
|
||||||
| `services/` | Внешние источники данных |
|
|
||||||
| `helpers/` | Вспомогательные функции |
|
|
||||||
| `config/` | Константы, конфигурация |
|
|
||||||
|
|
||||||
## Ключевой принцип
|
|
||||||
|
|
||||||
> Модуль живёт на самом низком уровне, где он используется.
|
|
||||||
> Поднимается выше только при переиспользовании в 2+ местах.
|
|
||||||
|
|
||||||
Если модуль используется только в одном месте — он остаётся на текущем уровне.
|
|
||||||
Как только он начинает использоваться в 2+ местах — выносится на уровень выше.
|
|
||||||
В крайнем случае — в `shared/`, где он доступен всем.
|
|
||||||
|
|
||||||
## Слои
|
|
||||||
|
|
||||||
Каждый нижний слой не знает о существовании верхних. Импорты идут строго сверху вниз.
|
|
||||||
|
|
||||||
```
|
|
||||||
app → layouts → screens → widgets → features → entities → shared
|
|
||||||
```
|
|
||||||
|
|
||||||
| Слой | Что лежит | Импортирует |
|
|
||||||
|------|-----------|-------------|
|
|
||||||
| **App** | Роутинг, провайдеры, глобальные стили. Композиция layout + screen для маршрута. | Все слои ниже |
|
|
||||||
| **Layouts** | Каркас страницы, общий для группы маршрутов. | widgets, features, entities, shared |
|
|
||||||
| **Screens** | Контент конкретной страницы. | widgets, features, entities, shared |
|
|
||||||
| **Widgets** | Составные блоки с данными/логикой, переиспользуемые в 2+ местах. | features, entities, shared |
|
|
||||||
| **Features** | Пользовательское действие или интерактивный сценарий. | entities, shared |
|
|
||||||
| **Entities** | Бизнес-сущность с отображением и типами. | shared |
|
|
||||||
| **Shared** | Переиспользуемые компоненты, утилиты, стили без бизнес-логики. | ничего |
|
|
||||||
|
|
||||||
Принципы:
|
|
||||||
|
|
||||||
- Импорты строго сверху вниз
|
|
||||||
- Модули одного слоя не знают друг о друге
|
|
||||||
- Layout получает контекстно-зависимые блоки через пропсы от app, а не импортирует их сам
|
|
||||||
- `entities/` и `features/` создаются осознанно — это не результат «вынесения» компонента из screen
|
|
||||||
|
|
||||||
### App
|
|
||||||
|
|
||||||
Точка входа приложения: роутинг (Next.js App Router), провайдеры, глобальные стили.
|
|
||||||
Находится на самом высоком уровне абстракции — может импортировать любой слой ниже.
|
|
||||||
Никакой бизнес-логики — только композиция.
|
|
||||||
|
|
||||||
```text
|
|
||||||
app/
|
|
||||||
├── layout.tsx # RootLayout: провайдеры, глобальные стили
|
|
||||||
├── page.tsx # Главная: MainLayout + HomeScreen
|
|
||||||
├── knv-new/
|
|
||||||
│ └── page.tsx # КНВ: MainLayout + KnvScreen
|
|
||||||
└── catalog/
|
|
||||||
└── page.tsx # Каталог: MainLayout + CatalogScreen
|
|
||||||
```
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
// app/knv-new/page.tsx
|
|
||||||
import { MainLayout } from '@/layouts/main'
|
|
||||||
import { KnvScreen } from '@/screens/knv'
|
|
||||||
|
|
||||||
export default function KnvNewPage() {
|
|
||||||
return (
|
|
||||||
<MainLayout>
|
|
||||||
<KnvScreen />
|
|
||||||
</MainLayout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Если layout требует разный контент в зависимости от страницы — app передаёт его через пропсы:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
// app/knv-new/page.tsx
|
|
||||||
import { MainLayout } from '@/layouts/main'
|
|
||||||
import { KnvScreen } from '@/screens/knv'
|
|
||||||
import { KnvHeader } from '@/widgets/knv-header'
|
|
||||||
|
|
||||||
export default function KnvNewPage() {
|
|
||||||
return (
|
|
||||||
<MainLayout header={<KnvHeader />}>
|
|
||||||
<KnvScreen />
|
|
||||||
</MainLayout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Layouts
|
|
||||||
|
|
||||||
Каркас страницы — общие элементы, одинаковые для группы маршрутов (header, footer, sidebar).
|
|
||||||
|
|
||||||
Если компонент внутри layout начинает использоваться в 2+ местах — он выносится в `widgets/` или `shared/ui/`.
|
|
||||||
|
|
||||||
```text
|
|
||||||
src/layouts/
|
|
||||||
└── main/
|
|
||||||
├── main.layout.tsx
|
|
||||||
├── ui/
|
|
||||||
│ ├── header/
|
|
||||||
│ │ └── header.tsx
|
|
||||||
│ └── footer/
|
|
||||||
│ └── footer.tsx
|
|
||||||
└── index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Screens
|
|
||||||
|
|
||||||
Контент конкретной страницы. Собирает локальные секции и переиспользуемые модули из нижних слоёв.
|
|
||||||
|
|
||||||
Если компонент внутри screen начинает использоваться в 2+ местах — он выносится в `widgets/` или `shared/ui/`.
|
|
||||||
|
|
||||||
```text
|
|
||||||
src/screens/
|
|
||||||
└── knv/
|
|
||||||
├── knv.screen.tsx
|
|
||||||
├── ui/
|
|
||||||
│ ├── hero-section/
|
|
||||||
│ │ └── hero-section.tsx
|
|
||||||
│ ├── products-section/
|
|
||||||
│ │ └── products-section.tsx
|
|
||||||
│ └── diseases-section/
|
|
||||||
│ └── diseases-section.tsx
|
|
||||||
└── index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Widgets
|
|
||||||
|
|
||||||
Составные блоки с данными и логикой, переиспользуемые в 2+ местах.
|
|
||||||
|
|
||||||
Если блок с логикой нужен только в одном месте — это компонент внутри `screens/{name}/ui/` или `layouts/{name}/ui/`, а не widget.
|
|
||||||
|
|
||||||
```text
|
|
||||||
src/widgets/
|
|
||||||
└── popular-products-slider/
|
|
||||||
├── popular-products-slider.widget.tsx
|
|
||||||
├── ui/
|
|
||||||
│ └── slider-card/
|
|
||||||
│ └── slider-card.tsx
|
|
||||||
├── hooks/
|
|
||||||
│ └── use-products.hook.ts
|
|
||||||
└── index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
Пользовательское действие или интерактивный сценарий: авторизация, заказ, добавление в корзину.
|
|
||||||
|
|
||||||
Feature создаётся осознанно, когда есть действие пользователя с бизнес-логикой. Компонент опционален — feature может экспортировать хуки, сторы, компоненты или всё вместе.
|
|
||||||
|
|
||||||
```text
|
|
||||||
src/features/
|
|
||||||
└── auth-by-email/
|
|
||||||
├── auth-by-email.feature.tsx
|
|
||||||
├── ui/
|
|
||||||
│ ├── login-form/
|
|
||||||
│ │ └── login-form.tsx
|
|
||||||
│ └── reset-password/
|
|
||||||
│ └── reset-password.tsx
|
|
||||||
├── hooks/
|
|
||||||
│ └── use-auth.hook.ts
|
|
||||||
├── stores/
|
|
||||||
│ └── auth.store.ts
|
|
||||||
└── index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Entities
|
|
||||||
|
|
||||||
Бизнес-сущность с отображением и типами: препарат, заболевание, врач, пользователь.
|
|
||||||
|
|
||||||
Entity создаётся осознанно, когда появляется бизнес-сущность. Компонент опционален — entity может быть только типами и хуком.
|
|
||||||
|
|
||||||
Отличие от `shared/ui/`: entity-компонент знает о бизнес-домене (принимает `Product`, а не абстрактные пропсы).
|
|
||||||
|
|
||||||
```text
|
|
||||||
src/entities/
|
|
||||||
├── product/
|
|
||||||
│ ├── product.entity.tsx
|
|
||||||
│ ├── ui/
|
|
||||||
│ │ └── product-card/
|
|
||||||
│ │ └── product-card.tsx
|
|
||||||
│ ├── types/
|
|
||||||
│ │ └── product.type.ts
|
|
||||||
│ └── index.ts
|
|
||||||
│
|
|
||||||
├── session/
|
|
||||||
│ ├── types/
|
|
||||||
│ │ └── session.type.ts
|
|
||||||
│ ├── hooks/
|
|
||||||
│ │ └── use-session.hook.ts
|
|
||||||
│ └── index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Shared
|
|
||||||
|
|
||||||
Переиспользуемые компоненты, утилиты, стили без бизнес-логики. Не знает о бизнес-домене — работает с абстрактными данными.
|
|
||||||
|
|
||||||
Структурирован как набор сегментов:
|
|
||||||
|
|
||||||
```text
|
|
||||||
src/shared/
|
|
||||||
├── ui/
|
|
||||||
│ ├── icon/
|
|
||||||
│ │ └── icon.tsx
|
|
||||||
│ ├── carousel/
|
|
||||||
│ │ ├── carousel.tsx
|
|
||||||
│ │ ├── ui/
|
|
||||||
│ │ │ ├── carousel-slide/
|
|
||||||
│ │ │ │ └── carousel-slide.tsx
|
|
||||||
│ │ │ └── carousel-dots/
|
|
||||||
│ │ │ └── carousel-dots.tsx
|
|
||||||
│ │ └── index.ts
|
|
||||||
│ ├── container/
|
|
||||||
│ └── button/
|
|
||||||
├── lib/
|
|
||||||
│ ├── format-date.ts
|
|
||||||
│ └── cn.ts
|
|
||||||
├── styles/
|
|
||||||
│ ├── variables.css
|
|
||||||
│ └── media.css
|
|
||||||
└── sprites/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Модуль
|
|
||||||
|
|
||||||
### Структура
|
|
||||||
|
|
||||||
```text
|
|
||||||
{name}/
|
|
||||||
├── {name}.{суффикс}.tsx # компонент (опционален)
|
|
||||||
├── ui/ # вложенные компоненты
|
|
||||||
├── hooks/ # хуки
|
|
||||||
├── stores/ # сторы
|
|
||||||
├── types/ # типы, интерфейсы, enums
|
|
||||||
├── styles/ # стили
|
|
||||||
├── lib/ # утилиты
|
|
||||||
├── services/ # внешние источники данных
|
|
||||||
├── helpers/ # вспомогательные функции
|
|
||||||
├── config/ # константы, конфигурация
|
|
||||||
└── index.ts # публичный API
|
|
||||||
```
|
|
||||||
|
|
||||||
### Именование компонента
|
|
||||||
|
|
||||||
Суффикс слоя получают **только модули первого уровня слоя** — те, что лежат непосредственно в корне слоя. Все компоненты (в `ui/`, любой глубины) именуются без суффикса. Без исключений.
|
|
||||||
|
|
||||||
| Слой | Суффикс | Пример |
|
|
||||||
|------|---------|--------|
|
|
||||||
| Layouts | `.layout.tsx` | `main.layout.tsx` |
|
|
||||||
| Screens | `.screen.tsx` | `knv.screen.tsx` |
|
|
||||||
| Widgets | `.widget.tsx` | `popular-products-slider.widget.tsx` |
|
|
||||||
| Features | `.feature.tsx` | `auth-by-email.feature.tsx` |
|
|
||||||
| Entities | `.entity.tsx` | `product.entity.tsx` |
|
|
||||||
|
|
||||||
Примеры:
|
|
||||||
|
|
||||||
```text
|
|
||||||
features/auth-by-email/auth-by-email.feature.tsx # модуль первого уровня → суффикс
|
|
||||||
features/auth-by-email/ui/login-form/login-form.tsx # компонент в ui/ → без суффикса
|
|
||||||
shared/ui/carousel/carousel.tsx # компонент в shared → без суффикса
|
|
||||||
```
|
|
||||||
|
|
||||||
### Правила импорта
|
|
||||||
|
|
||||||
Три уровня правил:
|
|
||||||
|
|
||||||
**Между слоями** — импорты строго сверху вниз:
|
|
||||||
|
|
||||||
```
|
|
||||||
app → layouts → screens → widgets → features → entities → shared
|
|
||||||
```
|
|
||||||
|
|
||||||
**Внутри модуля (не shared)** — сегменты доступны друг другу и компонентам. Компоненты внутри одного `ui/` не импортируют друг друга:
|
|
||||||
|
|
||||||
```text
|
|
||||||
features/auth-by-email/
|
|
||||||
├── ui/
|
|
||||||
│ ├── login-form/ # НЕ может импортировать reset-password
|
|
||||||
│ └── reset-password/ # НЕ может импортировать login-form
|
|
||||||
```
|
|
||||||
|
|
||||||
Если двум компонентам нужен общий код — он поднимается на уровень выше:
|
|
||||||
|
|
||||||
```text
|
|
||||||
features/auth/ui/login-form/ui/email-input/ # нужен соседу
|
|
||||||
→ features/auth/ui/email-input/ # поднимаем на уровень
|
|
||||||
→ shared/ui/email-input/ # если нужен за пределами фичи
|
|
||||||
```
|
|
||||||
|
|
||||||
Компоненты наследуют правила зависимостей **родительского слоя**:
|
|
||||||
|
|
||||||
- Компонент внутри `features/auth/ui/login-form/` может импортировать `entities/` и `shared/` — как и сам feature
|
|
||||||
- Компонент внутри `widgets/hero/ui/hero-stats/` может импортировать `features/`, `entities/`, `shared/` — как и сам widget
|
|
||||||
|
|
||||||
**Shared** — без ограничений на внутренние импорты. Компоненты в `shared/ui/` могут импортировать друг друга (`button` использует `icon`), `ui/` может использовать `lib/` и другие сегменты. Shared — фундамент, его компоненты строятся друг на друге.
|
|
||||||
|
|
||||||
Правила импорта между слоями enforceable через ESLint — настройка границ слоёв и запрет обратных зависимостей.
|
|
||||||
|
|
||||||
## Жизненный цикл модуля
|
|
||||||
|
|
||||||
Модуль не проектируется «на вырост». Он рождается на самом низком уровне
|
|
||||||
и поднимается только когда появляется реальная потребность.
|
|
||||||
|
|
||||||
Пример пути компонента `product-card`:
|
|
||||||
|
|
||||||
1. **Начало:** `screens/catalog/ui/product-card/` — нужен только на странице каталога.
|
|
||||||
2. **Переиспользование:** появился на странице поиска — выносим выше.
|
|
||||||
Куда именно зависит от природы:
|
|
||||||
- Составной блок с данными и логикой → `widgets/product-card/`
|
|
||||||
- Представление бизнес-сущности → `entities/product/ui/product-card/`
|
|
||||||
- Абстрактный UI без бизнес-логики → `shared/ui/product-card/`
|
|
||||||
|
|
||||||
Основной триггер подъёма — переиспользование в 2+ местах.
|
|
||||||
Но если очевидно что модуль будет переиспользоваться — разумно разместить его на нужном уровне сразу.
|
|
||||||
|
|
||||||
Как происходит подъём на практике:
|
|
||||||
|
|
||||||
1. Разработчик обнаруживает что компонент нужен в другом месте
|
|
||||||
2. Определяет целевой слой по природе компонента (widget / entity / shared)
|
|
||||||
3. Перемещает папку, обновляет импорты, добавляет суффикс если это модуль первого уровня слоя
|
|
||||||
4. Ревью подтверждает что подъём обоснован
|
|
||||||
|
|
||||||
Подъём — это обычный рефакторинг в рамках задачи, а не отдельная активность.
|
|
||||||
|
|
||||||
## Граничные случаи
|
|
||||||
|
|
||||||
| Ситуация | Решение | Почему |
|
|
||||||
|----------|---------|--------|
|
|
||||||
| Фильтр каталога только на одной странице, но с хуками и стором | Модуль в `screens/catalog/` с сегментами `hooks/`, `stores/` | Не feature — feature это действие пользователя с бизнес-логикой (авторизация, заказ), а не UI с состоянием |
|
|
||||||
| Компонент используется в 2 местах на одной странице | Остаётся в `screens/{name}/ui/` | Переиспользование внутри одного screen — не повод выносить в widget |
|
|
||||||
| Entity без UI (только типы и хук) | Нормально | Модуль не обязан иметь UI. `entities/session/` с типами и хуком — валидный модуль |
|
|
||||||
| Компонент нужен и в layout, и в screen | Выносить в `widgets/` или `shared/ui/` | Два разных слоя используют один компонент → переиспользование → подъём |
|
|
||||||
| Модуль screen содержит бизнес-логику (хуки, стор, сервисы) | Нормально | Это не значит что он должен стать feature. Модуль с логикой внутри screen — обычное дело, пока он не переиспользуется |
|
|
||||||
| Компонент в `shared/ui/button` хочет использовать `shared/ui/icon` | Разрешено | Shared — исключение: внутренние импорты без ограничений. Это фундамент, его компоненты строятся друг на друге |
|
|
||||||
|
|
||||||
## Запрещено
|
|
||||||
|
|
||||||
- **Не создавать feature без бизнес-логики** — кнопка без состояния и сайд-эффектов это компонент в `ui/`, а не feature
|
|
||||||
- **Не класть доменные типы в shared** — если тип знает о Product, Disease, User — он живёт в `entities/`, не в `shared/`
|
|
||||||
- **Не создавать entity или feature как результат «вынесения»** — они создаются осознанно: появилась бизнес-сущность → entity, появилось действие пользователя → feature
|
|
||||||
- **Не импортировать соседние компоненты в `ui/` (кроме shared)** — если двум компонентам нужен общий код, он поднимается на уровень выше
|
|
||||||
- **Не хранить в shared «помойку для всего»** — shared содержит переиспользуемые компоненты и утилиты без бизнес-логики, а не код который «непонятно куда положить»
|
|
||||||
|
|
||||||
## Почему так, а не иначе
|
|
||||||
|
|
||||||
### Почему `ui/` а не `modules/`
|
|
||||||
|
|
||||||
Внутри сегмента `ui/` всегда лежат единицы с обязательным UI (компоненты). Название точно отражает содержимое. `modules/` был нейтральнее, но скрывал природу вложенных единиц и создавал путаницу — модуль внутри модуля размывал понятие «модуль как единица слоя».
|
|
||||||
|
|
||||||
### Почему модуль и компонент — разные понятия
|
|
||||||
|
|
||||||
Модуль — единица первого уровня слоя, может не иметь UI. Компонент — вложенная единица с обязательным UI. Разделение снимает вопрос «а если нет `.tsx` — это всё ещё компонент?» и делает название сегмента `ui/` честным.
|
|
||||||
|
|
||||||
### Почему shared без ограничений на внутренние импорты
|
|
||||||
|
|
||||||
Shared — фундамент. Его компоненты строятся друг на друге: `button` использует `icon`, `carousel` использует `button`. Запрещать это — бороться с реальностью. Поднимать некуда — shared уже нижний слой.
|
|
||||||
|
|
||||||
### Почему нет слоя pages
|
|
||||||
|
|
||||||
Роутинг живёт в `app/` (Next.js App Router). Отдельный слой `pages` конфликтовал бы с файловой структурой Next.js и дублировал ответственность `app/`.
|
|
||||||
|
|
||||||
### Почему компоненты в одном `ui/` не импортируют друг друга (кроме shared)
|
|
||||||
|
|
||||||
Независимые компоненты легко выносить в другой слой при переиспользовании. Если компонент A зависит от соседа B — при подъёме A придётся тянуть B. Это усложняет рефакторинг и нарушает принцип колокации.
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
# NextJS Style Guide
|
|
||||||
|
|
||||||
Правила и стандарты разработки на NextJS и TypeScript: архитектура, типизация, стили, компоненты, API и инфраструктурные разделы.
|
|
||||||
|
|
||||||
## Для ассистентов
|
|
||||||
|
|
||||||
Полная документация в одном MD файле: https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/RULES.md
|
|
||||||
|
|
||||||
## Структура документации
|
|
||||||
|
|
||||||
### Workflow
|
|
||||||
|
|
||||||
**Что делать и в каком порядке** — пошаговые инструкции.
|
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
|
||||||
|--------|-------------------|
|
|
||||||
| Начало работы | Что нужно знать перед началом разработки? |
|
|
||||||
| Создание проекта | Как начать новый проект? |
|
|
||||||
| Генерация кода | Какие модули должны генерироваться из шаблонов? |
|
|
||||||
| Добавление страницы | Как добавить новую страницу в проект? |
|
|
||||||
| Добавление UI-модуля | Как создать компонент, фичу, виджет, сущность или layout? |
|
|
||||||
| Стилизация | Как стилизовать компоненты в проекте? |
|
|
||||||
| Получение данных | Как получать данные с сервера? |
|
|
||||||
| Управление состоянием | Как работать с состоянием? |
|
|
||||||
| Локализация | Как добавлять переводы и подключать локализацию? |
|
|
||||||
|
|
||||||
### Базовые правила
|
|
||||||
|
|
||||||
**Каким должен быть код** — стандарты, не привязанные к конкретной технологии.
|
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
|
||||||
|--------|-------------------|
|
|
||||||
| Технологии и библиотеки | Какой стек используем? |
|
|
||||||
| Архитектура | Как устроены слои FSD, зависимости, публичный API? |
|
|
||||||
| Стиль кода | Как оформлять код: отступы, кавычки, импорты, early return? |
|
|
||||||
| Именование | Как называть файлы, переменные, компоненты, хуки? |
|
|
||||||
| Документирование | Как писать JSDoc: что документировать, а что нет? |
|
|
||||||
| Типизация | Как типизировать: type vs interface, any/unknown? |
|
|
||||||
|
|
||||||
### Прикладные разделы
|
|
||||||
|
|
||||||
**Как это настроить и использовать** — конфигурация, структура и примеры кода для конкретных областей.
|
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
|
||||||
|--------|-------------------|
|
|
||||||
| Настройка VS Code | Как настроить редактор для проекта? |
|
|
||||||
| Структура проекта | Как организованы папки и файлы по FSD? |
|
|
||||||
| Компоненты | Как устроен компонент: файлы, пропсы, clsx? |
|
|
||||||
| Page-level компоненты | Как описывать layout, page, loading, error, not-found? |
|
|
||||||
| Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? |
|
|
||||||
| Стили | Как писать CSS: PostCSS Modules, вложенность, медиа, токены? |
|
|
||||||
| Изображения | _(не заполнен)_ |
|
|
||||||
| SVG-спрайты | _(не заполнен)_ |
|
|
||||||
| Видео | _(не заполнен)_ |
|
|
||||||
| API | _(не заполнен)_ |
|
|
||||||
| Stores | _(не заполнен)_ |
|
|
||||||
| Хуки | _(не заполнен)_ |
|
|
||||||
| Шрифты | _(не заполнен)_ |
|
|
||||||
| Локализация | _(не заполнен)_ |
|
|
||||||
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
---
|
|
||||||
title: Workflow
|
|
||||||
---
|
|
||||||
|
|
||||||
# Workflow
|
|
||||||
|
|
||||||
Порядок действий при разработке — от создания проекта до реализации фич.
|
|
||||||
|
|
||||||
## Создание проекта
|
|
||||||
|
|
||||||
Инициализация нового проекта из готового шаблона.
|
|
||||||
|
|
||||||
1. Создать проект из шаблона:
|
|
||||||
```bash
|
|
||||||
npx tiged git@gromlab.ru:templates/nextjs.git my-app
|
|
||||||
cd my-app
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
2. Проект готов к разработке — стек, структура FSD, конфигурация
|
|
||||||
редактора и шаблоны генерации уже настроены.
|
|
||||||
|
|
||||||
## Генерация кода
|
|
||||||
|
|
||||||
Создание модулей из шаблонов `.templates/` вместо ручного создания файлов.
|
|
||||||
|
|
||||||
1. Определить тип модуля и соответствующий шаблон:
|
|
||||||
|
|
||||||
| Модуль | Слой | Шаблон |
|
|
||||||
|------------|--------------|-------------|
|
|
||||||
| Компонент | `shared/ui/` | `component` |
|
|
||||||
| Фича | `features/` | `feature` |
|
|
||||||
| Виджет | `widgets/` | `widget` |
|
|
||||||
| Сущность | `entities/` | `entity` |
|
|
||||||
| Layout | `layouts/` | `layout` |
|
|
||||||
| Экран | `screens/` | `screen` |
|
|
||||||
| Стор | `model/` | `store` |
|
|
||||||
|
|
||||||
2. Сгенерировать модуль из шаблона.
|
|
||||||
3. Если подходящего шаблона нет — сначала создать шаблон, затем использовать.
|
|
||||||
|
|
||||||
Ручное создание файловой структуры модулей запрещено.
|
|
||||||
|
|
||||||
## Добавление страницы
|
|
||||||
|
|
||||||
Создание нового маршрута: экран + точка входа для роутинга.
|
|
||||||
|
|
||||||
1. Сгенерировать экран из шаблона `screen` в `src/screens/`.
|
|
||||||
2. Заполнить экран логикой и стилями.
|
|
||||||
3. Создать `page.tsx` в нужном маршруте `src/app/`.
|
|
||||||
|
|
||||||
`page.tsx` — тонкая обёртка: только `metadata` и рендер экрана.
|
|
||||||
Логика, стили и хуки размещаются в экране, не в `page.tsx`.
|
|
||||||
|
|
||||||
## Добавление UI-модуля
|
|
||||||
|
|
||||||
Создание компонента, фичи, виджета, сущности или layout.
|
|
||||||
|
|
||||||
1. Сгенерировать модуль из соответствующего шаблона в целевой слой.
|
|
||||||
2. Заполнить модуль логикой и стилями.
|
|
||||||
3. Дочерние компоненты — генерировать из шаблона `component` в папку `ui/`
|
|
||||||
внутри родителя.
|
|
||||||
|
|
||||||
Дочерние компоненты не экспортируются через `index.ts` родителя.
|
|
||||||
|
|
||||||
## Стилизация
|
|
||||||
|
|
||||||
Выбор инструмента стилизации по приоритету.
|
|
||||||
|
|
||||||
1. Использовать Mantine-компоненты и их пропсы.
|
|
||||||
2. Если Mantine не покрывает — использовать CSS-токены
|
|
||||||
(`--color-*`, `--space-*`, `--radius-*`).
|
|
||||||
3. Если нужна кастомная стилизация — PostCSS Modules.
|
|
||||||
|
|
||||||
Инлайн-стили (`style`), магические значения и глобальные стили
|
|
||||||
вне `app/styles/` запрещены.
|
|
||||||
|
|
||||||
## Получение данных
|
|
||||||
|
|
||||||
*Раздел в разработке* — SWR, генерация API-клиентов, сокеты.
|
|
||||||
|
|
||||||
## Управление состоянием
|
|
||||||
|
|
||||||
*Раздел в разработке* — когда создавать стор, что хранить локально и глобально.
|
|
||||||
|
|
||||||
## Локализация
|
|
||||||
|
|
||||||
*Раздел в разработке* — переводы и i18next.
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
title: Генерация кода
|
|
||||||
---
|
|
||||||
|
|
||||||
# Генерация кода
|
|
||||||
|
|
||||||
Как создавать модули в проекте с помощью шаблонов — какие модули покрыты генерацией и когда стоит создавать новые шаблоны.
|
|
||||||
|
|
||||||
## Какие модули генерируются из шаблонов
|
|
||||||
|
|
||||||
| Модуль | Слой | Шаблон |
|
|
||||||
|---|---|---|
|
|
||||||
| Компонент | `shared/ui/` | `component` |
|
|
||||||
| Фича | `features/` | `feature` |
|
|
||||||
| Виджет | `widgets/` | `widget` |
|
|
||||||
| Сущность | `entities/` | `entity` |
|
|
||||||
| Layout | `layouts/` | `layout` |
|
|
||||||
| Экран | `screens/` | `screen` |
|
|
||||||
| Стор | `model/` | `store` |
|
|
||||||
|
|
||||||
## Что нужно знать
|
|
||||||
|
|
||||||
В проекте принято создавать модули из шаблонов `.templates/`. Шаблоны задают единообразную файловую структуру и сокращают рутину — не нужно вручную создавать папки, файлы типов, стилей и экспорты.
|
|
||||||
|
|
||||||
Если для нужного модуля нет подходящего шаблона — стоит сначала создать шаблон, а затем использовать его.
|
|
||||||
|
|
||||||
## Когда создавать новый шаблон
|
|
||||||
|
|
||||||
- Повторяющаяся структура появляется больше одного раза.
|
|
||||||
- Существующий шаблон не покрывает нужный тип модуля.
|
|
||||||
|
|
||||||
Инструменты и синтаксис шаблонов — [Шаблоны и генерация кода](/applied/templates-generation).
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
title: Создание проекта
|
|
||||||
---
|
|
||||||
|
|
||||||
# Создание проекта
|
|
||||||
|
|
||||||
Как начать новый проект, соответствующий стандартам этого руководства.
|
|
||||||
|
|
||||||
## Что нужно знать
|
|
||||||
|
|
||||||
Новый проект создаётся из готового шаблона. Шаблон содержит настроенный стек, структуру FSD, конфигурацию редактора и шаблоны генерации кода — проект готов к разработке сразу после установки зависимостей.
|
|
||||||
|
|
||||||
### Создание из шаблона
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx tiged git@gromlab.ru:templates/nextjs.git my-app
|
|
||||||
cd my-app
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Что входит в шаблон
|
|
||||||
|
|
||||||
- Next.js + TypeScript (App Router)
|
|
||||||
- Mantine UI + PostCSS Modules
|
|
||||||
- Biome (линтинг и форматирование)
|
|
||||||
- Zustand, SWR
|
|
||||||
- Структура FSD (`screens/`, `widgets/`, `features/`, `entities/`, `shared/`)
|
|
||||||
- Шаблоны генерации (`.templates/`)
|
|
||||||
- Конфигурация VS Code (`.vscode/`)
|
|
||||||
- CSS-токены (цвета, отступы, радиусы, медиа)
|
|
||||||
- Open Graph метаданные
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
title: Добавление UI-модуля
|
|
||||||
---
|
|
||||||
|
|
||||||
# Добавление UI-модуля
|
|
||||||
|
|
||||||
Как создать компонент, фичу, виджет, сущность или layout в проекте.
|
|
||||||
|
|
||||||
## Что нужно знать
|
|
||||||
|
|
||||||
Все UI-модули создаются только из шаблонов `.templates/`. Ручное создание файловой структуры запрещено. Если подходящего шаблона нет — сначала создать шаблон в `.templates/`, затем использовать его.
|
|
||||||
|
|
||||||
## Порядок действий
|
|
||||||
|
|
||||||
1. [Сгенерировать](/applied/templates-generation) модуль из соответствующего шаблона в целевой слой.
|
|
||||||
2. Заполнить модуль логикой и стилями.
|
|
||||||
|
|
||||||
## Дочерние компоненты
|
|
||||||
|
|
||||||
Если модулю нужны внутренние подкомпоненты — [генерировать](/applied/templates-generation) их из шаблона `component` в папку `ui/` внутри родительского модуля. Дочерние компоненты не экспортируются через `index.ts` родителя.
|
|
||||||
|
|
||||||
Правила написания компонентов — [Компоненты](/applied/components).
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
---
|
|
||||||
title: Добавление страницы
|
|
||||||
---
|
|
||||||
|
|
||||||
# Добавление страницы
|
|
||||||
|
|
||||||
Как добавить новую страницу в проект по стандартам этого руководства.
|
|
||||||
|
|
||||||
## Что нужно знать
|
|
||||||
|
|
||||||
Страница в проекте — это два файла: экран в `src/screens/` (вся логика, стили, зависимости) и `page.tsx` в `src/app/` (точка входа для роутинга Next.js). Экран генерируется из шаблона, `page.tsx` создаётся вручную.
|
|
||||||
|
|
||||||
## Порядок действий
|
|
||||||
|
|
||||||
1. [Сгенерировать](/applied/templates-generation) экран из шаблона `screen` в папку `src/screens/`.
|
|
||||||
|
|
||||||
2. Заполнить экран логикой и стилями.
|
|
||||||
|
|
||||||
3. Создать `page.tsx` в нужном маршруте `src/app/`. Файл страницы должен быть тонким — только `metadata` и рендер экрана. Никакой логики, стилей и хуков в `page.tsx` не размещается — всё это живёт в экране.
|
|
||||||
|
|
||||||
## Правила
|
|
||||||
|
|
||||||
- Ручное создание файловой структуры экрана запрещено — только [генерация](/applied/templates-generation) из шаблона.
|
|
||||||
- Логика, стили и зависимости размещаются в экране, не в `page.tsx`.
|
|
||||||
- Каждая страница содержит `metadata` с `title` и `description`.
|
|
||||||
|
|
||||||
Примеры `page.tsx` и `metadata` — [Page-level компоненты](/applied/page-level).
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Получение данных
|
|
||||||
---
|
|
||||||
|
|
||||||
# Получение данных
|
|
||||||
|
|
||||||
Как получать данные с сервера — SWR, генерация API-клиентов, сокеты.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Локализация
|
|
||||||
---
|
|
||||||
|
|
||||||
# Локализация
|
|
||||||
|
|
||||||
Как добавлять переводы и подключать локализацию через i18next.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Управление состоянием
|
|
||||||
---
|
|
||||||
|
|
||||||
# Управление состоянием
|
|
||||||
|
|
||||||
Как работать с состоянием — когда создавать стор, что хранить локально и глобально.
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: Стилизация
|
|
||||||
---
|
|
||||||
|
|
||||||
# Стилизация
|
|
||||||
|
|
||||||
Как стилизовать компоненты в проекте — приоритет инструментов и правила их применения.
|
|
||||||
|
|
||||||
## Приоритет стилизации
|
|
||||||
|
|
||||||
Основной UI-фреймворк проекта — **Mantine**. При стилизации компонентов придерживаться следующего приоритета:
|
|
||||||
|
|
||||||
1. **Mantine-компоненты и их пропсы** — в первую очередь использовать встроенные возможности Mantine (пропсы, `classNames`, `styles`).
|
|
||||||
2. **Глобальные CSS-токены** (`--color-*`, `--space-*`, `--radius-*`) — для значений, которые не покрываются Mantine.
|
|
||||||
3. **PostCSS Modules** — когда Mantine не покрывает задачу и нужна кастомная стилизация.
|
|
||||||
|
|
||||||
## Что запрещено
|
|
||||||
|
|
||||||
- **Инлайн-стили** — использование атрибута `style` в компонентах строго запрещено.
|
|
||||||
- **Магические значения** — произвольные цвета, отступы и скругления запрещены, использовать токены.
|
|
||||||
- **Глобальные стили** вне `app/styles/` запрещены.
|
|
||||||
|
|
||||||
Правила написания CSS, вложенность, медиа-запросы и токены — [Стили](/applied/styles).
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
<!-- /index -->
|
|
||||||
# NextJS Style Guide
|
|
||||||
|
|
||||||
Rules and standards for NextJS and TypeScript development: architecture, typing, styles, components, API, and infrastructure.
|
|
||||||
|
|
||||||
## Documentation Structure
|
|
||||||
|
|
||||||
### Processes
|
|
||||||
|
|
||||||
**What to do** in a specific situation — step-by-step instructions.
|
|
||||||
|
|
||||||
| Section | Answers the question |
|
|
||||||
|---------|---------------------|
|
|
||||||
| Getting Started | What tools to install before starting development? |
|
|
||||||
| Creating an App | How to create a new project, where to get a template? |
|
|
||||||
| Creating Pages | How to add a page: routing and screen? |
|
|
||||||
| Creating Components | How to generate components using templates? |
|
|
||||||
| Styling | What to use: Mantine, tokens, or PostCSS? |
|
|
||||||
| Data Fetching | How to fetch data: SWR, codegen, sockets? |
|
|
||||||
| State Management | When and how to create a store (Zustand)? |
|
|
||||||
| Localization | How to add translations and work with i18next? |
|
|
||||||
|
|
||||||
### Basic Rules
|
|
||||||
|
|
||||||
**What the code should look like** — standards not tied to a specific technology.
|
|
||||||
|
|
||||||
| Section | Answers the question |
|
|
||||||
|---------|---------------------|
|
|
||||||
| Tech Stack | What stack do we use? |
|
|
||||||
| Architecture | How are FSD layers, dependencies, and public API structured? |
|
|
||||||
| Code Style | How to format code: indentation, quotes, imports, early return? |
|
|
||||||
| Naming | How to name files, variables, components, hooks? |
|
|
||||||
| Documentation | How to write JSDoc: what to document and what not? |
|
|
||||||
| Typing | How to type: type vs interface, any/unknown? |
|
|
||||||
|
|
||||||
### Applied Sections
|
|
||||||
|
|
||||||
**How a specific area works** — rules, structure, and code examples for specific technologies and tools.
|
|
||||||
|
|
||||||
| Section | Answers the question |
|
|
||||||
|---------|---------------------|
|
|
||||||
| Project Structure | How are folders and files organized by FSD? |
|
|
||||||
| Components | How is a component structured: files, props, clsx? |
|
|
||||||
| Page-level Components | How to define layout, page, loading, error, not-found? |
|
|
||||||
| Templates & Code Generation | How do templates work: syntax, variables, modifiers? |
|
|
||||||
| Styles | How to write CSS: PostCSS Modules, nesting, media, tokens? |
|
|
||||||
| Images | _(not filled)_ |
|
|
||||||
| SVG Sprites | _(not filled)_ |
|
|
||||||
| Video | _(not filled)_ |
|
|
||||||
| API | _(not filled)_ |
|
|
||||||
| Stores | _(not filled)_ |
|
|
||||||
| Hooks | _(not filled)_ |
|
|
||||||
| Fonts | _(not filled)_ |
|
|
||||||
| Localization | _(not filled)_ |
|
|
||||||
|
|
||||||
## For Assistants
|
|
||||||
|
|
||||||
Full documentation in a single MD file: https://gromlab.ru/docs/frontend-style-guide/raw/branch/main/generated/en/RULES.md
|
|
||||||
|
|
||||||
<!-- /basics/tech-stack -->
|
|
||||||
## Tech Stack
|
|
||||||
|
|
||||||
Base technology stack and libraries used in projects.
|
|
||||||
|
|
||||||
<!-- /basics/naming -->
|
|
||||||
## Naming
|
|
||||||
|
|
||||||
Naming should be predictable, concise, and reflect the meaning of the entity.
|
|
||||||
|
|
||||||
<!-- /basics/architecture -->
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
Architecture based on FSD (Feature-Sliced Design) and strict module boundaries.
|
|
||||||
|
|
||||||
<!-- /basics/code-style -->
|
|
||||||
## Code Style
|
|
||||||
|
|
||||||
Unified code formatting rules: indentation, line breaks, quotes, import order, and readability.
|
|
||||||
|
|
||||||
<!-- /basics/documentation -->
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
Documentation should help understand the purpose of an entity, not duplicate its types or obvious details.
|
|
||||||
|
|
||||||
<!-- /basics/typing -->
|
|
||||||
## Typing
|
|
||||||
|
|
||||||
Typing is required for all public interfaces, functions, and components.
|
|
||||||
|
|
||||||
<!-- /applied/project-structure -->
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
Base project structure and principles of module organization at folder and file level.
|
|
||||||
|
|
||||||
<!-- /applied/components -->
|
|
||||||
## Components
|
|
||||||
|
|
||||||
Rules for creating UI components across all FSD layers.
|
|
||||||
|
|
||||||
<!-- /applied/page-level -->
|
|
||||||
## Page-level Components
|
|
||||||
|
|
||||||
Next.js App Router special files used by the framework by convention: `layout.tsx`, `page.tsx`, `loading.tsx`, `error.tsx`, `not-found.tsx`, `template.tsx`.
|
|
||||||
|
|
||||||
<!-- /applied/templates-generation -->
|
|
||||||
## Templates & Code Generation
|
|
||||||
|
|
||||||
Template tools, syntax, and examples for code generation.
|
|
||||||
|
|
||||||
<!-- /applied/styles -->
|
|
||||||
## Styles
|
|
||||||
|
|
||||||
CSS writing rules: PostCSS Modules, nesting, media queries, variables, formatting.
|
|
||||||
|
|
||||||
<!-- /applied/images-sprites -->
|
|
||||||
## Images
|
|
||||||
|
|
||||||
<!-- /applied/svg-sprites -->
|
|
||||||
## SVG Sprites
|
|
||||||
|
|
||||||
<!-- /applied/video -->
|
|
||||||
## Video
|
|
||||||
|
|
||||||
<!-- /applied/api -->
|
|
||||||
## API
|
|
||||||
|
|
||||||
<!-- /applied/stores -->
|
|
||||||
## Stores
|
|
||||||
|
|
||||||
<!-- /applied/hooks -->
|
|
||||||
## Hooks
|
|
||||||
|
|
||||||
<!-- /applied/fonts -->
|
|
||||||
## Fonts
|
|
||||||
|
|
||||||
<!-- /applied/localization -->
|
|
||||||
## Localization
|
|
||||||
File diff suppressed because it is too large
Load Diff
166
notes
166
notes
@@ -1,153 +1,23 @@
|
|||||||
ФЛОУ
|
# TODO
|
||||||
- после создания компонента, заменить шаблонный коментарий документа на реальный.
|
|
||||||
|
|
||||||
|
## Триггеры: классификация и расширение
|
||||||
|
|
||||||
Проблема, неочевидность слоев (наследие FSD)
|
Текущий список триггеров слабо проработан. Нужно:
|
||||||
|
|
||||||
|
1. Классифицировать триггеры по группам:
|
||||||
|
- Создание — новые модули, компоненты, страницы
|
||||||
|
- Ресурсы — ассеты (иконки, шрифты, изображения, видео)
|
||||||
|
- Данные — API, сторы, серверные данные, формы
|
||||||
|
- Навигация — роутинг, middleware, редиректы
|
||||||
|
- Модификация — рефакторинг, перенос, удаление
|
||||||
|
- Инфраструктура — зависимости, переводы, настройка окружения
|
||||||
|
|
||||||
Архитектурные слои проекта
|
2. Добавить недостающие триггеры. Примеры пробелов:
|
||||||
Каждый нижний слой не знает о существовании верхних. Импорты идут только сверху вниз.
|
- Создание: утилита/хелпер, тип/интерфейс, контекст
|
||||||
pages → layouts → screens → widgets → features → entities → shared
|
- Данные: создать форму, добавить валидацию
|
||||||
---
|
- Навигация: динамический роут, middleware, редирект
|
||||||
1. Pages (pages/)
|
- Модификация: рефакторинг компонента, перенос модуля, удаление модуля
|
||||||
Точка входа маршрута. Только связывает layout и screen.
|
- Обработка ошибок: error boundary, fallback UI, error.tsx, not-found.tsx, loading.tsx
|
||||||
Правила:
|
- Авторизация: защита страницы, проверка прав
|
||||||
- Никакой логики, стилей, разметки кроме композиции
|
|
||||||
- Один page = один layout + один screen
|
|
||||||
Пример:
|
|
||||||
// pages/knv-new.js
|
|
||||||
import { KnvScreen } from 'src/screens/knv'
|
|
||||||
import { MainLayout } from 'src/layouts/main'
|
|
||||||
const KnvNewPage = () => (
|
|
||||||
<MainLayout>
|
|
||||||
<KnvScreen />
|
|
||||||
</MainLayout>
|
|
||||||
)
|
|
||||||
---
|
|
||||||
2. Layouts (src/layouts/)
|
|
||||||
Каркас страницы — общие элементы, которые одинаковы на всех страницах в рамках этого layout.
|
|
||||||
Содержит в ui/: header, footer, sidebar — дочерние компоненты, которые привязаны к layout и не переиспользуются отдельно.
|
|
||||||
Критерий: компонент одинаков на всех страницах, использующих этот layout? → layouts/{name}/ui/
|
|
||||||
Пример:
|
|
||||||
src/layouts/main/
|
|
||||||
├── main.layout.tsx # <Header /> + children + <Footer />
|
|
||||||
├── ui/
|
|
||||||
│ ├── header/ # всегда одинаковый на всех страницах
|
|
||||||
│ └── footer/ # всегда одинаковый на всех страницах
|
|
||||||
---
|
|
||||||
3. Screens (src/screens/)
|
|
||||||
Контент конкретной страницы. Собирает свои секции и переиспользуемые widgets/features/entities.
|
|
||||||
Содержит в ui/: блоки, которые существуют только на этой странице и не переиспользуются.
|
|
||||||
Критерий: компонент используется только на одной странице? → screens/{name}/ui/
|
|
||||||
Пример:
|
|
||||||
src/screens/knv/
|
|
||||||
├── knv.screen.tsx
|
|
||||||
├── ui/
|
|
||||||
│ ├── hero-section/ # hero только на главной КНВ
|
|
||||||
│ ├── products-section/ # секция препаратов только на главной
|
|
||||||
│ ├── diseases-section/ # секция заболеваний только на главной
|
|
||||||
│ └── doctor-section/ # секция врачей только на главной
|
|
||||||
Каждая секция внутри может использовать shared/ui компоненты:
|
|
||||||
// screens/knv/ui/products-section/products-section.widget.tsx
|
|
||||||
import { Carousel } from 'src/shared/ui/carousel'
|
|
||||||
import { ProductCard } from './ui/product-card' // локальный, пока не переиспользуется
|
|
||||||
Когда локальный компонент начинает использоваться на 2+ страницах — выносим в entities/ или shared/ui.
|
|
||||||
---
|
|
||||||
4. Widgets (src/widgets/)
|
|
||||||
Составные блоки с данными/логикой, которые переиспользуются на 2+ страницах.
|
|
||||||
Критерий: блок с бизнес-логикой + данными используется на нескольких страницах? → widgets/
|
|
||||||
Пример: Слайдер «Популярные препараты» с загрузкой данных из API, который показывается и на главной, и на странице заболевания, и в каталоге:
|
|
||||||
src/widgets/
|
|
||||||
├── popular-products-slider/
|
|
||||||
│ ├── popular-products-slider.widget.tsx # Carousel + ProductCard + useProducts()
|
|
||||||
│ ├── hooks/
|
|
||||||
│ │ └── use-products.hook.ts # запрос данных
|
|
||||||
Не widget: секция «Подобрать врача» которая есть только на главной → screens/knv/ui/
|
|
||||||
---
|
|
||||||
5. Features (src/features/)
|
|
||||||
Пользовательское действие или интерактивный сценарий. Содержит бизнес-логику взаимодействия.
|
|
||||||
Критерий: это действие пользователя (отправить форму, авторизоваться, добавить в корзину)? → features/
|
|
||||||
Примеры:
|
|
||||||
src/features/
|
|
||||||
├── auth/ # авторизация (форма + логика + стор)
|
|
||||||
│ ├── auth.feature.tsx
|
|
||||||
│ ├── hooks/
|
|
||||||
│ │ └── use-auth.hook.ts
|
|
||||||
│ └── stores/
|
|
||||||
│ └── auth.store.ts
|
|
||||||
│
|
|
||||||
├── order-drug/ # заказ препарата (кнопка + модалка + API)
|
|
||||||
│ ├── order-drug.feature.tsx
|
|
||||||
│ └── hooks/
|
|
||||||
│ └── use-order.hook.ts
|
|
||||||
Не feature: отображение карточки препарата без взаимодействия → entities/ или shared/ui
|
|
||||||
---
|
|
||||||
6. Entities (src/entities/)
|
|
||||||
Бизнес-сущность с её отображением и типами. Привязана к домену (препарат, заболевание, врач, пользователь).
|
|
||||||
Критерий: это представление бизнес-объекта, которое переиспользуется в разных контекстах? → entities/
|
|
||||||
Примеры:
|
|
||||||
src/entities/
|
|
||||||
├── product/ # Препарат
|
|
||||||
│ ├── ui/
|
|
||||||
│ │ └── product-card/ # карточка препарата (каталог, слайдеры, поиск)
|
|
||||||
│ ├── types/
|
|
||||||
│ │ └── product.type.ts # { id, name, mnn, indication }
|
|
||||||
│ └── index.ts
|
|
||||||
│
|
|
||||||
├── disease/ # Заболевание
|
|
||||||
│ ├── ui/
|
|
||||||
│ │ └── disease-card/
|
|
||||||
│ ├── types/
|
|
||||||
│ │ └── disease.type.ts
|
|
||||||
│ └── index.ts
|
|
||||||
Отличие от shared/ui: entity-компонент знает о бизнес-домене (принимает Product, а не абстрактные пропсы). shared/ui Button не знает ничего о бизнесе.
|
|
||||||
---
|
|
||||||
7. Shared (src/shared/)
|
|
||||||
Переиспользуемые компоненты, утилиты, стили без бизнес-логики.
|
|
||||||
Критерий: компонент не знает о бизнес-домене, работает с абстрактными данными? → shared/
|
|
||||||
src/shared/
|
|
||||||
├── ui/ # UI-компоненты
|
|
||||||
│ ├── carousel/ # принимает children, не знает о препаратах
|
|
||||||
│ ├── container/
|
|
||||||
│ ├── section/
|
|
||||||
│ └── icon-svg/
|
|
||||||
├── styles/ # CSS-переменные, media
|
|
||||||
│ ├── variables.css
|
|
||||||
│ └── media.css
|
|
||||||
├── sprites/ # SVG-спрайты
|
|
||||||
└── lib/ # утилиты, хелперы
|
|
||||||
---
|
|
||||||
Сводная таблица принятия решений
|
|
||||||
Вопрос Да → Нет ↓
|
|
||||||
Это точка входа маршрута? pages/ ↓
|
|
||||||
Одинаков на всех страницах layout? layouts/{name}/ui/ ↓
|
|
||||||
Используется только на одной странице? screens/{name}/ui/ ↓
|
|
||||||
Составной блок с данными на 2+ страницах? widgets/ ↓
|
|
||||||
Это действие пользователя с логикой? features/ ↓
|
|
||||||
Привязан к бизнес-сущности? entities/ ↓
|
|
||||||
Абстрактный UI без бизнес-логики? shared/ui/ —
|
|
||||||
|
|
||||||
|
3. Обновить секцию "Триггеры" в DEVELOP.md — перейти на новые группы.
|
||||||
И еще размышлений
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Что у нас по факту
|
|
||||||
Мы взяли FSD и добавили:
|
|
||||||
- Слой Layouts — каркас страницы
|
|
||||||
- Слой Screens — контент страницы с локальными компонентами
|
|
||||||
- Правило scope — компонент живёт максимально близко к месту использования, и поднимается на уровень выше только при переиспользовании
|
|
||||||
Это не FSD в чистом виде. Но и не что-то принципиально новое.
|
|
||||||
Существующие названия
|
|
||||||
Feature-Sliced Design (FSD) — то от чего отталкиваемся, но расходимся в деталях.
|
|
||||||
Screaming Architecture (Robert C. Martin) — структура проекта "кричит" о том, что он делает. Открываешь screens/knv/ui/ — видишь КНВ. Открываешь widgets/ — видишь глобальное. Это близко к нашему подходу.
|
|
||||||
Colocation Principle — держи код рядом с тем, где он используется. Именно этот принцип отличает нас от FSD: page-specific компоненты лежат внутри screen, а не в глобальном widgets/.
|
|
||||||
Как назвать
|
|
||||||
Я бы не стал придумывать новое название. Это:
|
|
||||||
> FSD с принципом колокации (FSD + Colocation)
|
|
||||||
Или проще — адаптированный FSD. Суть отличия укладывается в одно правило:
|
|
||||||
> Компонент живёт на самом низком уровне, где он используется. Поднимается выше только при переиспользовании на 2+ страницах.
|
|
||||||
Если хочется краткое название для внутренней документации — можно SLD (Scoped Layer Design), подчёркивая что каждый компонент привязан к scope (shared → entity → feature → widget → screen → layout → page).
|
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "nextjs-style-guide",
|
"name": "frontend-style-guide",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "nextjs-style-guide",
|
"name": "frontend-style-guide",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitepress": "^1.6.3"
|
"vitepress": "^1.6.3"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "nextjs-style-guide",
|
"name": "frontend-style-guide",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs": "node ./concat-md.js",
|
"build:ai": "node ./scripts/build-ai.js",
|
||||||
"dev": "vitepress dev .",
|
"dev": "vitepress dev .",
|
||||||
"build": "vitepress build .",
|
"build": "vitepress build .",
|
||||||
"serve": "vitepress serve ."
|
"serve": "vitepress serve ."
|
||||||
|
|||||||
88
scripts/build-ai.js
Normal file
88
scripts/build-ai.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { pathToFileURL } from "url";
|
||||||
|
|
||||||
|
const SRC_DIR = "./src";
|
||||||
|
const DIST_DIR = "./dist/ai";
|
||||||
|
const SCRIPTS_DIR = "./scripts";
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Сборка по манифесту
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function buildForFramework(framework) {
|
||||||
|
const manifestPath = path.join(SCRIPTS_DIR, `${framework}.build.js`);
|
||||||
|
|
||||||
|
if (!fs.existsSync(manifestPath)) {
|
||||||
|
console.error(`Манифест не найден: ${manifestPath}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const manifest = (await import(pathToFileURL(path.resolve(manifestPath)).href)).default;
|
||||||
|
const outDir = path.join(DIST_DIR, framework);
|
||||||
|
|
||||||
|
// Очищаем выходную директорию
|
||||||
|
if (fs.existsSync(outDir)) {
|
||||||
|
fs.rmSync(outDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\nСборка: ${manifest.name} (${framework})`);
|
||||||
|
console.log(`Выход: ${outDir}\n`);
|
||||||
|
|
||||||
|
const errors = [];
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (const [destRelative, srcRelative] of Object.entries(manifest.files)) {
|
||||||
|
const srcPath = path.join(SRC_DIR, srcRelative);
|
||||||
|
const destPath = path.join(outDir, destRelative);
|
||||||
|
|
||||||
|
if (!fs.existsSync(srcPath)) {
|
||||||
|
errors.push(` [!] Не найден: ${srcRelative}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
||||||
|
fs.copyFileSync(srcPath, destPath);
|
||||||
|
console.log(` ${destRelative}`);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
console.log(`\nОшибки:`);
|
||||||
|
errors.forEach((e) => console.log(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\nГотово: ${outDir} (${count} файлов)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Определяем что собирать
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
let frameworks = fs
|
||||||
|
.readdirSync(SCRIPTS_DIR)
|
||||||
|
.filter((f) => f.endsWith(".build.js"))
|
||||||
|
.map((f) => f.replace(".build.js", ""));
|
||||||
|
|
||||||
|
if (frameworks.length === 0) {
|
||||||
|
console.error("Не найдено ни одного манифеста *.build.js в scripts/");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --framework=nextjs
|
||||||
|
const fwArg = process.argv.find((a) => a.startsWith("--framework="));
|
||||||
|
if (fwArg) {
|
||||||
|
const fw = fwArg.split("=")[1];
|
||||||
|
if (frameworks.includes(fw)) {
|
||||||
|
frameworks = [fw];
|
||||||
|
} else {
|
||||||
|
console.error(`Фреймворк "${fw}" не найден. Доступные: ${frameworks.join(", ")}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const fw of frameworks) {
|
||||||
|
await buildForFramework(fw);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("\nВсе сборки завершены.");
|
||||||
69
scripts/nextjs.build.js
Normal file
69
scripts/nextjs.build.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Манифест сборки стайлгайда для Next.js.
|
||||||
|
*
|
||||||
|
* Ключ — путь файла в dist/ai/nextjs/.
|
||||||
|
* Значение — путь исходника относительно src/.
|
||||||
|
*
|
||||||
|
* Скрипт только копирует. Никакой генерации.
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: "Next.js",
|
||||||
|
|
||||||
|
files: {
|
||||||
|
// ── Точки входа ─────────────────────────────────────────────
|
||||||
|
"DEVELOP.md": "nextjs/DEVELOP.md",
|
||||||
|
|
||||||
|
// ── Базовые правила ─────────────────────────────────────────
|
||||||
|
"basics/architecture.md": "base/basics/architecture.md",
|
||||||
|
"basics/code-style.md": "base/basics/code-style.md",
|
||||||
|
"basics/documentation.md": "base/basics/documentation.md",
|
||||||
|
"basics/naming.md": "base/basics/naming.md",
|
||||||
|
"basics/tech-stack.md": "base/basics/tech-stack.md",
|
||||||
|
"basics/typing.md": "base/basics/typing.md",
|
||||||
|
|
||||||
|
// ── Прикладные разделы ──────────────────────────────────────
|
||||||
|
"applied/components.md": "base/applied/components.md",
|
||||||
|
"applied/styles.md": "base/applied/styles.md",
|
||||||
|
"applied/templates-generation.md": "base/applied/templates-generation.md",
|
||||||
|
"applied/hooks.md": "base/applied/hooks.md",
|
||||||
|
"applied/stores.md": "base/applied/stores.md",
|
||||||
|
"applied/api.md": "base/applied/api.md",
|
||||||
|
"applied/fonts.md": "base/applied/fonts.md",
|
||||||
|
"applied/localization.md": "base/applied/localization.md",
|
||||||
|
"applied/images-sprites.md": "base/applied/images-sprites.md",
|
||||||
|
"applied/svg-sprites.md": "base/applied/svg-sprites.md",
|
||||||
|
"applied/video.md": "base/applied/video.md",
|
||||||
|
"applied/vscode.md": "base/applied/vscode.md",
|
||||||
|
"applied/page-level.md": "nextjs/applied/page-level.md",
|
||||||
|
"applied/project-structure.md": "nextjs/applied/project-structure.md",
|
||||||
|
|
||||||
|
// ── Триггеры: разработка / создание ─────────────────────────
|
||||||
|
"triggers/develop/create-component.md": "base/triggers/develop/create-component.md",
|
||||||
|
"triggers/develop/create-feature.md": "base/triggers/develop/create-feature.md",
|
||||||
|
"triggers/develop/create-widget.md": "base/triggers/develop/create-widget.md",
|
||||||
|
"triggers/develop/create-entity.md": "base/triggers/develop/create-entity.md",
|
||||||
|
"triggers/develop/create-hook.md": "base/triggers/develop/create-hook.md",
|
||||||
|
"triggers/develop/create-store.md": "base/triggers/develop/create-store.md",
|
||||||
|
"triggers/develop/create-page.md": "nextjs/triggers/develop/create-page.md",
|
||||||
|
"triggers/develop/create-layout.md": "nextjs/triggers/develop/create-layout.md",
|
||||||
|
"triggers/develop/create-project.md": "nextjs/triggers/develop/create-project.md",
|
||||||
|
"triggers/develop/generate-module.md": "base/triggers/develop/generate-module.md",
|
||||||
|
|
||||||
|
// ── Триггеры: разработка / стилизация и ресурсы ─────────────
|
||||||
|
"triggers/develop/style-component.md": "base/triggers/develop/style-component.md",
|
||||||
|
"triggers/develop/add-icon.md": "base/triggers/develop/add-icon.md",
|
||||||
|
"triggers/develop/add-image.md": "base/triggers/develop/add-image.md",
|
||||||
|
"triggers/develop/add-video.md": "base/triggers/develop/add-video.md",
|
||||||
|
"triggers/develop/add-font.md": "base/triggers/develop/add-font.md",
|
||||||
|
|
||||||
|
// ── Триггеры: разработка / данные и состояние ───────────────
|
||||||
|
"triggers/develop/add-api-request.md": "base/triggers/develop/add-api-request.md",
|
||||||
|
"triggers/develop/connect-store.md": "base/triggers/develop/connect-store.md",
|
||||||
|
"triggers/develop/add-server-data.md": "nextjs/triggers/develop/add-server-data.md",
|
||||||
|
|
||||||
|
// ── Триггеры: разработка / инфраструктура ───────────────────
|
||||||
|
"triggers/develop/add-localization.md": "base/triggers/develop/add-localization.md",
|
||||||
|
"triggers/develop/add-dependency.md": "base/triggers/develop/add-dependency.md",
|
||||||
|
"triggers/develop/setup-vscode.md": "base/triggers/develop/setup-vscode.md",
|
||||||
|
},
|
||||||
|
};
|
||||||
5
src/base/applied/api.md
Normal file
5
src/base/applied/api.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
scope: applied
|
||||||
|
keywords: [api, запрос, fetch, SWR, эндпоинт, REST, клиент]
|
||||||
|
when: "Работа с API: запросы, клиенты, обработка ответов"
|
||||||
|
---
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Компоненты
|
title: Компоненты
|
||||||
|
scope: applied
|
||||||
|
keywords: [компонент, props, jsx, ui, clsx, cl, React, FC]
|
||||||
|
when: "Создание или редактирование React-компонентов: структура, пропсы, стили"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Компоненты
|
# Компоненты
|
||||||
|
|
||||||
Правила написания React-компонентов: файловая структура модуля, типизация пропсов, документирование и реализация. Раздел охватывает компоненты всех слоёв — от `shared/ui` до `screens`.
|
Правила написания React-компонентов: файловая структура модуля, типизация пропсов, документирование и реализация. Раздел охватывает компоненты всех слоёв — от `shared/ui` до `screens`.
|
||||||
@@ -110,3 +112,7 @@ export const Container = (props: ContainerProps) => {
|
|||||||
```ts
|
```ts
|
||||||
export { Container } from './container'
|
export { Container } from './container'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Дочерние компоненты
|
||||||
|
|
||||||
|
Если модулю нужны внутренние подкомпоненты — генерировать их из шаблона `component` в папку `ui/` внутри родительского модуля. Дочерние компоненты не экспортируются через `index.ts` родителя.
|
||||||
5
src/base/applied/fonts.md
Normal file
5
src/base/applied/fonts.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
scope: applied
|
||||||
|
keywords: [шрифт, font, next/font, подключение шрифта, woff]
|
||||||
|
when: "Подключение и настройка шрифтов"
|
||||||
|
---
|
||||||
5
src/base/applied/hooks.md
Normal file
5
src/base/applied/hooks.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
scope: applied
|
||||||
|
keywords: [хук, hook, use, кастомный хук, useState, useEffect]
|
||||||
|
when: "Создание или использование кастомных хуков"
|
||||||
|
---
|
||||||
5
src/base/applied/images-sprites.md
Normal file
5
src/base/applied/images-sprites.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
scope: applied
|
||||||
|
keywords: [изображение, картинка, image, next/image, public, оптимизация]
|
||||||
|
when: "Работа с изображениями: подключение, оптимизация"
|
||||||
|
---
|
||||||
5
src/base/applied/localization.md
Normal file
5
src/base/applied/localization.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
scope: applied
|
||||||
|
keywords: [i18n, локализация, перевод, язык, i18next, namespace]
|
||||||
|
when: "Локализация: добавление переводов, работа с i18next"
|
||||||
|
---
|
||||||
5
src/base/applied/stores.md
Normal file
5
src/base/applied/stores.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
scope: applied
|
||||||
|
keywords: [стор, store, zustand, состояние, глобальное состояние]
|
||||||
|
when: "Работа с глобальным состоянием: создание стора, подписка"
|
||||||
|
---
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Стили
|
title: Стили
|
||||||
|
scope: applied
|
||||||
|
keywords: [css, postcss, модули, css modules, токены, медиа-запросы, вложенность, класс]
|
||||||
|
when: "Стилизация: CSS Modules, PostCSS, переменные, медиа-запросы"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Стили
|
# Стили
|
||||||
|
|
||||||
Раздел описывает правила написания CSS: PostCSS Modules, вложенность, медиа-запросы, переменные, форматирование.
|
Раздел описывает правила написания CSS: PostCSS Modules, вложенность, медиа-запросы, переменные, форматирование.
|
||||||
@@ -267,3 +269,17 @@ title: Стили
|
|||||||
|
|
||||||
- Желательно не писать комментарии в CSS.
|
- Желательно не писать комментарии в CSS.
|
||||||
- Исключение — нетривиальные хаки и обходные решения, к которым стоит оставить пояснение.
|
- Исключение — нетривиальные хаки и обходные решения, к которым стоит оставить пояснение.
|
||||||
|
|
||||||
|
## Приоритет стилизации
|
||||||
|
|
||||||
|
Основной UI-фреймворк проекта — **Mantine**. При стилизации компонентов придерживаться следующего приоритета:
|
||||||
|
|
||||||
|
1. **Mantine-компоненты и их пропсы** — в первую очередь использовать встроенные возможности Mantine (пропсы, `classNames`, `styles`).
|
||||||
|
2. **Глобальные CSS-токены** (`--color-*`, `--space-*`, `--radius-*`) — для значений, которые не покрываются Mantine.
|
||||||
|
3. **PostCSS Modules** — когда Mantine не покрывает задачу и нужна кастомная стилизация.
|
||||||
|
|
||||||
|
## Что запрещено
|
||||||
|
|
||||||
|
- **Инлайн-стили** — использование атрибута `style` в компонентах строго запрещено.
|
||||||
|
- **Магические значения** — произвольные цвета, отступы и скругления запрещены, использовать токены.
|
||||||
|
- **Глобальные стили** вне `app/styles/` запрещены.
|
||||||
7
src/base/applied/svg-sprites.md
Normal file
7
src/base/applied/svg-sprites.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
title: SVG-спрайты
|
||||||
|
scope: applied
|
||||||
|
keywords: [svg, спрайт, иконка, icon, sprite]
|
||||||
|
when: "Работа с SVG-иконками и спрайтами"
|
||||||
|
---
|
||||||
|
# SVG-спрайты
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Шаблоны и генерация кода
|
title: Шаблоны и генерация кода
|
||||||
|
scope: applied
|
||||||
|
keywords: [шаблон, генерация, template, scaffold, plop, hygen, .templates]
|
||||||
|
when: "Генерация кода из шаблонов, создание новых шаблонов"
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- @formatter:off -->
|
<!-- @formatter:off -->
|
||||||
::: v-pre
|
::: v-pre
|
||||||
|
|
||||||
@@ -154,3 +156,20 @@ npx @gromlab/create <шаблон> <имя> <путь>
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## Какие модули генерируются из шаблонов
|
||||||
|
|
||||||
|
| Модуль | Слой | Шаблон |
|
||||||
|
|---|---|---|
|
||||||
|
| Компонент | `shared/ui/` | `component` |
|
||||||
|
| Фича | `features/` | `feature` |
|
||||||
|
| Виджет | `widgets/` | `widget` |
|
||||||
|
| Сущность | `entities/` | `entity` |
|
||||||
|
| Layout | `layouts/` | `layout` |
|
||||||
|
| Экран | `screens/` | `screen` |
|
||||||
|
| Стор | `model/` | `store` |
|
||||||
|
|
||||||
|
## Когда создавать новый шаблон
|
||||||
|
|
||||||
|
- Повторяющаяся структура появляется больше одного раза.
|
||||||
|
- Существующий шаблон не покрывает нужный тип модуля.
|
||||||
|
|
||||||
5
src/base/applied/video.md
Normal file
5
src/base/applied/video.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
scope: applied
|
||||||
|
keywords: [видео, video, плеер, mp4]
|
||||||
|
when: "Встраивание и работа с видео"
|
||||||
|
---
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Настройка VS Code
|
title: Настройка VS Code
|
||||||
|
scope: applied
|
||||||
|
keywords: [vscode, редактор, расширение, настройка, extension, .vscode]
|
||||||
|
when: "Настройка VS Code: расширения, settings.json, сниппеты"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Настройка VS Code
|
# Настройка VS Code
|
||||||
|
|
||||||
Каждый проект содержит папку `.vscode/` с конфигурацией редактора. Это гарантирует, что все участники команды работают с одинаковыми настройками форматирования, линтинга и расширениями.
|
Каждый проект содержит папку `.vscode/` с конфигурацией редактора. Это гарантирует, что все участники команды работают с одинаковыми настройками форматирования, линтинга и расширениями.
|
||||||
665
src/base/basics/architecture.md
Normal file
665
src/base/basics/architecture.md
Normal file
@@ -0,0 +1,665 @@
|
|||||||
|
---
|
||||||
|
title: Архитектура
|
||||||
|
scope: basics
|
||||||
|
keywords: [SLM Design, слой, модуль, сегмент, архитектура, FSD, scoped layered module]
|
||||||
|
when: "Организация кода: слои, модули, зависимости между модулями"
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- /index -->
|
||||||
|
# SLM Design
|
||||||
|
Scoped Layered Module Design — модульная архитектура фронтенд-приложений. Код организован по слоям ответственности, а модуль содержит всё, что ему нужно: компоненты, хуки, сторы, типы, стили.
|
||||||
|
|
||||||
|
## Преимущества
|
||||||
|
|
||||||
|
### Вертикальная организация домена
|
||||||
|
|
||||||
|
Бизнес-домен не разбивается по техническим слоям — сценарии, сущности, типы и UI живут в одном модуле. Это сокращает время навигации и упрощает сопровождение: все изменения домена локализованы.
|
||||||
|
|
||||||
|
### Dependency Injection без фреймворков
|
||||||
|
|
||||||
|
Cross-domain зависимости в бизнес-слое реализуются через фабрики — модуль декларирует что ему нужно, а точка использования предоставляет зависимости. Домены изолированы без DI-контейнеров, провайдеров и шин событий.
|
||||||
|
|
||||||
|
### Разделение ответственности без перегрузки слоёв
|
||||||
|
|
||||||
|
Сервисы приложения (`infrastructure/`), UI-кит (`ui/`) и общие ресурсы (`shared/`) — три разных слоя с разной природой. Ни один слой не превращается в свалку разнородного кода.
|
||||||
|
|
||||||
|
### Горизонтальная инкапсуляция
|
||||||
|
|
||||||
|
Вложенные модули (`parts/`) и направление зависимостей позволяют нескольким разработчикам работать над одной областью приложения параллельно, не затрагивая код друг друга.
|
||||||
|
|
||||||
|
### Колокация по умолчанию
|
||||||
|
|
||||||
|
Код начинает жизнь рядом с местом использования и поднимается в общие слои только при реальной потребности. Глобальные слои не засоряются преждевременными абстракциями.
|
||||||
|
|
||||||
|
### Явное разделение каркаса и контента
|
||||||
|
|
||||||
|
Каркас группы маршрутов (`layouts/`) и контент конкретной страницы (`screens/`) — независимые слои с собственной ответственностью.
|
||||||
|
|
||||||
|
### Масштабирование через группировку
|
||||||
|
|
||||||
|
При росте проекта слои не теряют структуру — модули группируются по естественным признакам: бизнес-домены по субдоменам, страницы по разделам, UI-компоненты по уровню абстракции (примитивы и композиции).
|
||||||
|
|
||||||
|
## Происхождение
|
||||||
|
|
||||||
|
SLM Design вырос на основе:
|
||||||
|
|
||||||
|
- **Feature-Sliced Design** — слоистая структура, публичный API модуля, направление зависимостей
|
||||||
|
- **Vertical Slice Architecture** — модуль как вертикальный срез, содержащий всё необходимое
|
||||||
|
- **Screaming Architecture** — структура проекта «кричит» о назначении: открыл `business/auth` — видишь авторизацию
|
||||||
|
- **Colocation Principle** — код живёт рядом с местом использования
|
||||||
|
|
||||||
|
## Пример структуры проекта
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/
|
||||||
|
├── app/
|
||||||
|
│
|
||||||
|
├── layouts/
|
||||||
|
│ ├── main/
|
||||||
|
│ └── dashboard/
|
||||||
|
│
|
||||||
|
├── screens/
|
||||||
|
│ ├── home/
|
||||||
|
│ ├── products/
|
||||||
|
│ ├── product-detail/
|
||||||
|
│ └── about/
|
||||||
|
│
|
||||||
|
├── widgets/
|
||||||
|
│ ├── page-heading/
|
||||||
|
│ ├── hero-section/
|
||||||
|
│ └── promo-banner/
|
||||||
|
│
|
||||||
|
├── business/
|
||||||
|
│ ├── auth/
|
||||||
|
│ ├── catalog/
|
||||||
|
│ ├── orders/
|
||||||
|
│ └── chat/
|
||||||
|
│
|
||||||
|
├── infrastructure/
|
||||||
|
│ ├── theme/
|
||||||
|
│ ├── i18n/
|
||||||
|
│ ├── backend-api/
|
||||||
|
│ └── logger/
|
||||||
|
│
|
||||||
|
├── ui/
|
||||||
|
│ ├── button/
|
||||||
|
│ ├── input/
|
||||||
|
│ ├── modal/
|
||||||
|
│ ├── toast/
|
||||||
|
│ └── dropdown/
|
||||||
|
│
|
||||||
|
└── shared/
|
||||||
|
├── lib/
|
||||||
|
├── types/
|
||||||
|
└── styles/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Принципы
|
||||||
|
|
||||||
|
- **Домен — единое целое.** Всё, что относится к домену, живёт в одном модуле.
|
||||||
|
- **Колокация.** Код рождается рядом с местом использования и поднимается только при необходимости.
|
||||||
|
- **Зависимости однонаправлены.** Импорты только сверху вниз, только через публичный API.
|
||||||
|
- **Архитектура — каркас, не клетка.** Правила фиксируют направление зависимостей и структуру модуля, остальное определяет команда.
|
||||||
|
|
||||||
|
<!-- /reference/layers -->
|
||||||
|
## Слои
|
||||||
|
|
||||||
|
Раздел описывает слои SLM: что такое слой, какие бывают, как между ними направлены зависимости и какие правила действуют на каждом.
|
||||||
|
|
||||||
|
### Определение
|
||||||
|
|
||||||
|
**Слой — уровень организации кода внутри `src/`. Каждый слой отвечает за свою область (каркас страницы, бизнес-логика, UI-кит) и задаёт правила для кода внутри: направление импортов, именование, допустимые связи между модулями.**
|
||||||
|
|
||||||
|
### Группы слоёв
|
||||||
|
|
||||||
|
Слои делятся на три группы:
|
||||||
|
|
||||||
|
| Группа | Слои | Описание |
|
||||||
|
|--------|------|----------|
|
||||||
|
| Композиция | `app`, `layouts`, `screens`, `widgets` | Собирают интерфейс из готовых блоков: маршруты, каркасы, страницы |
|
||||||
|
| Ядро | `business`, `infrastructure`, `ui` | Реализация продукта: бизнес-домены, техсервисы, UI-кит |
|
||||||
|
| Фундамент | `shared` | Общие ресурсы: утилиты, хелперы, стили, конфиги |
|
||||||
|
|
||||||
|
### Направление зависимостей
|
||||||
|
|
||||||
|
Любой импорт между модулями — только через публичный API.
|
||||||
|
|
||||||
|
```
|
||||||
|
app → [ layouts | screens ] → widgets → business → infrastructure → ui → shared
|
||||||
|
```
|
||||||
|
|
||||||
|
- `layouts` и `screens` — параллельные слои, не импортируют друг друга
|
||||||
|
- Модули одного слоя в группе «Композиция» изолированы друг от друга
|
||||||
|
- Модули одного слоя `infrastructure` и `ui` могут импортировать друг друга через публичный API
|
||||||
|
- Модули `business` — cross-domain зависимости по коду через фабрику, `import type` напрямую
|
||||||
|
- Импорт типов (`import type`) в «Ядре» разрешён в обоих направлениях
|
||||||
|
|
||||||
|
|
||||||
|
### Слой App
|
||||||
|
|
||||||
|
Точка входа приложения. Отвечает за запуск, роутинг и композицию маршрутов из layout и screen.
|
||||||
|
|
||||||
|
В отличие от остальных слоёв, `app/` не содержит модулей SLM. Здесь живут только инфраструктурные файлы, которые не могут быть никаким другим слоем: файлы фреймворка роутинга, точка запуска и код инициализации.
|
||||||
|
|
||||||
|
#### Требования
|
||||||
|
|
||||||
|
- Не содержит модулей SLM — только файлы фреймворка, роутинг, инициализация
|
||||||
|
- Содержит: файлы маршрутов, bootstrap, обработку ошибок верхнего уровня (404, error boundary), подключение глобальных стилей и ассетов
|
||||||
|
- Провайдеры и гарды — только подключает готовые из нижних слоёв, не реализует
|
||||||
|
- Не содержит бизнес-логику, UI-компоненты, хуки, сторы, сервисы
|
||||||
|
- Никем не импортируется
|
||||||
|
|
||||||
|
### Слой Layouts
|
||||||
|
|
||||||
|
Каркас страницы: общие элементы, одинаковые для группы маршрутов (header, footer, sidebar).
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/layouts/
|
||||||
|
├── main/
|
||||||
|
├── dashboard/
|
||||||
|
└── auth/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Требования
|
||||||
|
|
||||||
|
- Содержит только модули
|
||||||
|
- Не содержит бизнес-логику
|
||||||
|
- Контекстно-зависимые блоки принимает через пропсы от `app`, не импортирует напрямую
|
||||||
|
|
||||||
|
### Слой Screens
|
||||||
|
|
||||||
|
Контент конкретной страницы: собирает её из модулей нижних слоёв.
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/screens/
|
||||||
|
├── home/
|
||||||
|
├── products/
|
||||||
|
├── product-detail/
|
||||||
|
├── about/
|
||||||
|
└── contacts/
|
||||||
|
```
|
||||||
|
|
||||||
|
Когда количество страниц затрудняет навигацию — вводится группировка по разделам. Группа — папка для организации, не модуль (без `index.ts`).
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/screens/
|
||||||
|
├── shop/
|
||||||
|
│ ├── home/
|
||||||
|
│ ├── products/
|
||||||
|
│ ├── product-detail/
|
||||||
|
│ └── cart/
|
||||||
|
├── account/
|
||||||
|
│ ├── profile/
|
||||||
|
│ ├── settings/
|
||||||
|
│ └── order-history/
|
||||||
|
└── info/
|
||||||
|
├── about/
|
||||||
|
├── contacts/
|
||||||
|
└── faq/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Требования
|
||||||
|
|
||||||
|
- Содержит только модули
|
||||||
|
- Не содержит бизнес-логику
|
||||||
|
- Локальные одноразовые секции живут внутри screen-модуля, не выносятся в `widgets`/`business`
|
||||||
|
|
||||||
|
### Слой Widgets
|
||||||
|
|
||||||
|
Составной блок интерфейса, который компонует модули ядра, но не принадлежит конкретному бизнес-домену. Widget появляется когда блок используется в нескольких screens или layouts.
|
||||||
|
|
||||||
|
Если блок принадлежит домену — он живёт в `business/{area}/`, даже если переиспользуется. Если блок нужен только в одном месте — это `screens/{name}/parts/` или `layouts/{name}/parts/`, а не widget.
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/widgets/
|
||||||
|
├── page-heading/
|
||||||
|
├── hero-section/
|
||||||
|
├── onboarding-checklist/
|
||||||
|
├── promo-banner/
|
||||||
|
└── error-boundary/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Требования
|
||||||
|
|
||||||
|
- Не принадлежит конкретному бизнес-домену. Если блок доменный — он живёт в `business/`
|
||||||
|
- Используется в нескольких screens или layouts
|
||||||
|
|
||||||
|
### Слой Business
|
||||||
|
|
||||||
|
Бизнес-домены приложения: auth, catalog, orders, checkout, chat. Каждый домен — отдельный модуль со своими типами, логикой, UI и сервисами.
|
||||||
|
|
||||||
|
Слой входит в группу «Ядро». Импортирует `infrastructure/`, `ui/`, `shared/`. Cross-domain зависимости по коду реализуются через фабрику. `import type` между доменами разрешён напрямую.
|
||||||
|
|
||||||
|
Business объединяет то, что в FSD разделено на `features` и `entities`: пользовательские сценарии и бизнес-сущности живут вместе, внутри одного домена. Внутри домена сегменты разделяют ответственность: `types/` — доменная модель, `hooks/` и `services/` — сценарии и логика, `mappers/` — трансформация данных, `parts/` — составные блоки.
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/business/
|
||||||
|
├── auth/
|
||||||
|
├── catalog/
|
||||||
|
├── orders/
|
||||||
|
├── checkout/
|
||||||
|
└── chat/
|
||||||
|
```
|
||||||
|
|
||||||
|
Когда количество доменов затрудняет навигацию — вводится группировка по субдоменам. Группа — папка для организации, не модуль (без `index.ts`).
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/business/
|
||||||
|
├── commerce/
|
||||||
|
│ ├── catalog/
|
||||||
|
│ ├── cart/
|
||||||
|
│ ├── orders/
|
||||||
|
│ └── checkout/
|
||||||
|
└── communication/
|
||||||
|
├── chat/
|
||||||
|
└── notifications/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Требования
|
||||||
|
|
||||||
|
- Один модуль = один бизнес-домен
|
||||||
|
- Циклические зависимости между доменами запрещены
|
||||||
|
- Импорт кода между доменами — через фабрику. `import type` — напрямую
|
||||||
|
- Доменные типы (`User`, `Product`) живут здесь, не в `shared/`
|
||||||
|
|
||||||
|
### Слой Infrastructure
|
||||||
|
|
||||||
|
Техсервисы приложения: theme, i18n, API-адаптеры, logger, realtime. Каждый сервис — отдельный модуль.
|
||||||
|
|
||||||
|
Слой входит в группу «Ядро». Импортирует `infrastructure/`, `ui/`, `shared/`.
|
||||||
|
|
||||||
|
Отличие от `shared/`: infrastructure — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги).
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/infrastructure/
|
||||||
|
├── theme/
|
||||||
|
├── i18n/
|
||||||
|
├── backend-api/
|
||||||
|
├── maps-api/
|
||||||
|
├── logger/
|
||||||
|
├── feature-flags/
|
||||||
|
└── realtime/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Требования
|
||||||
|
|
||||||
|
- Один модуль = один техсервис
|
||||||
|
- Импортирует `infrastructure/`, `ui/`, `shared/`
|
||||||
|
|
||||||
|
### Слой UI
|
||||||
|
|
||||||
|
UI-кит без бизнес-логики: button, carousel, toast, modal.
|
||||||
|
|
||||||
|
Слой входит в группу «Ядро». Импортирует `ui/` и `shared/`.
|
||||||
|
|
||||||
|
Компоненты строятся друг на друге: `button` использует `icon`, `carousel` использует `button`.
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/ui/
|
||||||
|
├── button/
|
||||||
|
├── input/
|
||||||
|
├── icon/
|
||||||
|
├── carousel/
|
||||||
|
├── modal/
|
||||||
|
├── toast/
|
||||||
|
├── dropdown/
|
||||||
|
├── tabs/
|
||||||
|
└── tooltip/
|
||||||
|
```
|
||||||
|
|
||||||
|
Когда количество компонентов затрудняет навигацию — вводится группировка на примитивы и композиции. Примитивы (`button`, `icon`, `input`) не импортируют композиции. Композиции (`carousel`, `modal`, `dropdown`) строятся на примитивах.
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/ui/
|
||||||
|
├── primitives/
|
||||||
|
│ ├── button/
|
||||||
|
│ ├── input/
|
||||||
|
│ ├── icon/
|
||||||
|
│ └── badge/
|
||||||
|
└── composites/
|
||||||
|
├── carousel/
|
||||||
|
├── modal/
|
||||||
|
├── dropdown/
|
||||||
|
├── tabs/
|
||||||
|
└── tooltip/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Требования
|
||||||
|
|
||||||
|
- Не содержит бизнес-логику
|
||||||
|
- Импортирует только `ui/` и `shared/`
|
||||||
|
|
||||||
|
### Слой Shared
|
||||||
|
|
||||||
|
Общие ресурсы: утилиты, хелперы, стили, конфиги. Не знает о бизнес-домене.
|
||||||
|
|
||||||
|
Слой входит в группу «Фундамент» — ни о ком не знает, никого не импортирует.
|
||||||
|
|
||||||
|
Отличие от `infrastructure/`: infrastructure — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги).
|
||||||
|
|
||||||
|
Отличие от `ui/`: UI-компоненты (button, carousel, modal) живут в слое `ui/`, а не здесь.
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/shared/
|
||||||
|
├── lib/
|
||||||
|
├── types/
|
||||||
|
├── styles/
|
||||||
|
└── sprites/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Требования
|
||||||
|
|
||||||
|
- Не имеет runtime-состояния
|
||||||
|
|
||||||
|
<!-- /reference/modules -->
|
||||||
|
## Модули
|
||||||
|
|
||||||
|
Раздел описывает модули SLM: что такое модуль, из чего он состоит и как взаимодействует с остальным кодом.
|
||||||
|
|
||||||
|
### Определение
|
||||||
|
|
||||||
|
**Модуль — универсальный строительный блок архитектуры. Живёт на слое и содержит всё необходимое для своей работы: компоненты, хуки, сторы, сервисы, типы, стили. Набор содержимого не фиксирован — включаются только нужные части.**
|
||||||
|
|
||||||
|
### Модуль vs компонент
|
||||||
|
|
||||||
|
**Компонент** — один `.tsx` файл. Не имеет своих сегментов, использует сегменты родительского модуля. Живёт в корне или `ui/` сегменте модуля.
|
||||||
|
|
||||||
|
**Модуль** — папка, которая может содержать корневой компонент, сегменты (`hooks/`, `types/`, `styles/`, `ui/`, `parts/` и т.д.) и публичный API (`index.ts`).
|
||||||
|
|
||||||
|
```text
|
||||||
|
auth/
|
||||||
|
├── ui/
|
||||||
|
│ ├── auth-guard.tsx
|
||||||
|
│ └── logout-button.tsx
|
||||||
|
├── parts/
|
||||||
|
│ ├── login-form/
|
||||||
|
│ ├── registration-form/
|
||||||
|
│ └── restore-form/
|
||||||
|
├── hooks/
|
||||||
|
├── stores/
|
||||||
|
├── types/
|
||||||
|
├── auth.tsx # корневой компонент (опционален)
|
||||||
|
└── index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Структура
|
||||||
|
|
||||||
|
Модуль состоит из сегментов. Ни один сегмент не обязателен — модуль может состоять даже из одного `index.ts` с реэкспортом типов.
|
||||||
|
|
||||||
|
```text
|
||||||
|
{module-name}/
|
||||||
|
├── {module-name}.tsx # корневой компонент (опционален)
|
||||||
|
├── ui/ # компоненты модуля (только .tsx)
|
||||||
|
├── parts/ # вложенные модули (со своими сегментами)
|
||||||
|
├── hooks/ # хуки
|
||||||
|
├── stores/ # сторы состояния
|
||||||
|
├── services/ # внешние источники данных
|
||||||
|
├── mappers/ # трансформация данных между форматами
|
||||||
|
├── types/ # типы
|
||||||
|
├── styles/ # стили
|
||||||
|
├── lib/ # утилиты модуля
|
||||||
|
├── config/ # константы
|
||||||
|
└── index.ts # публичный API
|
||||||
|
```
|
||||||
|
|
||||||
|
Подробное описание каждого сегмента — в разделе [Сегменты](/reference/segments).
|
||||||
|
|
||||||
|
### Публичный API
|
||||||
|
|
||||||
|
Модуль экспортирует наружу только то, что нужно другим. Всё остальное — внутреннее.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// business/auth/index.ts
|
||||||
|
export type { User, Session } from './types/user.types'
|
||||||
|
export { useAuth } from './hooks/use-auth.hook'
|
||||||
|
export { AuthGuard } from './ui/auth-guard'
|
||||||
|
```
|
||||||
|
|
||||||
|
Импорт в обход `index.ts` запрещён:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Плохо
|
||||||
|
import { validateToken } from '@/business/auth/lib/tokens'
|
||||||
|
|
||||||
|
// Хорошо
|
||||||
|
import { useAuth } from '@/business/auth'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Фабрика
|
||||||
|
|
||||||
|
Если модуль зависит от кода другого бизнес-домена — он экспортирует фабрику. Фабрика декларирует необходимые зависимости и возвращает API модуля. Точка использования (screen, widget, layout) предоставляет зависимости при вызове.
|
||||||
|
|
||||||
|
Модуль без cross-domain зависимостей экспортирует API напрямую. Типы всегда экспортируются напрямую — `import type` не является runtime-зависимостью.
|
||||||
|
|
||||||
|
#### Модуль без зависимостей — прямой экспорт:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// business/auth/index.ts
|
||||||
|
export { useAuth } from './hooks/use-auth'
|
||||||
|
export { useCurrentUser } from './hooks/use-current-user'
|
||||||
|
export type { User, Session } from './types'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Модуль с зависимостями — фабрика:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// business/chat/types/deps.ts
|
||||||
|
import type { User } from '@/business/auth'
|
||||||
|
|
||||||
|
export interface ChatDeps {
|
||||||
|
useCurrentUser: () => User | null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// business/chat/index.ts
|
||||||
|
import type { ChatDeps } from './types/deps'
|
||||||
|
|
||||||
|
export function chatFactory(deps: ChatDeps) {
|
||||||
|
return {
|
||||||
|
useMessages: (roomId: string) => {
|
||||||
|
const user = deps.useCurrentUser()
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
useSendMessage: (roomId: string) => {
|
||||||
|
const user = deps.useCurrentUser()
|
||||||
|
return (text: string) => { /* ... */ }
|
||||||
|
},
|
||||||
|
useChatRooms: () => {
|
||||||
|
const user = deps.useCurrentUser()
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
ChatBadge: ({ count }: { count: number }) => { /* ... */ },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { Message, ChatRoom } from './types'
|
||||||
|
export type { ChatDeps } from './types/deps'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Использование на странице:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// screens/support/support.tsx
|
||||||
|
import { useCurrentUser } from '@/business/auth'
|
||||||
|
import { chatFactory } from '@/business/chat'
|
||||||
|
|
||||||
|
const chat = chatFactory({ useCurrentUser })
|
||||||
|
|
||||||
|
export function SupportScreen() {
|
||||||
|
const { useMessages, useSendMessage, ChatBadge } = chat
|
||||||
|
const messages = useMessages('support')
|
||||||
|
const sendMessage = useSendMessage('support')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ChatBadge count={messages.length} />
|
||||||
|
{messages.map(m => <MessageBubble key={m.id} {...m} />)}
|
||||||
|
<MessageInput onSend={sendMessage} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Жизненный цикл
|
||||||
|
|
||||||
|
Модуль рождается на самом низком уровне использования и поднимается выше только при реальной потребности.
|
||||||
|
|
||||||
|
- Нужен на одной странице → `screens/{name}/parts/`
|
||||||
|
- Появился в 2+ местах → поднимается по природе:
|
||||||
|
- абстрактный UI → `ui/`
|
||||||
|
- блок с данными/логикой → `widgets/`
|
||||||
|
- представление бизнес-домена → `business/{area}/parts/`
|
||||||
|
|
||||||
|
Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность.
|
||||||
|
|
||||||
|
<!-- /reference/segments -->
|
||||||
|
## Сегменты
|
||||||
|
|
||||||
|
Раздел описывает сегменты SLM: что такое сегмент, какие бывают и что в каждом из них лежит.
|
||||||
|
|
||||||
|
### Определение
|
||||||
|
|
||||||
|
**Сегмент — папка внутри модуля, которая группирует файлы по назначению. Набор сегментов не фиксирован — модуль включает только те, которые ему нужны. Команда сама определяет какие сегменты используются в проекте — архитектура даёт рекомендацию.**
|
||||||
|
|
||||||
|
### Обзор
|
||||||
|
|
||||||
|
| Сегмент | Содержимое |
|
||||||
|
|---------|------------|
|
||||||
|
| `ui/` | Компоненты модуля — только `.tsx` файлы |
|
||||||
|
| `parts/` | Вложенные модули со своими сегментами |
|
||||||
|
| `hooks/` | React-хуки |
|
||||||
|
| `stores/` | Сторы состояния |
|
||||||
|
| `services/` | Работа с внешними источниками данных |
|
||||||
|
| `mappers/` | Трансформация данных между форматами |
|
||||||
|
| `types/` | TypeScript-типы и интерфейсы |
|
||||||
|
| `styles/` | Стили |
|
||||||
|
| `lib/` | Утилиты и хелперы модуля |
|
||||||
|
| `config/` | Константы и конфигурация |
|
||||||
|
|
||||||
|
### Сегмент ui/
|
||||||
|
|
||||||
|
Компоненты, принадлежащие модулю. Содержит только `.tsx` файлы — без своих сегментов, стилей, типов, хуков. Использует сегменты родительского модуля.
|
||||||
|
|
||||||
|
```text
|
||||||
|
auth/
|
||||||
|
├── ui/
|
||||||
|
│ ├── auth-provider.tsx
|
||||||
|
│ ├── auth-guard.tsx
|
||||||
|
│ └── logout-button.tsx
|
||||||
|
├── types/
|
||||||
|
├── hooks/
|
||||||
|
└── index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Если компоненту нужны собственные сегменты — это уже не `ui/`, а `parts/`.
|
||||||
|
|
||||||
|
### Сегмент parts/
|
||||||
|
|
||||||
|
Вложенные модули со своими сегментами. Каждый элемент `parts/` — полноценный модуль: папка с компонентом, хуками, стилями, типами и т.д.
|
||||||
|
|
||||||
|
```text
|
||||||
|
home/
|
||||||
|
├── parts/
|
||||||
|
│ ├── hero-section/
|
||||||
|
│ │ ├── hero-section.tsx
|
||||||
|
│ │ ├── styles/
|
||||||
|
│ │ └── parts/
|
||||||
|
│ │ └── top-banner/
|
||||||
|
│ │ └── top-banner.tsx
|
||||||
|
│ └── features-section/
|
||||||
|
│ ├── features-section.tsx
|
||||||
|
│ └── hooks/
|
||||||
|
├── home.screen.tsx
|
||||||
|
└── index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Отличие от `ui/`: элемент `parts/` — модуль со своими сегментами. Элемент `ui/` — компонент, один `.tsx` файл.
|
||||||
|
|
||||||
|
Вложенность `parts/` инкапсулирует область разработки горизонтально: каждый разработчик работает в своём `parts/`-модуле, не затрагивая чужие. Это снижает конфликты при параллельной разработке.
|
||||||
|
|
||||||
|
Если вложенный модуль обрастает своими `parts/` — это сигнал, что он достаточно самостоятельный для подъёма на уровень выше.
|
||||||
|
|
||||||
|
### Сегмент hooks/
|
||||||
|
|
||||||
|
React-хуки модуля. Инкапсулируют логику, состояние, подписки, побочные эффекты.
|
||||||
|
|
||||||
|
```text
|
||||||
|
hooks/
|
||||||
|
├── use-auth.hook.ts
|
||||||
|
├── use-session.hook.ts
|
||||||
|
└── use-permissions.hook.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сегмент stores/
|
||||||
|
|
||||||
|
Сторы состояния модуля. Конкретная реализация зависит от выбранного стейт-менеджера (Zustand, MobX, Redux и т.д.).
|
||||||
|
|
||||||
|
```text
|
||||||
|
stores/
|
||||||
|
├── auth.store.ts
|
||||||
|
└── session.store.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сегмент services/
|
||||||
|
|
||||||
|
Работа с внешними источниками данных: API-вызовы, запросы, подписки.
|
||||||
|
|
||||||
|
```text
|
||||||
|
services/
|
||||||
|
├── auth.service.ts
|
||||||
|
└── token.service.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сегмент mappers/
|
||||||
|
|
||||||
|
Функции трансформации данных из одного формата в другой: DTO в доменный тип, доменный тип в DTO, доменный тип в ViewModel.
|
||||||
|
|
||||||
|
```text
|
||||||
|
mappers/
|
||||||
|
├── map-user.ts
|
||||||
|
├── map-product.ts
|
||||||
|
└── map-order-to-dto.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сегмент types/
|
||||||
|
|
||||||
|
TypeScript-типы и интерфейсы модуля. Доменные типы, DTO, пропсы компонентов.
|
||||||
|
|
||||||
|
```text
|
||||||
|
types/
|
||||||
|
├── user.type.ts
|
||||||
|
└── session.type.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сегмент styles/
|
||||||
|
|
||||||
|
Стили модуля. Формат зависит от выбранного подхода (CSS Modules, SCSS, CSS-in-JS и т.д.).
|
||||||
|
|
||||||
|
```text
|
||||||
|
styles/
|
||||||
|
├── auth.module.css
|
||||||
|
└── login-form.module.css
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сегмент lib/
|
||||||
|
|
||||||
|
Утилиты и хелперы, специфичные для модуля. Чистые функции без побочных эффектов.
|
||||||
|
|
||||||
|
```text
|
||||||
|
lib/
|
||||||
|
├── validate-email.ts
|
||||||
|
└── format-phone.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Отличие от `shared/lib/`: здесь лежат утилиты, нужные только этому модулю. Общие утилиты — в `shared/lib/`.
|
||||||
|
|
||||||
|
### Сегмент config/
|
||||||
|
|
||||||
|
Константы и конфигурация модуля: маршруты, лимиты, дефолтные значения.
|
||||||
|
|
||||||
|
```text
|
||||||
|
config/
|
||||||
|
├── routes.ts
|
||||||
|
└── constants.ts
|
||||||
|
```
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Стиль кода
|
title: Стиль кода
|
||||||
|
scope: basics
|
||||||
|
keywords: [форматирование, импорт, отступ, кавычки, early return, точка с запятой, линтер]
|
||||||
|
when: "Написание или ревью любого кода: форматирование, импорты, структура файла"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Стиль кода
|
# Стиль кода
|
||||||
|
|
||||||
Раздел описывает единые правила оформления кода: отступы, переносы, кавычки, порядок импортов и базовую читаемость.
|
Раздел описывает единые правила оформления кода: отступы, переносы, кавычки, порядок импортов и базовую читаемость.
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Документирование
|
title: Документирование
|
||||||
|
scope: basics
|
||||||
|
keywords: [JSDoc, комментарий, документирование, описание функции, описание компонента]
|
||||||
|
when: "Документирование кода: JSDoc для функций, компонентов, типов"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Документирование
|
# Документирование
|
||||||
|
|
||||||
Этот раздел описывает правила документирования кода: когда и как писать
|
Этот раздел описывает правила документирования кода: когда и как писать
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Начало работы
|
title: Начало работы
|
||||||
|
scope: workflow
|
||||||
|
keywords: [начало, onboarding, настройка, установка, первый запуск]
|
||||||
|
when: "Первый запуск проекта, знакомство со стеком"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Начало работы
|
# Начало работы
|
||||||
|
|
||||||
Что нужно знать перед началом разработки в проекте.
|
Что нужно знать перед началом разработки в проекте.
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Именование
|
title: Именование
|
||||||
|
scope: basics
|
||||||
|
keywords: [camelCase, kebab-case, PascalCase, имя файла, имя переменной, имя компонента, имя хука]
|
||||||
|
when: "Создание файлов, переменных, компонентов, хуков — выбор имени"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Именование
|
# Именование
|
||||||
|
|
||||||
Этот раздел описывает соглашения об именовании в проекте. Единые правила делают код предсказуемым и упрощают навигацию по проекту.
|
Этот раздел описывает соглашения об именовании в проекте. Единые правила делают код предсказуемым и упрощают навигацию по проекту.
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Технологии и библиотеки
|
title: Технологии и библиотеки
|
||||||
|
scope: basics
|
||||||
|
keywords: [стек, React, TypeScript, Next.js, Mantine, библиотека, зависимость]
|
||||||
|
when: "Выбор библиотеки или технологии, проверка допустимости зависимости"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Технологии и библиотеки
|
# Технологии и библиотеки
|
||||||
|
|
||||||
Этот раздел описывает базовый стек технологий и библиотек, принятый в проекте.
|
Этот раздел описывает базовый стек технологий и библиотек, принятый в проекте.
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Типизация
|
title: Типизация
|
||||||
|
scope: basics
|
||||||
|
keywords: [type, interface, generic, any, unknown, enum, типизация, пропсы]
|
||||||
|
when: "Типизация кода: выбор type vs interface, работа с generic, запрет any"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Типизация
|
# Типизация
|
||||||
|
|
||||||
Этот раздел описывает правила типизации: как типизировать компоненты, функции и работу с `any`/`unknown`.
|
Этот раздел описывает правила типизации: как типизировать компоненты, функции и работу с `any`/`unknown`.
|
||||||
35
src/base/triggers/develop/add-api-request.md
Normal file
35
src/base/triggers/develop/add-api-request.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
title: Добавить API-запрос
|
||||||
|
---
|
||||||
|
|
||||||
|
# Добавить API-запрос
|
||||||
|
|
||||||
|
Инструкция по добавлению запроса к серверу: создание клиента, хука, обработка ответа.
|
||||||
|
|
||||||
|
## Прочитай перед началом
|
||||||
|
|
||||||
|
- applied/api.md — правила API-слоя: клиенты, эндпоинты, обработка ошибок
|
||||||
|
- basics/typing.md — типизация запросов и ответов
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
1. Определи подход:
|
||||||
|
- Клиентские данные → SWR / хук
|
||||||
|
- Серверные данные → серверный компонент (RSC)
|
||||||
|
|
||||||
|
2. Опиши типы запроса и ответа.
|
||||||
|
|
||||||
|
3. Создай или расширь API-клиент (→ applied/api.md).
|
||||||
|
|
||||||
|
4. Создай хук для использования в компоненте (→ triggers/develop/create-hook.md).
|
||||||
|
|
||||||
|
## Смежные триггеры
|
||||||
|
|
||||||
|
- triggers/develop/create-hook.md — хук для запроса
|
||||||
|
- triggers/develop/create-component.md — компонент, использующий данные
|
||||||
|
|
||||||
|
## Проверь себя
|
||||||
|
|
||||||
|
- [ ] Типы запроса и ответа описаны
|
||||||
|
- [ ] Хук для использования в компоненте создан
|
||||||
|
- [ ] Обработка ошибок реализована
|
||||||
24
src/base/triggers/develop/add-dependency.md
Normal file
24
src/base/triggers/develop/add-dependency.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: Добавить зависимость
|
||||||
|
---
|
||||||
|
|
||||||
|
# Добавить зависимость
|
||||||
|
|
||||||
|
Инструкция по добавлению новой npm-зависимости в проект. Проверь допустимость перед установкой.
|
||||||
|
|
||||||
|
## Прочитай перед началом
|
||||||
|
|
||||||
|
- basics/tech-stack.md — разрешённый стек, допустимые библиотеки
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
1. Проверь, что библиотека не дублирует уже используемую (→ basics/tech-stack.md).
|
||||||
|
|
||||||
|
2. Проверь, что библиотека входит в разрешённый список или обоснуй необходимость.
|
||||||
|
|
||||||
|
3. Установи как `dependency` или `devDependency` в зависимости от назначения.
|
||||||
|
|
||||||
|
## Проверь себя
|
||||||
|
|
||||||
|
- [ ] Библиотека не дублирует уже используемую
|
||||||
|
- [ ] Библиотека входит в разрешённый список (→ basics/tech-stack.md)
|
||||||
28
src/base/triggers/develop/add-font.md
Normal file
28
src/base/triggers/develop/add-font.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
title: Подключить шрифт
|
||||||
|
---
|
||||||
|
|
||||||
|
# Подключить шрифт
|
||||||
|
|
||||||
|
Инструкция по подключению и настройке шрифта в проекте.
|
||||||
|
|
||||||
|
## Прочитай перед началом
|
||||||
|
|
||||||
|
- applied/fonts.md — правила подключения шрифтов: форматы, загрузка, CSS-переменные
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
1. Подготовь файлы шрифта (woff2).
|
||||||
|
|
||||||
|
2. Подключи шрифт по правилам (→ applied/fonts.md).
|
||||||
|
|
||||||
|
3. Зарегистрируй CSS-переменную для шрифта.
|
||||||
|
|
||||||
|
## Смежные триггеры
|
||||||
|
|
||||||
|
- triggers/develop/style-component.md — использование шрифта в стилях
|
||||||
|
|
||||||
|
## Проверь себя
|
||||||
|
|
||||||
|
- [ ] Файл шрифта в формате woff2
|
||||||
|
- [ ] CSS-переменная для шрифта зарегистрирована
|
||||||
29
src/base/triggers/develop/add-icon.md
Normal file
29
src/base/triggers/develop/add-icon.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
title: Добавить иконку
|
||||||
|
---
|
||||||
|
|
||||||
|
# Добавить иконку
|
||||||
|
|
||||||
|
Инструкция по добавлению SVG-иконки в проект через спрайт-систему.
|
||||||
|
|
||||||
|
## Прочитай перед началом
|
||||||
|
|
||||||
|
- applied/svg-sprites.md — правила SVG-спрайтов: структура, именование, использование
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
1. Подготовь SVG-файл: убери лишние атрибуты, оптимизируй.
|
||||||
|
|
||||||
|
2. Добавь SVG в спрайт по правилам (→ applied/svg-sprites.md).
|
||||||
|
|
||||||
|
3. Используй иконку в компоненте через компонент-обёртку.
|
||||||
|
|
||||||
|
## Смежные триггеры
|
||||||
|
|
||||||
|
- triggers/develop/create-component.md — если нужен компонент-обёртка для иконки
|
||||||
|
- triggers/develop/style-component.md — стилизация иконки (размер, цвет)
|
||||||
|
|
||||||
|
## Проверь себя
|
||||||
|
|
||||||
|
- [ ] SVG оптимизирован — убраны лишние атрибуты
|
||||||
|
- [ ] Иконка добавлена в спрайт по правилам (→ applied/svg-sprites.md)
|
||||||
30
src/base/triggers/develop/add-image.md
Normal file
30
src/base/triggers/develop/add-image.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
title: Добавить изображение
|
||||||
|
---
|
||||||
|
|
||||||
|
# Добавить изображение
|
||||||
|
|
||||||
|
Инструкция по добавлению и использованию растровых изображений в проекте.
|
||||||
|
|
||||||
|
## Прочитай перед началом
|
||||||
|
|
||||||
|
- applied/images-sprites.md — правила работы с изображениями: оптимизация, форматы, подключение
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
1. Определи тип изображения:
|
||||||
|
- Статическое (логотип, декор) → `public/`
|
||||||
|
- Динамическое (контентное) → URL из API
|
||||||
|
|
||||||
|
2. Оптимизируй изображение (формат, размер, сжатие).
|
||||||
|
|
||||||
|
3. Подключи в компоненте по правилам (→ applied/images-sprites.md).
|
||||||
|
|
||||||
|
## Смежные триггеры
|
||||||
|
|
||||||
|
- triggers/develop/create-component.md — если нужен компонент-обёртка для изображения
|
||||||
|
|
||||||
|
## Проверь себя
|
||||||
|
|
||||||
|
- [ ] Изображение оптимизировано (формат, размер, сжатие)
|
||||||
|
- [ ] Подключено по правилам (→ applied/images-sprites.md)
|
||||||
28
src/base/triggers/develop/add-localization.md
Normal file
28
src/base/triggers/develop/add-localization.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
title: Добавить перевод
|
||||||
|
---
|
||||||
|
|
||||||
|
# Добавить перевод
|
||||||
|
|
||||||
|
Инструкция по добавлению локализации: создание ключей перевода и подключение в компоненте.
|
||||||
|
|
||||||
|
## Прочитай перед началом
|
||||||
|
|
||||||
|
- applied/localization.md — правила локализации: namespace, ключи, форматирование
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
1. Определи namespace для переводов (→ applied/localization.md).
|
||||||
|
|
||||||
|
2. Добавь ключи перевода в файлы локализации.
|
||||||
|
|
||||||
|
3. Подключи переводы в компоненте (→ applied/localization.md).
|
||||||
|
|
||||||
|
## Смежные триггеры
|
||||||
|
|
||||||
|
- triggers/develop/create-component.md — если компонент ещё не создан
|
||||||
|
|
||||||
|
## Проверь себя
|
||||||
|
|
||||||
|
- [ ] Ключи перевода добавлены в файлы локализации
|
||||||
|
- [ ] Namespace определён (→ applied/localization.md)
|
||||||
27
src/base/triggers/develop/add-video.md
Normal file
27
src/base/triggers/develop/add-video.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
title: Добавить видео
|
||||||
|
---
|
||||||
|
|
||||||
|
# Добавить видео
|
||||||
|
|
||||||
|
Инструкция по встраиванию видео в проект.
|
||||||
|
|
||||||
|
## Прочитай перед началом
|
||||||
|
|
||||||
|
- applied/video.md — правила работы с видео: форматы, плеер, оптимизация
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
1. Определи тип видео:
|
||||||
|
- Локальное → `public/`
|
||||||
|
- Внешнее (YouTube, Vimeo) → embed
|
||||||
|
|
||||||
|
2. Подключи видео по правилам (→ applied/video.md).
|
||||||
|
|
||||||
|
## Смежные триггеры
|
||||||
|
|
||||||
|
- triggers/develop/create-component.md — если нужен компонент-обёртка для видео
|
||||||
|
|
||||||
|
## Проверь себя
|
||||||
|
|
||||||
|
- [ ] Видео подключено по правилам (→ applied/video.md)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user