forked from templates/nextjs-template
style: Обновлены правила код стайла
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Алиасы импортов
|
||||
description: Какие алиасы импортов есть в проекте и как ими пользоваться.
|
||||
keywords: [алиасы, aliases, paths, tsconfig, импорты, baseUrl, app, layouts, screens, widgets, business, infrastructure, ui, shared]
|
||||
keywords: [алиасы, aliases, paths, tsconfig, импорты, baseUrl, app, layouts, screens, widgets, business, infra, ui, shared]
|
||||
---
|
||||
|
||||
# Алиасы импортов
|
||||
@@ -21,7 +21,7 @@ keywords: [алиасы, aliases, paths, tsconfig, импорты, baseUrl, app,
|
||||
"screens/*": ["./src/screens/*"],
|
||||
"widgets/*": ["./src/widgets/*"],
|
||||
"business/*": ["./src/business/*"],
|
||||
"infrastructure/*": ["./src/infrastructure/*"],
|
||||
"infra/*": ["./src/infra/*"],
|
||||
"ui/*": ["./src/ui/*"],
|
||||
"shared/*": ["./src/shared/*"]
|
||||
}
|
||||
@@ -36,7 +36,7 @@ keywords: [алиасы, aliases, paths, tsconfig, импорты, baseUrl, app,
|
||||
- **Каждый импорт между модулями — через алиас слоя.** Относительные пути (`../../`) запрещены за пределами своего модуля.
|
||||
- **Внутри одного модуля** допустимы относительные импорты (`./model`, `./ui/button`) — это часть инкапсуляции модуля.
|
||||
- **Префикс `@/` не используется.** Имя слоя — само по себе адрес.
|
||||
- **Направление импортов** определяется архитектурой, не алиасами. Алиас разрешает импорт технически, но не отменяет правила слоёв (→ [Слои](../basics/architecture/reference/layers.md)).
|
||||
- **Направление импортов** определяется архитектурой, не алиасами. Алиас разрешает импорт технически, но не отменяет правила слоёв (→ [Слои](../basics/architecture/layers.md)).
|
||||
|
||||
**Хорошо**
|
||||
|
||||
|
||||
@@ -1,201 +1,165 @@
|
||||
---
|
||||
title: Компонент
|
||||
description: Как создавать React-компоненты внутри SLM-модулей.
|
||||
description: Как должен выглядеть сгенерированный React-компонент внутри SLM-модуля.
|
||||
---
|
||||
|
||||
# Компонент
|
||||
|
||||
Как создавать React-компоненты внутри SLM-модулей.
|
||||
Как должен выглядеть сгенерированный React-компонент внутри SLM-модуля.
|
||||
|
||||
## Назначение
|
||||
|
||||
Компонент — минимальная UI-единица проекта. Это один `.tsx` файл без собственной папки, сегментов и публичного API.
|
||||
Архитектурное определение компонента описано в разделе [Модули → Компонент](../basics/architecture/modules.md#компонент), а структура сегмента `ui/` — в разделе [Сегменты → ui/](../basics/architecture/segments.md#сегмент-ui).
|
||||
|
||||
Компонент может использовать стили, типы, хуки и другие ресурсы родительского модуля. Наличие связанных файлов в `styles/` или `types/` не превращает компонент в модуль.
|
||||
Эта страница не повторяет архитектурные ограничения. Она показывает, каким должен быть результат генерации компонента: структура папки, `.tsx`, типы, стили и локальный экспорт.
|
||||
|
||||
## Компонент или модуль
|
||||
::: danger Компоненты не создаются вручную
|
||||
Компоненты в проекте создаются только через кодогенератор: через [VS Code](./templates/templates-usage.md#через-vs-code) или [CLI](./templates/templates-usage.md#через-cli).
|
||||
|
||||
Классификация определяется границей владения:
|
||||
Ручное создание компонента запрещено. Это грубое нарушение правил работы в проекте для разработчика и AI-ассистента.
|
||||
|
||||
- `component` — один `.tsx` файл внутри модуля;
|
||||
- `module` — папка с `index.ts`, сегментами и собственной публичной границей.
|
||||
Если в проекте нет шаблона `.templates/component`, сначала создайте шаблон по разделу [Создание шаблонов](./templates/templates-create.md), и только потом генерируйте компонент на его основе.
|
||||
:::
|
||||
|
||||
## Создание
|
||||
|
||||
1. Проверьте, что в проекте есть шаблон `.templates/component`.
|
||||
2. Если шаблона нет — создайте его по разделу [Создание шаблонов](./templates/templates-create.md).
|
||||
3. Сгенерируйте компонент через [VS Code или CLI](./templates/templates-usage.md).
|
||||
|
||||
Структура и код ниже показывают ожидаемый результат генерации. Их нельзя использовать как инструкцию для ручного создания файлов.
|
||||
|
||||
## Структура
|
||||
|
||||
Компонент размещается в `ui/{component-name}/` родительского модуля.
|
||||
|
||||
Для каждого компонента обязательны `.tsx`, типы, стили и локальный `index.ts`.
|
||||
|
||||
```text
|
||||
user/
|
||||
├── ui/
|
||||
│ └── user-avatar.tsx # компонент
|
||||
├── styles/
|
||||
│ └── user-avatar.module.css # ресурс родительского модуля
|
||||
├── types/
|
||||
│ └── user-avatar.type.ts # ресурс родительского модуля
|
||||
└── user.tsx # корневой компонент модуля
|
||||
```
|
||||
|
||||
`user-avatar.tsx` остаётся компонентом, потому что у него нет собственной папки, `index.ts` и сегментов.
|
||||
|
||||
## Где размещать
|
||||
|
||||
Компонент размещается внутри модуля:
|
||||
|
||||
- В корне модуля, если это главная UI-сущность модуля.
|
||||
- В `ui/`, если это вспомогательный компонент модуля.
|
||||
|
||||
```text
|
||||
user/
|
||||
├── ui/
|
||||
│ └── user-avatar.tsx
|
||||
├── styles/
|
||||
│ ├── user.module.css
|
||||
│ └── user-avatar.module.css
|
||||
├── types/
|
||||
│ ├── user.type.ts
|
||||
│ └── user-avatar.type.ts
|
||||
├── user.tsx
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
`user.tsx` — корневой компонент модуля. `ui/user-avatar.tsx` — вспомогательный компонент этого же модуля.
|
||||
|
||||
## Что запрещено
|
||||
|
||||
- Заворачивать компонент в папку: `ui/header/header.tsx`.
|
||||
- Создавать для компонента отдельный `index.ts`.
|
||||
- Создавать собственные сегменты внутри папки компонента: `ui/header/styles/`, `ui/header/types/`, `ui/header/hooks/` и т.п.
|
||||
- Декларировать внутри `.tsx` сторы, сервисы, API-клиенты, мапперы или утилиты. Для этого есть сегменты родительского модуля.
|
||||
- Размещать бизнес-правила прямо в компоненте. Компонент может использовать готовые зависимости модуля, но не определяет их.
|
||||
- Размещать компонент в `parts/` напрямую. `parts/` содержит только модули.
|
||||
|
||||
**Плохо**
|
||||
|
||||
```text
|
||||
user/
|
||||
user-card/
|
||||
└── ui/
|
||||
└── user-avatar/
|
||||
└── user-status/
|
||||
├── styles/
|
||||
│ └── user-avatar.module.css
|
||||
├── user-avatar.tsx
|
||||
│ └── user-status.module.css
|
||||
├── types/
|
||||
│ └── user-status-props.type.ts
|
||||
├── user-status.tsx
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
**Хорошо**
|
||||
|
||||
```text
|
||||
user/
|
||||
├── ui/
|
||||
│ └── user-avatar.tsx
|
||||
├── styles/
|
||||
│ └── user-avatar.module.css
|
||||
└── types/
|
||||
└── user-avatar.type.ts
|
||||
```
|
||||
|
||||
## Стили и типы
|
||||
|
||||
Компонент использует ресурсы родительского модуля.
|
||||
|
||||
`styles/` и `types/` рядом с корневым компонентом — это сегменты модуля, а не собственные папки `.tsx` файла.
|
||||
|
||||
- CSS Module компонента лежит в `styles/` родительского модуля и называется по компоненту: `user-avatar.module.css`.
|
||||
- Если у компонента есть CSS Module, его корневой класс всегда называется `.root`.
|
||||
- Типы компонента лежат в `types/` родительского модуля и называются по компоненту: `user-avatar.type.ts`.
|
||||
- Локальный `type Props` внутри `.tsx` не используется. Типы пропсов всегда выносятся в `types/` родительского модуля.
|
||||
- Экспорт типа из `types/` не делает его публичным API. Публичным он становится только при реэкспорте из `index.ts` модуля.
|
||||
|
||||
Причина `.root`: в DevTools проще находить корневой DOM-узел компонента и отличать его от внутренних элементов.
|
||||
|
||||
## Реализация
|
||||
|
||||
- Компоненты объявляются через `const`.
|
||||
- `React.FC` не используется.
|
||||
- JSDoc-комментарий обязателен для компонента.
|
||||
- Пропсы деструктурируются в теле компонента.
|
||||
- `className` объединяется с `styles.root` через `cl()`.
|
||||
- Побочные эффекты и состояние выносятся в хуки модуля, если перестают быть тривиальными.
|
||||
- Компонент возвращает JSX и не содержит orchestration-код страницы или бизнес-домена.
|
||||
Пример ниже показывает файлы базового компонента.
|
||||
|
||||
`user/types/user-avatar.type.ts`
|
||||
### Типы
|
||||
|
||||
Файл типов делится на три части:
|
||||
|
||||
- `UserStatusParams` — собственные параметры компонента. Здесь лежат только данные, которые нужны именно этому компоненту.
|
||||
- `RootAttrs` — параметры корневой обёртки: `div`, `span`, `a`, `button` или другого HTML-элемента. Если компонент сам управляет `children`, они исключаются через `Omit`.
|
||||
- `UserStatusProps` — итоговые пропсы компонента. Тип объединяет собственные параметры и параметры корневой обёртки.
|
||||
|
||||
Собственные параметры и их поля документируются по правилам раздела [Документирование → Типы, интерфейсы, enum](../basics/documentation.md#типы-интерфейсы-enum).
|
||||
|
||||
`user-card/ui/user-status/types/user-status-props.type.ts`
|
||||
|
||||
```ts
|
||||
import type { ImageProps } from 'next/image'
|
||||
import type { ComponentPropsWithoutRef } from 'react'
|
||||
|
||||
/**
|
||||
* Параметры UserAvatar.
|
||||
* Параметры UserStatus.
|
||||
*/
|
||||
export type UserAvatarParams = {}
|
||||
export type UserStatusParams = {
|
||||
/** Текст статуса пользователя. */
|
||||
label: string
|
||||
/** Доступен ли пользователь сейчас. */
|
||||
isOnline: boolean
|
||||
}
|
||||
|
||||
/** Пропсы базового изображения. */
|
||||
type RootAttrs = ImageProps
|
||||
/** Атрибуты корневого элемента без children. */
|
||||
type RootAttrs = Omit<ComponentPropsWithoutRef<'span'>, 'children'>
|
||||
|
||||
export type UserAvatarProps = RootAttrs & UserAvatarParams
|
||||
export type UserStatusProps = RootAttrs & UserStatusParams
|
||||
```
|
||||
|
||||
`user/ui/user-avatar.tsx`
|
||||
### TSX
|
||||
|
||||
В `.tsx` лежит только сам компонент:
|
||||
|
||||
- Компонент объявляется через `const` и именованный экспорт.
|
||||
- `React.FC` не используется.
|
||||
- Параметры компонента типизируются через `Props`.
|
||||
- Возвращаемый тип не указывается: TypeScript корректно выводит JSX-результат, а явный `ReactElement` сужает допустимые варианты возврата.
|
||||
- JSDoc-комментарий обязателен и пишется по правилам раздела [Документирование → Компоненты](../basics/documentation.md#компоненты).
|
||||
- Пропсы деструктурируются в теле компонента, а не в сигнатуре.
|
||||
- Из пропсов обязательно выделяются `className` и `...rootAttrs`.
|
||||
- Функция конкатенации CSS-классов импортируется и именуется `cl`.
|
||||
- Корневой CSS-класс всегда называется `.root`.
|
||||
|
||||
Комментарий описывает назначение и сценарии применения компонента, а не DOM-разметку или внутреннюю реализацию.
|
||||
|
||||
`className` — внешний CSS-класс, который родитель может передать компоненту. `rootAttrs` — остальные атрибуты корневой обёртки: `id`, `aria-*`, `data-*`, обработчики событий и другие HTML-атрибуты. Они прокидываются на корневой DOM-элемент компонента.
|
||||
|
||||
`.root` нужен, чтобы в DevTools быстро находить корневой DOM-узел компонента и одинаково подключать внешний `className` к реальному корню.
|
||||
|
||||
`user-card/ui/user-status/user-status.tsx`
|
||||
|
||||
```tsx
|
||||
import cl from 'clsx'
|
||||
import Image from 'next/image'
|
||||
import type { UserAvatarProps } from '../types/user-avatar.type'
|
||||
import styles from '../styles/user-avatar.module.css'
|
||||
import type { UserStatusProps } from './types/user-status-props.type'
|
||||
import styles from './styles/user-status.module.css'
|
||||
|
||||
/**
|
||||
* Аватар пользователя.
|
||||
* Статус пользователя в карточке профиля.
|
||||
*
|
||||
* Используется для:
|
||||
* - отображения пользователя в карточке
|
||||
* - отображения пользователя в шапке профиля
|
||||
* - отображения текущей доступности пользователя
|
||||
* - визуального выделения онлайн- и офлайн-состояний
|
||||
*/
|
||||
export const UserAvatar = (props: UserAvatarProps) => {
|
||||
const { className, ...imageProps } = props
|
||||
export const UserStatus = (props: UserStatusProps) => {
|
||||
const { label, isOnline, className, ...rootAttrs } = props
|
||||
|
||||
return <Image {...imageProps} className={cl(styles.root, className)} />
|
||||
return (
|
||||
<span
|
||||
{...rootAttrs}
|
||||
className={cl(styles.root, isOnline && styles.online, className)}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
`user/styles/user-avatar.module.css`
|
||||
### Стили
|
||||
|
||||
`user-card/ui/user-status/styles/user-status.module.css`
|
||||
|
||||
```css
|
||||
.root {
|
||||
display: block;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.root::before {
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.online {
|
||||
color: var(--color-success);
|
||||
}
|
||||
```
|
||||
|
||||
## Когда нужен модуль
|
||||
### Локальный экспорт
|
||||
|
||||
Решение о выделении модуля остаётся за разработчиком. Поднимать компонент в модуль стоит, если он становится самостоятельной областью:
|
||||
`user-card/ui/user-status/index.ts`
|
||||
|
||||
- получает свои вложенные компоненты;
|
||||
- получает свои хуки, стор или сервисы;
|
||||
- получает внутренние мапперы или утилиты;
|
||||
- требует собственного публичного API;
|
||||
- начинает переиспользоваться вне родительского модуля;
|
||||
- становится отдельной зоной параллельной разработки.
|
||||
|
||||
Пример: страница — это screen-модуль, а самостоятельные секции страницы — вложенные модули в `parts/`.
|
||||
|
||||
```text
|
||||
screens/home/
|
||||
├── parts/
|
||||
│ ├── hero-section/
|
||||
│ │ ├── styles/
|
||||
│ │ │ └── hero-section.module.css
|
||||
│ │ ├── types/
|
||||
│ │ │ └── hero-section.type.ts
|
||||
│ │ ├── hero-section.tsx
|
||||
│ │ └── index.ts
|
||||
│ └── features-section/
|
||||
│ ├── styles/
|
||||
│ │ └── features-section.module.css
|
||||
│ ├── types/
|
||||
│ │ └── features-section.type.ts
|
||||
│ ├── features-section.tsx
|
||||
│ └── index.ts
|
||||
├── styles/
|
||||
│ └── home.module.css
|
||||
├── types/
|
||||
│ └── home.type.ts
|
||||
├── home.screen.tsx
|
||||
└── index.ts
|
||||
```ts
|
||||
export { UserStatus } from './user-status'
|
||||
export type { UserStatusProps } from './types/user-status-props.type'
|
||||
```
|
||||
|
||||
`hero-section` и `features-section` — модули, потому что это самостоятельные части страницы со своей структурой и публичной точкой входа.
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
---
|
||||
title: Локализация
|
||||
description: Как организовать локализацию как infrastructure-модуль.
|
||||
description: Как организовать локализацию как infra-модуль.
|
||||
---
|
||||
|
||||
# Локализация
|
||||
|
||||
Как организовать локализацию как infrastructure-модуль.
|
||||
Как организовать локализацию как infra-модуль.
|
||||
|
||||
## Назначение
|
||||
|
||||
Локализация — инфраструктурная подсистема приложения. Она отвечает за текущую локаль, словари, форматирование переводов и API для компонентов.
|
||||
|
||||
Код локализации живёт в `src/infrastructure/i18n/`. Компоненты и модули не читают словари напрямую — они используют публичный API infrastructure-модуля.
|
||||
Код локализации живёт в `src/infra/i18n/`. Компоненты и модули не читают словари напрямую — они используют публичный API infra-модуля.
|
||||
|
||||
## Структура
|
||||
|
||||
```text
|
||||
src/infrastructure/i18n/
|
||||
src/infra/i18n/
|
||||
├── config/
|
||||
│ └── i18n.config.ts
|
||||
├── dictionaries/
|
||||
@@ -31,16 +31,16 @@ src/infrastructure/i18n/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Набор сегментов может отличаться, но публичная точка входа остаётся одна — `infrastructure/i18n`.
|
||||
Набор сегментов может отличаться, но публичная точка входа остаётся одна — `infra/i18n`.
|
||||
|
||||
## Подключение
|
||||
|
||||
`app/` только подключает готовый провайдер локализации. Реализация провайдера, словари и конфиг остаются в `infrastructure/i18n/`.
|
||||
`app/` только подключает готовый провайдер локализации. Реализация провайдера, словари и конфиг остаются в `infra/i18n/`.
|
||||
|
||||
```tsx
|
||||
// src/app/layout.tsx
|
||||
import type { ReactNode } from 'react'
|
||||
import { I18nProvider } from 'infrastructure/i18n'
|
||||
import { I18nProvider } from 'infra/i18n'
|
||||
|
||||
type RootLayoutProps = {
|
||||
children: ReactNode
|
||||
@@ -62,7 +62,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
|
||||
Компоненты получают переводы через готовый API модуля локализации:
|
||||
|
||||
```tsx
|
||||
import { useTranslation } from 'infrastructure/i18n'
|
||||
import { useTranslation } from 'infra/i18n'
|
||||
|
||||
export const ProfileTitle = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -73,9 +73,9 @@ export const ProfileTitle = () => {
|
||||
|
||||
## Правила
|
||||
|
||||
- Локализация живёт в `infrastructure/i18n/`.
|
||||
- Локализация живёт в `infra/i18n/`.
|
||||
- `app/` только подключает готовый provider и передаёт locale.
|
||||
- Словари не импортируются напрямую в компоненты, screens или business-модули.
|
||||
- Ключи переводов не собираются динамически из строк, если это ломает типизацию и поиск.
|
||||
- Тексты интерфейса не хардкодятся в переиспользуемых компонентах, если они должны переводиться.
|
||||
- Форматирование дат, чисел и валют должно проходить через API локализации или отдельные утилиты infrastructure-модуля.
|
||||
- Форматирование дат, чисел и валют должно проходить через API локализации или отдельные утилиты infra-модуля.
|
||||
|
||||
@@ -1,162 +1,156 @@
|
||||
---
|
||||
title: Модуль
|
||||
description: Как создавать и организовывать SLM-модули в проекте.
|
||||
description: Как должен выглядеть сгенерированный SLM-модуль в проекте.
|
||||
---
|
||||
|
||||
# Модуль
|
||||
|
||||
Как создавать и организовывать SLM-модули в проекте.
|
||||
Как должен выглядеть сгенерированный SLM-модуль в проекте.
|
||||
|
||||
## Назначение
|
||||
|
||||
Модуль — основной строительный блок SLM-архитектуры. Это папка с публичным API (`index.ts`) и опциональными сегментами: компонентами, стилями, типами, хуками, сторами, сервисами и вложенными модулями.
|
||||
Архитектурное определение модуля описано в разделе [Архитектура → Модули](../basics/architecture/modules.md). Список сегментов описан в разделе [Архитектура → Сегменты](../basics/architecture/segments.md).
|
||||
|
||||
Если UI-сущность остаётся одним `.tsx` файлом и использует ресурсы родительского модуля — это [компонент](./component.md), а не модуль. Связанные файлы в `styles/` и `types/` родителя не создают новую модульную границу.
|
||||
Эта страница показывает прикладное оформление трёх типов модулей: UI, бизнес и инфраструктурный.
|
||||
|
||||
Архитектурное определение: [Модули SLM](../basics/architecture/reference/modules.md). Список сегментов: [Сегменты SLM](../basics/architecture/reference/segments.md).
|
||||
## Создание
|
||||
|
||||
## Когда нужен модуль
|
||||
1. Проверьте, что в проекте есть нужный шаблон в `.templates/`.
|
||||
2. Если шаблона нет — создайте его по разделу [Создание шаблонов](./templates/templates-create.md).
|
||||
3. Сгенерируйте модуль через [VS Code или CLI](./templates/templates-usage.md).
|
||||
|
||||
Создавайте модуль, если сущности нужны:
|
||||
## Типы модулей
|
||||
|
||||
- публичный API;
|
||||
- хуки, сторы, сервисы, мапперы или утилиты;
|
||||
- вложенные части;
|
||||
- переиспользование вне родительского модуля;
|
||||
- самостоятельная ответственность на слое.
|
||||
Архитектура определяет три типа модулей ([Типы модулей](../basics/architecture/modules.md#типы-модулей)):
|
||||
|
||||
Если понадобилась папка вокруг компонента — это сигнал, что нужен модуль.
|
||||
| Тип | Обязательный файл | Описание |
|
||||
|---|---|---|
|
||||
| UI-модуль | `{name}.tsx` | Модуль, выросший из компонента |
|
||||
| Бизнес-модуль | `{name}.factory.ts` | Модуль вокруг публичного runtime API |
|
||||
| Инфраструктурный модуль | нет | Модуль вокруг технического сервиса |
|
||||
|
||||
## Где размещать
|
||||
## UI-модуль
|
||||
|
||||
Модуль размещается на самом низком уровне использования.
|
||||
UI-модуль — это компонент, который перерос ограничения компонента: получил собственные хуки, вложенные модули в `parts/`, сценарную логику или публичный API. Внутренняя структура та же, что у компонента: корневой `.tsx`, типы, стили, `ui/`. Но без ограничений компонента.
|
||||
|
||||
- Нужен только одному модулю — размещается в `parts/` родителя.
|
||||
- Нужен одной странице — размещается в `screens/{name}/parts/`.
|
||||
- Нужен одному layout — размещается в `layouts/{name}/parts/`.
|
||||
- Переиспользуется между страницами или layout — поднимается в `widgets/`.
|
||||
- Представляет бизнес-домен — размещается в `business/`.
|
||||
- Является UI-китом — размещается в `ui/`.
|
||||
Подробное оформление компонентов внутри `ui/` описано в разделе [Компонент](./component.md).
|
||||
|
||||
`app/` не содержит модулей. Это слой файлового роутинга и инициализации.
|
||||
## Бизнес-модуль
|
||||
|
||||
## Структура
|
||||
Бизнес-модуль строится вокруг публичного runtime API. Ключевой файл — фабрика (`{name}.factory.ts`), которая возвращает всё, что нужно внешнему коду в runtime.
|
||||
|
||||
Минимальный UI-модуль:
|
||||
Архитектурное описание фабрики: [Архитектура → Фабрика](../basics/architecture/modules.md#фабрика).
|
||||
|
||||
### Структура
|
||||
|
||||
```text
|
||||
user/
|
||||
├── user.tsx
|
||||
└── index.ts
|
||||
business/customer/
|
||||
├── customer.factory.ts
|
||||
├── index.ts
|
||||
└── types/
|
||||
├── customer.type.ts
|
||||
├── customer-api.type.ts
|
||||
├── customer-deps.type.ts
|
||||
└── customer-factory.type.ts
|
||||
```
|
||||
|
||||
Модуль расширяется сегментами только при реальной потребности:
|
||||
### Типы
|
||||
|
||||
```text
|
||||
user/
|
||||
├── ui/
|
||||
│ └── user-avatar.tsx
|
||||
├── parts/
|
||||
│ └── user-posts/
|
||||
│ ├── user-posts.tsx
|
||||
│ └── index.ts
|
||||
├── styles/
|
||||
│ ├── user.module.css
|
||||
│ └── user-avatar.module.css
|
||||
├── types/
|
||||
│ ├── user.type.ts
|
||||
│ └── user-avatar.type.ts
|
||||
├── user.tsx
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Корневой компонент опционален. Business- и infrastructure-модули могут состоять только из хуков, сервисов, типов и публичного API.
|
||||
|
||||
## `ui/` и `parts/`
|
||||
|
||||
`ui/` содержит только компоненты: отдельные `.tsx` файлы без собственных сегментов.
|
||||
|
||||
`parts/` содержит только модули: каждая запись внутри `parts/` — папка с собственным `index.ts`. Отдельные `.tsx`, стили, хуки или произвольные файлы в `parts/` не кладутся.
|
||||
|
||||
```text
|
||||
user/
|
||||
├── ui/
|
||||
│ └── user-avatar.tsx # компонент
|
||||
└── parts/
|
||||
└── user-posts/ # модуль
|
||||
├── styles/
|
||||
│ └── user-posts.module.css
|
||||
├── user-posts.tsx
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Если компоненту в `ui/` понадобились стили или типы, они добавляются в `styles/` и `types/` родительского модуля. Если компоненту нужны собственные хуки, вложенные части или публичная граница — он переносится в `parts/` как модуль.
|
||||
|
||||
## Публичный API
|
||||
|
||||
`index.ts` — единственная точка входа в модуль. Внешние импорты внутренних файлов запрещены.
|
||||
`business/customer/types/customer-api.type.ts`
|
||||
|
||||
```ts
|
||||
// user/index.ts
|
||||
export { User } from './user'
|
||||
export type { UserProps } from './types/user.type'
|
||||
export type CustomerApi = {
|
||||
useCustomer: () => Customer
|
||||
CustomerCard: (props: CustomerCardProps) => ReactNode
|
||||
}
|
||||
```
|
||||
|
||||
`business/order/types/order-deps.type.ts`
|
||||
|
||||
```ts
|
||||
// Плохо: импорт в обход публичного API.
|
||||
import { UserPosts } from 'screens/user/parts/user-posts/user-posts'
|
||||
|
||||
// Хорошо: импорт через публичный API родительского модуля.
|
||||
import { User } from 'screens/user'
|
||||
export type OrderDeps = {
|
||||
customer: Pick<CustomerApi, 'useCustomer'>
|
||||
}
|
||||
```
|
||||
|
||||
Вложенный модуль имеет свой `index.ts`, но наружу родителя экспортируется только при необходимости.
|
||||
`business/order/types/order-factory.type.ts`
|
||||
|
||||
## Именование
|
||||
|
||||
Базовые правила описаны в разделе [Именование](../basics/naming.md).
|
||||
|
||||
- Папка модуля — `kebab-case`: `user-posts/`.
|
||||
- Файл корневого компонента повторяет имя папки: `user-posts/user-posts.tsx`.
|
||||
- Корневые модули слоёв наследуют роль слоя в имени файла: `screens/profile/profile.screen.tsx`, `layouts/main/main.layout.tsx`.
|
||||
- Корневой компонент именуется в `PascalCase`: `UserPosts`.
|
||||
- Если имя без контекста слишком общее, добавляется префикс родителя или роль слоя: `ProfileUserPosts`, `ProfileScreen`, `MainLayout`.
|
||||
|
||||
## Примеры
|
||||
|
||||
### Screen-модуль
|
||||
|
||||
```text
|
||||
screens/profile/
|
||||
├── ui/
|
||||
│ └── profile-heading.tsx
|
||||
├── parts/
|
||||
│ └── activity-feed/
|
||||
│ ├── styles/
|
||||
│ │ └── activity-feed.module.css
|
||||
│ ├── activity-feed.tsx
|
||||
│ └── index.ts
|
||||
├── styles/
|
||||
│ ├── profile.module.css
|
||||
│ └── profile-heading.module.css
|
||||
├── types/
|
||||
│ ├── profile.type.ts
|
||||
│ └── profile-heading.type.ts
|
||||
├── profile.screen.tsx
|
||||
└── index.ts
|
||||
```ts
|
||||
export type OrderFactory = (deps: OrderDeps) => OrderApi
|
||||
```
|
||||
|
||||
### Business-модуль без корневого компонента
|
||||
### Фабрика без зависимостей
|
||||
|
||||
`business/customer/customer.factory.ts`
|
||||
|
||||
```ts
|
||||
import type { CustomerFactory } from './types/customer-factory.type'
|
||||
|
||||
export const customerFactory: CustomerFactory = () => {
|
||||
return {
|
||||
useCustomer,
|
||||
CustomerCard,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Фабрика с зависимостями
|
||||
|
||||
`business/order/order.factory.ts`
|
||||
|
||||
```ts
|
||||
import type { OrderFactory } from './types/order-factory.type'
|
||||
|
||||
export const orderFactory: OrderFactory = (deps) => {
|
||||
return {
|
||||
useOrder,
|
||||
OrderCard,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Композиция на уровне screen
|
||||
|
||||
```tsx
|
||||
// screens/home/home.screen.tsx
|
||||
import { customerFactory } from '@/business/customer'
|
||||
import { orderFactory } from '@/business/order'
|
||||
|
||||
const customer = customerFactory()
|
||||
const order = orderFactory({ customer })
|
||||
|
||||
const { useOrder, OrderCard } = order
|
||||
|
||||
export const HomeScreen = () => {
|
||||
const currentOrder = useOrder()
|
||||
|
||||
return <OrderCard order={currentOrder} />
|
||||
}
|
||||
```
|
||||
|
||||
## Инфраструктурный модуль
|
||||
|
||||
Инфраструктурный модуль строится вокруг технического сервиса или интеграции. Его структура определяется природой сервиса — фиксированного корневого файла нет.
|
||||
|
||||
Архитектурное описание: [Архитектура → Типы модулей → Инфраструктурный модуль](../basics/architecture/modules.md#инфраструктурный-модуль).
|
||||
|
||||
Пример модуля темы:
|
||||
|
||||
```text
|
||||
business/auth/
|
||||
theme/
|
||||
├── index.ts
|
||||
├── config/
|
||||
├── hooks/
|
||||
│ └── use-auth.hook.ts
|
||||
├── services/
|
||||
│ └── auth.service.ts
|
||||
├── stores/
|
||||
│ └── auth.store.ts
|
||||
├── styles/
|
||||
└── ui/
|
||||
```
|
||||
|
||||
Пример модуля API-клиента:
|
||||
|
||||
```text
|
||||
backend-api/
|
||||
├── backend-api.client.ts
|
||||
├── config/
|
||||
├── types/
|
||||
│ └── auth.type.ts
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
@@ -13,7 +13,7 @@ description: Как работать со страницами и другими
|
||||
|
||||
Файлы роутинга не реализуют интерфейс. Они описывают маршрут: читают параметры, получают данные первого рендера, подготавливают кеш или состояние и передают результат в screen.
|
||||
|
||||
Границы слоя описаны в [Архитектура → Слои → App](../basics/architecture/reference/layers.md#слой-app).
|
||||
Границы слоя описаны в [Архитектура → Слои → App](../basics/architecture/layers.md#слой-app).
|
||||
|
||||
## Граница ответственности
|
||||
|
||||
@@ -25,7 +25,7 @@ description: Как работать со страницами и другими
|
||||
| Прогрев SWR-кеша, начальное состояние, подключение провайдеров | `src/app/**`, только через готовые обёртки из нижних слоёв |
|
||||
| UI страницы | `screens/` |
|
||||
| Каркас страницы: header, footer, sidebar | `layouts/` |
|
||||
| Провайдеры, сторы, хуки, API-клиенты, сервисы | нижние слои (`screens/`, `business/`, `infrastructure/`, `shared/`) |
|
||||
| Провайдеры, сторы, хуки, API-клиенты, сервисы | нижние слои (`screens/`, `business/`, `infra/`, `shared/`) |
|
||||
| CSS Modules и стили компонентов | рядом с компонентами, не в `src/app/**` |
|
||||
|
||||
## Что можно делать в `page.tsx`
|
||||
@@ -79,7 +79,7 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
|
||||
|
||||
```tsx
|
||||
import { notFound } from 'next/navigation'
|
||||
import { userApi } from 'infrastructure/backend-api'
|
||||
import { userApi } from 'infra/backend-api'
|
||||
import { UserScreen } from 'screens/user'
|
||||
|
||||
type UserPageProps = {
|
||||
@@ -109,7 +109,7 @@ import {
|
||||
backendApi,
|
||||
getCurrentUserKey,
|
||||
getPostListKey,
|
||||
} from 'infrastructure/backend-api'
|
||||
} from 'infra/backend-api'
|
||||
|
||||
type FeedLayoutProps = {
|
||||
children: ReactNode
|
||||
|
||||
@@ -46,7 +46,7 @@ src/
|
||||
├── screens/ # Контент конкретной страницы
|
||||
├── widgets/ # Составные блоки интерфейса, не привязанные к домену
|
||||
├── business/ # Бизнес-домены (auth, catalog, orders)
|
||||
├── infrastructure/ # Техсервисы (theme, i18n, API-адаптеры)
|
||||
├── infra/ # Техсервисы (theme, i18n, API-адаптеры)
|
||||
├── ui/ # UI-кит без бизнес-логики
|
||||
└── shared/ # Общие ресурсы (утилиты, типы, стили)
|
||||
```
|
||||
@@ -58,7 +58,7 @@ src/
|
||||
Точка входа приложения и файловый роутинг Next.js (`layout.tsx`, `page.tsx`, route-сегменты).
|
||||
`app/` подключает готовую инициализацию из нижних слоёв, но не реализует провайдеры, стили, UI-компоненты, хуки, сторы или сервисы.
|
||||
|
||||
Подробнее о границах слоя: [Архитектура → Слои → App](../basics/architecture/reference/layers.md#слой-app).
|
||||
Подробнее о границах слоя: [Архитектура → Слои → App](../basics/architecture/layers.md#слой-app).
|
||||
|
||||
```text
|
||||
src/app/
|
||||
|
||||
@@ -22,7 +22,7 @@ keywords: [шаблоны, templates, .templates, syntax, переменные,
|
||||
│ ├── styles/
|
||||
│ │ └── {{name.kebabCase}}.module.css
|
||||
│ ├── types/
|
||||
│ │ └── {{name.kebabCase}}.type.ts
|
||||
│ │ └── {{name.kebabCase}}-props.type.ts
|
||||
│ ├── {{name.kebabCase}}.tsx
|
||||
│ └── index.ts
|
||||
└── store/ # шаблон Zustand стора
|
||||
@@ -32,6 +32,12 @@ keywords: [шаблоны, templates, .templates, syntax, переменные,
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
## Обязательный шаблон компонента
|
||||
|
||||
Перед созданием компонентов в проекте должен существовать шаблон `.templates/component`.
|
||||
|
||||
Если шаблона нет, компонент не создаётся вручную. Сначала создаётся шаблон компонента, затем компонент генерируется через [VS Code или CLI](./templates-usage.md).
|
||||
|
||||
## Синтаксис шаблонов
|
||||
|
||||
### Переменные
|
||||
|
||||
@@ -8,6 +8,12 @@ keywords: [шаблоны, templates, generate, VS Code, CLI, gromlab/create, np
|
||||
|
||||
Генерация файлов из шаблонов через VS Code плагин и CLI.
|
||||
|
||||
::: danger Ручное создание запрещено
|
||||
Файлы, для которых есть шаблоны в `.templates/`, создаются только генератором. Ручное создание компонента, модуля, стора или другого шаблонного блока запрещено.
|
||||
|
||||
Если нужного шаблона нет, сначала создайте шаблон в `.templates/`, затем сгенерируйте код на его основе.
|
||||
:::
|
||||
|
||||
## Через VS Code
|
||||
|
||||
Template File Generator | gromlab ([Marketplace](https://marketplace.visualstudio.com/items?itemName=gromlab.vscode-templateFileGenerator), [Open VSX](https://open-vsx.org/extension/gromlab/vscode-templateFileGenerator)) — расширение для генерации файлов и папок из шаблонов через интерфейс редактора.
|
||||
|
||||
Reference in New Issue
Block a user