Compare commits
4 Commits
c46b843670
...
4ae6ac893d
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ae6ac893d | |||
| 965235e390 | |||
| e608017dce | |||
| 29bcf23dde |
31
.vitepress/cache/deps/_metadata.json
vendored
31
.vitepress/cache/deps/_metadata.json
vendored
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
12644
.vitepress/cache/deps/chunk-EAEFJUV4.js
vendored
12644
.vitepress/cache/deps/chunk-EAEFJUV4.js
vendored
File diff suppressed because it is too large
Load Diff
7
.vitepress/cache/deps/chunk-EAEFJUV4.js.map
vendored
7
.vitepress/cache/deps/chunk-EAEFJUV4.js.map
vendored
File diff suppressed because one or more lines are too long
3
.vitepress/cache/deps/package.json
vendored
3
.vitepress/cache/deps/package.json
vendored
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "module"
|
|
||||||
}
|
|
||||||
4505
.vitepress/cache/deps/vitepress___@vue_devtools-api.js
vendored
4505
.vitepress/cache/deps/vitepress___@vue_devtools-api.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
9731
.vitepress/cache/deps/vitepress___@vueuse_core.js
vendored
9731
.vitepress/cache/deps/vitepress___@vueuse_core.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
343
.vitepress/cache/deps/vue.js
vendored
343
.vitepress/cache/deps/vue.js
vendored
@@ -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
|
|
||||||
7
.vitepress/cache/deps/vue.js.map
vendored
7
.vitepress/cache/deps/vue.js.map
vendored
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"sources": [],
|
|
||||||
"sourcesContent": [],
|
|
||||||
"mappings": "",
|
|
||||||
"names": []
|
|
||||||
}
|
|
||||||
@@ -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' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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/`.
|
|
||||||
|
|||||||
@@ -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. **Примеры обязательны.** Прикладной раздел без примеров кода — незавершён.
|
|
||||||
|
|||||||
@@ -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-файла
|
||||||
|
|||||||
@@ -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/` для их стилей).
|
|
||||||
|
|||||||
@@ -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
|
├── features/ # Пользовательские сценарии
|
||||||
│ └── profile/
|
├── entities/ # Бизнес-сущности
|
||||||
│ ├── page.tsx # → ProfileScreen
|
└── shared/ # Переиспользуемый код (UI, утилиты, типы и др.)
|
||||||
│ └── [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/ # Пользовательские сценарии
|
|
||||||
│ └── auth-by-email/
|
|
||||||
│ ├── ui/
|
|
||||||
│ │ └── login-form.tsx
|
|
||||||
│ ├── model/
|
|
||||||
│ │ └── auth-by-email.store.ts
|
|
||||||
│ ├── auth-by-email.feature.tsx
|
|
||||||
│ └── index.ts
|
|
||||||
├── entities/ # Бизнес-сущности
|
|
||||||
│ └── user/
|
|
||||||
│ ├── 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_` доступны в клиентском коде. Остальные доступны только на сервере.
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 — статус выполнения
|
|
||||||
*/
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ title: Именование
|
|||||||
| React-компоненты | `PascalCase` |
|
| React-компоненты | `PascalCase` |
|
||||||
| Хуки | `useSomething` |
|
| Хуки | `useSomething` |
|
||||||
| CSS классы | `camelCase` |
|
| CSS классы | `camelCase` |
|
||||||
|
| Ключи enum | `SCREAMING_SNAKE_CASE` |
|
||||||
|
|
||||||
|
|
||||||
## Именование файлов
|
## Именование файлов
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Функции
|
## Функции
|
||||||
|
|
||||||
- Для публичных функций указывать возвращаемый тип.
|
- Для публичных функций указывать возвращаемый тип.
|
||||||
|
|||||||
@@ -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
|
├── features/ # Пользовательские сценарии
|
||||||
│ └── profile/
|
├── entities/ # Бизнес-сущности
|
||||||
│ ├── page.tsx # → ProfileScreen
|
└── shared/ # Переиспользуемый код (UI, утилиты, типы и др.)
|
||||||
│ └── [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/ # Пользовательские сценарии
|
|
||||||
│ └── auth-by-email/
|
|
||||||
│ ├── ui/
|
|
||||||
│ │ └── login-form.tsx
|
|
||||||
│ ├── model/
|
|
||||||
│ │ └── auth-by-email.store.ts
|
|
||||||
│ ├── auth-by-email.feature.tsx
|
|
||||||
│ └── index.ts
|
|
||||||
├── entities/ # Бизнес-сущности
|
|
||||||
│ └── user/
|
|
||||||
│ ├── 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` с общими для команды настройками.
|
||||||
Reference in New Issue
Block a user