chore: добавить frontend правила и шаблоны SLM

- добавлены frontend инструкции AGENTS и локальный style guide
- актуализированы SLM templates под Vite React и слой infra
- добавлены шаблоны component, infra и factory-based business
- нормализованы примеры документации под alias infra
This commit is contained in:
2026-05-05 14:05:43 +03:00
parent 56d551b43b
commit 2c88cc3eca
88 changed files with 5768 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
export { {{name.camelCase}}Factory } from './{{name.kebabCase}}.factory'
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 @@
/**
* Публичный runtime API бизнес-модуля {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Api = Record<string, never>

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
import type { {{name.pascalCase}}Factory } from './types/{{name.kebabCase}}-factory.type'
/**
* Создаёт runtime 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,2 @@
.root {
}

View File

@@ -0,0 +1,14 @@
import type { ComponentPropsWithoutRef, ReactNode } from 'react'
/**
* Параметры {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Params = {
/** Содержимое компонента. */
children?: ReactNode
}
/** Атрибуты корневого элемента без children. */
type RootAttrs = Omit<ComponentPropsWithoutRef<'div'>, 'children'>
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,12 @@
/**
* Ошибка API {{name.pascalCase}}.
*/
export class {{name.pascalCase}}Error extends Error {
constructor(
public readonly status: number,
message: string,
) {
super(message)
this.name = '{{name.pascalCase}}Error'
}
}

View File

@@ -0,0 +1,3 @@
export { {{name.pascalCase}}Client, create{{name.pascalCase}}Client } from './{{name.kebabCase}}.client'
export { {{name.pascalCase}}Error } from './errors/{{name.kebabCase}}.error'
export type { QueryParams } from './types/{{name.kebabCase}}-client.type'

View File

@@ -0,0 +1,4 @@
/**
* Query-параметры API {{name.pascalCase}}.
*/
export type QueryParams = Record<string, boolean | number | string | null | undefined>

View File

@@ -0,0 +1,44 @@
import { {{name.pascalCase}}Error } from './errors/{{name.kebabCase}}.error'
import type { QueryParams } from './types/{{name.kebabCase}}-client.type'
/**
* REST-клиент {{name.pascalCase}}.
*/
export class {{name.pascalCase}}Client {
constructor(
private readonly baseUrl: string,
private readonly defaultHeaders: Record<string, string> = {},
) {}
/**
* Выполняет GET-запрос к API {{name.pascalCase}}.
*/
async get<T>(path: string, params: QueryParams = {}): Promise<T> {
const base = `${this.baseUrl.replace(/\/+$/, '')}/`
const url = new URL(path.replace(/^\/+/, ''), base)
Object.entries(params).forEach(([key, value]) => {
if (value !== null && value !== undefined) {
url.searchParams.set(key, String(value))
}
})
const response = await fetch(url, {
headers: {
Accept: 'application/json',
...this.defaultHeaders,
},
})
if (!response.ok) {
throw new {{name.pascalCase}}Error(response.status, response.statusText)
}
return response.json() as Promise<T>
}
}
/**
* Создаёт REST-клиент {{name.pascalCase}} с заданным base URL.
*/
export const create{{name.pascalCase}}Client = (baseUrl: string) => new {{name.pascalCase}}Client(baseUrl)

View File

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

View File

@@ -0,0 +1,2 @@
.root {
}

View File

@@ -0,0 +1,14 @@
import type { ComponentPropsWithoutRef, ReactNode } from 'react'
/**
* Параметры {{name.pascalCase}}Layout.
*/
export type {{name.pascalCase}}LayoutParams = {
/** Содержимое layout. */
children?: ReactNode
}
/** Атрибуты корневого элемента без children. */
type RootAttrs = Omit<ComponentPropsWithoutRef<'div'>, 'children'>
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}}.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}}.type'

View File

@@ -0,0 +1,2 @@
.root {
}

View File

@@ -0,0 +1,14 @@
import type { ComponentPropsWithoutRef, ReactNode } from 'react'
/**
* Параметры {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Params = {
/** Содержимое модуля. */
children?: ReactNode
}
/** Атрибуты корневого элемента без children. */
type RootAttrs = Omit<ComponentPropsWithoutRef<'div'>, 'children'>
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}}.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}}Screen } from './{{name.kebabCase}}.screen'
export type { {{name.pascalCase}}ScreenProps } from './types/{{name.kebabCase}}.type'

View File

@@ -0,0 +1,2 @@
.root {
}

View File

@@ -0,0 +1,14 @@
import type { ComponentPropsWithoutRef, ReactNode } from 'react'
/**
* Параметры экрана {{name.pascalCase}}.
*/
export type {{name.pascalCase}}ScreenParams = {
/** Содержимое экрана. */
children?: ReactNode
}
/** Атрибуты корневого элемента без children. */
type RootAttrs = Omit<ComponentPropsWithoutRef<'div'>, 'children'>
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}}.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 (
<div {...rootAttrs} className={cl(styles.root, className)}>
{children}
</div>
)
}

View File

@@ -0,0 +1,2 @@
export { use{{name.pascalCase}}Store } from './{{name.kebabCase}}.store'
export type { {{name.pascalCase}}State } from './{{name.kebabCase}}.type'

View File

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

View File

@@ -0,0 +1,6 @@
/**
* Состояние {{name.pascalCase}}.
*/
export interface {{name.pascalCase}}State {
}

View File

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

View File

@@ -0,0 +1,2 @@
.root {
}

View File

@@ -0,0 +1,14 @@
import type { ComponentPropsWithoutRef, ReactNode } from 'react'
/**
* Параметры {{name.pascalCase}}.
*/
export type {{name.pascalCase}}Params = {
/** Содержимое UI-модуля. */
children?: ReactNode
}
/** Атрибуты корневого элемента без children. */
type RootAttrs = Omit<ComponentPropsWithoutRef<'div'>, 'children'>
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}}.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}}Widget } from './{{name.kebabCase}}'
export type { {{name.pascalCase}}WidgetProps } from './types/{{name.kebabCase}}.type'

View File

@@ -0,0 +1,2 @@
.root {
}

View File

@@ -0,0 +1,14 @@
import type { ComponentPropsWithoutRef, ReactNode } from 'react'
/**
* Параметры виджета {{name.pascalCase}}.
*/
export type {{name.pascalCase}}WidgetParams = {
/** Содержимое виджета. */
children?: ReactNode
}
/** Атрибуты корневого элемента без children. */
type RootAttrs = Omit<ComponentPropsWithoutRef<'div'>, 'children'>
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}}.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>
)
}