Compare commits
2 Commits
028a69f3ac
...
a6cd14585b
| Author | SHA1 | Date | |
|---|---|---|---|
| a6cd14585b | |||
| 1195c7b75d |
@@ -121,7 +121,11 @@ export default defineConfig({
|
|||||||
// `docs/public/` содержит сгенерированные `.md`-копии и `llms.txt` для LLM
|
// `docs/public/` содержит сгенерированные `.md`-копии и `llms.txt` для LLM
|
||||||
// (попадают в корень `dist/` как статика). Исключаем из сканирования
|
// (попадают в корень `dist/` как статика). Исключаем из сканирования
|
||||||
// страниц, иначе VitePress рендерит их как HTML-страницы.
|
// страниц, иначе VitePress рендерит их как HTML-страницы.
|
||||||
srcExclude: ['public/**'],
|
//
|
||||||
|
// `DEVELOP.md` и `MAP.md` — файлы архива (точка входа и карта).
|
||||||
|
// Содержат относительные ссылки, на сайте им делать нечего —
|
||||||
|
// эту роль выполняют сайдбар и `llms.txt`.
|
||||||
|
srcExclude: ['public/**', '**/DEVELOP.md', '**/MAP.md'],
|
||||||
lang: 'ru-RU',
|
lang: 'ru-RU',
|
||||||
title: 'NextJS Style Guide',
|
title: 'NextJS Style Guide',
|
||||||
description: 'Стандарты разработки на Next.js + TypeScript с архитектурой SLM',
|
description: 'Стандарты разработки на Next.js + TypeScript с архитектурой SLM',
|
||||||
|
|||||||
110
CONTRIBUTING.md
110
CONTRIBUTING.md
@@ -25,20 +25,25 @@ docs/
|
|||||||
├── index.md # Лендинг (URL `/`)
|
├── index.md # Лендинг (URL `/`)
|
||||||
└── docs/ # Контент документации (URL `/docs/...`)
|
└── docs/ # Контент документации (URL `/docs/...`)
|
||||||
├── index.md # Главная страница
|
├── index.md # Главная страница
|
||||||
├── workflow.md
|
├── workflow.md # Подсказки
|
||||||
├── workflow/ # Процессы разработки
|
├── basics/ # Базовые правила: каким должен быть код
|
||||||
├── basics/ # Базовые правила
|
|
||||||
│ ├── tech-stack.md
|
│ ├── tech-stack.md
|
||||||
│ ├── architecture/
|
│ ├── architecture/
|
||||||
│ ├── code-style.md
|
│ ├── code-style.md
|
||||||
│ ├── naming.md
|
│ ├── naming.md
|
||||||
│ ├── documentation.md
|
│ ├── documentation.md
|
||||||
│ └── typing.md
|
│ └── typing.md
|
||||||
├── setup/ # Установка: разовая настройка проекта
|
├── creating-project/ # Создание проекта: как поднять новый проект
|
||||||
|
│ ├── from-template.md
|
||||||
|
│ ├── manual.md
|
||||||
|
│ └── nextjs.md
|
||||||
|
├── setup/ # Настройка: разовая настройка инструментов
|
||||||
│ ├── aliases.md
|
│ ├── aliases.md
|
||||||
│ ├── biome.md
|
│ ├── biome.md
|
||||||
│ ├── postcss.md
|
│ ├── postcss.md
|
||||||
|
│ ├── styles.md
|
||||||
│ ├── svg-sprites.md
|
│ ├── svg-sprites.md
|
||||||
|
│ ├── templates.md
|
||||||
│ └── vscode.md
|
│ └── vscode.md
|
||||||
└── usage/ # Использование: повседневная работа
|
└── usage/ # Использование: повседневная работа
|
||||||
├── project-structure.md
|
├── project-structure.md
|
||||||
@@ -64,36 +69,81 @@ generate-llms.ts # Скрипт генерации llms.txt и R
|
|||||||
|
|
||||||
### Добавление нового раздела
|
### Добавление нового раздела
|
||||||
|
|
||||||
1. Создать `.md`-файл в нужной папке (`docs/docs/basics/`, `docs/docs/setup/` или `docs/docs/usage/`).
|
1. Создать `.md`-файл в нужной папке: `basics/`, `creating-project/`,
|
||||||
|
`setup/` или `usage/`.
|
||||||
2. Добавить пункт в сайдбар — `.vitepress/config.ts`.
|
2. Добавить пункт в сайдбар — `.vitepress/config.ts`.
|
||||||
Сайдбар — единственный источник порядка и группировки для `llms.txt`.
|
Сайдбар — единственный источник порядка и группировки для `llms.txt`.
|
||||||
3. Запустить `npm run llms` для обновления `llms.txt` и README.
|
3. Запустить `npm run llms` для обновления `llms.txt` и README.
|
||||||
|
|
||||||
## Два типа документации
|
## Типы разделов
|
||||||
|
|
||||||
### Базовые правила
|
Документация разделена на четыре группы. Каждая отвечает на свой вопрос
|
||||||
|
и имеет свою природу — это влияет на содержимое и структуру страницы.
|
||||||
|
|
||||||
|
### Базовые правила (`basics/`)
|
||||||
|
|
||||||
**Отвечает на вопрос:** «Каким должен быть любой код?»
|
**Отвечает на вопрос:** «Каким должен быть любой код?»
|
||||||
|
|
||||||
Универсальные стандарты, **не привязанные к конкретной области**.
|
Универсальные стандарты, **не привязанные к конкретной области**.
|
||||||
Правило базовое, если оно применимо ко всему коду одинаково: именование переменных, оформление импортов, когда использовать `type` vs `interface`.
|
Правило базовое, если оно применимо ко всему коду одинаково: именование
|
||||||
|
переменных, оформление импортов, когда использовать `type` vs `interface`.
|
||||||
|
|
||||||
Примеры в базовых правилах допускаются, но служат иллюстрацией принципа, а не инструкцией по конкретной области.
|
Примеры в базовых правилах допускаются, но служат иллюстрацией принципа,
|
||||||
|
а не инструкцией по конкретной области.
|
||||||
|
|
||||||
**Граница:** если правило касается только одной области (только стили, только компоненты, только API) — оно живёт в прикладном разделе, не в базовых.
|
**Граница:** если правило касается только одной области (только стили,
|
||||||
|
только компоненты, только API) — оно живёт в прикладном разделе, не в базовых.
|
||||||
|
|
||||||
### Прикладные разделы
|
### Создание проекта (`creating-project/`)
|
||||||
|
|
||||||
**Отвечает на вопрос:** «Как работать с X?»
|
**Отвечает на вопрос:** «Как поднять новый проект?»
|
||||||
|
|
||||||
Полное описание конкретной области: структура файлов, правила, именование, типизация, примеры.
|
Сценарии запуска нового проекта целиком: из шаблона, вручную, чистая
|
||||||
|
установка фреймворка. Раздел описывает порядок шагов на уровне всего
|
||||||
|
проекта; детали отдельных инструментов лежат в `setup/`.
|
||||||
|
|
||||||
**Граница:** прикладной раздел не дублирует базовые правила.
|
**Граница:** не дублирует разделы `setup/`. Ссылается на них как на
|
||||||
Если правило уже описано в базовых — прикладной раздел ссылается на него, но не повторяет.
|
шаги в общем сценарии.
|
||||||
|
|
||||||
|
### Настройка (`setup/`)
|
||||||
|
|
||||||
|
**Отвечает на вопрос:** «Как поставить и сконфигурировать инструмент
|
||||||
|
в новом проекте?»
|
||||||
|
|
||||||
|
Разовая установка отдельного инструмента или подсистемы (линтер,
|
||||||
|
CSS-процессор, генератор спрайтов, шаблоны). Каждый раздел —
|
||||||
|
самостоятельная подсистема. Выполняется один раз при заведении
|
||||||
|
проекта или при смене мажорной версии инструмента.
|
||||||
|
|
||||||
|
Типичная структура `setup/`-страницы: требования → установка (шаги) →
|
||||||
|
конфиг → проверка.
|
||||||
|
|
||||||
|
**Граница:** `setup/` — про настройку, `usage/` — про написание кода
|
||||||
|
с использованием уже настроенного инструмента.
|
||||||
|
|
||||||
|
### Использование (`usage/`)
|
||||||
|
|
||||||
|
**Отвечает на вопрос:** «Как этим пользоваться в коде?»
|
||||||
|
|
||||||
|
Повседневная работа: как писать компоненты, стили, как получать данные,
|
||||||
|
как работать со сторами, локализацией, ассетами. Полное описание
|
||||||
|
конкретной области: структура файлов, правила, именование, типизация, примеры.
|
||||||
|
|
||||||
|
Шаблон страницы описан ниже в секции «Структура прикладного раздела».
|
||||||
|
|
||||||
|
**Граница:** прикладной раздел не дублирует базовые правила. Если правило
|
||||||
|
уже описано в `basics/` — прикладной раздел ссылается на него, но не
|
||||||
|
повторяет.
|
||||||
|
|
||||||
## Структура прикладного раздела
|
## Структура прикладного раздела
|
||||||
|
|
||||||
Шаблон ниже описывает все допустимые секции. Раздел включает только те секции, которые для него релевантны — пустые секции не создаются.
|
Шаблон ниже относится к разделам `usage/` (повседневная работа).
|
||||||
|
Разделы `setup/` и `creating-project/` имеют другую структуру —
|
||||||
|
ориентированную на пошаговую установку (требования → установка →
|
||||||
|
проверка).
|
||||||
|
|
||||||
|
Шаблон описывает все допустимые секции. Раздел включает только те,
|
||||||
|
которые для него релевантны — пустые секции не создаются.
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# {Название}
|
# {Название}
|
||||||
@@ -179,16 +229,21 @@ generate-llms.ts # Скрипт генерации llms.txt и R
|
|||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
title: Название раздела
|
title: Название раздела
|
||||||
|
description: Описание раздела одним предложением.
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
Значение `title` совпадает с текстом `h1`-заголовка в файле.
|
- `title` совпадает с текстом `h1`-заголовка в файле.
|
||||||
|
- `description` совпадает с абзацем-описанием сразу под `h1`.
|
||||||
|
|
||||||
|
Подробнее о требованиях к самому заголовку и описанию — секция
|
||||||
|
«Заголовок и описание» ниже.
|
||||||
|
|
||||||
### Заголовок и описание
|
### Заголовок и описание
|
||||||
|
|
||||||
Каждая страница начинается с `h1`-заголовка и абзаца-описания сразу под ним.
|
Каждая страница начинается с `h1`-заголовка и абзаца-описания сразу под ним.
|
||||||
Эта пара — **навигационный маркер**: попадает в сайдбар, `llms.txt`,
|
Эта пара — **навигационный маркер**: попадает в сайдбар, `llms.txt`,
|
||||||
`README.md` архива и должна за секунду давать читателю или LLM понять,
|
`MAP.md` архива и должна за секунду давать читателю или LLM понять,
|
||||||
**когда сюда нужно идти**.
|
**когда сюда нужно идти**.
|
||||||
|
|
||||||
#### Структура заголовков
|
#### Структура заголовков
|
||||||
@@ -206,11 +261,26 @@ title: Название раздела
|
|||||||
- Самодостаточен — читается без контекста сайдбара.
|
- Самодостаточен — читается без контекста сайдбара.
|
||||||
- Исключение: имя инструмента допустимо, если оно — единственное
|
- Исключение: имя инструмента допустимо, если оно — единственное
|
||||||
устойчивое имя самой области (`PostCSS`, `Biome`, `VS Code`).
|
устойчивое имя самой области (`PostCSS`, `Biome`, `VS Code`).
|
||||||
|
- Если страница вложена в семантическую группу
|
||||||
|
(`Архитектура → Слои`, `Данные → REST → Серверные компоненты`)
|
||||||
|
и короткое имя теряет смысл при прямой ссылке — `h1` поднимает
|
||||||
|
имя родителя в заголовок: `Слои SLM`, `Сегменты SLM`. В сайдбаре
|
||||||
|
допустимо оставить короткий вариант (`Слои`, `Сегменты`) — там
|
||||||
|
путь группы виден через дерево.
|
||||||
|
- Подъём в заголовок применяется только когда читается грамматически
|
||||||
|
естественно (`Слои SLM`, `Автогенерация REST-клиента`). Если
|
||||||
|
получается натянутая конструкция (`REST в серверных компонентах`) —
|
||||||
|
заголовок остаётся коротким, а контекст полностью переносится
|
||||||
|
в описание. Заголовок и описание — пара: если один не несёт
|
||||||
|
контекст, его обязательно несёт второй.
|
||||||
|
|
||||||
**Хорошо:** «Алиасы импортов», «Структура проекта», «SVG-спрайты».
|
**Хорошо:** «Алиасы импортов», «Структура проекта», «SVG-спрайты»,
|
||||||
|
«Слои SLM», «Автогенерация REST-клиента».
|
||||||
|
|
||||||
**Плохо:** «Установка и настройка» (что устанавливаем?),
|
**Плохо:** «Установка и настройка» (что устанавливаем?),
|
||||||
«Использование» (что используем?), «Введение» (во что?).
|
«Использование» (что используем?), «Введение» (во что?),
|
||||||
|
«Сегменты» (чего сегменты?), «REST в серверных компонентах»
|
||||||
|
(грамматически натянуто — лучше короткий h1 + контекст в описании).
|
||||||
|
|
||||||
#### Описание
|
#### Описание
|
||||||
|
|
||||||
|
|||||||
@@ -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, не использовать "жёстко" прописанные строки.
|
|
||||||
58
README.md
58
README.md
@@ -1,6 +1,6 @@
|
|||||||
# NextJS Style Guide
|
# NextJS Style Guide
|
||||||
|
|
||||||
Соглашения по разработке Next.js проектов: архитектура и слои приложения, структура кода, организация модулей, стилизация, типизация и инфраструктура.
|
Стандарты разработки фронтенд-приложений на Next.js и TypeScript.
|
||||||
|
|
||||||
Сайт: https://nextjs-style-guide.gromlab.ru
|
Сайт: https://nextjs-style-guide.gromlab.ru
|
||||||
|
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
|
|
||||||
## Структура документации
|
## Структура документации
|
||||||
|
|
||||||
### Workflow
|
### Подсказки
|
||||||
|
|
||||||
[Workflow](docs/docs/workflow.md) — пошаговая карта типовых задач: с чего начать, как добавить страницу/компонент, как подключить данные, стили, локализацию.
|
[Подсказки](docs/docs/workflow.md) — короткие ответы на типовые вопросы и решения для спорных ситуаций.
|
||||||
|
|
||||||
### Базовые правила
|
### Базовые правила
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
|--------|-------------------|
|
|--------|-------------------|
|
||||||
| Технологии и библиотеки | Какой стек используем? |
|
| Технологии и библиотеки | Какой стек используем? |
|
||||||
| Именование | Как называть файлы, переменные, компоненты, хуки? |
|
| Именование | Как называть файлы, переменные, компоненты, хуки? |
|
||||||
| Архитектура: Обзор | Что такое SLM и зачем она нужна? |
|
| SLM Design | Что такое SLM и зачем она нужна? |
|
||||||
| Архитектура: Слои | Какие слои есть и как между ними устроены зависимости? |
|
| Архитектура: Слои | Какие слои есть и как между ними устроены зависимости? |
|
||||||
| Архитектура: Модули | Что такое модуль и как он устроен? |
|
| Архитектура: Модули | Что такое модуль и как он устроен? |
|
||||||
| Архитектура: Сегменты | Какие сегменты есть внутри модуля? |
|
| Архитектура: Сегменты | Какие сегменты есть внутри модуля? |
|
||||||
@@ -37,20 +37,27 @@
|
|||||||
| Документирование | Как писать JSDoc: что документировать, а что нет? |
|
| Документирование | Как писать JSDoc: что документировать, а что нет? |
|
||||||
| Типизация | Как типизировать: type vs interface, any/unknown? |
|
| Типизация | Как типизировать: type vs interface, any/unknown? |
|
||||||
|
|
||||||
### Установка и настройка
|
### Создание проекта
|
||||||
|
|
||||||
**Как поднять и сконфигурировать проект** — пошаговая настройка инструментов и инфраструктуры.
|
**Как начать новый проект** — варианты установки и эталонный набор инструментов.
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
| Раздел | Отвечает на вопрос |
|
||||||
|--------|-------------------|
|
|--------|-------------------|
|
||||||
| Создание проекта: Из шаблона | Как начать проект из готового шаблона? |
|
| Создание проекта из шаблона | Как начать проект из готового шаблона? |
|
||||||
| Создание проекта: Вручную | Как поднять проект с нуля без шаблона? |
|
| Создание проекта вручную | Как поднять проект с нуля без шаблона? |
|
||||||
| Next.js | Как настроить Next.js под проект? |
|
| Чистая установка Next.js | Как поставить голый Next.js под дальнейшую сборку? |
|
||||||
| Алиасы | Как настроить путевые алиасы импортов? |
|
|
||||||
| Biome | Как настроить Biome (линтер и форматер)? |
|
### Настройка
|
||||||
| Стили | Как подключить и настроить стили проекта? |
|
|
||||||
|
**Как сконфигурировать проект** — пошаговая настройка инструментов и инфраструктуры.
|
||||||
|
|
||||||
|
| Раздел | Отвечает на вопрос |
|
||||||
|
|--------|-------------------|
|
||||||
|
| Алиасы импортов | Как настроить алиасы импортов? |
|
||||||
|
| Biome | Как настроить линтер и форматтер? |
|
||||||
| PostCSS | Какие плагины PostCSS нужны и как их настроить? |
|
| PostCSS | Какие плагины PostCSS нужны и как их настроить? |
|
||||||
| SVG-спрайты | Как настроить генерацию SVG-спрайтов? |
|
| Стили | Как подключить базовые стили и токены? |
|
||||||
|
| SVG-спрайты | Как подключить генерацию SVG-спрайтов? |
|
||||||
| Шаблоны генерации | Как подключить шаблоны для кодогенерации? |
|
| Шаблоны генерации | Как подключить шаблоны для кодогенерации? |
|
||||||
| VS Code | Как настроить редактор для проекта? |
|
| VS Code | Как настроить редактор для проекта? |
|
||||||
|
|
||||||
@@ -62,19 +69,26 @@
|
|||||||
|--------|-------------------|
|
|--------|-------------------|
|
||||||
| Структура проекта | Как организованы папки и файлы по SLM? |
|
| Структура проекта | Как организованы папки и файлы по SLM? |
|
||||||
| Компоненты | Как устроен компонент: файлы, пропсы, clsx? |
|
| Компоненты | Как устроен компонент: файлы, пропсы, clsx? |
|
||||||
| Страницы (App Router) | Как описывать layout, page, loading, error, not-found? |
|
| Файлы роутинга | Как описывать layout, page, loading, error, not-found? |
|
||||||
| Данные: Введение | Как устроена работа с данными в проекте? |
|
|
||||||
| Данные: REST: Клиенты: Автоматическая генерация | Как сгенерировать REST-клиент автоматически из OpenAPI? |
|
|
||||||
| Данные: REST: Клиенты: Ручное создание | Как написать REST-клиент вручную? |
|
|
||||||
| Данные: REST: Получение данных: Серверные компоненты | Как получать данные в серверных компонентах? |
|
|
||||||
| Данные: REST: Получение данных: Клиентские компоненты | Как получать данные в клиентских компонентах (SWR)? |
|
|
||||||
| Данные: Realtime | Как работать с realtime-каналами и сокетами? |
|
|
||||||
| Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? |
|
| Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? |
|
||||||
| Стили | Как писать CSS: PostCSS Modules, вложенность, медиа, токены? |
|
| Стили | Как писать CSS: вложенность, медиа, токены? |
|
||||||
| Изображения | _(не заполнен)_ |
|
|
||||||
| SVG-спрайты | Как использовать SVG-спрайты в коде? |
|
| SVG-спрайты | Как использовать SVG-спрайты в коде? |
|
||||||
|
| Изображения | _(не заполнен)_ |
|
||||||
| Видео | _(не заполнен)_ |
|
| Видео | _(не заполнен)_ |
|
||||||
| Stores | _(не заполнен)_ |
|
| Stores | _(не заполнен)_ |
|
||||||
| Хуки | _(не заполнен)_ |
|
| Хуки | _(не заполнен)_ |
|
||||||
| Шрифты | _(не заполнен)_ |
|
| Шрифты | _(не заполнен)_ |
|
||||||
| Локализация | _(не заполнен)_ |
|
| Локализация | _(не заполнен)_ |
|
||||||
|
|
||||||
|
### Данные
|
||||||
|
|
||||||
|
**Как работать с источниками данных** — REST, realtime и потребление в компонентах.
|
||||||
|
|
||||||
|
| Раздел | Отвечает на вопрос |
|
||||||
|
|--------|-------------------|
|
||||||
|
| Источники данных | Как устроена работа с данными в проекте? |
|
||||||
|
| REST: Автоматическая генерация | Как сгенерировать REST-клиент автоматически из OpenAPI? |
|
||||||
|
| REST: Ручное создание | Как написать REST-клиент вручную? |
|
||||||
|
| REST: Серверные компоненты | Как получать данные в серверных компонентах? |
|
||||||
|
| REST: Клиентские компоненты | Как получать данные в клиентских компонентах? |
|
||||||
|
| Realtime | Как работать с realtime-каналами и сокетами? |
|
||||||
|
|||||||
@@ -30,16 +30,16 @@
|
|||||||
**Описание:** Архитектурный подход проекта: что такое SLM и как он устроен.
|
**Описание:** Архитектурный подход проекта: что такое SLM и как он устроен.
|
||||||
|
|
||||||
### docs/docs/basics/architecture/reference/layers.md
|
### docs/docs/basics/architecture/reference/layers.md
|
||||||
**Заголовок:** Слои
|
**Заголовок:** Слои SLM
|
||||||
**Описание:** Из каких слоёв состоит архитектура и как они связаны.
|
**Описание:** Из каких слоёв состоит SLM-архитектура и как они связаны.
|
||||||
|
|
||||||
### docs/docs/basics/architecture/reference/modules.md
|
### docs/docs/basics/architecture/reference/modules.md
|
||||||
**Заголовок:** Модули
|
**Заголовок:** Модули SLM
|
||||||
**Описание:** Что такое модуль в архитектуре и как он устроен.
|
**Описание:** Что такое модуль в SLM-архитектуре и как он устроен.
|
||||||
|
|
||||||
### docs/docs/basics/architecture/reference/segments.md
|
### docs/docs/basics/architecture/reference/segments.md
|
||||||
**Заголовок:** Сегменты
|
**Заголовок:** Сегменты SLM
|
||||||
**Описание:** Что такое сегмент модуля и какие они бывают.
|
**Описание:** Что такое сегмент модуля в SLM-архитектуре и какие они бывают.
|
||||||
|
|
||||||
### docs/docs/basics/code-style.md
|
### docs/docs/basics/code-style.md
|
||||||
**Заголовок:** Стиль кода
|
**Заголовок:** Стиль кода
|
||||||
@@ -154,11 +154,11 @@
|
|||||||
**Описание:** Какие источники данных используются в проекте и как с ними работать.
|
**Описание:** Какие источники данных используются в проекте и как с ними работать.
|
||||||
|
|
||||||
### docs/docs/usage/data/rest/clients/auto.md
|
### docs/docs/usage/data/rest/clients/auto.md
|
||||||
**Заголовок:** Автоматическая генерация
|
**Заголовок:** Автогенерация REST-клиента
|
||||||
**Описание:** Генерация REST-клиента из OpenAPI-спецификации.
|
**Описание:** Генерация REST-клиента из OpenAPI-спецификации.
|
||||||
|
|
||||||
### docs/docs/usage/data/rest/clients/manual.md
|
### docs/docs/usage/data/rest/clients/manual.md
|
||||||
**Заголовок:** Ручное создание
|
**Заголовок:** Ручное создание REST-клиента
|
||||||
**Описание:** Создание REST-клиента вручную, когда нет OpenAPI-спецификации.
|
**Описание:** Создание REST-клиента вручную, когда нет OpenAPI-спецификации.
|
||||||
|
|
||||||
### docs/docs/usage/data/rest/fetching/server.md
|
### docs/docs/usage/data/rest/fetching/server.md
|
||||||
|
|||||||
103
docs/docs/DEVELOP.md
Normal file
103
docs/docs/DEVELOP.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
title: Гид для агента
|
||||||
|
description: Что AI-агент обязан прочитать перед началом работы, а что — по задаче.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Обязательное чтение перед началом работы
|
||||||
|
|
||||||
|
Этот документ определяет **строгий порядок действий агента перед выполнением любых задач**.
|
||||||
|
|
||||||
|
## Общее правило
|
||||||
|
|
||||||
|
Перед началом работы над **любой задачей** агент **обязан ознакомиться с базовой документацией проекта**.
|
||||||
|
|
||||||
|
Нарушение этого порядка считается ошибкой.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Порядок обязательного чтения
|
||||||
|
|
||||||
|
Агент должен читать документацию **строго в следующем порядке**:
|
||||||
|
|
||||||
|
### 1. Архитектура (КРИТИЧЕСКИ ВАЖНО)
|
||||||
|
|
||||||
|
* [Архитектура: Обзор](./basics/architecture/index.md)
|
||||||
|
* [Архитектура: Слои](./basics/architecture/reference/layers.md)
|
||||||
|
* [Архитектура: Модули](./basics/architecture/reference/modules.md)
|
||||||
|
* [Архитектура: Сегменты](./basics/architecture/reference/segments.md)
|
||||||
|
|
||||||
|
**Архитектура — это самое важное в проекте.**
|
||||||
|
|
||||||
|
Агент обязан:
|
||||||
|
|
||||||
|
* строго понимать архитектурный подход (SLM)
|
||||||
|
* соблюдать архитектуру **на 100% без отклонений**
|
||||||
|
* не предлагать решений, нарушающих архитектурные принципы
|
||||||
|
* не упрощать архитектуру даже ради скорости выполнения задачи
|
||||||
|
|
||||||
|
Любое нарушение архитектуры недопустимо.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Базовые правила
|
||||||
|
|
||||||
|
После архитектуры необходимо изучить:
|
||||||
|
|
||||||
|
* [Технологии и библиотеки](./basics/tech-stack.md)
|
||||||
|
* [Именование](./basics/naming.md)
|
||||||
|
* [Стиль кода](./basics/code-style.md)
|
||||||
|
* [Документирование](./basics/documentation.md)
|
||||||
|
* [Типизация](./basics/typing.md)
|
||||||
|
|
||||||
|
Агент обязан применять эти правила во всех решениях.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Использование карты документации
|
||||||
|
|
||||||
|
Для поиска дополнительных сведений агент должен использовать:
|
||||||
|
|
||||||
|
* [MAP.md](./MAP.md)
|
||||||
|
|
||||||
|
MAP.md содержит ссылки на все прикладные и вспомогательные разделы.
|
||||||
|
|
||||||
|
Агент может:
|
||||||
|
|
||||||
|
* переходить к нужным разделам через MAP.md
|
||||||
|
* уточнять детали реализации
|
||||||
|
* искать примеры и частные случаи
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Запрещено
|
||||||
|
|
||||||
|
Агенту запрещено:
|
||||||
|
|
||||||
|
* начинать выполнение задачи без изучения архитектуры
|
||||||
|
* игнорировать базовые правила
|
||||||
|
* принимать решения, противоречащие архитектуре
|
||||||
|
* придумывать собственные подходы, если они не описаны в документации
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ожидаемое поведение агента
|
||||||
|
|
||||||
|
Перед выполнением задачи агент должен:
|
||||||
|
|
||||||
|
1. Изучить архитектуру
|
||||||
|
2. Изучить базовые правила
|
||||||
|
3. При необходимости открыть MAP.md и найти релевантные разделы
|
||||||
|
4. Только после этого приступать к решению задачи
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Приоритеты
|
||||||
|
|
||||||
|
При принятии решений агент должен руководствоваться следующим приоритетом:
|
||||||
|
|
||||||
|
1. **Архитектура**
|
||||||
|
2. Базовые правила
|
||||||
|
3. Документация из MAP.md
|
||||||
|
4. Задача пользователя
|
||||||
|
|
||||||
|
Если задача противоречит архитектуре — задача должна быть переосмыслена, а не выполнена напрямую.
|
||||||
54
docs/docs/MAP.md
Normal file
54
docs/docs/MAP.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Карта документации
|
||||||
|
|
||||||
|
Список всех разделов архива с относительными ссылками. Точка входа
|
||||||
|
— `DEVELOP.md` рядом с этим файлом.
|
||||||
|
|
||||||
|
## Подсказки
|
||||||
|
|
||||||
|
- [Подсказки](./workflow.md) — Короткие ответы на типовые вопросы и решения для спорных ситуаций.
|
||||||
|
|
||||||
|
## Базовые правила
|
||||||
|
|
||||||
|
- [Технологии и библиотеки](./basics/tech-stack.md) — Какие библиотеки и инструменты используются в проекте.
|
||||||
|
- [Именование](./basics/naming.md) — Как называть переменные, файлы и прочие сущности в коде.
|
||||||
|
- [Архитектура: Обзор](./basics/architecture/index.md) — Архитектурный подход проекта: что такое SLM и как он устроен.
|
||||||
|
- [Архитектура: Слои](./basics/architecture/reference/layers.md) — Из каких слоёв состоит SLM-архитектура и как они связаны.
|
||||||
|
- [Архитектура: Модули](./basics/architecture/reference/modules.md) — Что такое модуль в SLM-архитектуре и как он устроен.
|
||||||
|
- [Архитектура: Сегменты](./basics/architecture/reference/segments.md) — Что такое сегмент модуля в SLM-архитектуре и какие они бывают.
|
||||||
|
- [Стиль кода](./basics/code-style.md) — Как оформляется код в проекте.
|
||||||
|
- [Документирование](./basics/documentation.md) — Что и как документировать в коде.
|
||||||
|
- [Типизация](./basics/typing.md) — Как типизируется код в проекте.
|
||||||
|
|
||||||
|
## Создание проекта
|
||||||
|
|
||||||
|
- [Из шаблона](./creating-project/from-template.md) — Создание нового проекта на основе готового шаблона.
|
||||||
|
- [По гайду вручную](./creating-project/manual.md) — Поэтапное создание нового проекта без использования шаблона.
|
||||||
|
- [Чистый Next.js](./creating-project/nextjs.md) — Установка Next.js без лишнего шаблона — голый каркас под дальнейшую сборку.
|
||||||
|
|
||||||
|
## Настройка
|
||||||
|
|
||||||
|
- [Алиасы импортов](./setup/aliases.md) — Какие алиасы импортов есть в проекте и как ими пользоваться.
|
||||||
|
- [Biome](./setup/biome.md) — Установка и настройка линтера-форматтера в новом проекте.
|
||||||
|
- [PostCSS](./setup/postcss.md) — Установка и настройка CSS-процессора в новом проекте.
|
||||||
|
- [Стили](./setup/styles.md) — Подготовка стилевой основы проекта: токены, медиа-запросы, глобальные стили.
|
||||||
|
- [SVG-спрайты](./setup/svg-sprites.md) — Подключение SVG-спрайтов в новом проекте.
|
||||||
|
- [Шаблоны генерации](./setup/templates.md) — Подключение шаблонов кодогенерации в новом проекте.
|
||||||
|
- [VS Code](./setup/vscode.md) — Единые настройки редактора и расширений для команды.
|
||||||
|
|
||||||
|
## Использование
|
||||||
|
|
||||||
|
- [Структура проекта](./usage/project-structure.md) — Из чего состоит проект и где что лежит.
|
||||||
|
- [Компоненты](./usage/components.md) — Как устроен и пишется React-компонент в проекте.
|
||||||
|
- [Страницы (App Router)](./usage/page-level.md) — Что должно лежать в файлах роутинга, а что — в экранах.
|
||||||
|
- [Шаблоны и генерация кода](./usage/templates-generation.md) — Как устроены шаблоны кодогенерации и как ими пользоваться.
|
||||||
|
- [Стили](./usage/styles.md) — Как пишутся стили в проекте.
|
||||||
|
- [SVG-спрайты](./usage/svg-sprites.md) — Как добавлять и использовать SVG-иконки в коде.
|
||||||
|
|
||||||
|
## Данные
|
||||||
|
|
||||||
|
- [Введение](./usage/data/index.md) — Какие источники данных используются в проекте и как с ними работать.
|
||||||
|
- [REST: Клиенты: Автоматическая генерация](./usage/data/rest/clients/auto.md) — Генерация REST-клиента из OpenAPI-спецификации.
|
||||||
|
- [REST: Клиенты: Ручное создание](./usage/data/rest/clients/manual.md) — Создание REST-клиента вручную, когда нет OpenAPI-спецификации.
|
||||||
|
- [REST: Получение данных: Серверные компоненты](./usage/data/rest/fetching/server.md) — Получение REST-данных в серверных компонентах.
|
||||||
|
- [REST: Получение данных: Клиентские компоненты](./usage/data/rest/fetching/client.md) — Получение REST-данных в клиентских компонентах.
|
||||||
|
- [Realtime](./usage/data/realtime.md) — Работа с push-данными от сервера: подписки и события.
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: Слои
|
title: Слои SLM
|
||||||
description: Из каких слоёв состоит архитектура и как они связаны.
|
description: Из каких слоёв состоит SLM-архитектура и как они связаны.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Слои
|
# Слои SLM
|
||||||
|
|
||||||
Из каких слоёв состоит архитектура и как они связаны.
|
Из каких слоёв состоит SLM-архитектура и как они связаны.
|
||||||
|
|
||||||
## Определение
|
## Определение
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: Модули
|
title: Модули SLM
|
||||||
description: Что такое модуль в архитектуре и как он устроен.
|
description: Что такое модуль в SLM-архитектуре и как он устроен.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Модули
|
# Модули SLM
|
||||||
|
|
||||||
Что такое модуль в архитектуре и как он устроен.
|
Что такое модуль в SLM-архитектуре и как он устроен.
|
||||||
|
|
||||||
## Определение
|
## Определение
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: Сегменты
|
title: Сегменты SLM
|
||||||
description: Что такое сегмент модуля и какие они бывают.
|
description: Что такое сегмент модуля в SLM-архитектуре и какие они бывают.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Сегменты
|
# Сегменты SLM
|
||||||
|
|
||||||
Что такое сегмент модуля и какие они бывают.
|
Что такое сегмент модуля в SLM-архитектуре и какие они бывают.
|
||||||
|
|
||||||
## Определение
|
## Определение
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ description: Стандарты разработки фронтенд-прило
|
|||||||
|
|
||||||
## Структура документации
|
## Структура документации
|
||||||
|
|
||||||
### Workflow
|
### Подсказки
|
||||||
|
|
||||||
[Workflow](/docs/workflow) — пошаговая карта типовых задач: с чего начать, как добавить страницу/компонент, как подключить данные, стили, локализацию.
|
[Подсказки](/docs/workflow) — короткие ответы на типовые вопросы и решения для спорных ситуаций.
|
||||||
|
|
||||||
### Базовые правила
|
### Базовые правила
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ description: Стандарты разработки фронтенд-прило
|
|||||||
|--------|-------------------|
|
|--------|-------------------|
|
||||||
| Технологии и библиотеки | Какой стек используем? |
|
| Технологии и библиотеки | Какой стек используем? |
|
||||||
| Именование | Как называть файлы, переменные, компоненты, хуки? |
|
| Именование | Как называть файлы, переменные, компоненты, хуки? |
|
||||||
| Архитектура: Обзор | Что такое SLM и зачем она нужна? |
|
| SLM Design | Что такое SLM и зачем она нужна? |
|
||||||
| Архитектура: Слои | Какие слои есть и как между ними устроены зависимости? |
|
| Архитектура: Слои | Какие слои есть и как между ними устроены зависимости? |
|
||||||
| Архитектура: Модули | Что такое модуль и как он устроен? |
|
| Архитектура: Модули | Что такое модуль и как он устроен? |
|
||||||
| Архитектура: Сегменты | Какие сегменты есть внутри модуля? |
|
| Архитектура: Сегменты | Какие сегменты есть внутри модуля? |
|
||||||
@@ -40,20 +40,27 @@ description: Стандарты разработки фронтенд-прило
|
|||||||
| Документирование | Как писать JSDoc: что документировать, а что нет? |
|
| Документирование | Как писать JSDoc: что документировать, а что нет? |
|
||||||
| Типизация | Как типизировать: type vs interface, any/unknown? |
|
| Типизация | Как типизировать: type vs interface, any/unknown? |
|
||||||
|
|
||||||
### Установка и настройка
|
### Создание проекта
|
||||||
|
|
||||||
**Как поднять и сконфигурировать проект** — пошаговая настройка инструментов и инфраструктуры.
|
**Как начать новый проект** — варианты установки и эталонный набор инструментов.
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
| Раздел | Отвечает на вопрос |
|
||||||
|--------|-------------------|
|
|--------|-------------------|
|
||||||
| Создание проекта: Из шаблона | Как начать проект из готового шаблона? |
|
| Создание проекта из шаблона | Как начать проект из готового шаблона? |
|
||||||
| Создание проекта: Вручную | Как поднять проект с нуля без шаблона? |
|
| Создание проекта вручную | Как поднять проект с нуля без шаблона? |
|
||||||
| Next.js | Как настроить Next.js под проект? |
|
| Чистая установка Next.js | Как поставить голый Next.js под дальнейшую сборку? |
|
||||||
| Алиасы | Как настроить путевые алиасы импортов? |
|
|
||||||
| Biome | Как настроить Biome (линтер и форматер)? |
|
### Настройка
|
||||||
| Стили | Как подключить и настроить стили проекта? |
|
|
||||||
|
**Как сконфигурировать проект** — пошаговая настройка инструментов и инфраструктуры.
|
||||||
|
|
||||||
|
| Раздел | Отвечает на вопрос |
|
||||||
|
|--------|-------------------|
|
||||||
|
| Алиасы импортов | Как настроить алиасы импортов? |
|
||||||
|
| Biome | Как настроить линтер и форматтер? |
|
||||||
| PostCSS | Какие плагины PostCSS нужны и как их настроить? |
|
| PostCSS | Какие плагины PostCSS нужны и как их настроить? |
|
||||||
| SVG-спрайты | Как настроить генерацию SVG-спрайтов? |
|
| Стили | Как подключить базовые стили и токены? |
|
||||||
|
| SVG-спрайты | Как подключить генерацию SVG-спрайтов? |
|
||||||
| Шаблоны генерации | Как подключить шаблоны для кодогенерации? |
|
| Шаблоны генерации | Как подключить шаблоны для кодогенерации? |
|
||||||
| VS Code | Как настроить редактор для проекта? |
|
| VS Code | Как настроить редактор для проекта? |
|
||||||
|
|
||||||
@@ -65,19 +72,26 @@ description: Стандарты разработки фронтенд-прило
|
|||||||
|--------|-------------------|
|
|--------|-------------------|
|
||||||
| Структура проекта | Как организованы папки и файлы по SLM? |
|
| Структура проекта | Как организованы папки и файлы по SLM? |
|
||||||
| Компоненты | Как устроен компонент: файлы, пропсы, clsx? |
|
| Компоненты | Как устроен компонент: файлы, пропсы, clsx? |
|
||||||
| Страницы (App Router) | Как описывать layout, page, loading, error, not-found? |
|
| Файлы роутинга | Как описывать layout, page, loading, error, not-found? |
|
||||||
| Данные: Введение | Как устроена работа с данными в проекте? |
|
|
||||||
| Данные: REST: Клиенты: Автоматическая генерация | Как сгенерировать REST-клиент автоматически из OpenAPI? |
|
|
||||||
| Данные: REST: Клиенты: Ручное создание | Как написать REST-клиент вручную? |
|
|
||||||
| Данные: REST: Получение данных: Серверные компоненты | Как получать данные в серверных компонентах? |
|
|
||||||
| Данные: REST: Получение данных: Клиентские компоненты | Как получать данные в клиентских компонентах (SWR)? |
|
|
||||||
| Данные: Realtime | Как работать с realtime-каналами и сокетами? |
|
|
||||||
| Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? |
|
| Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? |
|
||||||
| Стили | Как писать CSS: PostCSS Modules, вложенность, медиа, токены? |
|
| Стили | Как писать CSS: вложенность, медиа, токены? |
|
||||||
| Изображения | _(не заполнен)_ |
|
|
||||||
| SVG-спрайты | Как использовать SVG-спрайты в коде? |
|
| SVG-спрайты | Как использовать SVG-спрайты в коде? |
|
||||||
|
| Изображения | _(не заполнен)_ |
|
||||||
| Видео | _(не заполнен)_ |
|
| Видео | _(не заполнен)_ |
|
||||||
| Stores | _(не заполнен)_ |
|
| Stores | _(не заполнен)_ |
|
||||||
| Хуки | _(не заполнен)_ |
|
| Хуки | _(не заполнен)_ |
|
||||||
| Шрифты | _(не заполнен)_ |
|
| Шрифты | _(не заполнен)_ |
|
||||||
| Локализация | _(не заполнен)_ |
|
| Локализация | _(не заполнен)_ |
|
||||||
|
|
||||||
|
### Данные
|
||||||
|
|
||||||
|
**Как работать с источниками данных** — REST, realtime и потребление в компонентах.
|
||||||
|
|
||||||
|
| Раздел | Отвечает на вопрос |
|
||||||
|
|--------|-------------------|
|
||||||
|
| Источники данных | Как устроена работа с данными в проекте? |
|
||||||
|
| REST: Автоматическая генерация | Как сгенерировать REST-клиент автоматически из OpenAPI? |
|
||||||
|
| REST: Ручное создание | Как написать REST-клиент вручную? |
|
||||||
|
| REST: Серверные компоненты | Как получать данные в серверных компонентах? |
|
||||||
|
| REST: Клиентские компоненты | Как получать данные в клиентских компонентах? |
|
||||||
|
| Realtime | Как работать с realtime-каналами и сокетами? |
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: Автоматическая генерация
|
title: Автогенерация REST-клиента
|
||||||
description: Генерация REST-клиента из OpenAPI-спецификации.
|
description: Генерация REST-клиента из OpenAPI-спецификации.
|
||||||
keywords: [api, rest, openapi, codegen, генерация, клиент, api-codegen, gromlab, infrastructure, swagger-typescript-api]
|
keywords: [api, rest, openapi, codegen, генерация, клиент, api-codegen, gromlab, infrastructure, swagger-typescript-api]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Автоматическая генерация
|
# Автогенерация REST-клиента
|
||||||
|
|
||||||
Генерация REST-клиента из OpenAPI-спецификации.
|
Генерация REST-клиента из OpenAPI-спецификации.
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: Ручное создание
|
title: Ручное создание REST-клиента
|
||||||
description: "Создание REST-клиента вручную, когда нет OpenAPI-спецификации."
|
description: "Создание REST-клиента вручную, когда нет OpenAPI-спецификации."
|
||||||
keywords: [api, rest, клиент, ручной, fetch, infrastructure, api-клиент]
|
keywords: [api, rest, клиент, ручной, fetch, infrastructure, api-клиент]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Ручное создание
|
# Ручное создание REST-клиента
|
||||||
|
|
||||||
Создание REST-клиента вручную, когда нет OpenAPI-спецификации.
|
Создание REST-клиента вручную, когда нет OpenAPI-спецификации.
|
||||||
|
|
||||||
|
|||||||
@@ -241,13 +241,20 @@ const copyDirSync = (
|
|||||||
/**
|
/**
|
||||||
* Скопировать все `.md`-файлы документации в `docs/public/docs/`,
|
* Скопировать все `.md`-файлы документации в `docs/public/docs/`,
|
||||||
* чтобы они попали в build `dist/` и были доступны по URL `/docs/path.md`.
|
* чтобы они попали в build `dist/` и были доступны по URL `/docs/path.md`.
|
||||||
|
*
|
||||||
|
* `DEVELOP.md` исключается — это точка входа архива, ссылается
|
||||||
|
* на офлайн-файлы (`MAP.md`), которых нет на сайте.
|
||||||
*/
|
*/
|
||||||
const copyMdFiles = (): void => {
|
const copyMdFiles = (): void => {
|
||||||
const srcDir = 'docs/docs';
|
const srcDir = 'docs/docs';
|
||||||
const destDir = path.join(PUBLIC_DIR, 'docs');
|
const destDir = path.join(PUBLIC_DIR, 'docs');
|
||||||
if (!fs.existsSync(srcDir)) return;
|
if (!fs.existsSync(srcDir)) return;
|
||||||
|
|
||||||
const copied = copyDirSync(srcDir, destDir, (name) => name.endsWith('.md'));
|
const copied = copyDirSync(
|
||||||
|
srcDir,
|
||||||
|
destDir,
|
||||||
|
(name) => name.endsWith('.md') && name !== 'DEVELOP.md',
|
||||||
|
);
|
||||||
console.log(`скопировано ${copied} .md-файлов в ${destDir}`);
|
console.log(`скопировано ${copied} .md-файлов в ${destDir}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -304,98 +311,31 @@ const transformLinksInDir = (rootDir: string): void => {
|
|||||||
walk(rootDir);
|
walk(rootDir);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Сгенерировать `README.md` — точка входа архива. Карта документации
|
|
||||||
* с относительными ссылками, описаниями из frontmatter/первого абзаца
|
|
||||||
* и метаинфо сборки.
|
|
||||||
*/
|
|
||||||
const buildArchiveReadme = (rootDir: string): void => {
|
|
||||||
const sidebar = cfg.themeConfig.sidebar;
|
|
||||||
const blockquote = cfg.llmsBlockquote ?? cfg.description ?? '';
|
|
||||||
const context = cfg.llmsContext;
|
|
||||||
|
|
||||||
const entries = flattenSidebar(sidebar).filter(
|
|
||||||
// «Главная» из sidebar — это страница раздела для веба, в архиве не нужна.
|
|
||||||
(e) => e.section !== 'Главная',
|
|
||||||
);
|
|
||||||
const grouped = groupBySection(entries);
|
|
||||||
|
|
||||||
const lines: string[] = [];
|
|
||||||
lines.push(`# ${cfg.title}`);
|
|
||||||
lines.push('');
|
|
||||||
if (blockquote) {
|
|
||||||
lines.push(`> ${blockquote}`);
|
|
||||||
lines.push('');
|
|
||||||
}
|
|
||||||
if (context) {
|
|
||||||
lines.push(context);
|
|
||||||
lines.push('');
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.push('## Содержание');
|
|
||||||
lines.push('');
|
|
||||||
|
|
||||||
for (const [section, items] of grouped) {
|
|
||||||
lines.push(`### ${section}`);
|
|
||||||
lines.push('');
|
|
||||||
for (const entry of items) {
|
|
||||||
const targetRel = './' + linkToArchiveRel(entry.link);
|
|
||||||
const filePath = path.join(rootDir, linkToArchiveRel(entry.link));
|
|
||||||
|
|
||||||
let description: string | null = null;
|
|
||||||
if (fs.existsSync(filePath)) {
|
|
||||||
const raw = fs.readFileSync(filePath, 'utf8');
|
|
||||||
const { data, body } = parseFrontmatter(raw);
|
|
||||||
description = data.description || firstParagraphAfterH1(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
const display = entry.prefix
|
|
||||||
? `${entry.prefix}: ${entry.text}`
|
|
||||||
: entry.text;
|
|
||||||
const descPart = description ? ` — ${description}` : '';
|
|
||||||
lines.push(`- [${display}](${targetRel})${descPart}`);
|
|
||||||
}
|
|
||||||
lines.push('');
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.push('---');
|
|
||||||
lines.push('');
|
|
||||||
lines.push(`Версия: ${VERSION} · Сборка: ${BUILD_DATE}`);
|
|
||||||
lines.push('');
|
|
||||||
|
|
||||||
fs.writeFileSync(path.join(rootDir, 'README.md'), lines.join('\n'), 'utf8');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Собрать `nextjs-style-guide.zip`. Внутри — папка `nextjs-style-guide/`
|
* Собрать `nextjs-style-guide.zip`. Внутри — папка `nextjs-style-guide/`
|
||||||
* с `.md`-файлами, README, `llms-full.txt` и `VERSION`. Внутренние ссылки
|
* с `.md`-файлами, DEVELOP.md (точка входа), MAP.md (навигационная карта)
|
||||||
* преобразуются в относительные.
|
* и `VERSION`. Внутренние ссылки преобразуются в относительные.
|
||||||
|
*
|
||||||
|
* Точка входа архива — `docs/docs/DEVELOP.md`, навигационная карта —
|
||||||
|
* `docs/docs/MAP.md`. Оба файла редактируются вручную и копируются
|
||||||
|
* в архив как есть.
|
||||||
*/
|
*/
|
||||||
const buildZip = (): void => {
|
const buildZip = (): void => {
|
||||||
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nsg-'));
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nsg-'));
|
||||||
const stage = path.join(tmpRoot, 'nextjs-style-guide');
|
const stage = path.join(tmpRoot, 'nextjs-style-guide');
|
||||||
fs.mkdirSync(stage, { recursive: true });
|
fs.mkdirSync(stage, { recursive: true });
|
||||||
|
|
||||||
// 1. Копируем все .md в staging.
|
// 1. Копируем все .md в staging (включая DEVELOP.md и MAP.md).
|
||||||
copyDirSync('docs/docs', stage, (name) => name.endsWith('.md'));
|
copyDirSync('docs/docs', stage, (name) => name.endsWith('.md'));
|
||||||
|
|
||||||
// 2. Удаляем веб-index.md — в архиве его роль выполняет README.md.
|
// 2. Удаляем веб-index.md — в архиве его роль выполняет DEVELOP.md.
|
||||||
const indexPath = path.join(stage, 'index.md');
|
const indexPath = path.join(stage, 'index.md');
|
||||||
if (fs.existsSync(indexPath)) fs.unlinkSync(indexPath);
|
if (fs.existsSync(indexPath)) fs.unlinkSync(indexPath);
|
||||||
|
|
||||||
// 3. Преобразуем абсолютные ссылки `/docs/...` в относительные.
|
// 3. Преобразуем абсолютные ссылки `/docs/...` в относительные.
|
||||||
transformLinksInDir(stage);
|
transformLinksInDir(stage);
|
||||||
|
|
||||||
// 4. Генерируем точку входа README.md.
|
// 4. Метаинформация сборки.
|
||||||
buildArchiveReadme(stage);
|
|
||||||
|
|
||||||
// 5. Кладём llms-full.txt — удобно для одноразового чтения LLM.
|
|
||||||
const llmsFullSrc = path.join(PUBLIC_DIR, 'llms-full.txt');
|
|
||||||
if (fs.existsSync(llmsFullSrc)) {
|
|
||||||
fs.copyFileSync(llmsFullSrc, path.join(stage, 'llms-full.txt'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Метаинформация сборки.
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(stage, 'VERSION'),
|
path.join(stage, 'VERSION'),
|
||||||
`${VERSION}\n${BUILD_DATE}\n`,
|
`${VERSION}\n${BUILD_DATE}\n`,
|
||||||
|
|||||||
153
notes
153
notes
@@ -1,153 +0,0 @@
|
|||||||
ФЛОУ
|
|
||||||
- после создания компонента, заменить шаблонный коментарий документа на реальный.
|
|
||||||
|
|
||||||
|
|
||||||
Проблема, неочевидность слоев (наследие FSD)
|
|
||||||
|
|
||||||
|
|
||||||
Архитектурные слои проекта
|
|
||||||
Каждый нижний слой не знает о существовании верхних. Импорты идут только сверху вниз.
|
|
||||||
pages → layouts → screens → widgets → features → entities → shared
|
|
||||||
---
|
|
||||||
1. Pages (pages/)
|
|
||||||
Точка входа маршрута. Только связывает layout и screen.
|
|
||||||
Правила:
|
|
||||||
- Никакой логики, стилей, разметки кроме композиции
|
|
||||||
- Один 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/ —
|
|
||||||
|
|
||||||
|
|
||||||
И еще размышлений
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Что у нас по факту
|
|
||||||
Мы взяли 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).
|
|
||||||
Reference in New Issue
Block a user