refactor: перенести сборку в проекты
- перенесены каноны и VitePress-конфиги в projects/<slug> - добавлены корневой и проектные build.ts для сборки артефактов - добавлены shared-библиотеки сборки в projects/_shared/lib - обновлены CI, Dockerfile, package.json, gitignore и README - удалена сборка frontend-агента
This commit is contained in:
153
projects/nextjs-style-guide/canons/basics/code-style.md
Normal file
153
projects/nextjs-style-guide/canons/basics/code-style.md
Normal file
@@ -0,0 +1,153 @@
|
||||
---
|
||||
title: Стиль кода
|
||||
description: Как оформляется код в проекте.
|
||||
---
|
||||
|
||||
# Стиль кода
|
||||
|
||||
Как оформляется код в проекте.
|
||||
|
||||
## Отступы
|
||||
|
||||
- 2 пробела (не табы).
|
||||
|
||||
## Длина строк
|
||||
|
||||
- Ориентироваться на 100 символов, но превышение допустимо, если строка читается легко.
|
||||
- Переносить выражение на новые строки, когда строка становится плохо читаемой.
|
||||
- Не переносить строку внутри строковых литералов без необходимости.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const config = createRequestConfig(
|
||||
endpoint,
|
||||
{
|
||||
headers: {
|
||||
'X-Request-Id': requestId,
|
||||
'X-User-Id': userId,
|
||||
},
|
||||
params: {
|
||||
page,
|
||||
pageSize,
|
||||
sort: 'createdAt',
|
||||
},
|
||||
},
|
||||
timeoutMs,
|
||||
);
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: длинная строка с вложенными структурами плохо читается.
|
||||
const config = createRequestConfig(endpoint, { headers: { 'X-Request-Id': requestId, 'X-User-Id': userId }, params: { page, pageSize, sort: 'createdAt' } }, timeoutMs);
|
||||
```
|
||||
|
||||
## Кавычки
|
||||
|
||||
- В JavaScript/TypeScript использовать одинарные кавычки.
|
||||
- В JSX/TSX для атрибутов использовать двойные кавычки.
|
||||
- Шаблонные строки использовать только при интерполяции или многострочном тексте.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const label = 'Сохранить';
|
||||
const title = `Привет, ${name}`;
|
||||
```
|
||||
|
||||
```tsx
|
||||
<input type="text" placeholder="Введите имя" />
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: двойные кавычки в TS и конкатенация вместо шаблонной строки.
|
||||
const label = "Сохранить";
|
||||
const title = 'Привет, ' + name;
|
||||
```
|
||||
|
||||
```tsx
|
||||
// Плохо: одинарные кавычки в JSX-атрибутах.
|
||||
<input type='text' placeholder='Введите имя' />
|
||||
```
|
||||
|
||||
## Точки с запятой и запятые
|
||||
|
||||
- Допускаются упущения точки с запятой, если код остаётся читаемым и однозначным.
|
||||
- В многострочных массивах, объектах и параметрах функции запятая в конце допускается, но не обязательна.
|
||||
|
||||
## Импорты
|
||||
|
||||
- В именованных импортах использовать пробелы внутри фигурных скобок.
|
||||
- Типы импортировать через `import type`.
|
||||
- `default` экспорт избегать, использовать именованные. `default` импорт допустим (например, стили CSS Modules, сторонние библиотеки).
|
||||
- Избегать импорта всего модуля через `*`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
import { MyComponent } from 'MyComponent';
|
||||
import type { User } from '../model/types';
|
||||
import styles from './styles/button.module.css';
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: отсутствие пробелов в именованном импорте.
|
||||
import type {User} from '../model/types';
|
||||
// Плохо: default экспорт.
|
||||
export default MyComponent;
|
||||
```
|
||||
|
||||
## Ранние возвраты (early return)
|
||||
|
||||
- Использовать ранние возвраты для упрощения чтения.
|
||||
- Избегать `else` после `return`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const getName = (user?: { name: string }) => {
|
||||
if (!user) {
|
||||
return 'Гость';
|
||||
}
|
||||
|
||||
return user.name;
|
||||
};
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: лишний else после return усложняет чтение.
|
||||
const getName = (user?: { name: string }) => {
|
||||
if (user) {
|
||||
return user.name;
|
||||
} else {
|
||||
return 'Гость';
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Форматирование объектов и массивов
|
||||
|
||||
- В многострочных объектах каждое свойство на новой строке.
|
||||
- В многострочных массивах каждый элемент на новой строке.
|
||||
- Объекты и массивы можно писать в одну строку, если длина строки не превышает 100 символов.
|
||||
- В однострочных объектах и массивах использовать пробелы после запятых.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const roles = ['admin', 'editor', 'viewer'];
|
||||
const options = { id: 1, name: 'User' };
|
||||
|
||||
const config = {
|
||||
url: '/api/users',
|
||||
method: 'GET',
|
||||
params: { page: 1, pageSize: 20 },
|
||||
};
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: нет пробелов после запятых и объект слишком длинный для одной строки.
|
||||
const roles = ['admin','editor','viewer'];
|
||||
const options = { id: 1,name: 'User' };
|
||||
const config = { url: '/api/users', method: 'GET', params: { page: 1, pageSize: 20 } };
|
||||
```
|
||||
134
projects/nextjs-style-guide/canons/basics/documentation.md
Normal file
134
projects/nextjs-style-guide/canons/basics/documentation.md
Normal file
@@ -0,0 +1,134 @@
|
||||
---
|
||||
title: Документирование
|
||||
description: Что и как документировать в коде.
|
||||
---
|
||||
|
||||
# Документирование
|
||||
|
||||
Что и как документировать в коде.
|
||||
|
||||
## Общие правила
|
||||
|
||||
- Документировать публичные функции, компоненты, типы, интерфейсы и enum.
|
||||
- Не документировать очевидное — если название говорит само за себя, комментарий не нужен.
|
||||
- Не документировать параметры, возвращаемые значения и типы пропсов — они видны из сигнатуры.
|
||||
- Описание через пользу и назначение, а не через внутреннюю реализацию.
|
||||
- Описание завершается точкой.
|
||||
|
||||
## Функции
|
||||
|
||||
Для документирования функций используется шаблон. Описание механики опционально —
|
||||
добавляется когда логика нетривиальна.
|
||||
|
||||
**Шаблон**
|
||||
```ts
|
||||
/**
|
||||
* <Что делает функция в 1 строке>.
|
||||
*
|
||||
* <Опционально: описание сложной механики или важных нюансов>.
|
||||
*/
|
||||
```
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
/**
|
||||
* Форматирует цену с символом валюты.
|
||||
*/
|
||||
export const formatPrice = (value: number): string => { ... }
|
||||
|
||||
/**
|
||||
* Рекурсивно собирает дерево категорий из плоского списка.
|
||||
*
|
||||
* Группирует элементы по parentId, начиная с корневых (parentId = null).
|
||||
* Категории без родителя попадают в корень дерева.
|
||||
*/
|
||||
export const buildCategoryTree = (categories: Category[]): CategoryTree[] => { ... }
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: дублирует сигнатуру.
|
||||
/**
|
||||
* @param value - число
|
||||
* @returns строка с ценой
|
||||
*/
|
||||
```
|
||||
|
||||
## Компоненты
|
||||
|
||||
Компонент описывает своё **назначение** и **сценарии применения** — это помогает понять, когда и где его использовать, без необходимости читать реализацию.
|
||||
|
||||
**Шаблон**
|
||||
```ts
|
||||
/**
|
||||
* <Назначение компонента в 1 строке>.
|
||||
*
|
||||
* Используется для:
|
||||
* - <сценарий 1>
|
||||
* - <сценарий 2>
|
||||
* - <сценарий 3>
|
||||
*/
|
||||
```
|
||||
|
||||
**Хорошо**
|
||||
```tsx
|
||||
/**
|
||||
* Контейнер с адаптивной максимальной шириной.
|
||||
*
|
||||
* Используется для:
|
||||
* - обёртки контента страниц с ограничением ширины
|
||||
* - центрирования блоков в лейауте
|
||||
*/
|
||||
export const Container = (props: ContainerProps) => { ... }
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```tsx
|
||||
// Плохо: описывает реализацию, а не назначение.
|
||||
/**
|
||||
* Рендерит div с className и htmlAttr.
|
||||
*/
|
||||
|
||||
// Плохо: нет описания вообще.
|
||||
export const Container = (props: ContainerProps) => { ... }
|
||||
```
|
||||
|
||||
## Типы, интерфейсы, enum
|
||||
|
||||
Документируются назначение сущности и каждое её поле.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
/**
|
||||
* Фильтры списка задач.
|
||||
*/
|
||||
export enum TodoFilter {
|
||||
/** Все задачи. */
|
||||
ALL = 'all',
|
||||
/** Только активные. */
|
||||
ACTIVE = 'active',
|
||||
/** Только завершённые. */
|
||||
COMPLETED = 'completed',
|
||||
}
|
||||
|
||||
/**
|
||||
* Задача пользователя.
|
||||
*/
|
||||
export interface TodoItem {
|
||||
/** Уникальный идентификатор задачи. */
|
||||
id: string;
|
||||
/** Текст задачи. */
|
||||
text: string;
|
||||
/** Статус выполнения. */
|
||||
completed: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: описывает очевидное.
|
||||
export interface TodoItem {
|
||||
/** id — это id */
|
||||
id: string;
|
||||
}
|
||||
```
|
||||
146
projects/nextjs-style-guide/canons/basics/naming.md
Normal file
146
projects/nextjs-style-guide/canons/basics/naming.md
Normal file
@@ -0,0 +1,146 @@
|
||||
---
|
||||
title: Именование
|
||||
description: Как называть переменные, файлы и прочие сущности в коде.
|
||||
---
|
||||
|
||||
# Именование
|
||||
|
||||
Как называть переменные, файлы и прочие сущности в коде.
|
||||
|
||||
## Базовые правила
|
||||
|
||||
| Что | Рекомендуется |
|
||||
| ---------------- | ---------------------- |
|
||||
| Папки | `kebab-case` |
|
||||
| Файлы | `kebab-case` |
|
||||
| Переменные | `camelCase` |
|
||||
| Константы | `SCREAMING_SNAKE_CASE` |
|
||||
| Классы | `PascalCase` |
|
||||
| React-компоненты | `PascalCase` |
|
||||
| Хуки | `useSomething` |
|
||||
| CSS классы | `camelCase` |
|
||||
| Ключи enum | `SCREAMING_SNAKE_CASE` |
|
||||
|
||||
|
||||
## Именование файлов
|
||||
|
||||
Суффикс обозначает роль или тип файла. Пишется в единственном числе.
|
||||
Формат: `name.<suffix>.ts`.
|
||||
|
||||
**Хуки**
|
||||
- `use-name.hook.ts` — файл хука, функция именуется `useName`
|
||||
|
||||
**Логика**
|
||||
- `.store.ts` — стор
|
||||
- `.service.ts` — сервис
|
||||
|
||||
**Корневые компоненты слоёв**
|
||||
- `.screen.tsx` — корневой компонент screen-модуля: `screens/profile/profile.screen.tsx`, компонент `ProfileScreen`
|
||||
- `.layout.tsx` — корневой компонент layout-модуля: `layouts/main/main.layout.tsx`, компонент `MainLayout`
|
||||
|
||||
Обычные и вложенные модули не получают суффикс слоя: `ui/button/button.tsx`, `screens/profile/parts/activity-feed/activity-feed.tsx`.
|
||||
|
||||
**Типы и контракты**
|
||||
- `.type.ts` — типы и интерфейсы
|
||||
- `.interface.ts` — интерфейсы
|
||||
- `.enum.ts` — enum
|
||||
- `.dto.ts` — внешние DTO
|
||||
- `.schema.ts` — схемы валидации
|
||||
- `.constant.ts` — константы
|
||||
- `.config.ts` — конфигурация
|
||||
|
||||
**Утилиты**
|
||||
- `.util.ts` — утилиты
|
||||
- `.helper.ts` — вспомогательные функции
|
||||
- `.lib.ts` — библиотечный код
|
||||
|
||||
**Тесты**
|
||||
- `.test.ts` — тесты
|
||||
- `.mock.ts` — моки
|
||||
|
||||
**Хорошо**
|
||||
```text
|
||||
business/
|
||||
└── auth-by-email/
|
||||
├── ui/
|
||||
│ └── login-form.tsx
|
||||
├── hooks/
|
||||
│ └── use-auth.hook.ts
|
||||
├── stores/
|
||||
│ └── auth.store.ts
|
||||
├── types/
|
||||
│ └── auth.type.ts
|
||||
├── auth-by-email.tsx
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```text
|
||||
business/
|
||||
└── authByEmail/
|
||||
├── LoginForm.tsx
|
||||
├── useAuth.ts
|
||||
├── authStore.ts
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
## Булевы значения
|
||||
|
||||
- Использовать префиксы `is`, `has`, `can`, `should`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const isReady = true;
|
||||
const hasAccess = false;
|
||||
const canSubmit = true;
|
||||
const shouldRedirect = false;
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: неясное булево значение без префикса.
|
||||
const ready = true;
|
||||
const access = false;
|
||||
const submit = true;
|
||||
```
|
||||
|
||||
## События и обработчики
|
||||
|
||||
- Обработчики начинать с `handle`.
|
||||
- События и колбэки начинать с `on`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const handleSubmit = () => { ... };
|
||||
const onSubmit = () => { ... };
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: неочевидное назначение имени.
|
||||
const submitClick = () => { ... };
|
||||
```
|
||||
|
||||
## Коллекции
|
||||
|
||||
- Для массивов использовать имена во множественном числе.
|
||||
- Для словарей/мап — использовать суффиксы `ById`, `Map`, `Dict`.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const users = [];
|
||||
const usersById = {} as Record<string, User>;
|
||||
const userIds = ['u1', 'u2'];
|
||||
const ordersMap = new Map<string, Order>();
|
||||
const featureFlagsDict = { beta: true, legacy: false } as Record<string, boolean>;
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: имя не отражает, что это коллекция.
|
||||
const user = [];
|
||||
// Плохо: словарь назван как массив.
|
||||
const usersMap = [];
|
||||
// Плохо: по имени непонятно, что это словарь.
|
||||
const users = {} as Record<string, User>;
|
||||
```
|
||||
42
projects/nextjs-style-guide/canons/basics/tech-stack.md
Normal file
42
projects/nextjs-style-guide/canons/basics/tech-stack.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Технологии и библиотеки
|
||||
description: Какие библиотеки и инструменты используются в проекте.
|
||||
---
|
||||
|
||||
# Технологии и библиотеки
|
||||
|
||||
Какие библиотеки и инструменты используются в проекте.
|
||||
|
||||
## Что используем
|
||||
|
||||
### Стек
|
||||
- `React` / `TypeScript` — основной стек для UI и приложения.
|
||||
- `Next.js` — для продуктовых сайтов.
|
||||
|
||||
### Архитектура
|
||||
- `SLM Design` — собственная модульная архитектура проекта. Подробнее в разделе [Архитектура](/docs/basics/architecture/).
|
||||
|
||||
### UI компоненты
|
||||
- `Mantine UI` — базовые UI-компоненты.
|
||||
|
||||
### Работа с данными (API)
|
||||
- `@gromlab/api-codegen` — генерация API‑клиентов и типов.
|
||||
- `SWR` — получение, кеширование, ревалидация, дедубликация.
|
||||
- `SWR (useSWRSubscription)` — сокеты, реалтайм подписки.
|
||||
|
||||
### Store
|
||||
- `Zustand` — глобальное состояние.
|
||||
|
||||
### Локализация
|
||||
- `i18next (i18n)` — локализация всех пользовательских текстов.
|
||||
|
||||
### Тестирование
|
||||
- `Vitest` — тестирование.
|
||||
|
||||
### Стили
|
||||
- `PostCSS Modules` — изоляция стилей.
|
||||
- `Mobile First` — подход к адаптивной верстке.
|
||||
- `clsx` — конкатенация CSS‑классов.
|
||||
|
||||
### Генерация
|
||||
- `@gromlab/create` — шаблонизатор для создания слоёв и других файлов из шаблонов.
|
||||
62
projects/nextjs-style-guide/canons/basics/typing.md
Normal file
62
projects/nextjs-style-guide/canons/basics/typing.md
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Типизация
|
||||
description: Как типизируется код в проекте.
|
||||
---
|
||||
|
||||
# Типизация
|
||||
|
||||
Как типизируется код в проекте.
|
||||
|
||||
## Общие правила
|
||||
|
||||
- Указывать типы для параметров компонентов и параметров функций.
|
||||
- Предпочитать `type` для описания сущностей и `interface` для расширяемых контрактов.
|
||||
- Избегать `any` и `unknown` без необходимости.
|
||||
- Не использовать `ts-ignore`, кроме крайних случаев с явным комментарием причины.
|
||||
|
||||
## React-компоненты
|
||||
|
||||
- Пропсы компонента типизировать через отдельный `Props`.
|
||||
- Возвращаемый тип компонента не указывать: TypeScript корректно выводит JSX-результат, а явный `ReactElement` сужает допустимые варианты возврата.
|
||||
|
||||
## Функции
|
||||
|
||||
- Для публичных функций указывать возвращаемый тип.
|
||||
- Не полагаться на неявный вывод для важных API.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
export const formatPrice = (value: number): string => {
|
||||
return `${value} ₽`;
|
||||
};
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: нет явного возвращаемого типа.
|
||||
export const formatPrice = (value: number) => {
|
||||
return `${value} ₽`;
|
||||
};
|
||||
```
|
||||
|
||||
## Работа с any/unknown
|
||||
|
||||
- `any` использовать только для временных заглушек.
|
||||
- `unknown` сужать через проверки перед использованием.
|
||||
|
||||
**Хорошо**
|
||||
```ts
|
||||
const parse = (value: unknown): string => {
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
```
|
||||
|
||||
**Плохо**
|
||||
```ts
|
||||
// Плохо: any отключает проверку типов.
|
||||
const parse = (value: any) => value;
|
||||
```
|
||||
Reference in New Issue
Block a user