From 7382499886d691e046bc59108b8a417eed10f2db Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Thu, 2 Apr 2026 17:01:22 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=D0=BF=D1=80=D0=B8=D0=B2=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B8=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=20?= =?UTF-8?q?=D0=BA=20=D1=81=D1=82=D0=B0=D0=B9=D0=BB=D0=B3=D0=B0=D0=B9=D0=B4?= =?UTF-8?q?=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Убран FC из всех шаблонов и компонентов - Заменён interface на type, .interface.ts на .type.ts - Добавлен паттерн Params/Props/RootAttrs в типы - Деструктуризация пропсов перенесена в тело компонента - Добавлены styles/ и types/ для feature, entity, widget, screen - Добавлен расширенный JSDoc-шаблон с назначением и сценариями - Исправлен баг в index.ts шаблона component - Добавлены .editorconfig, .env.example - Добавлен organizeImports.biome в .vscode/settings.json - Исправлен .gitignore для .env.example - Переписан README под проект-шаблон - Удалён CLAUDE.md --- .editorconfig | 12 +++ .env.example | 2 + .gitignore | 1 + .../component/{{name.kebabCase}}/index.ts | 2 +- .../types/{{name.kebabCase}}.interface.ts | 6 -- .../types/{{name.kebabCase}}.type.ts | 11 +++ .../{{name.kebabCase}}/{{name.kebabCase}}.tsx | 15 ++-- .../styles/{{name.kebabCase}}.module.css | 2 + .../types/{{name.kebabCase}}.type.ts | 11 +++ .../{{name.kebabCase}}.entity.tsx | 18 +++-- .../styles/{{name.kebabCase}}.module.css | 2 + .../types/{{name.kebabCase}}.type.ts | 11 +++ .../{{name.kebabCase}}.feature.tsx | 18 +++-- .../types/{{name.kebabCase}}.interface.ts | 6 -- .../types/{{name.kebabCase}}.type.ts | 11 +++ .../{{name.kebabCase}}.layout.tsx | 16 ++-- .../types/{{name.kebabCase}}.type.ts | 11 +++ .../{{name.kebabCase}}.screen.tsx | 17 +++-- .../types/{{name.kebabCase}}.type.ts | 11 +++ .../{{name.kebabCase}}.widget.tsx | 17 +++-- .vscode/settings.json | 3 +- CLAUDE.md | 1 - README.md | 76 +++++++++++++------ src/screens/home/home.screen.tsx | 3 +- 24 files changed, 212 insertions(+), 71 deletions(-) create mode 100644 .editorconfig create mode 100644 .env.example delete mode 100644 .templates/component/{{name.kebabCase}}/types/{{name.kebabCase}}.interface.ts create mode 100644 .templates/component/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts create mode 100644 .templates/entity/{{name.kebabCase}}/styles/{{name.kebabCase}}.module.css create mode 100644 .templates/entity/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts create mode 100644 .templates/feature/{{name.kebabCase}}/styles/{{name.kebabCase}}.module.css create mode 100644 .templates/feature/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts delete mode 100644 .templates/layout/{{name.kebabCase}}/types/{{name.kebabCase}}.interface.ts create mode 100644 .templates/layout/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts create mode 100644 .templates/screen/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts create mode 100644 .templates/widget/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts delete mode 100644 CLAUDE.md diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8c52ff9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..adb7788 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +# Базовый URL приложения +NEXT_PUBLIC_APP_URL=http://localhost:3000 diff --git a/.gitignore b/.gitignore index 5ef6a52..7b8da95 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* +!.env.example # vercel .vercel diff --git a/.templates/component/{{name.kebabCase}}/index.ts b/.templates/component/{{name.kebabCase}}/index.ts index 9a661a6..3bd4b91 100644 --- a/.templates/component/{{name.kebabCase}}/index.ts +++ b/.templates/component/{{name.kebabCase}}/index.ts @@ -1 +1 @@ -export { {name.pascalCase} } from './{{name.kebabCase}}' +export { {{name.pascalCase}} } from './{{name.kebabCase}}' diff --git a/.templates/component/{{name.kebabCase}}/types/{{name.kebabCase}}.interface.ts b/.templates/component/{{name.kebabCase}}/types/{{name.kebabCase}}.interface.ts deleted file mode 100644 index 4245be4..0000000 --- a/.templates/component/{{name.kebabCase}}/types/{{name.kebabCase}}.interface.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { HTMLAttributes } from 'react' - -/** - * Параметры {{name.pascalCase}}. - */ -export interface {{name.pascalCase}}Props extends HTMLAttributes {} diff --git a/.templates/component/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts b/.templates/component/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts new file mode 100644 index 0000000..5ba1c73 --- /dev/null +++ b/.templates/component/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts @@ -0,0 +1,11 @@ +import type { HTMLAttributes } from 'react' + +/** + * Параметры {{name.pascalCase}}. + */ +export type {{name.pascalCase}}Params = {} + +/** HTML-атрибуты корневого элемента. */ +type RootAttrs = HTMLAttributes + +export type {{name.pascalCase}}Props = RootAttrs & {{name.pascalCase}}Params diff --git a/.templates/component/{{name.kebabCase}}/{{name.kebabCase}}.tsx b/.templates/component/{{name.kebabCase}}/{{name.kebabCase}}.tsx index fc89841..9a53d60 100644 --- a/.templates/component/{{name.kebabCase}}/{{name.kebabCase}}.tsx +++ b/.templates/component/{{name.kebabCase}}/{{name.kebabCase}}.tsx @@ -1,15 +1,20 @@ -import type { FC } from 'react' import cl from 'clsx' -import type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}.interface' +import type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}.type' import styles from './styles/{{name.kebabCase}}.module.css' /** - * {{name.pascalCase}}. + * <Назначение компонента {{name.pascalCase}} в 1 строке>. + * + * Используется для: + * - <сценарий 1> + * - <сценарий 2> */ -export const {{name.pascalCase}}: FC<{{name.pascalCase}}Props> = ({ className, ...htmlAttr }) => { +export const {{name.pascalCase}} = (props: {{name.pascalCase}}Props) => { + const { children, className, ...htmlAttr } = props + return (
- {{name.kebabCase}} + {children}
) } diff --git a/.templates/entity/{{name.kebabCase}}/styles/{{name.kebabCase}}.module.css b/.templates/entity/{{name.kebabCase}}/styles/{{name.kebabCase}}.module.css new file mode 100644 index 0000000..c3a2af6 --- /dev/null +++ b/.templates/entity/{{name.kebabCase}}/styles/{{name.kebabCase}}.module.css @@ -0,0 +1,2 @@ +.root { +} diff --git a/.templates/entity/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts b/.templates/entity/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts new file mode 100644 index 0000000..3d4394d --- /dev/null +++ b/.templates/entity/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts @@ -0,0 +1,11 @@ +import type { HTMLAttributes } from 'react' + +/** + * Параметры сущности {{name.pascalCase}}. + */ +export type {{name.pascalCase}}EntityParams = {} + +/** HTML-атрибуты корневого элемента. */ +type RootAttrs = HTMLAttributes + +export type {{name.pascalCase}}EntityProps = RootAttrs & {{name.pascalCase}}EntityParams diff --git a/.templates/entity/{{name.kebabCase}}/{{name.kebabCase}}.entity.tsx b/.templates/entity/{{name.kebabCase}}/{{name.kebabCase}}.entity.tsx index 686275b..c877110 100644 --- a/.templates/entity/{{name.kebabCase}}/{{name.kebabCase}}.entity.tsx +++ b/.templates/entity/{{name.kebabCase}}/{{name.kebabCase}}.entity.tsx @@ -1,12 +1,20 @@ -import type { FC } from 'react' +import cl from 'clsx' +import type { {{name.pascalCase}}EntityProps } from './types/{{name.kebabCase}}.type' +import styles from './styles/{{name.kebabCase}}.module.css' /** - * Сущность {{name.pascalCase}}. + * <Назначение сущности {{name.pascalCase}} в 1 строке>. + * + * Используется для: + * - <сценарий 1> + * - <сценарий 2> */ -export const {{name.pascalCase}}Entity: FC = () => { +export const {{name.pascalCase}}Entity = (props: {{name.pascalCase}}EntityProps) => { + const { children, className, ...htmlAttr } = props + return ( -
- {{name.kebabCase}} +
+ {children}
) } diff --git a/.templates/feature/{{name.kebabCase}}/styles/{{name.kebabCase}}.module.css b/.templates/feature/{{name.kebabCase}}/styles/{{name.kebabCase}}.module.css new file mode 100644 index 0000000..c3a2af6 --- /dev/null +++ b/.templates/feature/{{name.kebabCase}}/styles/{{name.kebabCase}}.module.css @@ -0,0 +1,2 @@ +.root { +} diff --git a/.templates/feature/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts b/.templates/feature/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts new file mode 100644 index 0000000..3eb635f --- /dev/null +++ b/.templates/feature/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts @@ -0,0 +1,11 @@ +import type { HTMLAttributes } from 'react' + +/** + * Параметры фичи {{name.pascalCase}}. + */ +export type {{name.pascalCase}}FeatureParams = {} + +/** HTML-атрибуты корневого элемента. */ +type RootAttrs = HTMLAttributes + +export type {{name.pascalCase}}FeatureProps = RootAttrs & {{name.pascalCase}}FeatureParams diff --git a/.templates/feature/{{name.kebabCase}}/{{name.kebabCase}}.feature.tsx b/.templates/feature/{{name.kebabCase}}/{{name.kebabCase}}.feature.tsx index b4f76a2..9d53878 100644 --- a/.templates/feature/{{name.kebabCase}}/{{name.kebabCase}}.feature.tsx +++ b/.templates/feature/{{name.kebabCase}}/{{name.kebabCase}}.feature.tsx @@ -1,12 +1,20 @@ -import type { FC } from 'react' +import cl from 'clsx' +import type { {{name.pascalCase}}FeatureProps } from './types/{{name.kebabCase}}.type' +import styles from './styles/{{name.kebabCase}}.module.css' /** - * Фича {{name.pascalCase}}. + * <Назначение фичи {{name.pascalCase}} в 1 строке>. + * + * Используется для: + * - <сценарий 1> + * - <сценарий 2> */ -export const {{name.pascalCase}}Feature: FC = () => { +export const {{name.pascalCase}}Feature = (props: {{name.pascalCase}}FeatureProps) => { + const { children, className, ...htmlAttr } = props + return ( -
- {{name.kebabCase}} +
+ {children}
) } diff --git a/.templates/layout/{{name.kebabCase}}/types/{{name.kebabCase}}.interface.ts b/.templates/layout/{{name.kebabCase}}/types/{{name.kebabCase}}.interface.ts deleted file mode 100644 index 4d3639e..0000000 --- a/.templates/layout/{{name.kebabCase}}/types/{{name.kebabCase}}.interface.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Параметры {{name.pascalCase}}Layout. - */ -export interface {{name.pascalCase}}LayoutProps { - children: React.ReactNode -} diff --git a/.templates/layout/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts b/.templates/layout/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts new file mode 100644 index 0000000..10f6669 --- /dev/null +++ b/.templates/layout/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts @@ -0,0 +1,11 @@ +import type { HTMLAttributes } from 'react' + +/** + * Параметры {{name.pascalCase}}Layout. + */ +export type {{name.pascalCase}}LayoutParams = {} + +/** HTML-атрибуты корневого элемента. */ +type RootAttrs = HTMLAttributes + +export type {{name.pascalCase}}LayoutProps = RootAttrs & {{name.pascalCase}}LayoutParams diff --git a/.templates/layout/{{name.kebabCase}}/{{name.kebabCase}}.layout.tsx b/.templates/layout/{{name.kebabCase}}/{{name.kebabCase}}.layout.tsx index 6a03002..e9f49e1 100644 --- a/.templates/layout/{{name.kebabCase}}/{{name.kebabCase}}.layout.tsx +++ b/.templates/layout/{{name.kebabCase}}/{{name.kebabCase}}.layout.tsx @@ -1,13 +1,19 @@ -import type { FC } from 'react' -import type { {{name.pascalCase}}LayoutProps } from './types/{{name.kebabCase}}.interface' +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}}. + * <Назначение layout {{name.pascalCase}} в 1 строке>. + * + * Используется для: + * - <сценарий 1> + * - <сценарий 2> */ -export const {{name.pascalCase}}Layout: FC<{{name.pascalCase}}LayoutProps> = ({ children }) => { +export const {{name.pascalCase}}Layout = (props: {{name.pascalCase}}LayoutProps) => { + const { children, className, ...htmlAttr } = props + return ( -
+
{children}
) diff --git a/.templates/screen/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts b/.templates/screen/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts new file mode 100644 index 0000000..3907720 --- /dev/null +++ b/.templates/screen/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts @@ -0,0 +1,11 @@ +import type { HTMLAttributes } from 'react' + +/** + * Параметры экрана {{name.pascalCase}}. + */ +export type {{name.pascalCase}}ScreenParams = {} + +/** HTML-атрибуты корневого элемента. */ +type RootAttrs = HTMLAttributes + +export type {{name.pascalCase}}ScreenProps = RootAttrs & {{name.pascalCase}}ScreenParams diff --git a/.templates/screen/{{name.kebabCase}}/{{name.kebabCase}}.screen.tsx b/.templates/screen/{{name.kebabCase}}/{{name.kebabCase}}.screen.tsx index 8ecd729..8ec6d7e 100644 --- a/.templates/screen/{{name.kebabCase}}/{{name.kebabCase}}.screen.tsx +++ b/.templates/screen/{{name.kebabCase}}/{{name.kebabCase}}.screen.tsx @@ -1,13 +1,20 @@ -import type { FC } from 'react' +import cl from 'clsx' +import type { {{name.pascalCase}}ScreenProps } from './types/{{name.kebabCase}}.type' import styles from './styles/{{name.kebabCase}}.module.css' /** - * Экран {{name.pascalCase}}. + * <Назначение экрана {{name.pascalCase}} в 1 строке>. + * + * Используется для: + * - <сценарий 1> + * - <сценарий 2> */ -export const {{name.pascalCase}}Screen: FC = () => { +export const {{name.pascalCase}}Screen = (props: {{name.pascalCase}}ScreenProps) => { + const { children, className, ...htmlAttr } = props + return ( -
- {{name.kebabCase}} +
+ {children}
) } diff --git a/.templates/widget/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts b/.templates/widget/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts new file mode 100644 index 0000000..9025222 --- /dev/null +++ b/.templates/widget/{{name.kebabCase}}/types/{{name.kebabCase}}.type.ts @@ -0,0 +1,11 @@ +import type { HTMLAttributes } from 'react' + +/** + * Параметры виджета {{name.pascalCase}}. + */ +export type {{name.pascalCase}}WidgetParams = {} + +/** HTML-атрибуты корневого элемента. */ +type RootAttrs = HTMLAttributes + +export type {{name.pascalCase}}WidgetProps = RootAttrs & {{name.pascalCase}}WidgetParams diff --git a/.templates/widget/{{name.kebabCase}}/{{name.kebabCase}}.widget.tsx b/.templates/widget/{{name.kebabCase}}/{{name.kebabCase}}.widget.tsx index f40c747..a4db24b 100644 --- a/.templates/widget/{{name.kebabCase}}/{{name.kebabCase}}.widget.tsx +++ b/.templates/widget/{{name.kebabCase}}/{{name.kebabCase}}.widget.tsx @@ -1,13 +1,20 @@ -import type { FC } from 'react' +import cl from 'clsx' +import type { {{name.pascalCase}}WidgetProps } from './types/{{name.kebabCase}}.type' import styles from './styles/{{name.kebabCase}}.module.css' /** - * Виджет {{name.pascalCase}}. + * <Назначение виджета {{name.pascalCase}} в 1 строке>. + * + * Используется для: + * - <сценарий 1> + * - <сценарий 2> */ -export const {{name.pascalCase}}Widget: FC = () => { +export const {{name.pascalCase}}Widget = (props: {{name.pascalCase}}WidgetProps) => { + const { children, className, ...htmlAttr } = props + return ( -
- {{name.kebabCase}} +
+ {children}
) } diff --git a/.vscode/settings.json b/.vscode/settings.json index e87ab80..99ca629 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,8 @@ "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll.biome": "explicit" + "source.fixAll.biome": "explicit", + "source.organizeImports.biome": "explicit" }, "files.associations": { "*.css": "postcss" diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 43c994c..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -@AGENTS.md diff --git a/README.md b/README.md index e215bc4..ce45134 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,64 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Next.js Template -## Getting Started +Шаблон проекта на Next.js, построенный по [стайлгайду](https://gromlab.ru/docs/nextjs-style-guide). -First, run the development server: +## Стек -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +- **Next.js 16** — фреймворк +- **React 19** / **TypeScript 5** — UI и типизация +- **Mantine UI 8** — компоненты +- **Zustand 5** — глобальное состояние +- **SWR 2** — получение данных +- **PostCSS Modules** — стили +- **Biome** — линтер и форматтер +- **clsx** — конкатенация CSS-классов + +## Архитектура + +Проект использует кастомизированный FSD (Feature-Sliced Design): + +``` +src/ +├── app/ # Роутинг Next.js, провайдеры, глобальные стили +├── screens/ # Собранные страницы (UI) +├── layouts/ # Каркасы и шаблоны страниц +├── widgets/ # Крупные самостоятельные блоки интерфейса +├── features/ # Пользовательские сценарии +├── entities/ # Бизнес-сущности +└── shared/ # Переиспользуемый код (UI, утилиты, типы) ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Зависимости идут строго сверху вниз: `app` -> `screens` -> `layouts` -> `widgets` -> `features` -> `entities` -> `shared`. -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +## Быстрый старт -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +```bash +npx tiged git@gromlab.ru:templates/nextjs.git my-app +cd my-app +npm install +npm run dev +``` -## Learn More +## Генерация кода -To learn more about Next.js, take a look at the following resources: +Модули создаются из шаблонов `.templates/`: -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +```bash +npx @gromlab/create component button src/shared/ui +npx @gromlab/create feature auth src/features +npx @gromlab/create widget header src/widgets +npx @gromlab/create entity user src/entities +npx @gromlab/create layout admin src/layouts +npx @gromlab/create screen profile src/screens +npx @gromlab/create store auth src/shared/model +``` -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +## Скрипты -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +| Команда | Описание | +|---|---| +| `npm run dev` | Запуск dev-сервера | +| `npm run build` | Сборка для продакшена | +| `npm run start` | Запуск продакшен-сервера | +| `npm run lint` | Проверка кода (Biome) | +| `npm run format` | Форматирование кода (Biome) | diff --git a/src/screens/home/home.screen.tsx b/src/screens/home/home.screen.tsx index d08b367..620c9fc 100644 --- a/src/screens/home/home.screen.tsx +++ b/src/screens/home/home.screen.tsx @@ -1,11 +1,10 @@ import { Container, Image, Stack, Text, Title } from '@mantine/core'; -import type { FC } from 'react'; import styles from './styles/home.module.css'; /** * Главный экран приложения. */ -export const HomeScreen: FC = () => { +export const HomeScreen = () => { return (