Compare commits

...

4 Commits

Author SHA1 Message Date
4ae6ac893d docs: убрать устаревшее правило о кэше VitePress из AGENTS.md
All checks were successful
CI/CD Pipeline / docker (push) Successful in 39s
CI/CD Pipeline / deploy (push) Successful in 7s
- удалена строка «Не коммитить .vitepress/cache/» (кэш уже исключён из отслеживания git)
2026-04-01 10:45:50 +03:00
965235e390 docs: убрать Workflow и Чеклист из структуры прикладного раздела
- удалены секции «Workflow» и «Чеклист» из шаблона
- обновлено описание раздела: о чём раздел и какие аспекты он охватывает
- обновлён порядок секций: убраны «процесс» и «проверка»
- удалён принцип «Workflow vs правила», нумерация сдвинута
2026-04-01 10:45:22 +03:00
e608017dce chore: удалить кэш VitePress из отслеживания git
- удалены файлы .vitepress/cache из индекса (правило в .gitignore уже существовало, но файлы были закоммичены ранее)
2026-04-01 10:38:16 +03:00
29bcf23dde docs: переработать компоненты, структуру проекта и документирование
- переработан раздел «Компоненты»: добавлены правила организации, типизации, реализации
- переработан раздел «Структура проекта»: упрощён, добавлены корень репозитория и конфиг-файлы
- переработан раздел «Документирование»: добавлены шаблоны для функций, компонентов, типов
- обновлён CONTRIBUTING.md: добавлены секции Workflow и Чеклист, правила разделены на Реализацию и Организацию
- перенесены типы компонентов из «Типизации» в «Компоненты»
- обновлён шаблон генерации: деструктуризация пропсов в теле, children вместо текста
- добавлен SCREAMING_SNAKE_CASE для ключей enum в «Именование»
- перемещён «Настройка VS Code» в конец сайдбара
- обновлён порядок файлов в concat-md.js и перегенерирован RULES.md
2026-04-01 10:35:07 +03:00
21 changed files with 599 additions and 27880 deletions

View File

@@ -1,31 +0,0 @@
{
"hash": "e66d10e4",
"configHash": "bca2cdcc",
"lockfileHash": "5778a81f",
"browserHash": "5fea5472",
"optimized": {
"vue": {
"src": "../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "cabb4131",
"needsInterop": false
},
"vitepress > @vue/devtools-api": {
"src": "../../../node_modules/@vue/devtools-api/dist/index.js",
"file": "vitepress___@vue_devtools-api.js",
"fileHash": "66e76a7b",
"needsInterop": false
},
"vitepress > @vueuse/core": {
"src": "../../../node_modules/@vueuse/core/index.mjs",
"file": "vitepress___@vueuse_core.js",
"fileHash": "6d04ab1e",
"needsInterop": false
}
},
"chunks": {
"chunk-EAEFJUV4": {
"file": "chunk-EAEFJUV4.js"
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +0,0 @@
{
"type": "module"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,343 +0,0 @@
import {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBaseVNode,
createBlock,
createCommentVNode,
createElementBlock,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
} from "./chunk-EAEFJUV4.js";
export {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBlock,
createCommentVNode,
createElementBlock,
createBaseVNode as createElementVNode,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
};
//# sourceMappingURL=vue.js.map

View File

@@ -1,7 +0,0 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

View File

@@ -19,10 +19,8 @@ const ruSidebar = [
{ {
text: 'Прикладные разделы', text: 'Прикладные разделы',
items: [ items: [
{ text: 'Настройка VS Code', link: '/applied/vscode' },
{ text: 'Структура проекта', link: '/applied/project-structure' }, { text: 'Структура проекта', link: '/applied/project-structure' },
{ text: 'UI и компоненты', link: '/applied/components' }, { text: 'Компоненты', link: '/applied/components' },
{ text: 'Компоненты (new)', link: '/applied/components-new' },
{ text: 'Страницы (App Router)', link: '/applied/page-level' }, { text: 'Страницы (App Router)', link: '/applied/page-level' },
{ text: 'Шаблоны и генерация кода', link: '/applied/templates-generation' }, { text: 'Шаблоны и генерация кода', link: '/applied/templates-generation' },
{ text: 'Стили', link: '/applied/styles' }, { text: 'Стили', link: '/applied/styles' },
@@ -34,6 +32,7 @@ const ruSidebar = [
{ text: 'Хуки', link: '/applied/hooks' }, { text: 'Хуки', link: '/applied/hooks' },
{ text: 'Шрифты', link: '/applied/fonts' }, { text: 'Шрифты', link: '/applied/fonts' },
{ text: 'Локализация', link: '/applied/localization' }, { text: 'Локализация', link: '/applied/localization' },
{ text: 'Настройка VS Code', link: '/applied/vscode' },
], ],
}, },
]; ];

View File

@@ -6,4 +6,3 @@
- После изменений в `.md`-файлах — запустить `npm run docs` для обновления RULES.md. - После изменений в `.md`-файлах — запустить `npm run docs` для обновления RULES.md.
- При добавлении нового раздела — обновить сайдбар (`.vitepress/config.ts`) - При добавлении нового раздела — обновить сайдбар (`.vitepress/config.ts`)
и порядок файлов (`concat-md.js`). и порядок файлов (`concat-md.js`).
- Не коммитить `.vitepress/cache/`.

View File

@@ -25,7 +25,6 @@
docs/ docs/
├── ru/ # Русская версия (основная) ├── ru/ # Русская версия (основная)
│ ├── index.md # Главная страница │ ├── index.md # Главная страница
│ ├── workflow.md # Workflow (единый файл)
│ ├── basics/ # Базовые правила │ ├── basics/ # Базовые правила
│ │ ├── tech-stack.md │ │ ├── tech-stack.md
│ │ ├── architecture.md │ │ ├── architecture.md
@@ -63,16 +62,7 @@ concat-md.js # Скрипт генерации RULES.md
2. Добавить пункт в сайдбар — `.vitepress/config.ts` (оба языка, если есть перевод). 2. Добавить пункт в сайдбар — `.vitepress/config.ts` (оба языка, если есть перевод).
3. Добавить файл в массив `fileOrder``concat-md.js` (для генерации RULES.md). 3. Добавить файл в массив `fileOrder``concat-md.js` (для генерации RULES.md).
## Три типа документации ## Два типа документации
### Workflow
**Отвечает на вопрос:** «Что делать и в каком порядке?»
Пошаговые инструкции. Описывает процесс, а не правила.
Не содержит развёрнутых примеров кода — только минимальные команды и шаги.
Живёт в одном файле `workflow.md`.
### Базовые правила ### Базовые правила
@@ -101,12 +91,12 @@ concat-md.js # Скрипт генерации RULES.md
```markdown ```markdown
# {Название} # {Название}
Одно-два предложения: что это за область, зачем она нужна. Краткое описание: о чём раздел и какие аспекты работы с областью он охватывает.
## Что нужно знать ## Что нужно знать
Контекст перед правилами: технологии, термины, принципы работы. Неочевидная информация, которую читатель должен знать перед чтением раздела.
Не правила — фундамент, без которого правила будут непонятны. Если для раздела нет такой вводной — секция не создаётся.
## Структура ## Структура
@@ -115,9 +105,25 @@ concat-md.js # Скрипт генерации RULES.md
## Правила ## Правила
Конкретные требования к реализации в этой области. Конкретные требования, специфичные для области. Делятся на две подсекции:
Формат — маркированный или нумерованный список.
### Реализация
Как написан код внутри файла: синтаксис, паттерны, API.
Отвечает на вопрос: «Как писать код?»
Примеры: объявление через `const`, деструктуризация пропсов, формат вызова `cl()`, способ подключения стилей, структура хука.
### Организация
Как компонент/модуль встроен в проект: файловые границы, зоны ответственности, экспорт.
Отвечает на вопрос: «Где что лежит и за что отвечает?»
Примеры: один компонент — один файл, вложенные компоненты в `ui/`, логика выносится в `model/`.
Формат обеих подсекций — маркированный список.
Для неочевидных случаев — блоки «Хорошо / Плохо». Для неочевидных случаев — блоки «Хорошо / Плохо».
Если в области нет правил одной из категорий — подсекция не создаётся.
## Именование ## Именование
@@ -138,11 +144,13 @@ concat-md.js # Скрипт генерации RULES.md
Полноценные примеры кода. Полноценные примеры кода.
Каждый пример с путём к файлу и пояснениями. Каждый пример с путём к файлу и пояснениями.
``` ```
### Порядок секций ### Порядок секций
Порядок фиксированный: контекст → структура → правила → специализации базовых правил → примеры. Порядок фиксированный: контекст → структура → правила → специализации базовых правил → примеры.
Логика: читатель сначала понимает «что это», потом «где это лежит», потом «как это делать», и в конце видит полный пример. Логика: читатель сначала понимает «что это», потом «где это лежит», потом «как это делать», и в конце видит полный пример.
### Секции-расширения базовых правил ### Секции-расширения базовых правил
@@ -215,6 +223,5 @@ title: Название раздела
1. **Не дублировать.** Одна мысль живёт в одном месте. Остальные ссылаются. 1. **Не дублировать.** Одна мысль живёт в одном месте. Остальные ссылаются.
2. **Базовое vs прикладное.** Если правило применимо ко всему коду — оно базовое. Если только к одной области — прикладное. 2. **Базовое vs прикладное.** Если правило применимо ко всему коду — оно базовое. Если только к одной области — прикладное.
3. **Workflow vs правила.** Workflow описывает процесс (шаги). Правила описывают результат (каким должен быть код). 3. **Пустые секции не создавать.** Если для раздела нет специфики по именованию — секции «Именование» в нём нет.
4. **Пустые секции не создавать.** Если для раздела нет специфики по именованию — секции «Именование» в нём нет. 4. **Примеры обязательны.** Прикладной раздел без примеров кода — незавершён.
5. **Примеры обязательны.** Прикладной раздел без примеров кода — незавершён.

View File

@@ -15,7 +15,6 @@ const fileOrder = [
"basics/documentation.md", "basics/documentation.md",
"basics/typing.md", "basics/typing.md",
// applied // applied
"applied/vscode.md",
"applied/project-structure.md", "applied/project-structure.md",
"applied/components.md", "applied/components.md",
"applied/page-level.md", "applied/page-level.md",
@@ -29,6 +28,7 @@ const fileOrder = [
"applied/hooks.md", "applied/hooks.md",
"applied/fonts.md", "applied/fonts.md",
"applied/localization.md", "applied/localization.md",
"applied/vscode.md",
]; ];
// Удалить frontmatter из содержимого md-файла // Удалить frontmatter из содержимого md-файла

View File

@@ -1,10 +1,20 @@
--- ---
title: UI и компоненты title: Компоненты
--- ---
# UI и компоненты # Компоненты
Раздел описывает правила создания UIкомпонентов. Эти правила обязательны для всех слоёв FSD: `app`, `screens`, `layouts`, `widgets`, `features`, `entities`, `shared`. Правила написания React-компонентов: файловая структура модуля, типизация пропсов, документирование и реализация. Раздел охватывает компоненты всех слоёв — от `shared/ui` до `screens`.
Архитектурные слои и их назначение описаны в разделе [Архитектура](/basics/architecture).
## Правила организации
1. Один компонент — один файл.
2. Компонент не содержит бизнес-логики — логика и сайд-эффекты выносятся в хуки или сторы.
3. Дочерние компоненты размещаются в сегменте `ui/` и подчиняются тем же правилам структуры.
4. Публичный API модуля — только `index.ts`. Прямые импорты внутренних файлов запрещены.
## Базовая структура компонента ## Базовая структура компонента
@@ -20,26 +30,48 @@ container/
└── index.ts └── index.ts
``` ```
## Пример базового компонента ## Именования
`styles/container.module.css` - Имя корневого css класса всегда `.root`
```scss - Интерфейс именуется `{ComponentName}Props`.
.root {}
``` ## Типизация
В CSS Modules использование имени класса **.root** — это общепринятое соглашение (best practice)
- Компонент типизируется через `FC<Props>`.
- Интерфейс пропсов наследует HTML-атрибуты своего корневого элемента.
- `children` отдельно не объявляется — приходит из `HTMLAttributes`.
## Реализация
- Пропсы деструктурируются в теле компонента, не в параметрах.
- Порядок: пользовательские → системные (`children`, `className`) → `...htmlAttr`.
- `className` объединяется с корневым классом через `cl()`: `cl(styles.root, className)`.
- `...htmlAttr` прокидывается на корневой элемент.
## Пример
`container/types/container.interface.ts`
`types/container.interface.ts`
```ts ```ts
import type { HTMLAttributes } from 'react' import type { HTMLAttributes } from 'react'
/** /**
* Параметры контейнера. * Параметры компонента Container.
*/ */
export interface ContainerProps extends HTMLAttributes<HTMLDivElement> {} export interface ContainerProps extends HTMLAttributes<HTMLDivElement> {}
``` ```
Интерфес параметров компонента всегда наследует свойства своего тега: div, button, итд..
`container.tsx` `container/styles/container.module.css`
```css
.root {
max-width: var(--content-width);
margin: 0 auto;
padding: 0 var(--spacing-4);
}
```
`container/container.tsx`
```tsx ```tsx
import type { FC } from 'react' import type { FC } from 'react'
@@ -51,39 +83,23 @@ import styles from './styles/container.module.css'
* Контейнер с адаптивной максимальной шириной. * Контейнер с адаптивной максимальной шириной.
* *
* Используется для: * Используется для:
* - ограничения ширины контента * - обёртки контента страниц с ограничением ширины
* - центрирования содержимого * - центрирования блоков в лейауте
* - построения адаптивной сетки страницы
*/ */
export const Container: FC<ContainerProps> = ({ className, ...htmlAttr }) => { export const Container: FC<ContainerProps> = (props) => {
const { children, className, ...htmlAttr } = props
return ( return (
<div {...htmlAttr} className={cl(styles.root, className)}> <div {...htmlAttr} className={cl(styles.root, className)}>
Container... {children}
</div> </div>
) )
} }
``` ```
- Компонент объявляется через `const` и экспортируется именованно. `container/index.ts`
- Пропсы деструктурируются в сигнатуре; если их больше двух — деструктуризацию переносим в тело компонента.
- Из пропсов отдельно извлекаются `className` и `...htmlAttr`, чтобы корректно объединять классы и прокидывать остальные атрибуты.
- `cl` — короткое имя функции для конкатенации CSSклассов.
- `FC<>` используется для декларации `children`.
`index.ts`
```ts ```ts
export { Container } from './container' export { Container } from './container'
``` ```
## Шаблоны и генерация кода
Создание компонентов — **только через шаблоны**. Ручное создание файловой структуры компонента запрещено. Это обеспечивает единообразие каркаса, одинаковые папки и имена файлов, уменьшает ручные ошибки и ускоряет старт работы.
После генерации через **@gromlab/create** — проверить название компонента/файлов и заполнить описание назначения. Подробный порядок действий и перечень обязательных шаблонов — в разделе «Workflow».
## Вложенные (дочерние) компоненты
Если для реализации функционала нужны компоненты, которые используются только внутри текущего компонента, создавайте их как вложенные в папке `ui/`. Такие компоненты не экспортируются наружу и используются только локально.
Вложенные компоненты подчиняются тем же правилам по структуре, именованию и стилю (включая папку `styles/` для их стилей).

View File

@@ -4,146 +4,96 @@ title: Структура проекта
# Структура проекта # Структура проекта
Раздел описывает базовую структуру проекта Next.js (App Router) и принципы организации модулей на уровне папок и файлов. Раздел описывает расположение файлов и папок в проекте Next.js (App Router).
## Базовая структура проекта ## Корень репозитория
```text
project-root/
├── .templates/ # Шаблоны для генерации модулей
├── .vscode/ # Настройки и рекомендуемые расширения VS Code
├── public/ # Статика, доступная по прямому URL
├── src/ # Исходный код приложения
├── .env.example # Переменные окружения проекта (шаблон)
├── .env # Переменные окружения проекта (не коммитить)
├── .gitignore
├── AGENTS.md # Инструкции для AI-агентов
├── biome.json # Линтер и форматтер (вместо ESLint + Prettier)
├── next.config.ts # Конфигурация Next.js
├── package.json # Зависимости и скрипты
├── postcss.config.mjs # Конфигурация PostCSS
└── tsconfig.json # Конфигурация TypeScript
```
## Папка `public/`
Хранит статические файлы, которые отдаются по прямому URL без обработки сборщиком:
```text
public/
└── og-image.png
```
Компоненты, стили и другой исходный код здесь не размещаются.
## Папка `src/`
```text ```text
src/ src/
├── app/ # Слой app: роутинг, провайдеры, глобальные стили ├── app/ # Роутинг Next.js, провайдеры, глобальные стили
│ ├── providers/ # Провайдеры и обёртки приложения ├── screens/ # Собраные страницы (UI)
│ ├── styles/ # Глобальные стили, CSS-переменные, custom media ├── layouts/ # Шаблоны
│ ├── layout.tsx # Корневой layout (провайдеры, стили, метаданные) ├── widgets/ # Крупные самостоятельные блоки интерфейса
│ ├── page.tsx # Главная страница → HomeScreen
│ └── profile/
│ ├── page.tsx # → ProfileScreen
│ └── [id]/
│ └── page.tsx # → ProfileDetailScreen
├── screens/ # UI-компоненты страниц
│ ├── home/
│ │ ├── home.screen.tsx
│ │ └── index.ts
│ └── profile/
│ ├── profile.screen.tsx
│ └── index.ts
├── layouts/ # Общие шаблоны и каркасы страниц
│ └── main-layout/
│ ├── main-layout.layout.tsx
│ └── index.ts
├── widgets/ # Крупные блоки интерфейса
│ └── header/
│ ├── header.widget.tsx
│ └── index.ts
├── features/ # Пользовательские сценарии ├── features/ # Пользовательские сценарии
│ └── auth-by-email/
│ ├── ui/
│ │ └── login-form.tsx
│ ├── model/
│ │ └── auth-by-email.store.ts
│ ├── auth-by-email.feature.tsx
│ └── index.ts
├── entities/ # Бизнес-сущности ├── entities/ # Бизнес-сущности
│ └── user/ └── shared/ # Переиспользуемый код (UI, утилиты, типы и др.)
│ ├── model/
│ │ └── user.store.ts
│ ├── user.entity.tsx
│ └── index.ts
└── shared/ # Общие ресурсы проекта
├── ui/ # Повторно используемые UI-элементы
│ └── icon/
│ ├── styles/
│ │ └── icon.module.css
│ ├── types/
│ │ └── icon.interface.ts
│ ├── icon.tsx
│ └── index.ts
├── lib/ # Утилиты и хелперы
├── services/ # Общие сервисы и клиенты
├── config/ # Общие конфигурации и константы
└── assets/ # Ресурсы
├── images/
├── icons/
├── fonts/
└── video/
``` ```
## Слой `app/` Принципы организации слоёв описаны в разделе [Архитектура](../basics/architecture).
Папка `app/` совмещает две роли: инициализация приложения (провайдеры, глобальные стили) и файловый роутинг Next.js (route-сегменты, `layout.tsx`, `page.tsx`). ### Папка `app/`
- `providers/` и `styles/` -- это инфраструктура приложения, они не являются частью роутинга. Совмещает два слоя: инициализацию приложения по FSD (провайдеры, глобальные стили) и файловый роутинг Next.js (`layout.tsx`, `page.tsx`, route-сегменты).
- Route-сегменты (вложенные папки с `page.tsx`) -- это роутинг Next.js. Они не содержат логики, только импортируют экраны из `screens/`.
Компоненты, хуки, стили и утилиты не размещаются внутри route-сегментов -- всё это живёт в соответствующих слоях FSD.
## Правила организации
- В слоях FSD (`features`, `entities`, `widgets`, `screens` и т.д.) `ui/` используется только для дочерних элементов, которые относятся к модулю и не экспортируются отдельно. Главные компоненты, которые составляют сам слой, держат собственные `*.feature.tsx`, `*.widget.tsx` и т. п., а `ui/` служит для вспомогательных мелких компонентов.
- В `shared/ui/` хранятся базовые UI-элементы/компоненты, которыми пользуются сразу несколько модулей; в этом случае они экспортируются наружу и не считаются «дочерними» для слоя.
- Если модуль строится вокруг «главного» компонента (`*.feature.tsx`, `*.screen.tsx`, `*.widget.tsx`), помещайте его в корень модуля и экспортируйте через `index.ts`. Проверяйте, что `ui/` не используется просто как «контейнер» слоя.
- Каждый слой и модуль хранится в собственной папке.
- Внутренние реализации разделяются на `ui/`, `model/`, `styles/`, `helpers/`, `lib/`, `api/`.
- Публичный API модуля объявляется в `index.ts`.
- Внутренние файлы не импортируются напрямую извне.
- Не смешивать ответственность разных слоёв в одном модуле.
## Пример организации структуры
**Плохо** -- плоская структура без архитектуры:
```text ```text
src/ src/app/
├── components/ ├── providers/ # Провайдеры приложения
│ ├── Header.tsx ├── styles/ # Глобальные стили
│ ├── LoginForm.tsx ├── layout.tsx # Корневой layout
│ ├── UserCard.tsx └── page.tsx # Главная страница
│ └── Button.tsx
├── hooks/
│ ├── useAuth.ts
│ └── useUser.ts
├── api/
│ ├── auth.ts
│ └── user.ts
├── styles/
│ ├── header.module.css
│ └── login.module.css
├── types/
│ └── user.ts
└── utils/
└── format.ts
``` ```
Нет слоёв, нет границ ответственности -- Header и Button лежат рядом, хотя это разные уровни абстракции. LoginForm знает про API напрямую. При росте проекта `components/` превращается в свалку. ## Папка `.templates/`
Содержит шаблоны для генерации кода. Каждый подкаталог — шаблон отдельного типа модуля:
**Хорошо** -- та же функциональность в FSD:
```text ```text
src/ .templates/
├── widgets/ ├── component/ # Шаблон компонента
│ └── header/ ├── screen/ # Шаблон экрана
│ ├── header.widget.tsx ├── layout/ # Шаблон layout
│ └── index.ts ├── widget/ # Шаблон виджета
├── features/ ├── feature/ # Шаблон фичи
│ └── auth-by-email/ ├── entity/ # Шаблон сущности
│ ├── ui/ └── store/ # Шаблон стора
│ │ └── login-form.tsx
│ ├── model/
│ │ └── auth.store.ts
│ ├── auth-by-email.feature.tsx
│ └── index.ts
├── entities/
│ └── user/
│ ├── ui/
│ │ └── user-card.tsx
│ ├── model/
│ │ └── user.store.ts
│ ├── user.entity.tsx
│ └── index.ts
└── shared/
├── ui/
│ └── button/
│ ├── button.tsx
│ └── index.ts
└── lib/
└── format.ts
``` ```
Каждый элемент на своём слое, с публичным API и чёткими границами ответственности. Подробнее о генерации описано в разделе [Шаблоны и генерация кода](./templates-generation).
## Конфигурационные файлы
| Файл | Назначение |
|---|---|
| `next.config.ts` | Настройки Next.js: редиректы, переменные окружения, webpack |
| `tsconfig.json` | Настройки TypeScript: пути, строгость, таргет |
| `biome.json` | Правила линтера и форматтера Biome |
| `postcss.config.mjs` | Подключение PostCSS-плагинов (CSS Modules, custom media) |
| `package.json` | Зависимости, версии, npm-скрипты |
| `AGENTS.md` | Инструкции для AI-агентов, работающих в проекте |
## Переменные окружения
- `.env` — переменные окружения проекта, запрещено коммитить
- `.env.example` — шаблон, коммитится в репозиторий
Переменные с префиксом `NEXT_PUBLIC_` доступны в клиентском коде. Остальные доступны только на сервере.

View File

@@ -105,10 +105,12 @@ import styles from './styles/{{name.kebabCase}}.module.css'
/** /**
* {{name.pascalCase}}. * {{name.pascalCase}}.
*/ */
export const {{name.pascalCase}}: FC<{{name.pascalCase}}Props> = ({ className, ...htmlAttr }) => { export const {{name.pascalCase}}: FC<{{name.pascalCase}}Props> = (props) => {
const { children, className, ...htmlAttr } = props
return ( return (
<div {...htmlAttr} className={cl(styles.root, className)}> <div {...htmlAttr} className={cl(styles.root, className)}>
{{name.kebabCase}} {children}
</div> </div>
) )
} }

View File

@@ -4,61 +4,131 @@ title: Документирование
# Документирование # Документирование
Этот раздел описывает правила документирования кода: когда и как писать комментарии к функциям, компонентам, типам и интерфейсам. Этот раздел описывает правила документирования кода: когда и как писать
комментарии к компонентам, функциям, типам и интерфейсам.
## Правила ## Общие правила
- Документировать только назначение функций, компонентов, типов, интерфейсов и enum. - Документировать публичные функции, компоненты, типы, интерфейсы и enum.
- Не документировать параметры, возвращаемые значения, типы пропсов и очевидные детали. - Не документировать очевидное — если название говорит само за себя, комментарий не нужен.
- В интерфейсах, типах и enum описывать только смысл поля или значения. - Не документировать параметры, возвращаемые значения и типы пропсов — они видны из сигнатуры.
- Описание должно быть кратким, информативным и завершаться точкой. - Описание через пользу и назначение, а не через внутреннюю реализацию.
- Описание завершается точкой.
## Примеры ## Функции
Для документирования функций используется шаблон. Описание механики опционально —
добавляется когда логика нетривиальна.
**Шаблон**
```ts
/**
* <Что делает функция в 1 строке>.
*
* <Опционально: описание сложной механики или важных нюансов>.
*/
```
**Хорошо** **Хорошо**
```ts ```ts
/** /**
* Список задач пользователя. * Форматирует цену с символом валюты.
*/ */
export const TodoList = memo(() => { ... }); 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: FC<ContainerProps> = (props) => { ... }
```
**Плохо**
```tsx
// Плохо: описывает реализацию, а не назначение.
/**
* Рендерит div с className и htmlAttr.
*/
// Плохо: нет описания вообще.
export const Container: FC<ContainerProps> = (props) => { ... }
```
## Типы, интерфейсы, enum
Документируются назначение сущности и каждое её поле.
**Хорошо**
```ts
/**
* Фильтры списка задач.
*/
export enum TodoFilter {
/** Все задачи. */
ALL = 'all',
/** Только активные. */
ACTIVE = 'active',
/** Только завершённые. */
COMPLETED = 'completed',
}
/**
* Задача пользователя.
*/ */
export interface TodoItem { export interface TodoItem {
/** Уникальный идентификатор задачи. */ /** Уникальный идентификатор задачи. */
id: string; id: string;
/** Текст задачи. */ /** Текст задачи. */
text: string; text: string;
/** Статус выполнения задачи. */ /** Статус выполнения. */
completed: boolean; completed: boolean;
} }
/**
* Перечисление фильтров задач.
*/
export enum TodoFilter {
/** Все задачи. */
All = 'all',
/** Только активные задачи. */
Active = 'active',
/** Только выполненные задачи. */
Completed = 'completed',
}
``` ```
**Плохо** **Плохо**
```ts ```ts
// Плохо: дублирование параметров и возвращаемых значений. // Плохо: описывает очевидное.
/** export interface TodoItem {
* @param id - идентификатор задачи /** id — это id */
* @returns объект задачи id: string;
*/ }
// Плохо: описание очевидных деталей.
/**
* id — идентификатор задачи
* text — текст задачи
* completed — статус выполнения
*/
``` ```

View File

@@ -18,6 +18,7 @@ title: Именование
| React-компоненты | `PascalCase` | | React-компоненты | `PascalCase` |
| Хуки | `useSomething` | | Хуки | `useSomething` |
| CSS классы | `camelCase` | | CSS классы | `camelCase` |
| Ключи enum | `SCREAMING_SNAKE_CASE` |
## Именование файлов ## Именование файлов

View File

@@ -13,45 +13,6 @@ title: Типизация
- Избегать `any` и `unknown` без необходимости. - Избегать `any` и `unknown` без необходимости.
- Не использовать `ts-ignore`, кроме крайних случаев с явным комментарием причины. - Не использовать `ts-ignore`, кроме крайних случаев с явным комментарием причины.
## Типы для компонентов
- Типизировать параметры и публичный интерфейс компонента.
- Дефолтные значения описывать явно в коде.
**Хорошо**
```tsx
/**
* Параметры кнопки.
*/
interface ButtonProps extends HTMLAttributes<HTMLDivElement> {
/** Текст кнопки. */
label: string;
/** Обработчик клика по кнопке. */
onClick: () => void;
}
/**
* Кнопка с пользовательскими стилями.
*/
export const Button: FC<ButtonProps> = ({ className, label, onClick, ...htmlAttr }) => {
return (
<div {...htmlAttr} className={cl(styles.root, className)}>
button
</div>
)
}
```
**Плохо**
```tsx
// Плохо: параметры не типизированы.
export const Button = (props) => (
<button type="button" onClick={props.onClick}>
{props.label}
</button>
);
```
## Функции ## Функции
- Для публичных функций указывать возвращаемый тип. - Для публичных функций указывать возвращаемый тип.

View File

@@ -200,6 +200,7 @@
| React-компоненты | `PascalCase` | | React-компоненты | `PascalCase` |
| Хуки | `useSomething` | | Хуки | `useSomething` |
| CSS классы | `camelCase` | | CSS классы | `camelCase` |
| Ключи enum | `SCREAMING_SNAKE_CASE` |
### Именование файлов ### Именование файлов
@@ -540,63 +541,133 @@ const config = { url: '/api/users', method: 'GET', params: { page: 1, pageSize:
<!-- /basics/documentation --> <!-- /basics/documentation -->
## Документирование ## Документирование
Этот раздел описывает правила документирования кода: когда и как писать комментарии к функциям, компонентам, типам и интерфейсам. Этот раздел описывает правила документирования кода: когда и как писать
комментарии к компонентам, функциям, типам и интерфейсам.
### Правила ### Общие правила
- Документировать только назначение функций, компонентов, типов, интерфейсов и enum. - Документировать публичные функции, компоненты, типы, интерфейсы и enum.
- Не документировать параметры, возвращаемые значения, типы пропсов и очевидные детали. - Не документировать очевидное — если название говорит само за себя, комментарий не нужен.
- В интерфейсах, типах и enum описывать только смысл поля или значения. - Не документировать параметры, возвращаемые значения и типы пропсов — они видны из сигнатуры.
- Описание должно быть кратким, информативным и завершаться точкой. - Описание через пользу и назначение, а не через внутреннюю реализацию.
- Описание завершается точкой.
### Примеры ### Функции
Для документирования функций используется шаблон. Описание механики опционально —
добавляется когда логика нетривиальна.
**Шаблон**
```ts
/**
* <Что делает функция в 1 строке>.
*
* <Опционально: описание сложной механики или важных нюансов>.
*/
```
**Хорошо** **Хорошо**
```ts ```ts
/** /**
* Список задач пользователя. * Форматирует цену с символом валюты.
*/ */
export const TodoList = memo(() => { ... }); 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: FC<ContainerProps> = (props) => { ... }
```
**Плохо**
```tsx
// Плохо: описывает реализацию, а не назначение.
/**
* Рендерит div с className и htmlAttr.
*/
// Плохо: нет описания вообще.
export const Container: FC<ContainerProps> = (props) => { ... }
```
### Типы, интерфейсы, enum
Документируются назначение сущности и каждое её поле.
**Хорошо**
```ts
/**
* Фильтры списка задач.
*/
export enum TodoFilter {
/** Все задачи. */
ALL = 'all',
/** Только активные. */
ACTIVE = 'active',
/** Только завершённые. */
COMPLETED = 'completed',
}
/**
* Задача пользователя.
*/ */
export interface TodoItem { export interface TodoItem {
/** Уникальный идентификатор задачи. */ /** Уникальный идентификатор задачи. */
id: string; id: string;
/** Текст задачи. */ /** Текст задачи. */
text: string; text: string;
/** Статус выполнения задачи. */ /** Статус выполнения. */
completed: boolean; completed: boolean;
} }
/**
* Перечисление фильтров задач.
*/
export enum TodoFilter {
/** Все задачи. */
All = 'all',
/** Только активные задачи. */
Active = 'active',
/** Только выполненные задачи. */
Completed = 'completed',
}
``` ```
**Плохо** **Плохо**
```ts ```ts
// Плохо: дублирование параметров и возвращаемых значений. // Плохо: описывает очевидное.
/** export interface TodoItem {
* @param id - идентификатор задачи /** id — это id */
* @returns объект задачи id: string;
*/ }
// Плохо: описание очевидных деталей.
/**
* id — идентификатор задачи
* text — текст задачи
* completed — статус выполнения
*/
``` ```
<!-- /basics/typing --> <!-- /basics/typing -->
@@ -611,45 +682,6 @@ export enum TodoFilter {
- Избегать `any` и `unknown` без необходимости. - Избегать `any` и `unknown` без необходимости.
- Не использовать `ts-ignore`, кроме крайних случаев с явным комментарием причины. - Не использовать `ts-ignore`, кроме крайних случаев с явным комментарием причины.
### Типы для компонентов
- Типизировать параметры и публичный интерфейс компонента.
- Дефолтные значения описывать явно в коде.
**Хорошо**
```tsx
/**
* Параметры кнопки.
*/
interface ButtonProps extends HTMLAttributes<HTMLDivElement> {
/** Текст кнопки. */
label: string;
/** Обработчик клика по кнопке. */
onClick: () => void;
}
/**
* Кнопка с пользовательскими стилями.
*/
export const Button: FC<ButtonProps> = ({ className, label, onClick, ...htmlAttr }) => {
return (
<div {...htmlAttr} className={cl(styles.root, className)}>
button
</div>
)
}
```
**Плохо**
```tsx
// Плохо: параметры не типизированы.
export const Button = (props) => (
<button type="button" onClick={props.onClick}>
{props.label}
</button>
);
```
### Функции ### Функции
- Для публичных функций указывать возвращаемый тип. - Для публичных функций указывать возвращаемый тип.
@@ -692,242 +724,117 @@ const parse = (value: unknown): string => {
const parse = (value: any) => value; const parse = (value: any) => value;
``` ```
<!-- /applied/vscode -->
## Настройка VS Code
Каждый проект содержит папку `.vscode/` с конфигурацией редактора. Это гарантирует, что все участники команды работают с одинаковыми настройками форматирования, линтинга и расширениями.
### Структура `.vscode/`
```text
.vscode/
├── extensions.json # Рекомендуемые расширения
└── settings.json # Настройки редактора для проекта
```
Оба файла коммитятся в репозиторий.
### Расширения
Файл `.vscode/extensions.json` определяет список расширений, которые VS Code предложит установить при открытии проекта.
```json
// .vscode/extensions.json
{
"recommendations": [
"biomejs.biome",
"MyTemplateGenerator.mytemplategenerator",
"csstools.postcss"
]
}
```
| Расширение | Назначение |
|---|---|
| [Biome](https://marketplace.visualstudio.com/items?itemName=biomejs.biome) | Линтинг и форматирование кода. Заменяет ESLint и Prettier |
| [MyTemplateGenerator](https://open-vsx.org/extension/MyTemplateGenerator/mytemplategenerator) | Генерация файлов и папок из шаблонов `.templates/` через контекстное меню |
| [PostCSS Language Support](https://marketplace.visualstudio.com/items?itemName=csstools.postcss) | Подсветка синтаксиса и автодополнение для PostCSS (`@custom-media`, `@nest` и др.) |
#### Зачем это нужно
- Новый участник команды получает все нужные расширения одним кликом.
- Нет разночтений: все используют одинаковый форматтер и линтер.
- Расширения привязаны к проекту, а не к конкретному разработчику.
### Настройки редактора
Файл `.vscode/settings.json` переопределяет пользовательские настройки VS Code на уровне проекта.
```json
// .vscode/settings.json
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
"files.associations": {
"*.css": "postcss"
}
}
```
#### Разбор настроек
| Настройка | Значение | Что делает |
|---|---|---|
| `editor.defaultFormatter` | `biomejs.biome` | Biome используется как единственный форматтер для всех файлов |
| `editor.formatOnSave` | `true` | Код автоматически форматируется при каждом сохранении |
| `codeActionsOnSave.source.fixAll.biome` | `explicit` | Biome автоматически применяет безопасные исправления при сохранении |
| `codeActionsOnSave.source.organizeImports.biome` | `explicit` | Импорты сортируются и группируются автоматически при сохранении |
| `files.associations` | `"*.css": "postcss"` | Все CSS-файлы открываются с подсветкой PostCSS вместо стандартного CSS |
#### Зачем это нужно
- **Единый стиль кода** -- форматирование происходит автоматически, невозможно закоммитить неформатированный код.
- **Автофикс при сохранении** -- распространённые ошибки линтинга исправляются без ручного вмешательства.
- **Сортировка импортов** -- импорты всегда в одном порядке, без конфликтов при мерже.
- **PostCSS-подсветка** -- кастомные at-правила (`@custom-media`, `@define-mixin`) подсвечиваются корректно, а не как ошибки.
### Что не должно быть в `.vscode/`
Не коммитятся файлы, специфичные для конкретного разработчика:
- **Не коммитить**: отладочные конфигурации с локальными путями, персональные сниппеты, настройки тем оформления.
- **Коммитить**: только `extensions.json` и `settings.json` с общими для команды настройками.
<!-- /applied/project-structure --> <!-- /applied/project-structure -->
## Структура проекта ## Структура проекта
Раздел описывает базовую структуру проекта Next.js (App Router) и принципы организации модулей на уровне папок и файлов. Раздел описывает расположение файлов и папок в проекте Next.js (App Router).
### Базовая структура проекта ### Корень репозитория
```text
project-root/
├── .templates/ # Шаблоны для генерации модулей
├── .vscode/ # Настройки и рекомендуемые расширения VS Code
├── public/ # Статика, доступная по прямому URL
├── src/ # Исходный код приложения
├── .env.example # Переменные окружения проекта (шаблон)
├── .env # Переменные окружения проекта (не коммитить)
├── .gitignore
├── AGENTS.md # Инструкции для AI-агентов
├── biome.json # Линтер и форматтер (вместо ESLint + Prettier)
├── next.config.ts # Конфигурация Next.js
├── package.json # Зависимости и скрипты
├── postcss.config.mjs # Конфигурация PostCSS
└── tsconfig.json # Конфигурация TypeScript
```
### Папка `public/`
Хранит статические файлы, которые отдаются по прямому URL без обработки сборщиком:
```text
public/
└── og-image.png
```
Компоненты, стили и другой исходный код здесь не размещаются.
### Папка `src/`
```text ```text
src/ src/
├── app/ # Слой app: роутинг, провайдеры, глобальные стили ├── app/ # Роутинг Next.js, провайдеры, глобальные стили
│ ├── providers/ # Провайдеры и обёртки приложения ├── screens/ # Собраные страницы (UI)
│ ├── styles/ # Глобальные стили, CSS-переменные, custom media ├── layouts/ # Шаблоны
│ ├── layout.tsx # Корневой layout (провайдеры, стили, метаданные) ├── widgets/ # Крупные самостоятельные блоки интерфейса
│ ├── page.tsx # Главная страница → HomeScreen
│ └── profile/
│ ├── page.tsx # → ProfileScreen
│ └── [id]/
│ └── page.tsx # → ProfileDetailScreen
├── screens/ # UI-компоненты страниц
│ ├── home/
│ │ ├── home.screen.tsx
│ │ └── index.ts
│ └── profile/
│ ├── profile.screen.tsx
│ └── index.ts
├── layouts/ # Общие шаблоны и каркасы страниц
│ └── main-layout/
│ ├── main-layout.layout.tsx
│ └── index.ts
├── widgets/ # Крупные блоки интерфейса
│ └── header/
│ ├── header.widget.tsx
│ └── index.ts
├── features/ # Пользовательские сценарии ├── features/ # Пользовательские сценарии
│ └── auth-by-email/
│ ├── ui/
│ │ └── login-form.tsx
│ ├── model/
│ │ └── auth-by-email.store.ts
│ ├── auth-by-email.feature.tsx
│ └── index.ts
├── entities/ # Бизнес-сущности ├── entities/ # Бизнес-сущности
│ └── user/ └── shared/ # Переиспользуемый код (UI, утилиты, типы и др.)
│ ├── model/
│ │ └── user.store.ts
│ ├── user.entity.tsx
│ └── index.ts
└── shared/ # Общие ресурсы проекта
├── ui/ # Повторно используемые UI-элементы
│ └── icon/
│ ├── styles/
│ │ └── icon.module.css
│ ├── types/
│ │ └── icon.interface.ts
│ ├── icon.tsx
│ └── index.ts
├── lib/ # Утилиты и хелперы
├── services/ # Общие сервисы и клиенты
├── config/ # Общие конфигурации и константы
└── assets/ # Ресурсы
├── images/
├── icons/
├── fonts/
└── video/
``` ```
### Слой `app/` Принципы организации слоёв описаны в разделе [Архитектура](../basics/architecture).
Папка `app/` совмещает две роли: инициализация приложения (провайдеры, глобальные стили) и файловый роутинг Next.js (route-сегменты, `layout.tsx`, `page.tsx`). #### Папка `app/`
- `providers/` и `styles/` -- это инфраструктура приложения, они не являются частью роутинга. Совмещает два слоя: инициализацию приложения по FSD (провайдеры, глобальные стили) и файловый роутинг Next.js (`layout.tsx`, `page.tsx`, route-сегменты).
- Route-сегменты (вложенные папки с `page.tsx`) -- это роутинг Next.js. Они не содержат логики, только импортируют экраны из `screens/`.
```text
src/app/
├── providers/ # Провайдеры приложения
├── styles/ # Глобальные стили
├── layout.tsx # Корневой layout
└── page.tsx # Главная страница
```
### Папка `.templates/`
Содержит шаблоны для генерации кода. Каждый подкаталог — шаблон отдельного типа модуля:
```text
.templates/
├── component/ # Шаблон компонента
├── screen/ # Шаблон экрана
├── layout/ # Шаблон layout
├── widget/ # Шаблон виджета
├── feature/ # Шаблон фичи
├── entity/ # Шаблон сущности
└── store/ # Шаблон стора
```
Подробнее о генерации описано в разделе [Шаблоны и генерация кода](./templates-generation).
### Конфигурационные файлы
| Файл | Назначение |
|---|---|
| `next.config.ts` | Настройки Next.js: редиректы, переменные окружения, webpack |
| `tsconfig.json` | Настройки TypeScript: пути, строгость, таргет |
| `biome.json` | Правила линтера и форматтера Biome |
| `postcss.config.mjs` | Подключение PostCSS-плагинов (CSS Modules, custom media) |
| `package.json` | Зависимости, версии, npm-скрипты |
| `AGENTS.md` | Инструкции для AI-агентов, работающих в проекте |
### Переменные окружения
- `.env` — переменные окружения проекта, запрещено коммитить
- `.env.example` — шаблон, коммитится в репозиторий
Переменные с префиксом `NEXT_PUBLIC_` доступны в клиентском коде. Остальные доступны только на сервере.
<!-- /applied/components -->
## Компоненты
Правила написания React-компонентов: файловая структура модуля, типизация пропсов, документирование и реализация. Раздел охватывает компоненты всех слоёв — от `shared/ui` до `screens`.
Архитектурные слои и их назначение описаны в разделе [Архитектура](/basics/architecture).
Компоненты, хуки, стили и утилиты не размещаются внутри route-сегментов -- всё это живёт в соответствующих слоях FSD.
### Правила организации ### Правила организации
- В слоях FSD (`features`, `entities`, `widgets`, `screens` и т.д.) `ui/` используется только для дочерних элементов, которые относятся к модулю и не экспортируются отдельно. Главные компоненты, которые составляют сам слой, держат собственные `*.feature.tsx`, `*.widget.tsx` и т. п., а `ui/` служит для вспомогательных мелких компонентов. 1. Один компонент — один файл.
- В `shared/ui/` хранятся базовые UI-элементы/компоненты, которыми пользуются сразу несколько модулей; в этом случае они экспортируются наружу и не считаются «дочерними» для слоя. 2. Компонент не содержит бизнес-логики — логика и сайд-эффекты выносятся в хуки или сторы.
- Если модуль строится вокруг «главного» компонента (`*.feature.tsx`, `*.screen.tsx`, `*.widget.tsx`), помещайте его в корень модуля и экспортируйте через `index.ts`. Проверяйте, что `ui/` не используется просто как «контейнер» слоя. 3. Дочерние компоненты размещаются в сегменте `ui/` и подчиняются тем же правилам структуры.
- Каждый слой и модуль хранится в собственной папке. 4. Публичный API модуля — только `index.ts`. Прямые импорты внутренних файлов запрещены.
- Внутренние реализации разделяются на `ui/`, `model/`, `styles/`, `helpers/`, `lib/`, `api/`.
- Публичный API модуля объявляется в `index.ts`.
- Внутренние файлы не импортируются напрямую извне.
- Не смешивать ответственность разных слоёв в одном модуле.
### Пример организации структуры
**Плохо** -- плоская структура без архитектуры:
```text
src/
├── components/
│ ├── Header.tsx
│ ├── LoginForm.tsx
│ ├── UserCard.tsx
│ └── Button.tsx
├── hooks/
│ ├── useAuth.ts
│ └── useUser.ts
├── api/
│ ├── auth.ts
│ └── user.ts
├── styles/
│ ├── header.module.css
│ └── login.module.css
├── types/
│ └── user.ts
└── utils/
└── format.ts
```
Нет слоёв, нет границ ответственности -- Header и Button лежат рядом, хотя это разные уровни абстракции. LoginForm знает про API напрямую. При росте проекта `components/` превращается в свалку.
**Хорошо** -- та же функциональность в FSD:
```text
src/
├── widgets/
│ └── header/
│ ├── header.widget.tsx
│ └── index.ts
├── features/
│ └── auth-by-email/
│ ├── ui/
│ │ └── login-form.tsx
│ ├── model/
│ │ └── auth.store.ts
│ ├── auth-by-email.feature.tsx
│ └── index.ts
├── entities/
│ └── user/
│ ├── ui/
│ │ └── user-card.tsx
│ ├── model/
│ │ └── user.store.ts
│ ├── user.entity.tsx
│ └── index.ts
└── shared/
├── ui/
│ └── button/
│ ├── button.tsx
│ └── index.ts
└── lib/
└── format.ts
```
Каждый элемент на своём слое, с публичным API и чёткими границами ответственности.
<!-- /applied/components -->
## UI и компоненты
Раздел описывает правила создания UIкомпонентов. Эти правила обязательны для всех слоёв FSD: `app`, `screens`, `layouts`, `widgets`, `features`, `entities`, `shared`.
### Базовая структура компонента ### Базовая структура компонента
@@ -943,26 +850,48 @@ container/
└── index.ts └── index.ts
``` ```
### Пример базового компонента ### Именования
`styles/container.module.css` - Имя корневого css класса всегда `.root`
```scss - Интерфейс именуется `{ComponentName}Props`.
.root {}
``` ### Типизация
В CSS Modules использование имени класса **.root** — это общепринятое соглашение (best practice)
- Компонент типизируется через `FC<Props>`.
- Интерфейс пропсов наследует HTML-атрибуты своего корневого элемента.
- `children` отдельно не объявляется — приходит из `HTMLAttributes`.
### Реализация
- Пропсы деструктурируются в теле компонента, не в параметрах.
- Порядок: пользовательские → системные (`children`, `className`) → `...htmlAttr`.
- `className` объединяется с корневым классом через `cl()`: `cl(styles.root, className)`.
- `...htmlAttr` прокидывается на корневой элемент.
### Пример
`container/types/container.interface.ts`
`types/container.interface.ts`
```ts ```ts
import type { HTMLAttributes } from 'react' import type { HTMLAttributes } from 'react'
/** /**
* Параметры контейнера. * Параметры компонента Container.
*/ */
export interface ContainerProps extends HTMLAttributes<HTMLDivElement> {} export interface ContainerProps extends HTMLAttributes<HTMLDivElement> {}
``` ```
Интерфес параметров компонента всегда наследует свойства своего тега: div, button, итд..
`container.tsx` `container/styles/container.module.css`
```css
.root {
max-width: var(--content-width);
margin: 0 auto;
padding: 0 var(--spacing-4);
}
```
`container/container.tsx`
```tsx ```tsx
import type { FC } from 'react' import type { FC } from 'react'
@@ -974,43 +903,26 @@ import styles from './styles/container.module.css'
* Контейнер с адаптивной максимальной шириной. * Контейнер с адаптивной максимальной шириной.
* *
* Используется для: * Используется для:
* - ограничения ширины контента * - обёртки контента страниц с ограничением ширины
* - центрирования содержимого * - центрирования блоков в лейауте
* - построения адаптивной сетки страницы
*/ */
export const Container: FC<ContainerProps> = ({ className, ...htmlAttr }) => { export const Container: FC<ContainerProps> = (props) => {
const { children, className, ...htmlAttr } = props
return ( return (
<div {...htmlAttr} className={cl(styles.root, className)}> <div {...htmlAttr} className={cl(styles.root, className)}>
Container... {children}
</div> </div>
) )
} }
``` ```
- Компонент объявляется через `const` и экспортируется именованно. `container/index.ts`
- Пропсы деструктурируются в сигнатуре; если их больше двух — деструктуризацию переносим в тело компонента.
- Из пропсов отдельно извлекаются `className` и `...htmlAttr`, чтобы корректно объединять классы и прокидывать остальные атрибуты.
- `cl` — короткое имя функции для конкатенации CSSклассов.
- `FC<>` используется для декларации `children`.
`index.ts`
```ts ```ts
export { Container } from './container' export { Container } from './container'
``` ```
### Шаблоны и генерация кода
Создание компонентов — **только через шаблоны**. Ручное создание файловой структуры компонента запрещено. Это обеспечивает единообразие каркаса, одинаковые папки и имена файлов, уменьшает ручные ошибки и ускоряет старт работы.
После генерации через **@gromlab/create** — проверить название компонента/файлов и заполнить описание назначения. Подробный порядок действий и перечень обязательных шаблонов — в разделе «Workflow».
### Вложенные (дочерние) компоненты
Если для реализации функционала нужны компоненты, которые используются только внутри текущего компонента, создавайте их как вложенные в папке `ui/`. Такие компоненты не экспортируются наружу и используются только локально.
Вложенные компоненты подчиняются тем же правилам по структуре, именованию и стилю (включая папку `styles/` для их стилей).
<!-- /applied/page-level --> <!-- /applied/page-level -->
## Страницы (App Router) ## Страницы (App Router)
@@ -1295,10 +1207,12 @@ import styles from './styles/{{name.kebabCase}}.module.css'
/** /**
* {{name.pascalCase}}. * {{name.pascalCase}}.
*/ */
export const {{name.pascalCase}}: FC<{{name.pascalCase}}Props> = ({ className, ...htmlAttr }) => { export const {{name.pascalCase}}: FC<{{name.pascalCase}}Props> = (props) => {
const { children, className, ...htmlAttr } = props
return ( return (
<div {...htmlAttr} className={cl(styles.root, className)}> <div {...htmlAttr} className={cl(styles.root, className)}>
{{name.kebabCase}} {children}
</div> </div>
) )
} }
@@ -1607,3 +1521,88 @@ npx @gromlab/create <шаблон> <имя> <путь>
<!-- /applied/svg-sprites --> <!-- /applied/svg-sprites -->
## SVG-спрайты ## SVG-спрайты
<!-- /applied/vscode -->
## Настройка VS Code
Каждый проект содержит папку `.vscode/` с конфигурацией редактора. Это гарантирует, что все участники команды работают с одинаковыми настройками форматирования, линтинга и расширениями.
### Структура `.vscode/`
```text
.vscode/
├── extensions.json # Рекомендуемые расширения
└── settings.json # Настройки редактора для проекта
```
Оба файла коммитятся в репозиторий.
### Расширения
Файл `.vscode/extensions.json` определяет список расширений, которые VS Code предложит установить при открытии проекта.
```json
// .vscode/extensions.json
{
"recommendations": [
"biomejs.biome",
"MyTemplateGenerator.mytemplategenerator",
"csstools.postcss"
]
}
```
| Расширение | Назначение |
|---|---|
| [Biome](https://marketplace.visualstudio.com/items?itemName=biomejs.biome) | Линтинг и форматирование кода. Заменяет ESLint и Prettier |
| [MyTemplateGenerator](https://open-vsx.org/extension/MyTemplateGenerator/mytemplategenerator) | Генерация файлов и папок из шаблонов `.templates/` через контекстное меню |
| [PostCSS Language Support](https://marketplace.visualstudio.com/items?itemName=csstools.postcss) | Подсветка синтаксиса и автодополнение для PostCSS (`@custom-media`, `@nest` и др.) |
#### Зачем это нужно
- Новый участник команды получает все нужные расширения одним кликом.
- Нет разночтений: все используют одинаковый форматтер и линтер.
- Расширения привязаны к проекту, а не к конкретному разработчику.
### Настройки редактора
Файл `.vscode/settings.json` переопределяет пользовательские настройки VS Code на уровне проекта.
```json
// .vscode/settings.json
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
"files.associations": {
"*.css": "postcss"
}
}
```
#### Разбор настроек
| Настройка | Значение | Что делает |
|---|---|---|
| `editor.defaultFormatter` | `biomejs.biome` | Biome используется как единственный форматтер для всех файлов |
| `editor.formatOnSave` | `true` | Код автоматически форматируется при каждом сохранении |
| `codeActionsOnSave.source.fixAll.biome` | `explicit` | Biome автоматически применяет безопасные исправления при сохранении |
| `codeActionsOnSave.source.organizeImports.biome` | `explicit` | Импорты сортируются и группируются автоматически при сохранении |
| `files.associations` | `"*.css": "postcss"` | Все CSS-файлы открываются с подсветкой PostCSS вместо стандартного CSS |
#### Зачем это нужно
- **Единый стиль кода** -- форматирование происходит автоматически, невозможно закоммитить неформатированный код.
- **Автофикс при сохранении** -- распространённые ошибки линтинга исправляются без ручного вмешательства.
- **Сортировка импортов** -- импорты всегда в одном порядке, без конфликтов при мерже.
- **PostCSS-подсветка** -- кастомные at-правила (`@custom-media`, `@define-mixin`) подсвечиваются корректно, а не как ошибки.
### Что не должно быть в `.vscode/`
Не коммитятся файлы, специфичные для конкретного разработчика:
- **Не коммитить**: отладочные конфигурации с локальными путями, персональные сниппеты, настройки тем оформления.
- **Коммитить**: только `extensions.json` и `settings.json` с общими для команды настройками.