feat: add example

This commit is contained in:
2026-08-01 09:31:08 +03:00
parent 15805e28df
commit 26b59686a5
434 changed files with 34975 additions and 4995 deletions

View File

@@ -0,0 +1,351 @@
---
name: template-generation
description: "Используй при создании, изменении или проверке повторяемой файловой структуры и шаблонов генерации. Триггеры: .templates, @gromlab/create, Template File Generator, scaffold, шаблон, генератор, создать компонент, модуль, layout, screen, widget, business, store, hook, service, page-entry, boilerplate, index.ts, типы, стили, тесты, повторить структуру без copy-paste, настроить генерацию файлов. НЕ используй для одноразовой точечной правки, code style, SLM-архитектуры без генерации файлов, Next.js routing, REST-клиентов или SVG sprites."
---
<!-- Generated from src/SKILL.md. Do not edit manually. -->
# Template Generation
## Генерация Файлов
Шаблоны генерации - способ создавать повторяемые структуры проекта через генератор, а не вручную.
Шаблон фиксирует проектное соглашение один раз: структуру папок, имена файлов, экспорты, типы, стили, тесты, базовую реализацию и правила именования. После этого страницы, модули, компоненты и другие повторяемые сущности создаются одинаково, без копипасты и случайных отличий.
### Рабочий Алгоритм
1. Определи, создаётся ли повторяемая структура или одноразовая правка.
2. Если задача затрагивает размещение кода, сначала определи архитектурное место через профильный skill. Для SLM Design используй `slm-design`.
3. Найди область шаблонов: корень проекта, приложение, пакет или самостоятельный участок монорепозитория.
4. Проверь наличие `.templates/`, локального README, npm scripts, scaffold-скриптов и документации проекта.
5. Если подходящий шаблон есть, запусти генератор из каталога области шаблонов.
6. Если шаблона нет, но структура повторяемая, создай шаблон в `.templates/` выбранной области и затем сгенерируй через него нужные файлы.
7. Если структура одноразовая или шаблон усложнит задачу, создай файлы вручную и не добавляй новый шаблон.
8. После генерации проверь структуру, имена, экспорты, публичный API и соответствие архитектуре проекта.
9. Если существующий шаблон устарел, исправь шаблон и затем регенерируй или точечно приведи результат к актуальному соглашению.
Не ограничивайся выполнением команды. Выбери правильный путь: использовать существующий шаблон, создать новый, обновить устаревший или отказаться от шаблона.
### Жёсткие Правила
- Не создавай повторяемую структуру вручную, пока не проверил наличие подходящего шаблона или генератора.
- Не копируй существующий модуль как способ генерации новой сущности.
- Не создавай новый шаблон в корне репозитория, если соглашение относится только к конкретному приложению или пакету.
- Если в `.templates/` есть `README.md`, прочитай его перед выбором шаблона.
- Для `@gromlab/create` запускай команду из каталога, где лежит нужная `.templates/`.
- Не передавай в `@gromlab/create` путь к шаблонам через flags. Позиционный `[путь]` является путём вывода.
- Не выдумывай внешний источник шаблонов. Используй локальные инструкции проекта или явное указание пользователя.
- Не меняй существующий генератор, CLI или набор шаблонов без задачи на изменение генерации.
- Не закрепляй в шаблоне нарушение архитектуры, импортов, публичного API или code style проекта.
### Локальные Материалы Skill
Каноны ниже достаточны для выбора шаблона, области и команды генерации. Для редких сценариев открывай только нужный локальный файл:
- [Настройка шаблонов](./reference/canons/setup.md) - установка и проверка набора шаблонов.
- [Next.js App Router + SLM](./reference/examples/nextjs-app-router-slm/README.md) - рабочий набор `.templates/`.
### Разделы Спецификации
- [Выбор Шаблона](#выбор-шаблона) - когда использовать существующий шаблон, когда создавать новый и когда отказаться от шаблона.
- [Область Шаблонов](#область-шаблонов) - где лежит `.templates/` в обычном проекте и монорепозитории.
- [Использование](#использование-шаблонов) - генерация через CLI, VS Code расширение и проектные генераторы.
- [Создание Шаблонов](#создание-шаблонов) - структура `.templates/`, переменные, модификаторы и требования к шаблону.
- [Настройка](./reference/canons/setup.md) - первичная установка или проверка набора шаблонов.
- [Примеры Next.js App Router + SLM](./reference/examples/nextjs-app-router-slm/README.md) - рабочий набор `.templates/` для Next.js App Router и SLM Design.
### Проблема
Каждый новый модуль, компонент, store или scaffold требует однотипной структуры файлов и boilerplate-кода. Ручное создание приводит к расхождениям, забытым `index.ts`, неверным именам, устаревшим копиям и ошибкам переименования после copy-paste.
### Решение
Повторяемые структуры создаются через `.templates/` и проектный генератор. Генератор принимает имя сущности, подставляет его в переменные шаблона и создаёт готовую структуру в нужной области проекта.
### Принципы
- Сначала проверь существующий шаблон или генератор проекта.
- Если шаблон есть, используй генератор вместо ручного создания файлов.
- Если шаблона нет, но структура повторяемая, сначала создай шаблон, затем сгенерируй через него нужные файлы.
- Ручное создание допустимо для уникального одноразового кода, точечных правок и случаев, где шаблон усложняет задачу больше, чем помогает.
- В монорепозитории выбирай `.templates/` внутри правильной области: приложения, пакета или самостоятельного участка репозитория.
- Локальные инструкции проекта и README внутри `.templates/` имеют приоритет над общими примерами.
## Выбор Шаблона
### Главное Правило
Когда нужно создать повторяемую структуру файлов, сначала проверь наличие шаблона или генератора.
Если подходящий шаблон есть, используй его.
Если шаблона нет, но сущность повторяемая и может понадобиться снова, сначала создай шаблон, затем создай файлы проекта через него.
### Повторяемые Структуры
К повторяемым структурам относятся:
- страницы;
- модули;
- компоненты;
- layout;
- screen;
- widget;
- business-домен;
- store;
- state manager;
- hook;
- service;
- scaffold из нескольких связанных файлов или папок.
Не создавай такие структуры вручную по умолчанию. Сначала проверь, можно ли создать их через шаблон или генератор проекта.
### Что Оформлять В Шаблон
Оформляй структуру в шаблон, если выполняется хотя бы одно условие:
- структура состоит из нескольких связанных файлов или папок;
- имена файлов, папок, типов, компонентов или экспортов выводятся из одного имени сущности;
- есть повторяемые экспорты, типы, стили, тесты или другой boilerplate;
- такая сущность может понадобиться в проекте повторно;
- ручное создание легко приведёт к расхождениям между похожими сущностями;
- структура отражает архитектурное или командное соглашение проекта.
### Когда Писать Вручную
Создавай без шаблона, если:
- код уникален для конкретной задачи;
- меняется существующий файл, а не создаётся повторяемая структура;
- структура не будет переиспользоваться;
- это небольшое точечное изменение;
- шаблон усложнит работу больше, чем поможет;
- человек явно попросил не использовать шаблоны.
Если есть сомнение, считай scaffold, boilerplate и группы связанных файлов кандидатами на шаблон.
### Анти-Паттерны
- Копировать существующий модуль и переименовывать его вручную.
- Создавать компонент, модуль или store руками при наличии подходящего шаблона.
- Добавлять новый тип повторяемой структуры без шаблона, если он отражает командное соглашение.
- Создавать шаблон в глобальной области, если структура нужна только конкретному приложению или пакету.
## Область Шаблонов
### Определение
Каталог, внутри которого лежит `.templates/`, является областью шаблонов.
Для `apps/web/.templates/` область шаблонов - `apps/web`. Для `packages/ui/.templates/` область шаблонов - `packages/ui`.
### Размещение
`.templates/` не обязана лежать в корне git-репозитория. В монорепозитории у каждого приложения или пакета могут быть свои шаблоны:
```text
apps/web/.templates/
apps/admin/.templates/
packages/ui/.templates/
```
Шаблоны размещаются в той области, где действует соответствующее проектное соглашение.
### Выбор Области
Перед генерацией определи правильную область шаблонов:
1. Найди ближайшую или явно подходящую `.templates/`.
2. Проверь README внутри `.templates/`, если он есть.
3. Убедись, что выбранный шаблон относится к нужному приложению, пакету или модулю.
4. Создавай новый шаблон в той `.templates/`, которая принадлежит этой области.
5. Запускай CLI из каталога области шаблонов.
Не считай корень репозитория единственным местом для `.templates/`.
### Локальный README
Если в найденной `.templates/` есть `README.md`, прочитай его перед выбором шаблона.
README может описывать доступные шаблоны, назначение, область применения, параметры и примеры команд. Локальный README имеет приоритет над общими примерами этого reference.
### Монорепозиторий
В монорепозитории выбирай `.templates/` по месту создаваемой сущности:
- код приложения `apps/web` генерируется из `apps/web/.templates/`;
- код админки `apps/admin` генерируется из `apps/admin/.templates/`;
- общий UI-пакет генерируется из `packages/ui/.templates/` или локальных шаблонов конкретного пакета;
- общий infra/shared-пакет использует шаблоны своей области.
Если шаблон нужен только одному приложению, не выноси его в корень репозитория без причины.
## Использование Шаблонов
### Приоритет Инструментов
Используй генератор проекта, если он явно задан локальной документацией или scripts.
Если проект использует `.templates/` без другого генератора, основной способ для AI-агента - CLI `@gromlab/create` через `npx`.
Для разработчика-человека удобный способ - VS Code расширение `Template File Generator | gromlab`.
### CLI
```bash
npx @gromlab/create <шаблон> <имя> [путь]
```
CLI ищет `.templates/` только в текущей рабочей директории.
Позиционный `[путь]` - папка вывода относительно текущей рабочей директории, а не путь к шаблонам.
В монорепозитории запускай CLI из каталога области шаблонов.
Глобальная установка не нужна. Используй CLI через `npx`.
Не используй `--templates`, `--templatesPath`, `--templates-path`, `--out` или `--output`: эти опции не поддерживаются CLI.
### Примеры CLI
```bash
npx @gromlab/create component header-nav src/compositions/layouts/default-layout/ui
npx @gromlab/create module hero-section src/compositions/screens/home/parts
npx @gromlab/create widget header src/compositions/widgets
npx @gromlab/create layout default-layout src/compositions/layouts
npx @gromlab/create business auth src/business
npx @gromlab/create store auth src/business/auth/stores
```
Пример для монорепозитория с шаблонами в `apps/web/.templates/`:
```bash
# рабочая директория: apps/web
npx @gromlab/create module button src/ui
```
### VS Code
`Template File Generator | gromlab` позволяет создавать файлы и папки из `.templates/` через интерфейс редактора:
1. Открыть контекстное меню на целевой папке.
2. Выбрать `Generate from template`.
3. Выбрать шаблон.
4. Ввести имя сущности.
Расширение устанавливается на машину разработчика, а не в проект.
### После Генерации
После генерации проверь:
- файлы созданы в правильной области проекта;
- имена файлов, типов, компонентов и экспортов соответствуют шаблону;
- публичные API не открывают лишние внутренние детали;
- результат не нарушает архитектуру проекта;
- если шаблон устарел, обнови шаблон, а не исправляй каждый новый scaffold вручную.
## Создание Шаблонов
<!-- @formatter:off -->
### Структура
Шаблоны лежат в `.templates/` внутри нужной области шаблонов. Каждый подкаталог внутри `.templates/` - отдельный шаблон.
```text
.templates/
├── component/
├── module/
├── screen/
├── layout/
├── widget/
├── business/
├── business-with-deps/
├── business-composition/
├── page-entry/
├── store/
└── hook/
```
### Содержимое Шаблона
Шаблон должен описывать проектное соглашение, а не только создавать пустые файлы.
Фиксируй в шаблоне:
- структуру папок;
- имена файлов;
- публичные и локальные экспорты;
- типы;
- стили;
- тесты, если они приняты в проекте;
- базовый boilerplate;
- правила именования.
После создания шаблона используй его для генерации нужной структуры.
### Переменные
Используй переменные для частей, которые меняются между сгенерированными сущностями.
Переменные можно применять в именах файлов, именах папок и содержимом файлов:
```text
{{name}}
{{name.pascalCase}}
{{name.camelCase}}
{{name.kebabCase}}
{{name.snakeCase}}
{{name.screamingSnakeCase}}
```
`name` - дефолтная переменная, которую генератор получает вторым позиционным аргументом.
### Пример
```text
.templates/component/
└── {{name.kebabCase}}/
├── styles/
│ └── {{name.kebabCase}}.module.css
├── types/
│ └── {{name.kebabCase}}-props.type.ts
├── {{name.kebabCase}}.tsx
└── index.ts
```
```ts
// .templates/component/{{name.kebabCase}}/{{name.kebabCase}}.tsx
export const {{name.pascalCase}} = () => {
return null
}
```
```ts
// .templates/component/{{name.kebabCase}}/index.ts
export { {{name.pascalCase}} } from './{{name.kebabCase}}'
```
### README Для Шаблонов
Если набор шаблонов неочевиден, добавь `.templates/README.md`.
Опиши в README:
- список шаблонов;
- назначение каждого шаблона;
- область применения;
- обязательные параметры;
- примеры команд;
- отличия похожих шаблонов.
### Запреты
- Не создавай шаблон, который закрепляет нарушение архитектуры проекта.
- Не добавляй в шаблон продуктовые детали, если шаблон должен быть общим для приложения или пакета.
- Не используй copy-paste существующего модуля вместо шаблона для повторяемой структуры.
- Не придумывай внешний источник шаблонов без инструкции проекта или явного указания человека.
<!-- @formatter:on -->

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Template Generation"
short_description: "Шаблоны генерации и повторяемые структуры файлов"
default_prompt: "Use $template-generation to create, update, or apply file generation templates for repeatable project structures."

View File

@@ -0,0 +1,37 @@
---
title: Настройка Шаблонов
description: Первичная установка и проверка набора шаблонов генерации
---
# Настройка Шаблонов
## Когда Нужна Настройка
Настройка нужна, если в проекте или выбранной области монорепозитория нет `.templates/`, но команда использует генерацию файлов как стандартный способ создания повторяемых структур.
Не перезаписывай существующую `.templates/` без согласования.
## Установка Стандартного Набора
Если проекту нужен стандартный набор Next.js App Router + SLM, сначала проверь локальный пример [nextjs-app-router-slm](../examples/nextjs-app-router-slm/README.md) и перенеси в `.templates/` только подходящие шаблоны.
Если в `./examples` нет подходящего шаблона, создай нужный шаблон в процессе работы с проектом на основе фактической структуры проекта, локальных соглашений и правил генерации.
## Проверка Установки
Проверь генерацию тестовой сущности из области шаблонов:
```bash
npx @gromlab/create component test src/ui
```
После проверки удали тестовую сущность.
## Чеклист
- В правильной области проекта есть `.templates/`.
- Внутри `.templates/` есть нужные шаблоны или согласованный кастомный набор.
- Если есть `.templates/README.md`, он описывает назначение шаблонов.
- CLI запускается из области шаблонов.
- Пробная генерация отрабатывает без ошибок.
- Тестовый scaffold удалён после проверки.

View File

@@ -0,0 +1,18 @@
'use client'
import { useContext } from 'react'
import { {{name.pascalCase}}BusinessContext } from '../providers/{{name.kebabCase}}-business.provider'
import type { {{name.pascalCase}}Business } from '../types/{{name.kebabCase}}-business.type'
/**
* Возвращает business API, доступный внутри композиционного модуля {{name.pascalCase}}.
*/
export const use{{name.pascalCase}}Business = (): {{name.pascalCase}}Business => {
const business = useContext({{name.pascalCase}}BusinessContext)
if (!business) {
throw new Error('use{{name.pascalCase}}Business must be used within {{name.pascalCase}}BusinessProvider')
}
return business
}

View File

@@ -0,0 +1,4 @@
export { {{name.pascalCase}}BusinessProvider } from './providers/{{name.kebabCase}}-business.provider'
export { use{{name.pascalCase}}Business } from './hooks/use-{{name.kebabCase}}-business.hook'
export type { {{name.pascalCase}}Business } from './types/{{name.kebabCase}}-business.type'
export type { {{name.pascalCase}}BusinessProviderProps } from './types/{{name.kebabCase}}-business-provider-props.type'

View File

@@ -0,0 +1,27 @@
'use client'
import { createContext } from 'react'
import type { {{name.pascalCase}}Business } from '../types/{{name.kebabCase}}-business.type'
import type { {{name.pascalCase}}BusinessProviderProps } from '../types/{{name.kebabCase}}-business-provider-props.type'
/**
* Context business API для композиционного модуля {{name.pascalCase}}.
*/
export const {{name.pascalCase}}BusinessContext = createContext<{{name.pascalCase}}Business | null>(null)
/**
* Провайдер business API для композиционного модуля {{name.pascalCase}}.
*
* Используется для:
* - передачи собранных business-фабрик вложенным модулям
* - сохранения единой client boundary для business API
*/
export const {{name.pascalCase}}BusinessProvider = (props: {{name.pascalCase}}BusinessProviderProps) => {
const { children, value } = props
return (
<{{name.pascalCase}}BusinessContext.Provider value={value}>
{children}
</{{name.pascalCase}}BusinessContext.Provider>
)
}

View File

@@ -0,0 +1,12 @@
import type { ReactNode } from 'react'
import type { {{name.pascalCase}}Business } from './{{name.kebabCase}}-business.type'
/**
* Параметры провайдера business API для {{name.pascalCase}}.
*/
export type {{name.pascalCase}}BusinessProviderProps = {
/** Вложенное дерево композиционного модуля. */
children: ReactNode
/** Собранный business API. */
value: {{name.pascalCase}}Business
}

View File

@@ -0,0 +1,4 @@
/**
* Business API, доступный внутри композиционного модуля {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Business = object

View File

@@ -0,0 +1,5 @@
export { {{name.camelCase}}Factory } from './{{name.kebabCase}}.factory'
export type { {{name.pascalCase}} } from './types/{{name.kebabCase}}.type'
export type { {{name.pascalCase}}Api } from './types/{{name.kebabCase}}-api.type'
export type { {{name.pascalCase}}Deps } from './types/{{name.kebabCase}}-deps.type'
export type { {{name.pascalCase}}Factory } from './types/{{name.kebabCase}}-factory.type'

View File

@@ -0,0 +1,4 @@
/**
* Публичный API бизнес-модуля {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Api = object

View File

@@ -0,0 +1,4 @@
/**
* Зависимости бизнес-модуля {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Deps = object

View File

@@ -0,0 +1,7 @@
import type { {{name.pascalCase}}Api } from './{{name.kebabCase}}-api.type'
import type { {{name.pascalCase}}Deps } from './{{name.kebabCase}}-deps.type'
/**
* Фабрика публичного API бизнес-модуля {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Factory = (deps: {{name.pascalCase}}Deps) => {{name.pascalCase}}Api

View File

@@ -0,0 +1,4 @@
/**
* Доменная сущность {{name.pascalCase}}.
*/
export type {{name.pascalCase}} = object

View File

@@ -0,0 +1,8 @@
import type { {{name.pascalCase}}Factory } from './types/{{name.kebabCase}}-factory.type'
/**
* Создаёт публичный API бизнес-модуля {{name.pascalCase}}.
*/
export const {{name.camelCase}}Factory: {{name.pascalCase}}Factory = (_deps) => {
return {}
}

View File

@@ -0,0 +1,4 @@
export { {{name.camelCase}}Factory } from './{{name.kebabCase}}.factory'
export type { {{name.pascalCase}} } from './types/{{name.kebabCase}}.type'
export type { {{name.pascalCase}}Api } from './types/{{name.kebabCase}}-api.type'
export type { {{name.pascalCase}}Factory } from './types/{{name.kebabCase}}-factory.type'

View File

@@ -0,0 +1,4 @@
/**
* Публичный API бизнес-модуля {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Api = object

View File

@@ -0,0 +1,6 @@
import type { {{name.pascalCase}}Api } from './{{name.kebabCase}}-api.type'
/**
* Фабрика публичного API бизнес-модуля {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Factory = () => {{name.pascalCase}}Api

View File

@@ -0,0 +1,4 @@
/**
* Доменная сущность {{name.pascalCase}}.
*/
export type {{name.pascalCase}} = object

View File

@@ -0,0 +1,8 @@
import type { {{name.pascalCase}}Factory } from './types/{{name.kebabCase}}-factory.type'
/**
* Создаёт публичный API бизнес-модуля {{name.pascalCase}}.
*/
export const {{name.camelCase}}Factory: {{name.pascalCase}}Factory = () => {
return {}
}

View File

@@ -0,0 +1,2 @@
export { {{name.pascalCase}} } from './{{name.kebabCase}}'
export type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}-props.type'

View File

@@ -0,0 +1,16 @@
import type { ComponentPropsWithoutRef } from 'react'
/**
* Параметры {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Params = object
/**
* Атрибуты корневого элемента {{name.pascalCase}}.
*/
type RootAttrs = ComponentPropsWithoutRef<'div'>
/**
* Props компонента {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Props = RootAttrs & {{name.pascalCase}}Params

View File

@@ -0,0 +1,20 @@
import cl from 'clsx'
import type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}-props.type'
import styles from './styles/{{name.kebabCase}}.module.css'
/**
* <Назначение компонента {{name.pascalCase}} в 1 строке>.
*
* Используется для:
* - <сценарий 1>
* - <сценарий 2>
*/
export const {{name.pascalCase}} = (props: {{name.pascalCase}}Props) => {
const { children, className, ...rootAttrs } = props
return (
<div {...rootAttrs} className={cl(styles.root, className)}>
{children}
</div>
)
}

View File

@@ -0,0 +1,2 @@
export { {{name.pascalCase}}Layout } from './{{name.kebabCase}}.layout'
export type { {{name.pascalCase}}LayoutProps } from './types/{{name.kebabCase}}-layout-props.type'

View File

@@ -0,0 +1,16 @@
import type { ComponentPropsWithoutRef } from 'react'
/**
* Параметры layout {{name.pascalCase}}.
*/
export type {{name.pascalCase}}LayoutParams = object
/**
* Атрибуты корневого элемента layout {{name.pascalCase}}.
*/
type RootAttrs = ComponentPropsWithoutRef<'div'>
/**
* Props layout {{name.pascalCase}}.
*/
export type {{name.pascalCase}}LayoutProps = RootAttrs & {{name.pascalCase}}LayoutParams

View File

@@ -0,0 +1,20 @@
import cl from 'clsx'
import type { {{name.pascalCase}}LayoutProps } from './types/{{name.kebabCase}}-layout-props.type'
import styles from './styles/{{name.kebabCase}}.module.css'
/**
* <Назначение layout {{name.pascalCase}} в 1 строке>.
*
* Используется для:
* - <сценарий 1>
* - <сценарий 2>
*/
export const {{name.pascalCase}}Layout = (props: {{name.pascalCase}}LayoutProps) => {
const { children, className, ...rootAttrs } = props
return (
<div {...rootAttrs} className={cl(styles.root, className)}>
{children}
</div>
)
}

View File

@@ -0,0 +1,2 @@
export { {{name.pascalCase}} } from './{{name.kebabCase}}'
export type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}-props.type'

View File

@@ -0,0 +1,16 @@
import type { ComponentPropsWithoutRef } from 'react'
/**
* Параметры модуля {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Params = object
/**
* Атрибуты корневого элемента модуля {{name.pascalCase}}.
*/
type RootAttrs = ComponentPropsWithoutRef<'div'>
/**
* Props модуля {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Props = RootAttrs & {{name.pascalCase}}Params

View File

@@ -0,0 +1,20 @@
import cl from 'clsx'
import type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}-props.type'
import styles from './styles/{{name.kebabCase}}.module.css'
/**
* <Назначение компонента {{name.pascalCase}} в 1 строке>.
*
* Используется для:
* - <сценарий 1>
* - <сценарий 2>
*/
export const {{name.pascalCase}} = (props: {{name.pascalCase}}Props) => {
const { children, className, ...rootAttrs } = props
return (
<div {...rootAttrs} className={cl(styles.root, className)}>
{children}
</div>
)
}

View File

@@ -0,0 +1 @@
export { {{name.pascalCase}}PageEntry } from './{{name.kebabCase}}.entry'

View File

@@ -0,0 +1,23 @@
import { ComponentRenderer } from 'infra/cms/component-renderer'
import { digitalPlatformApi } from '@biocadless/digital-platform-api'
import type { CmsPageEntryProps } from 'infra/cms/cms-page-entry-registry'
/**
* Входная точка CMS-страницы {{name.pascalCase}}.
*
* Используется для:
* - загрузки дерева CMS-компонентов страницы
* - подключения страницы к dynamic CMS routing
*/
export const {{name.pascalCase}}PageEntry = async (props: CmsPageEntryProps) => {
const { pageId } = props
const components = await digitalPlatformApi.componentInstances.listV1({ pageId })
return (
<>
{components.map((component) => (
<ComponentRenderer key={component.id} data={component} />
))}
</>
)
}

View File

@@ -0,0 +1,2 @@
export { {{name.pascalCase}}Screen } from './{{name.kebabCase}}.screen'
export type { {{name.pascalCase}}ScreenProps } from './types/{{name.kebabCase}}-screen-props.type'

View File

@@ -0,0 +1,16 @@
import type { ComponentPropsWithoutRef } from 'react'
/**
* Параметры экрана {{name.pascalCase}}.
*/
export type {{name.pascalCase}}ScreenParams = object
/**
* Атрибуты корневого элемента экрана {{name.pascalCase}}.
*/
type RootAttrs = ComponentPropsWithoutRef<'main'>
/**
* Props экрана {{name.pascalCase}}.
*/
export type {{name.pascalCase}}ScreenProps = RootAttrs & {{name.pascalCase}}ScreenParams

View File

@@ -0,0 +1,20 @@
import cl from 'clsx'
import type { {{name.pascalCase}}ScreenProps } from './types/{{name.kebabCase}}-screen-props.type'
import styles from './styles/{{name.kebabCase}}.module.css'
/**
* <Назначение экрана {{name.pascalCase}} в 1 строке>.
*
* Используется для:
* - <сценарий 1>
* - <сценарий 2>
*/
export const {{name.pascalCase}}Screen = (props: {{name.pascalCase}}ScreenProps) => {
const { children, className, ...rootAttrs } = props
return (
<main {...rootAttrs} className={cl(styles.root, className)}>
{children}
</main>
)
}

View File

@@ -0,0 +1,7 @@
import { create } from 'zustand'
import type { {{name.pascalCase}}Store } from './{{name.kebabCase}}.type'
/**
* Стор {{name.pascalCase}}.
*/
export const use{{name.pascalCase}}Store = create<{{name.pascalCase}}Store>()(() => ({}))

View File

@@ -0,0 +1,14 @@
/**
* Состояние {{name.pascalCase}}.
*/
export type {{name.pascalCase}}State = object
/**
* Действия {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Actions = object
/**
* Стор {{name.pascalCase}} с состоянием и действиями.
*/
export type {{name.pascalCase}}Store = {{name.pascalCase}}State & {{name.pascalCase}}Actions

View File

@@ -0,0 +1,2 @@
export { {{name.pascalCase}}Widget } from './{{name.kebabCase}}.widget'
export type { {{name.pascalCase}}WidgetProps } from './types/{{name.kebabCase}}-widget-props.type'

View File

@@ -0,0 +1,16 @@
import type { ComponentPropsWithoutRef } from 'react'
/**
* Параметры виджета {{name.pascalCase}}.
*/
export type {{name.pascalCase}}WidgetParams = object
/**
* Атрибуты корневого элемента виджета {{name.pascalCase}}.
*/
type RootAttrs = ComponentPropsWithoutRef<'div'>
/**
* Props виджета {{name.pascalCase}}.
*/
export type {{name.pascalCase}}WidgetProps = RootAttrs & {{name.pascalCase}}WidgetParams

View File

@@ -0,0 +1,20 @@
import cl from 'clsx'
import type { {{name.pascalCase}}WidgetProps } from './types/{{name.kebabCase}}-widget-props.type'
import styles from './styles/{{name.kebabCase}}.module.css'
/**
* <Назначение виджета {{name.pascalCase}} в 1 строке>.
*
* Используется для:
* - <сценарий 1>
* - <сценарий 2>
*/
export const {{name.pascalCase}}Widget = (props: {{name.pascalCase}}WidgetProps) => {
const { children, className, ...rootAttrs } = props
return (
<div {...rootAttrs} className={cl(styles.root, className)}>
{children}
</div>
)
}

View File

@@ -0,0 +1,60 @@
# Шаблоны Next.js App Router + SLM
Пример содержит рабочую папку `.templates/` для генерации повторяемых SLM-сущностей через `@gromlab/create` или VS Code Template File Generator.
Перед использованием скопируй `.templates/` в нужную область проекта: приложение, пакет или самостоятельный участок монорепозитория.
Запускай CLI из каталога области, где лежит `.templates/`:
```bash
npx @gromlab/create <template> <name> [path]
```
Позиционный `[path]` - путь вывода относительно текущей рабочей директории, а не путь к шаблонам.
## Шаблоны
| Шаблон | Для чего | Куда генерировать |
|---|---|---|
| `component` | Презентационный компонент внутри `ui/` родительского модуля. Не владеет данными, сценариями и вложенной архитектурой. | `*/ui` |
| `module` | Обычный SLM-модуль с корневым `.tsx`, стилями, типами и публичным API. Подходит для `parts/` и UI-модулей слоя `src/ui`. | `*/parts`, `src/ui` |
| `screen` | Корневой screen-модуль страницы. | `src/compositions/screens` |
| `layout` | Layout-модуль композиции страницы. | `src/compositions/layouts` |
| `widget` | Переиспользуемый composition widget, не привязанный к одной странице. | `src/compositions/widgets` |
| `business` | Business-домен без runtime-зависимостей на другие домены. | `src/business` |
| `business-with-deps` | Business-домен с runtime-зависимостями, которые передаются через аргумент фабрики. | `src/business` |
| `store` | Zustand store внутри сегмента `stores/` конкретного модуля. | `*/stores` |
| `page-entry` | CMS-specific entry point для страницы, которая рендерится через dynamic routing. | `src/compositions/page-entries` |
| `business-composition` | Provider, hook и типы для передачи собранного business API внутри composition module. | внутри `src/compositions/**/<module>` |
## Компонент И Модуль
`component` создаёт презентационную единицу внутри сегмента `ui/`. Такой компонент работает только в границе родительского модуля и не импортирует проектный код за его пределами.
`module` создаёт архитектурную единицу SLM. У модуля есть публичный API через `index.ts`; он может иметь свои `hooks/`, `stores/`, `services/`, `parts/`, `ui/`, `types/` и `styles/` по мере необходимости.
Если UI-сущности нужны данные, сценарная логика, вложенные модули или собственные зависимости, используй `module`, а не `component`.
## CMS Page Entry
Шаблон `page-entry` намеренно содержит CMS-specific импорты из реального проекта:
- `infra/cms/component-renderer`;
- `@biocadless/digital-platform-api`;
- `infra/cms/cms-page-entry-registry`.
Перед копированием этого шаблона в другой проект адаптируй импорты, тип props и способ загрузки дерева компонентов под локальную CMS-интеграцию.
## Примеры CLI
```bash
npx @gromlab/create screen cabinet src/compositions/screens
npx @gromlab/create module hero-section src/compositions/screens/home/parts
npx @gromlab/create component header-nav src/compositions/layouts/default-layout/ui
npx @gromlab/create widget clinic-map src/compositions/widgets
npx @gromlab/create business auth src/business
npx @gromlab/create business-with-deps user src/business
npx @gromlab/create store auth src/business/auth/stores
npx @gromlab/create page-entry knv src/compositions/page-entries
npx @gromlab/create business-composition knv-page src/compositions/page-entries/knv
```