Compare commits
8 Commits
v3
...
e5e4ace91a
| Author | SHA1 | Date | |
|---|---|---|---|
| e5e4ace91a | |||
| 5a773a5b4f | |||
| f645b2ad40 | |||
| 90bf360c06 | |||
| 5cf0f0f8ba | |||
| 464c709859 | |||
| 64db18917b | |||
| ae103e962e |
@@ -1,93 +1,67 @@
|
|||||||
import { defineConfig } from 'vitepress';
|
import { defineConfig } from 'vitepress';
|
||||||
|
|
||||||
const ruSidebar = [
|
const sidebar = [
|
||||||
|
{
|
||||||
|
text: 'Главная',
|
||||||
|
link: '/docs/',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: 'Workflow',
|
text: 'Workflow',
|
||||||
link: '/ru/workflow',
|
link: '/docs/workflow',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Базовые правила',
|
text: 'Базовые правила',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Технологии и библиотеки', link: '/ru/basics/tech-stack' },
|
{ text: 'Технологии и библиотеки', link: '/docs/basics/tech-stack' },
|
||||||
{ text: 'Именование', link: '/ru/basics/naming' },
|
{ text: 'Именование', link: '/docs/basics/naming' },
|
||||||
{
|
{
|
||||||
text: 'Архитектура',
|
text: 'Архитектура',
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Обзор', link: '/ru/basics/architecture/' },
|
{ text: 'Обзор', link: '/docs/basics/architecture/' },
|
||||||
{ text: 'Слои', link: '/ru/basics/architecture/reference/layers' },
|
{ text: 'Слои', link: '/docs/basics/architecture/reference/layers' },
|
||||||
{ text: 'Модули', link: '/ru/basics/architecture/reference/modules' },
|
{ text: 'Модули', link: '/docs/basics/architecture/reference/modules' },
|
||||||
{ text: 'Сегменты', link: '/ru/basics/architecture/reference/segments' },
|
{ text: 'Сегменты', link: '/docs/basics/architecture/reference/segments' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ text: 'Стиль кода', link: '/ru/basics/code-style' },
|
{ text: 'Стиль кода', link: '/docs/basics/code-style' },
|
||||||
{ text: 'Документирование', link: '/ru/basics/documentation' },
|
{ text: 'Документирование', link: '/docs/basics/documentation' },
|
||||||
{ text: 'Типизация', link: '/ru/basics/typing' },
|
{ text: 'Типизация', link: '/docs/basics/typing' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Прикладные разделы',
|
text: 'Прикладные разделы',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Структура проекта', link: '/ru/applied/project-structure' },
|
{ text: 'Структура проекта', link: '/docs/applied/project-structure' },
|
||||||
{ text: 'Компоненты', link: '/ru/applied/components' },
|
{ text: 'Алиасы', link: '/docs/applied/aliases' },
|
||||||
{ text: 'Страницы (App Router)', link: '/ru/applied/page-level' },
|
{ text: 'Компоненты', link: '/docs/applied/components' },
|
||||||
{ text: 'Шаблоны и генерация кода', link: '/ru/applied/templates-generation' },
|
{ text: 'Страницы (App Router)', link: '/docs/applied/page-level' },
|
||||||
{ text: 'Стили', link: '/ru/applied/styles' },
|
{ text: 'Шаблоны и генерация кода', link: '/docs/applied/templates-generation' },
|
||||||
{ text: 'Изображения', link: '/ru/applied/images-sprites' },
|
{
|
||||||
{ text: 'SVG-спрайты', link: '/ru/applied/svg-sprites' },
|
text: 'Стили',
|
||||||
{ text: 'Видео', link: '/ru/applied/video' },
|
collapsed: true,
|
||||||
{ text: 'API', link: '/ru/applied/api' },
|
items: [
|
||||||
{ text: 'Stores', link: '/ru/applied/stores' },
|
{ text: 'PostCSS', link: '/docs/applied/styles/postcss' },
|
||||||
{ text: 'Хуки', link: '/ru/applied/hooks' },
|
{ text: 'Использование', link: '/docs/applied/styles/usage' },
|
||||||
{ text: 'Шрифты', link: '/ru/applied/fonts' },
|
|
||||||
{ text: 'Локализация', link: '/ru/applied/localization' },
|
|
||||||
{ text: 'Настройка VS Code', link: '/ru/applied/vscode' },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
{ text: 'Изображения', link: '/docs/applied/images-sprites' },
|
||||||
|
|
||||||
const enSidebar = [
|
|
||||||
{
|
{
|
||||||
text: 'Processes',
|
text: 'SVG-спрайты',
|
||||||
|
collapsed: true,
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Getting Started', link: '/en/workflow/getting-started' },
|
{ text: 'Установка и настройка', link: '/docs/applied/svg-sprites/setup' },
|
||||||
{ text: 'Creating an App', link: '/en/workflow/creating-app' },
|
{ text: 'Использование', link: '/docs/applied/svg-sprites/usage' },
|
||||||
{ text: 'Creating Pages', link: '/en/workflow/creating-pages' },
|
|
||||||
{ text: 'Creating Components', link: '/en/workflow/creating-components' },
|
|
||||||
{ text: 'Styling', link: '/en/workflow/styling' },
|
|
||||||
{ text: 'Data Fetching', link: '/en/workflow/data-fetching' },
|
|
||||||
{ text: 'State Management', link: '/en/workflow/state-management' },
|
|
||||||
{ text: 'Localization', link: '/en/workflow/localization' },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{ text: 'Видео', link: '/docs/applied/video' },
|
||||||
text: 'Basic Rules',
|
{ text: 'API', link: '/docs/applied/api' },
|
||||||
items: [
|
{ text: 'Stores', link: '/docs/applied/stores' },
|
||||||
{ text: 'Tech Stack', link: '/en/basics/tech-stack' },
|
{ text: 'Хуки', link: '/docs/applied/hooks' },
|
||||||
{ text: 'Architecture', link: '/en/basics/architecture' },
|
{ text: 'Шрифты', link: '/docs/applied/fonts' },
|
||||||
{ text: 'Code Style', link: '/en/basics/code-style' },
|
{ text: 'Локализация', link: '/docs/applied/localization' },
|
||||||
{ text: 'Naming', link: '/en/basics/naming' },
|
{ text: 'Biome', link: '/docs/applied/biome' },
|
||||||
{ text: 'Documentation', link: '/en/basics/documentation' },
|
{ text: 'Настройка VS Code', link: '/docs/applied/vscode' },
|
||||||
{ text: 'Typing', link: '/en/basics/typing' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Applied Sections',
|
|
||||||
items: [
|
|
||||||
{ text: 'VS Code Setup', link: '/en/applied/vscode' },
|
|
||||||
{ text: 'Project Structure', link: '/en/applied/project-structure' },
|
|
||||||
{ text: 'Components', link: '/en/applied/components' },
|
|
||||||
{ text: 'Page-level Components', link: '/en/applied/page-level' },
|
|
||||||
{ text: 'Templates & Code Generation', link: '/en/applied/templates-generation' },
|
|
||||||
{ text: 'Styles', link: '/en/applied/styles' },
|
|
||||||
{ text: 'Images', link: '/en/applied/images-sprites' },
|
|
||||||
{ text: 'SVG Sprites', link: '/en/applied/svg-sprites' },
|
|
||||||
{ text: 'Video', link: '/en/applied/video' },
|
|
||||||
{ text: 'API', link: '/en/applied/api' },
|
|
||||||
{ text: 'Stores', link: '/en/applied/stores' },
|
|
||||||
{ text: 'Hooks', link: '/en/applied/hooks' },
|
|
||||||
{ text: 'Fonts', link: '/en/applied/fonts' },
|
|
||||||
{ text: 'Localization', link: '/en/applied/localization' },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -115,8 +89,9 @@ export default defineConfig({
|
|||||||
// (попадают в корень `dist/` как статика). Исключаем из сканирования
|
// (попадают в корень `dist/` как статика). Исключаем из сканирования
|
||||||
// страниц, иначе VitePress рендерит их как HTML-страницы.
|
// страниц, иначе VitePress рендерит их как HTML-страницы.
|
||||||
srcExclude: ['public/**'],
|
srcExclude: ['public/**'],
|
||||||
|
lang: 'ru-RU',
|
||||||
title: 'NextJS Style Guide',
|
title: 'NextJS Style Guide',
|
||||||
description: 'Правила и стандарты разработки на NextJS и TypeScript',
|
description: 'Стандарты разработки на Next.js + TypeScript с архитектурой SLM',
|
||||||
|
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [utf8TextPlugin],
|
plugins: [utf8TextPlugin],
|
||||||
@@ -125,38 +100,18 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
locales: {
|
|
||||||
root: {
|
|
||||||
label: 'Languages',
|
|
||||||
lang: 'en',
|
|
||||||
},
|
|
||||||
ru: {
|
|
||||||
label: 'Русский',
|
|
||||||
lang: 'ru-RU',
|
|
||||||
link: '/ru/',
|
|
||||||
description: 'Стандарты разработки на Next.js + TypeScript с архитектурой SLM',
|
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
sidebar: ruSidebar,
|
sidebar,
|
||||||
|
socialLinks: [
|
||||||
|
{ icon: 'github', link: 'https://gromlab.ru/docs/nextjs-style-guide' },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
// Расширенный блок описания для llms.txt — даёт LLM полный
|
// Расширенный блок описания для llms.txt — даёт LLM полный
|
||||||
// технический контекст: стек, методология, охват тем.
|
// технический контекст: стек, методология, охват тем.
|
||||||
|
// Используется в generate-llms.ts.
|
||||||
llmsBlockquote:
|
llmsBlockquote:
|
||||||
'Стандарты разработки frontend-приложений на Next.js (App Router) + TypeScript + React с архитектурой SLM (Scoped Layered Module Design — модульная архитектура со слоями ответственности, где каждый модуль содержит всё необходимое: компоненты, хуки, сторы, типы, стили).',
|
'Стандарты разработки frontend-приложений на Next.js (App Router) + TypeScript + React с архитектурой SLM (Scoped Layered Module Design — модульная архитектура со слоями ответственности, где каждый модуль содержит всё необходимое: компоненты, хуки, сторы, типы, стили).',
|
||||||
llmsContext:
|
llmsContext:
|
||||||
'Стек: React, TypeScript, Next.js App Router, Mantine UI, SWR, Zustand, i18next, PostCSS Modules, Vitest, clsx.\n\nДокументация покрывает архитектуру SLM (слои, модули, сегменты, направление зависимостей, публичный API), правила оформления кода (именование, форматирование, импорты, типизация, JSDoc), реализацию компонентов и хуков, работу с App Router, кодогенерацию из шаблонов, стилизацию (Mobile First, токены), работу с API и сокетами, управление состоянием через Zustand, локализацию, ассеты (шрифты, изображения, SVG-спрайты) и настройку VS Code.',
|
'Стек: React, TypeScript, Next.js App Router, Mantine UI, SWR, Zustand, i18next, PostCSS Modules, Vitest, clsx.\n\nДокументация покрывает архитектуру SLM (слои, модули, сегменты, направление зависимостей, публичный API), правила оформления кода (именование, форматирование, импорты, типизация, JSDoc), реализацию компонентов и хуков, работу с App Router, кодогенерацию из шаблонов, стилизацию (Mobile First, токены), работу с API и сокетами, управление состоянием через Zustand, локализацию, ассеты (шрифты, изображения, SVG-спрайты) и настройку VS Code.',
|
||||||
},
|
} as any);
|
||||||
en: {
|
|
||||||
label: 'English',
|
|
||||||
lang: 'en-US',
|
|
||||||
link: '/en/',
|
|
||||||
description: 'Next.js + TypeScript development standards with SLM architecture',
|
|
||||||
themeConfig: {
|
|
||||||
sidebar: enSidebar,
|
|
||||||
},
|
|
||||||
llmsBlockquote:
|
|
||||||
'Frontend development standards for Next.js (App Router) + TypeScript + React projects with SLM architecture (Scoped Layered Module Design — a modular architecture with responsibility layers, where each module contains everything it needs: components, hooks, stores, types, styles).',
|
|
||||||
llmsContext:
|
|
||||||
'Stack: React, TypeScript, Next.js App Router, Mantine UI, SWR, Zustand, i18next, PostCSS Modules, Vitest, clsx.\n\nThe documentation covers SLM architecture (layers, modules, segments, dependency direction, public API), code conventions (naming, formatting, imports, typing, JSDoc), component and hook implementation, App Router usage, code generation from templates, styling (Mobile First, design tokens), API and socket integration, state management via Zustand, localization, assets (fonts, images, SVG sprites), and VS Code setup.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -15,3 +15,5 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
117
CONTRIBUTING.md
117
CONTRIBUTING.md
@@ -7,9 +7,8 @@
|
|||||||
Документационный сайт с правилами и стандартами фронтенд-разработки на Next.js + TypeScript.
|
Документационный сайт с правилами и стандартами фронтенд-разработки на Next.js + TypeScript.
|
||||||
|
|
||||||
- Движок: VitePress
|
- Движок: VitePress
|
||||||
- Языки: русский (основной), английский
|
- Язык: русский
|
||||||
- Русская версия: `docs/ru/`
|
- Контент: `docs/docs/`
|
||||||
- Английская версия: `docs/en/`
|
|
||||||
|
|
||||||
## Команды
|
## Команды
|
||||||
|
|
||||||
@@ -17,51 +16,53 @@
|
|||||||
|---------|-----------|
|
|---------|-----------|
|
||||||
| `npm run dev` | Локальный сервер разработки |
|
| `npm run dev` | Локальный сервер разработки |
|
||||||
| `npm run build` | Сборка статического сайта |
|
| `npm run build` | Сборка статического сайта |
|
||||||
| `npm run llms` | Генерация `generated/{lang}/llms.txt` (карта документации для LLM) и README |
|
| `npm run llms` | Генерация `llms.txt` (карта документации для LLM) и README |
|
||||||
|
|
||||||
## Структура файлов
|
## Структура файлов
|
||||||
|
|
||||||
```
|
```
|
||||||
docs/
|
docs/
|
||||||
├── ru/ # Русская версия (основная)
|
├── index.md # Лендинг (URL `/`)
|
||||||
│ ├── index.md # Главная страница
|
└── docs/ # Контент документации (URL `/docs/...`)
|
||||||
│ ├── basics/ # Базовые правила
|
├── index.md # Главная страница
|
||||||
│ │ ├── tech-stack.md
|
├── workflow.md
|
||||||
│ │ ├── architecture.md
|
├── workflow/ # Процессы разработки
|
||||||
│ │ ├── code-style.md
|
├── basics/ # Базовые правила
|
||||||
│ │ ├── naming.md
|
│ ├── tech-stack.md
|
||||||
│ │ ├── documentation.md
|
│ ├── architecture/
|
||||||
│ │ └── typing.md
|
│ ├── code-style.md
|
||||||
│ └── applied/ # Прикладные разделы
|
│ ├── naming.md
|
||||||
│ ├── vscode.md
|
│ ├── documentation.md
|
||||||
│ ├── project-structure.md
|
│ └── typing.md
|
||||||
│ ├── components.md
|
└── applied/ # Прикладные разделы
|
||||||
│ ├── page-level.md
|
├── vscode.md
|
||||||
│ ├── templates-generation.md
|
├── project-structure.md
|
||||||
│ ├── styles.md
|
├── components.md
|
||||||
│ ├── images-sprites.md
|
├── page-level.md
|
||||||
│ ├── svg-sprites.md
|
├── templates-generation.md
|
||||||
│ ├── video.md
|
├── styles.md
|
||||||
│ ├── api.md
|
├── images-sprites.md
|
||||||
│ ├── stores.md
|
├── svg-sprites.md
|
||||||
│ ├── hooks.md
|
├── video.md
|
||||||
│ ├── fonts.md
|
├── api.md
|
||||||
│ └── localization.md
|
├── stores.md
|
||||||
├── en/ # Английская версия (зеркало ru/)
|
├── hooks.md
|
||||||
|
├── fonts.md
|
||||||
|
└── localization.md
|
||||||
.vitepress/
|
.vitepress/
|
||||||
├── config.ts # Конфигурация VitePress, сайдбары, локали
|
└── config.ts # Конфигурация VitePress, сайдбар
|
||||||
generated/
|
|
||||||
├── ru/llms.txt # Карта документации для LLM (ru, llmstxt.org)
|
|
||||||
└── en/llms.txt # Карта документации для LLM (en, llmstxt.org)
|
|
||||||
generate-llms.ts # Скрипт генерации llms.txt и README
|
generate-llms.ts # Скрипт генерации llms.txt и README
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Сгенерированные артефакты (`docs/public/`): `llms.txt`, `llms-full.txt`,
|
||||||
|
`nextjs-style-guide.zip`, `manifest.json`, копии `.md` в `docs/public/docs/`.
|
||||||
|
|
||||||
### Добавление нового раздела
|
### Добавление нового раздела
|
||||||
|
|
||||||
1. Создать `.md`-файл в нужной папке (`basics/` или `applied/`).
|
1. Создать `.md`-файл в нужной папке (`docs/docs/basics/` или `docs/docs/applied/`).
|
||||||
2. Добавить пункт в сайдбар — `.vitepress/config.ts` (оба языка, если есть перевод).
|
2. Добавить пункт в сайдбар — `.vitepress/config.ts`.
|
||||||
Сайдбар — единственный источник порядка и группировки для `llms.txt`.
|
Сайдбар — единственный источник порядка и группировки для `llms.txt`.
|
||||||
3. Запустить `npm run llms` для обновления `generated/{lang}/llms.txt`.
|
3. Запустить `npm run llms` для обновления `llms.txt` и README.
|
||||||
|
|
||||||
## Два типа документации
|
## Два типа документации
|
||||||
|
|
||||||
@@ -185,6 +186,50 @@ title: Название раздела
|
|||||||
- Подсекции внутри `h2` — `h3`.
|
- Подсекции внутри `h2` — `h3`.
|
||||||
- `h4` не используется.
|
- `h4` не используется.
|
||||||
|
|
||||||
|
### Вводный абзац
|
||||||
|
|
||||||
|
Абзац сразу после `h1` отвечает на вопрос «о чём этот раздел?».
|
||||||
|
Он попадает в `llms.txt` и `README.md` архива как краткое описание,
|
||||||
|
поэтому должен быть плотным и без воды.
|
||||||
|
|
||||||
|
**Правила:**
|
||||||
|
|
||||||
|
- Не начинать с «Раздел описывает», «Этот раздел», «В этом разделе»,
|
||||||
|
«Здесь рассмотрено», «В этом документе».
|
||||||
|
- Начинать с подлежащего — самой темы (`Слои SLM:`, `Соглашения об именовании:`).
|
||||||
|
- Двоеточие или тире для перечисления **категорий и областей**, а не
|
||||||
|
конкретных значений из содержимого.
|
||||||
|
- Не дублировать содержимое: если внутри раздела 12 правил —
|
||||||
|
не перечислять их во вводном абзаце.
|
||||||
|
- Не аргументировать («единые правила делают код предсказуемым»).
|
||||||
|
- 1–2 предложения.
|
||||||
|
|
||||||
|
**Проверка:** если при добавлении нового правила/инструмента/раздела
|
||||||
|
вводный абзац придётся править — он слишком конкретный.
|
||||||
|
|
||||||
|
**Хорошо:**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Слои SLM: назначение, классификация, направление зависимостей, правила.
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Базовый стек проекта по областям: UI, архитектура, данные, состояние,
|
||||||
|
локализация, тестирование, стили, генерация кода.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо:**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Раздел описывает слои SLM: что такое слой, какие бывают, как между
|
||||||
|
ними направлены зависимости и какие правила действуют на каждом.
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Этот раздел описывает базовый стек технологий и библиотек, принятый в
|
||||||
|
проекте. React, TypeScript, Next.js, SWR, Zustand, i18next.
|
||||||
|
```
|
||||||
|
|
||||||
### Примеры кода
|
### Примеры кода
|
||||||
|
|
||||||
- Блоки кода с указанием языка: ` ```tsx `, ` ```css `, ` ```bash `, ` ```text `.
|
- Блоки кода с указанием языка: ` ```tsx `, ` ```css `, ` ```bash `, ` ```text `.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM node:24-alpine AS build
|
FROM node:24-alpine AS build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
# zip нужен для упаковки nextjs-style-guide-{lang}.zip
|
# zip нужен для упаковки nextjs-style-guide.zip
|
||||||
RUN apk add --no-cache zip
|
RUN apk add --no-cache zip
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|||||||
99
README.md
99
README.md
@@ -1,58 +1,69 @@
|
|||||||
# NextJS Style Guide
|
# NextJS Style Guide
|
||||||
|
|
||||||
Rules and standards for NextJS and TypeScript development: architecture, typing, styles, components, API, and infrastructure.
|
Соглашения по разработке Next.js проектов: архитектура и слои приложения, структура кода, организация модулей, стилизация, типизация и инфраструктура.
|
||||||
|
|
||||||
## Documentation Structure
|
## Использование
|
||||||
|
|
||||||
### Processes
|
**Для AI-агентов:**
|
||||||
|
|
||||||
**What to do** in a specific situation — step-by-step instructions.
|
- [llms.txt](/llms.txt) — Карта разделов, оглавление со ссылками на разделы.
|
||||||
|
- [llms-full.txt](/llms-full.txt) — Вся документация одним файлом.
|
||||||
|
|
||||||
| Section | Answers the question |
|
**Для проекта:**
|
||||||
|---------|---------------------|
|
|
||||||
| Getting Started | What tools to install before starting development? |
|
|
||||||
| Creating an App | How to create a new project, where to get a template? |
|
|
||||||
| Creating Pages | How to add a page: routing and screen? |
|
|
||||||
| Creating Components | How to generate components using templates? |
|
|
||||||
| Styling | What to use: Mantine, tokens, or PostCSS? |
|
|
||||||
| Data Fetching | How to fetch data: SWR, codegen, sockets? |
|
|
||||||
| State Management | When and how to create a store (Zustand)? |
|
|
||||||
| Localization | How to add translations and work with i18next? |
|
|
||||||
|
|
||||||
### Basic Rules
|
- [nextjs-style-guide.zip](/nextjs-style-guide.zip) — Набор Markdown-файлов для распаковки в `./ai/nextjs-style-guide/` или другую папку проекта.
|
||||||
|
|
||||||
**What the code should look like** — standards not tied to a specific technology.
|
## Структура документации
|
||||||
|
|
||||||
| Section | Answers the question |
|
### Workflow
|
||||||
|---------|---------------------|
|
|
||||||
| Tech Stack | What stack do we use? |
|
|
||||||
| Architecture | How are FSD layers, dependencies, and public API structured? |
|
|
||||||
| Code Style | How to format code: indentation, quotes, imports, early return? |
|
|
||||||
| Naming | How to name files, variables, components, hooks? |
|
|
||||||
| Documentation | How to write JSDoc: what to document and what not? |
|
|
||||||
| Typing | How to type: type vs interface, any/unknown? |
|
|
||||||
|
|
||||||
### Applied Sections
|
**Что делать и в каком порядке** — пошаговые инструкции.
|
||||||
|
|
||||||
**How a specific area works** — rules, structure, and code examples for specific technologies and tools.
|
| Раздел | Отвечает на вопрос |
|
||||||
|
|--------|-------------------|
|
||||||
|
| Начало работы | Что нужно знать перед началом разработки? |
|
||||||
|
| Создание проекта | Как начать новый проект? |
|
||||||
|
| Генерация кода | Какие модули должны генерироваться из шаблонов? |
|
||||||
|
| Добавление страницы | Как добавить новую страницу в проект? |
|
||||||
|
| Добавление UI-модуля | Как создать компонент, бизнес-модуль, виджет или layout? |
|
||||||
|
| Стилизация | Как стилизовать компоненты в проекте? |
|
||||||
|
| Получение данных | Как получать данные с сервера? |
|
||||||
|
| Управление состоянием | Как работать с состоянием? |
|
||||||
|
| Локализация | Как добавлять переводы и подключать локализацию? |
|
||||||
|
|
||||||
|
### Базовые правила
|
||||||
|
|
||||||
|
**Каким должен быть код** — стандарты, не привязанные к конкретной технологии.
|
||||||
|
|
||||||
|
| Раздел | Отвечает на вопрос |
|
||||||
|
|--------|-------------------|
|
||||||
|
| Технологии и библиотеки | Какой стек используем? |
|
||||||
|
| Архитектура | Как устроены слои SLM, зависимости, публичный API? |
|
||||||
|
| Стиль кода | Как оформлять код: отступы, кавычки, импорты, early return? |
|
||||||
|
| Именование | Как называть файлы, переменные, компоненты, хуки? |
|
||||||
|
| Документирование | Как писать JSDoc: что документировать, а что нет? |
|
||||||
|
| Типизация | Как типизировать: type vs interface, any/unknown? |
|
||||||
|
|
||||||
|
### Прикладные разделы
|
||||||
|
|
||||||
|
**Как это настроить и использовать** — конфигурация, структура и примеры кода для конкретных областей.
|
||||||
|
|
||||||
|
| Раздел | Отвечает на вопрос |
|
||||||
|
|--------|-------------------|
|
||||||
|
| Настройка VS Code | Как настроить редактор для проекта? |
|
||||||
|
| Структура проекта | Как организованы папки и файлы по SLM? |
|
||||||
|
| Компоненты | Как устроен компонент: файлы, пропсы, clsx? |
|
||||||
|
| Page-level компоненты | Как описывать layout, page, loading, error, not-found? |
|
||||||
|
| Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? |
|
||||||
|
| Стили | Как писать CSS: PostCSS Modules, вложенность, медиа, токены? |
|
||||||
|
| Изображения | _(не заполнен)_ |
|
||||||
|
| SVG-спрайты | _(не заполнен)_ |
|
||||||
|
| Видео | _(не заполнен)_ |
|
||||||
|
| API | _(не заполнен)_ |
|
||||||
|
| Stores | _(не заполнен)_ |
|
||||||
|
| Хуки | _(не заполнен)_ |
|
||||||
|
| Шрифты | _(не заполнен)_ |
|
||||||
|
| Локализация | _(не заполнен)_ |
|
||||||
|
|
||||||
| Section | Answers the question |
|
|
||||||
|---------|---------------------|
|
|
||||||
| Project Structure | How are folders and files organized by FSD? |
|
|
||||||
| Components | How is a component structured: files, props, clsx? |
|
|
||||||
| Page-level Components | How to define layout, page, loading, error, not-found? |
|
|
||||||
| Templates & Code Generation | How do templates work: syntax, variables, modifiers? |
|
|
||||||
| Styles | How to write CSS: PostCSS Modules, nesting, media, tokens? |
|
|
||||||
| Images | _(not filled)_ |
|
|
||||||
| SVG Sprites | _(not filled)_ |
|
|
||||||
| Video | _(not filled)_ |
|
|
||||||
| API | _(not filled)_ |
|
|
||||||
| Stores | _(not filled)_ |
|
|
||||||
| Hooks | _(not filled)_ |
|
|
||||||
| Fonts | _(not filled)_ |
|
|
||||||
| Localization | _(not filled)_ |
|
|
||||||
|
|
||||||
## For Assistants
|
|
||||||
|
|
||||||
Documentation map with links to all sections ([llmstxt.org](https://llmstxt.org) format):
|
|
||||||
https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/en/llms.txt
|
|
||||||
|
|||||||
61
README_RU.md
61
README_RU.md
@@ -1,61 +0,0 @@
|
|||||||
# NextJS Style Guide
|
|
||||||
|
|
||||||
Правила и стандарты разработки на NextJS и TypeScript: архитектура, типизация, стили, компоненты, API и инфраструктурные разделы.
|
|
||||||
|
|
||||||
## Для ассистентов
|
|
||||||
|
|
||||||
Карта документации со ссылками на все разделы (формат [llmstxt.org](https://llmstxt.org)):
|
|
||||||
https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/llms.txt
|
|
||||||
|
|
||||||
## Структура документации
|
|
||||||
|
|
||||||
### Workflow
|
|
||||||
|
|
||||||
**Что делать и в каком порядке** — пошаговые инструкции.
|
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
|
||||||
|--------|-------------------|
|
|
||||||
| Начало работы | Что нужно знать перед началом разработки? |
|
|
||||||
| Создание проекта | Как начать новый проект? |
|
|
||||||
| Генерация кода | Какие модули должны генерироваться из шаблонов? |
|
|
||||||
| Добавление страницы | Как добавить новую страницу в проект? |
|
|
||||||
| Добавление UI-модуля | Как создать компонент, бизнес-модуль, виджет или layout? |
|
|
||||||
| Стилизация | Как стилизовать компоненты в проекте? |
|
|
||||||
| Получение данных | Как получать данные с сервера? |
|
|
||||||
| Управление состоянием | Как работать с состоянием? |
|
|
||||||
| Локализация | Как добавлять переводы и подключать локализацию? |
|
|
||||||
|
|
||||||
### Базовые правила
|
|
||||||
|
|
||||||
**Каким должен быть код** — стандарты, не привязанные к конкретной технологии.
|
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
|
||||||
|--------|-------------------|
|
|
||||||
| Технологии и библиотеки | Какой стек используем? |
|
|
||||||
| Архитектура | Как устроены слои SLM, зависимости, публичный API? |
|
|
||||||
| Стиль кода | Как оформлять код: отступы, кавычки, импорты, early return? |
|
|
||||||
| Именование | Как называть файлы, переменные, компоненты, хуки? |
|
|
||||||
| Документирование | Как писать JSDoc: что документировать, а что нет? |
|
|
||||||
| Типизация | Как типизировать: type vs interface, any/unknown? |
|
|
||||||
|
|
||||||
### Прикладные разделы
|
|
||||||
|
|
||||||
**Как это настроить и использовать** — конфигурация, структура и примеры кода для конкретных областей.
|
|
||||||
|
|
||||||
| Раздел | Отвечает на вопрос |
|
|
||||||
|--------|-------------------|
|
|
||||||
| Настройка VS Code | Как настроить редактор для проекта? |
|
|
||||||
| Структура проекта | Как организованы папки и файлы по SLM? |
|
|
||||||
| Компоненты | Как устроен компонент: файлы, пропсы, clsx? |
|
|
||||||
| Page-level компоненты | Как описывать layout, page, loading, error, not-found? |
|
|
||||||
| Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? |
|
|
||||||
| Стили | Как писать CSS: PostCSS Modules, вложенность, медиа, токены? |
|
|
||||||
| Изображения | _(не заполнен)_ |
|
|
||||||
| SVG-спрайты | _(не заполнен)_ |
|
|
||||||
| Видео | _(не заполнен)_ |
|
|
||||||
| API | _(не заполнен)_ |
|
|
||||||
| Stores | _(не заполнен)_ |
|
|
||||||
| Хуки | _(не заполнен)_ |
|
|
||||||
| Шрифты | _(не заполнен)_ |
|
|
||||||
| Локализация | _(не заполнен)_ |
|
|
||||||
|
|
||||||
78
docs/docs/applied/aliases.md
Normal file
78
docs/docs/applied/aliases.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
title: Алиасы
|
||||||
|
keywords: [алиасы, aliases, paths, tsconfig, импорты, baseUrl, app, layouts, screens, widgets, business, infrastructure, ui, shared]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Алиасы
|
||||||
|
|
||||||
|
Импорты в проекте идут через алиасы слоёв SLM-архитектуры — по одному на каждый слой `src/`. Префикс `@/` **не используется**: имя слоя само по себе однозначно адресует код.
|
||||||
|
|
||||||
|
Слои и направление зависимостей — [Архитектура: слои](/docs/basics/architecture/reference/layers).
|
||||||
|
|
||||||
|
## Конфиг
|
||||||
|
|
||||||
|
`tsconfig.json` в корне проекта:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"app/*": ["./src/app/*"],
|
||||||
|
"layouts/*": ["./src/layouts/*"],
|
||||||
|
"screens/*": ["./src/screens/*"],
|
||||||
|
"widgets/*": ["./src/widgets/*"],
|
||||||
|
"business/*": ["./src/business/*"],
|
||||||
|
"infrastructure/*": ["./src/infrastructure/*"],
|
||||||
|
"ui/*": ["./src/ui/*"],
|
||||||
|
"shared/*": ["./src/shared/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Восемь алиасов — ровно по числу слоёв. Других алиасов в проекте нет.
|
||||||
|
|
||||||
|
## Правила
|
||||||
|
|
||||||
|
- **Каждый импорт между модулями — через алиас слоя.** Относительные пути (`../../`) запрещены за пределами своего модуля.
|
||||||
|
- **Внутри одного модуля** допустимы относительные импорты (`./model`, `./ui/button`) — это часть инкапсуляции модуля.
|
||||||
|
- **Префикс `@/` не используется.** Имя слоя — само по себе адрес.
|
||||||
|
- **Направление импортов** определяется архитектурой, не алиасами. Алиас разрешает импорт технически, но не отменяет правила слоёв (→ [Слои](/docs/basics/architecture/reference/layers)).
|
||||||
|
|
||||||
|
**Хорошо**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { Button } from 'ui/button'
|
||||||
|
import { useUser } from 'business/user'
|
||||||
|
import { formatDate } from 'shared/utils/date'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Относительный путь между модулями
|
||||||
|
import { Button } from '../../../ui/button'
|
||||||
|
|
||||||
|
// Префикс @/, которого нет в paths
|
||||||
|
import { Button } from '@/ui/button'
|
||||||
|
|
||||||
|
// Алиас на src — не предусмотрен
|
||||||
|
import { Button } from 'src/ui/button'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Внутри модуля
|
||||||
|
|
||||||
|
Внутри своего модуля — относительные пути:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// src/ui/button/button.tsx
|
||||||
|
import styles from './button.module.css'
|
||||||
|
import { Icon } from './icon'
|
||||||
|
```
|
||||||
|
|
||||||
|
Не использовать алиас на самого себя:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Плохо — алиас вместо относительного пути внутри модуля
|
||||||
|
import { Icon } from 'ui/button/icon'
|
||||||
|
```
|
||||||
80
docs/docs/applied/biome.md
Normal file
80
docs/docs/applied/biome.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
---
|
||||||
|
title: Biome
|
||||||
|
keywords: [biome, линтер, форматтер, lint, format, biome.json, "@biomejs/biome", замена eslint, замена prettier]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Biome
|
||||||
|
|
||||||
|
Единый линтер и форматтер для JS/TS/JSON в проекте. Заменяет связку ESLint + Prettier одним инструментом.
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
- Node.js 18+.
|
||||||
|
- Проект без установленного ESLint и Prettier (они конфликтуют с Biome).
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
1. Установить пакет:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --save-dev --save-exact @biomejs/biome
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Инициализировать конфиг:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @biomejs/biome init
|
||||||
|
```
|
||||||
|
|
||||||
|
В корне появится `biome.json` с дефолтными настройками.
|
||||||
|
|
||||||
|
3. Привести `biome.json` к стандартному виду — добавить override для `*.css` (см. «Стандартный `biome.json`»). Делается сразу после `init`, до первого запуска `lint`/`check`.
|
||||||
|
|
||||||
|
4. Добавить скрипты в `package.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"lint": "biome lint .",
|
||||||
|
"format": "biome format --write .",
|
||||||
|
"check": "biome check --write ."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Скрипт | Что делает |
|
||||||
|
|--------|-----------|
|
||||||
|
| `lint` | Проверка правил без правок |
|
||||||
|
| `format` | Автоформатирование всех файлов |
|
||||||
|
| `check` | Lint + format + organize imports в один проход (основная команда) |
|
||||||
|
|
||||||
|
## Стандартный `biome.json`
|
||||||
|
|
||||||
|
Дефолтный `biome.json`, созданный `biome init`, кастомизируется ровно одним блоком — `overrides` для `*.css` с отключённым правилом `suspicious/noUnknownAtRules`. Этот override **обязателен по умолчанию во всех проектах**, независимо от того, подключены ли уже стили: проектный CSS-стек использует `@custom-media` и другие нестандартные at-правила, которые Biome не распознаёт; без override `npm run lint` падает.
|
||||||
|
|
||||||
|
Фрагмент, который добавляется в `biome.json`:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"includes": ["**/*.css"],
|
||||||
|
"linter": {
|
||||||
|
"rules": {
|
||||||
|
"suspicious": {
|
||||||
|
"noUnknownAtRules": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Если в `biome.json` уже есть массив `overrides` — добавить элемент в него; не дублировать массив.
|
||||||
|
|
||||||
|
Прочая настройка правил Biome — отдельная задача, не входит в стандартный канон.
|
||||||
|
|
||||||
|
## Интеграция с VS Code
|
||||||
|
|
||||||
|
Расширение `biomejs.biome` и автоформатирование при сохранении настраиваются в [Настройка VS Code](/docs/applied/vscode).
|
||||||
@@ -6,7 +6,7 @@ title: Компоненты
|
|||||||
|
|
||||||
Правила написания React-компонентов: файловая структура модуля, типизация пропсов, документирование и реализация. Раздел охватывает компоненты всех слоёв — от `shared/ui` до `screens`.
|
Правила написания React-компонентов: файловая структура модуля, типизация пропсов, документирование и реализация. Раздел охватывает компоненты всех слоёв — от `shared/ui` до `screens`.
|
||||||
|
|
||||||
Архитектурные слои и их назначение описаны в разделе [Архитектура](/ru/basics/architecture/).
|
Архитектурные слои и их назначение описаны в разделе [Архитектура](/docs/basics/architecture/).
|
||||||
|
|
||||||
|
|
||||||
## Правила организации
|
## Правила организации
|
||||||
@@ -43,7 +43,7 @@ container/
|
|||||||
- **`type` вместо `interface`** — гибче для пропсов: поддерживает union, intersection, mapped types. Declaration merging пропсам не нужно.
|
- **`type` вместо `interface`** — гибче для пропсов: поддерживает union, intersection, mapped types. Declaration merging пропсам не нужно.
|
||||||
- **Без `FC`** — неявно добавляет `children`, усложняет дженерики, не даёт преимуществ перед аннотацией параметра.
|
- **Без `FC`** — неявно добавляет `children`, усложняет дженерики, не даёт преимуществ перед аннотацией параметра.
|
||||||
- **Типы в `types/`, а не в `.tsx`** — предотвращает циклические зависимости (компонент импортирует хук, хук импортирует тип из компонента) и разделяет ответственность: `.tsx` для рендера, `.type.ts` для данных.
|
- **Типы в `types/`, а не в `.tsx`** — предотвращает циклические зависимости (компонент импортирует хук, хук импортирует тип из компонента) и разделяет ответственность: `.tsx` для рендера, `.type.ts` для данных.
|
||||||
- **Без возвращаемого типа** — TypeScript выводит из JSX. Осознанное исключение из [базового правила](/ru/basics/typing).
|
- **Без возвращаемого типа** — TypeScript выводит из JSX. Осознанное исключение из [базового правила](/docs/basics/typing).
|
||||||
|
|
||||||
## Реализация
|
## Реализация
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ title: Структура проекта
|
|||||||
|
|
||||||
# Структура проекта
|
# Структура проекта
|
||||||
|
|
||||||
Раздел описывает расположение файлов и папок в проекте Next.js (App Router).
|
Файловая организация Next.js-проекта по архитектуре SLM.
|
||||||
|
|
||||||
## Корень репозитория
|
## Корень репозитория
|
||||||
|
|
||||||
71
docs/docs/applied/styles/postcss.md
Normal file
71
docs/docs/applied/styles/postcss.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
title: PostCSS
|
||||||
|
keywords: [postcss, postcss.config.mjs, postcss-custom-media, postcss-nesting, autoprefixer, postcss-global-data, csstools, "@custom-media", "@nest", css-процессор]
|
||||||
|
---
|
||||||
|
|
||||||
|
# PostCSS
|
||||||
|
|
||||||
|
Установка и настройка CSS-процессора PostCSS в проекте: набор плагинов, конфиг `postcss.config.mjs`. Выполняется один раз при заведении проекта.
|
||||||
|
|
||||||
|
Правила написания CSS в компонентах — [Использование](/docs/applied/styles/usage).
|
||||||
|
|
||||||
|
## Зачем PostCSS
|
||||||
|
|
||||||
|
Подключаем ради двух вещей:
|
||||||
|
|
||||||
|
- **Вложенность** — `&:hover`, `&::before`, `&._active` и `@media` внутри селектора. Без процессора нативный CSS не покрывает всех нужных кейсов вложенности.
|
||||||
|
- **`@custom-media`** — единые breakpoints проекта (`@media (--md)`) вместо магических `min-width`. Определяются в одном месте, переиспользуются везде.
|
||||||
|
|
||||||
|
Autoprefixer и `@csstools/postcss-global-data` идут довеском под эти две задачи.
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
- Next.js 14+ (App Router).
|
||||||
|
- Node.js 18+.
|
||||||
|
|
||||||
|
CSS Modules поддерживаются Next.js из коробки — отдельной установки не требуют.
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
1. Установить PostCSS-плагины как devDependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -D postcss-custom-media postcss-nesting autoprefixer @csstools/postcss-global-data
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Создать `postcss.config.mjs` в корне проекта (см. «Конфиг»).
|
||||||
|
|
||||||
|
## Конфиг
|
||||||
|
|
||||||
|
Файл `postcss.config.mjs` в корне проекта.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// postcss.config.mjs
|
||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
'@csstools/postcss-global-data': {
|
||||||
|
files: ['src/shared/styles/media.css'],
|
||||||
|
},
|
||||||
|
'postcss-custom-media': {},
|
||||||
|
'postcss-nesting': {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Разбор плагинов
|
||||||
|
|
||||||
|
| Плагин | Назначение |
|
||||||
|
|--------|------------|
|
||||||
|
| `@csstools/postcss-global-data` | Подгружает определения `@custom-media` из `src/shared/styles/media.css` перед обработкой каждого CSS-модуля. Семантика — «глобальный файл определений, который не импортируется в исходники» |
|
||||||
|
| `postcss-custom-media` | Поддержка `@custom-media --md (...)` и использования `@media (--md) {}`. Определения берутся из файла, который подгрузил `postcss-global-data` |
|
||||||
|
| `postcss-nesting` | Нативная CSS-вложенность: `&:hover`, `&::before`, `&._active` |
|
||||||
|
| `autoprefixer` | Добавление вендорных префиксов по browserslist |
|
||||||
|
|
||||||
|
### Почему внешний файл с `@custom-media`, а не `@import`
|
||||||
|
|
||||||
|
`@custom-media` — глобальные определения, одинаковые для всего проекта. Держим их в `src/shared/styles/media.css`. `@csstools/postcss-global-data` подгружает этот файл перед каждым модулем, а `postcss-custom-media` заменяет `@media (--md)` на конкретные `@media (min-width: ...)` на этапе сборки. Сами определения в бандл не попадают.
|
||||||
|
|
||||||
|
Опция `importFrom` у `postcss-custom-media` удалена в v10+; её роль теперь выполняет `@csstools/postcss-global-data`.
|
||||||
|
|
||||||
|
Импортировать `media.css` в файлы компонентов **не нужно** и запрещено правилами (см. [Использование](/docs/applied/styles/usage), раздел «Импорт стилей»).
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: Стили
|
title: Использование
|
||||||
---
|
---
|
||||||
|
|
||||||
# Стили
|
# Использование
|
||||||
|
|
||||||
Раздел описывает правила написания CSS: PostCSS Modules, вложенность, медиа-запросы, переменные, форматирование.
|
Правила написания CSS: PostCSS Modules, форматирование, переменные. Установка и настройка процессора — [PostCSS](/docs/applied/styles/postcss).
|
||||||
|
|
||||||
## Общие правила
|
## Общие правила
|
||||||
|
|
||||||
108
docs/docs/applied/svg-sprites/setup.md
Normal file
108
docs/docs/applied/svg-sprites/setup.md
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
---
|
||||||
|
title: Установка и настройка
|
||||||
|
keywords: [svg-sprites, установка, настройка, config, пакет, "@gromlab/svg-sprites", svg-sprites.config.ts]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Установка и настройка
|
||||||
|
|
||||||
|
Первичная настройка пакета `@gromlab/svg-sprites` в проекте. Выполняется один раз при заведении проекта и при смене мажорной версии пакета.
|
||||||
|
|
||||||
|
Что такое спрайты, как с ними работать и как управлять цветом — [Использование](/docs/applied/svg-sprites/usage).
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
- Node.js 18+
|
||||||
|
- React 18+
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
1. Установить пакет:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @gromlab/svg-sprites
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Создать `svg-sprites.config.ts` в корне проекта (см. «Стандартный конфиг»).
|
||||||
|
|
||||||
|
3. Создать папку входа для SVG-файлов в слое `shared`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p src/shared/sprites/icons
|
||||||
|
```
|
||||||
|
|
||||||
|
Источники спрайтов живут в `src/shared/sprites/<group>/` — это слой `shared` SLM-архитектуры (см. [Структура проекта](/docs/applied/project-structure), [Архитектура](/docs/basics/architecture/)). В `src/` посторонних каталогов вне слоёв не заводим.
|
||||||
|
|
||||||
|
4. Добавить скрипты в `package.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"sprite": "svg-sprites",
|
||||||
|
"predev": "svg-sprites",
|
||||||
|
"prebuild": "svg-sprites"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Хуки `predev` и `prebuild` гарантируют, что спрайты и типы всегда актуальны перед запуском и сборкой.
|
||||||
|
|
||||||
|
5. Добавить сгенерированные артефакты в `.gitignore`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
# Сгенерированные спрайты и React-компонент
|
||||||
|
/public/sprites/
|
||||||
|
/src/ui/svg-sprite/
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Выполнить первую генерацию:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run sprite
|
||||||
|
```
|
||||||
|
|
||||||
|
## Стандартный конфиг
|
||||||
|
|
||||||
|
Файл `svg-sprites.config.ts` в корне проекта. Это канон — отклонения только по явной причине.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// svg-sprites.config.ts
|
||||||
|
import { defineConfig } from '@gromlab/svg-sprites'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
output: 'public/sprites',
|
||||||
|
publicPath: '/sprites',
|
||||||
|
react: 'src/ui/svg-sprite',
|
||||||
|
sprites: [
|
||||||
|
{ name: 'icons', input: 'src/shared/sprites/icons' },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Фиксированные значения
|
||||||
|
|
||||||
|
| Опция | Значение | Почему так |
|
||||||
|
|-------|----------|------------|
|
||||||
|
| `output` | `public/sprites` | Единая папка статики Next.js |
|
||||||
|
| `publicPath` | `/sprites` | URL-путь без `public/` (Next.js раздаёт `public/` как `/`) |
|
||||||
|
| `react` | `src/ui/svg-sprite` | Слой `ui/` из архитектуры проекта (→ [Архитектура](/docs/basics/architecture/)) |
|
||||||
|
| `sprites[0].name` | `icons` | Основной спрайт всегда называется `icons` |
|
||||||
|
|
||||||
|
### Трансформации
|
||||||
|
|
||||||
|
Все значения по умолчанию оставлять включёнными:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
transform: {
|
||||||
|
removeSize: true,
|
||||||
|
replaceColors: true,
|
||||||
|
addTransition: true,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Явно прописывать блок `transform` не нужно — пакет применяет эти значения по умолчанию.
|
||||||
|
|
||||||
|
Отключать `replaceColors` — только для отдельного спрайта с фиксированной палитрой (например, брендовые логотипы). Делать это на уровне спрайта, не глобально.
|
||||||
|
|
||||||
|
### Режим
|
||||||
|
|
||||||
|
По умолчанию `mode: 'stack'` — не указывать явно. Переход на `symbol` требует обоснования: превью и примеры в пакете оптимизированы под `stack`.
|
||||||
55
docs/docs/applied/svg-sprites/usage.md
Normal file
55
docs/docs/applied/svg-sprites/usage.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
title: Использование
|
||||||
|
keywords: [svg, спрайт, sprite, иконка, icon, SvgSprite, превью, preview, цвет, color]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Использование
|
||||||
|
|
||||||
|
Работа с SVG-иконками через сгенерированный компонент `<SvgSprite/>`. Установка пакета — [Установка и настройка](/docs/applied/svg-sprites/setup).
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
1. **Положить SVG в папку спрайта:**
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/shared/sprites/icons/new-icon.svg
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Импортировать компонент.** Компонент `<SvgSprite/>` генерируется пакетом вместе с типами имён иконок — автодополнение работает без ручных описаний:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { SvgSprite } from 'ui/svg-sprite'
|
||||||
|
|
||||||
|
<SvgSprite icon="new-icon" />
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Посмотреть и пощупать иконку — в превью.** Пакет генерирует HTML-превью рядом со спрайтом (`public/sprites/icons.preview.html`). Там виден набор иконок, имена и поведение цвета.
|
||||||
|
|
||||||
|
## Управление цветом
|
||||||
|
|
||||||
|
При сборке цвета в SVG заменяются на CSS-переменные `--icon-color-N`. Управление — через обычный CSS родителя.
|
||||||
|
|
||||||
|
**Моно-иконка** наследует `color` родителя (`--icon-color-1` по умолчанию `currentColor`):
|
||||||
|
|
||||||
|
```css
|
||||||
|
.button {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Точечное переопределение** — через переменную:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.icon-danger {
|
||||||
|
--icon-color-1: var(--color-danger);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Мульти-иконка** — переменные задаются явно, порядок виден в превью:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.folder {
|
||||||
|
--icon-color-1: var(--color-folder-bg);
|
||||||
|
--icon-color-2: var(--color-folder-accent);
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: Архитектура
|
title: Архитектура
|
||||||
description: "Раздел описывает архитектуру проекта: из каких слоёв состоит приложение, как организован код внутри слоёв и какие правила управляют зависимостями."
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# SLM Design
|
# SLM Design
|
||||||
@@ -4,7 +4,7 @@ title: Слои
|
|||||||
|
|
||||||
# Слои
|
# Слои
|
||||||
|
|
||||||
Раздел описывает слои SLM: что такое слой, какие бывают, как между ними направлены зависимости и какие правила действуют на каждом.
|
Слои SLM: назначение, классификация, направление зависимостей, правила.
|
||||||
|
|
||||||
## Определение
|
## Определение
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ title: Модули
|
|||||||
|
|
||||||
# Модули
|
# Модули
|
||||||
|
|
||||||
Раздел описывает модули SLM: что такое модуль, из чего он состоит и как взаимодействует с остальным кодом.
|
Модули SLM: состав, границы, взаимодействие с остальным кодом.
|
||||||
|
|
||||||
## Определение
|
## Определение
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ auth/
|
|||||||
└── index.ts # публичный API
|
└── index.ts # публичный API
|
||||||
```
|
```
|
||||||
|
|
||||||
Подробное описание каждого сегмента — в разделе [Сегменты](/ru/basics/architecture/reference/segments).
|
Подробное описание каждого сегмента — в разделе [Сегменты](/docs/basics/architecture/reference/segments).
|
||||||
|
|
||||||
## Публичный API
|
## Публичный API
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ title: Сегменты
|
|||||||
|
|
||||||
# Сегменты
|
# Сегменты
|
||||||
|
|
||||||
Раздел описывает сегменты SLM: что такое сегмент, какие бывают и что в каждом из них лежит.
|
Сегменты SLM: типы, назначение, что лежит внутри каждого.
|
||||||
|
|
||||||
## Определение
|
## Определение
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ title: Стиль кода
|
|||||||
|
|
||||||
# Стиль кода
|
# Стиль кода
|
||||||
|
|
||||||
Раздел описывает единые правила оформления кода: отступы, переносы, кавычки, порядок импортов и базовую читаемость.
|
Единые правила оформления кода: форматирование, импорты, читаемость.
|
||||||
|
|
||||||
## Отступы
|
## Отступы
|
||||||
|
|
||||||
@@ -4,8 +4,7 @@ title: Документирование
|
|||||||
|
|
||||||
# Документирование
|
# Документирование
|
||||||
|
|
||||||
Этот раздел описывает правила документирования кода: когда и как писать
|
Правила документирования кода: что и когда документировать через JSDoc.
|
||||||
комментарии к компонентам, функциям, типам и интерфейсам.
|
|
||||||
|
|
||||||
## Общие правила
|
## Общие правила
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ title: Именование
|
|||||||
|
|
||||||
# Именование
|
# Именование
|
||||||
|
|
||||||
Этот раздел описывает соглашения об именовании в проекте. Единые правила делают код предсказуемым и упрощают навигацию по проекту.
|
Соглашения об именовании в коде: что и как называть.
|
||||||
|
|
||||||
## Базовые правила
|
## Базовые правила
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ title: Технологии и библиотеки
|
|||||||
|
|
||||||
# Технологии и библиотеки
|
# Технологии и библиотеки
|
||||||
|
|
||||||
Этот раздел описывает базовый стек технологий и библиотек, принятый в проекте.
|
Базовый стек проекта по областям: UI, архитектура, данные, состояние, локализация, тестирование, стили, генерация кода.
|
||||||
|
|
||||||
## Что используем
|
## Что используем
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ title: Технологии и библиотеки
|
|||||||
- `Next.js` — для продуктовых сайтов.
|
- `Next.js` — для продуктовых сайтов.
|
||||||
|
|
||||||
### Архитектура
|
### Архитектура
|
||||||
- `SLM Design` — собственная модульная архитектура проекта. Подробнее в разделе [Архитектура](/ru/basics/architecture/).
|
- `SLM Design` — собственная модульная архитектура проекта. Подробнее в разделе [Архитектура](/docs/basics/architecture/).
|
||||||
|
|
||||||
### UI компоненты
|
### UI компоненты
|
||||||
- `Mantine UI` — базовые UI-компоненты.
|
- `Mantine UI` — базовые UI-компоненты.
|
||||||
@@ -4,7 +4,7 @@ title: Типизация
|
|||||||
|
|
||||||
# Типизация
|
# Типизация
|
||||||
|
|
||||||
Этот раздел описывает правила типизации: как типизировать компоненты, функции и работу с `any`/`unknown`.
|
Правила типизации в TypeScript: общие принципы и работа с динамическими типами.
|
||||||
|
|
||||||
## Общие правила
|
## Общие правила
|
||||||
|
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
# NextJS Style Guide
|
# NextJS Style Guide
|
||||||
|
|
||||||
Правила и стандарты разработки на NextJS и TypeScript: архитектура, типизация, стили, компоненты, API и инфраструктурные разделы.
|
Соглашения по разработке Next.js проектов: архитектура и слои приложения, структура кода, организация модулей, стилизация, типизация и инфраструктура.
|
||||||
|
|
||||||
## Для ассистентов
|
## Использование
|
||||||
|
|
||||||
Карта документации со ссылками на все разделы (формат [llmstxt.org](https://llmstxt.org)):
|
**Для AI-агентов:**
|
||||||
https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/llms.txt
|
|
||||||
|
- [llms.txt](/llms.txt) — Карта разделов, оглавление со ссылками на разделы.
|
||||||
|
- [llms-full.txt](/llms-full.txt) — Вся документация одним файлом.
|
||||||
|
|
||||||
|
**Для проекта:**
|
||||||
|
|
||||||
|
- [nextjs-style-guide.zip](/nextjs-style-guide.zip) — Набор Markdown-файлов для распаковки в `./ai/nextjs-style-guide/` или другую папку проекта.
|
||||||
|
|
||||||
## Структура документации
|
## Структура документации
|
||||||
|
|
||||||
@@ -59,3 +65,5 @@ https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/llms.txt
|
|||||||
| Шрифты | _(не заполнен)_ |
|
| Шрифты | _(не заполнен)_ |
|
||||||
| Локализация | _(не заполнен)_ |
|
| Локализация | _(не заполнен)_ |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -28,4 +28,4 @@ title: Генерация кода
|
|||||||
- Повторяющаяся структура появляется больше одного раза.
|
- Повторяющаяся структура появляется больше одного раза.
|
||||||
- Существующий шаблон не покрывает нужный тип модуля.
|
- Существующий шаблон не покрывает нужный тип модуля.
|
||||||
|
|
||||||
Инструменты и синтаксис шаблонов — [Шаблоны и генерация кода](/ru/applied/templates-generation).
|
Инструменты и синтаксис шаблонов — [Шаблоны и генерация кода](/docs/applied/templates-generation).
|
||||||
@@ -12,11 +12,11 @@ title: Добавление UI-модуля
|
|||||||
|
|
||||||
## Порядок действий
|
## Порядок действий
|
||||||
|
|
||||||
1. [Сгенерировать](/ru/applied/templates-generation) модуль из соответствующего шаблона в целевой слой.
|
1. [Сгенерировать](/docs/applied/templates-generation) модуль из соответствующего шаблона в целевой слой.
|
||||||
2. Заполнить модуль логикой и стилями.
|
2. Заполнить модуль логикой и стилями.
|
||||||
|
|
||||||
## Дочерние компоненты
|
## Дочерние компоненты
|
||||||
|
|
||||||
Если модулю нужны внутренние подкомпоненты — [генерировать](/ru/applied/templates-generation) их из шаблона `component` в папку `ui/` внутри родительского модуля. Дочерние компоненты не экспортируются через `index.ts` родителя.
|
Если модулю нужны внутренние подкомпоненты — [генерировать](/docs/applied/templates-generation) их из шаблона `component` в папку `ui/` внутри родительского модуля. Дочерние компоненты не экспортируются через `index.ts` родителя.
|
||||||
|
|
||||||
Правила написания компонентов — [Компоненты](/ru/applied/components).
|
Правила написания компонентов — [Компоненты](/docs/applied/components).
|
||||||
@@ -12,7 +12,7 @@ title: Добавление страницы
|
|||||||
|
|
||||||
## Порядок действий
|
## Порядок действий
|
||||||
|
|
||||||
1. [Сгенерировать](/ru/applied/templates-generation) экран из шаблона `screen` в папку `src/screens/`.
|
1. [Сгенерировать](/docs/applied/templates-generation) экран из шаблона `screen` в папку `src/screens/`.
|
||||||
|
|
||||||
2. Заполнить экран логикой и стилями.
|
2. Заполнить экран логикой и стилями.
|
||||||
|
|
||||||
@@ -20,8 +20,8 @@ title: Добавление страницы
|
|||||||
|
|
||||||
## Правила
|
## Правила
|
||||||
|
|
||||||
- Ручное создание файловой структуры экрана запрещено — только [генерация](/ru/applied/templates-generation) из шаблона.
|
- Ручное создание файловой структуры экрана запрещено — только [генерация](/docs/applied/templates-generation) из шаблона.
|
||||||
- Логика, стили и зависимости размещаются в экране, не в `page.tsx`.
|
- Логика, стили и зависимости размещаются в экране, не в `page.tsx`.
|
||||||
- Каждая страница содержит `metadata` с `title` и `description`.
|
- Каждая страница содержит `metadata` с `title` и `description`.
|
||||||
|
|
||||||
Примеры `page.tsx` и `metadata` — [Page-level компоненты](/ru/applied/page-level).
|
Примеры `page.tsx` и `metadata` — [Page-level компоненты](/docs/applied/page-level).
|
||||||
@@ -10,7 +10,7 @@ title: Начало работы
|
|||||||
|
|
||||||
**Next.js** (App Router), **Mantine**, **Zustand**, **SLM Design**.
|
**Next.js** (App Router), **Mantine**, **Zustand**, **SLM Design**.
|
||||||
|
|
||||||
Подробнее — [Технологии и библиотеки](/ru/basics/tech-stack).
|
Подробнее — [Технологии и библиотеки](/docs/basics/tech-stack).
|
||||||
|
|
||||||
## Ключевые особенности
|
## Ключевые особенности
|
||||||
|
|
||||||
@@ -19,4 +19,4 @@ title: Начало работы
|
|||||||
|
|
||||||
## Настройка окружения
|
## Настройка окружения
|
||||||
|
|
||||||
Открыть проект в VS Code и установить рекомендуемые расширения — редактор предложит это автоматически. Подробнее — [Настройка VS Code](/ru/applied/vscode).
|
Открыть проект в VS Code и установить рекомендуемые расширения — редактор предложит это автоматически. Подробнее — [Настройка VS Code](/docs/applied/vscode).
|
||||||
@@ -20,4 +20,4 @@ title: Стилизация
|
|||||||
- **Магические значения** — произвольные цвета, отступы и скругления запрещены, использовать токены.
|
- **Магические значения** — произвольные цвета, отступы и скругления запрещены, использовать токены.
|
||||||
- **Глобальные стили** вне `app/styles/` запрещены.
|
- **Глобальные стили** вне `app/styles/` запрещены.
|
||||||
|
|
||||||
Правила написания CSS, вложенность, медиа-запросы и токены — [Стили](/ru/applied/styles).
|
Правила написания CSS, вложенность, медиа-запросы и токены — [Стили: использование](/docs/applied/styles/usage).
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: API
|
|
||||||
---
|
|
||||||
|
|
||||||
# API
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Components
|
|
||||||
---
|
|
||||||
|
|
||||||
# Components
|
|
||||||
|
|
||||||
Rules for creating UI components across all FSD layers.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Fonts
|
|
||||||
---
|
|
||||||
|
|
||||||
# Fonts
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Hooks
|
|
||||||
---
|
|
||||||
|
|
||||||
# Hooks
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Images
|
|
||||||
---
|
|
||||||
|
|
||||||
# Images
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Localization
|
|
||||||
---
|
|
||||||
|
|
||||||
# Localization
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Page-level Components
|
|
||||||
---
|
|
||||||
|
|
||||||
# Page-level Components
|
|
||||||
|
|
||||||
Next.js App Router special files used by the framework by convention: `layout.tsx`, `page.tsx`, `loading.tsx`, `error.tsx`, `not-found.tsx`, `template.tsx`.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Project Structure
|
|
||||||
---
|
|
||||||
|
|
||||||
# Project Structure
|
|
||||||
|
|
||||||
Base project structure and principles of module organization at folder and file level.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Stores
|
|
||||||
---
|
|
||||||
|
|
||||||
# Stores
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Styles
|
|
||||||
---
|
|
||||||
|
|
||||||
# Styles
|
|
||||||
|
|
||||||
CSS writing rules: PostCSS Modules, nesting, media queries, variables, formatting.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: SVG Sprites
|
|
||||||
---
|
|
||||||
|
|
||||||
# SVG Sprites
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Templates & Code Generation
|
|
||||||
---
|
|
||||||
|
|
||||||
# Templates & Code Generation
|
|
||||||
|
|
||||||
Template tools, syntax, and examples for code generation.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: Video
|
|
||||||
---
|
|
||||||
|
|
||||||
# Video
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Architecture
|
|
||||||
---
|
|
||||||
|
|
||||||
# Architecture
|
|
||||||
|
|
||||||
Architecture based on FSD (Feature-Sliced Design) and strict module boundaries.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Code Style
|
|
||||||
---
|
|
||||||
|
|
||||||
# Code Style
|
|
||||||
|
|
||||||
Unified code formatting rules: indentation, line breaks, quotes, import order, and readability.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Documentation
|
|
||||||
---
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
|
|
||||||
Documentation should help understand the purpose of an entity, not duplicate its types or obvious details.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Naming
|
|
||||||
---
|
|
||||||
|
|
||||||
# Naming
|
|
||||||
|
|
||||||
Naming should be predictable, concise, and reflect the meaning of the entity.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Tech Stack
|
|
||||||
---
|
|
||||||
|
|
||||||
# Tech Stack
|
|
||||||
|
|
||||||
Base technology stack and libraries used in projects.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Typing
|
|
||||||
---
|
|
||||||
|
|
||||||
# Typing
|
|
||||||
|
|
||||||
Typing is required for all public interfaces, functions, and components.
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# NextJS Style Guide
|
|
||||||
|
|
||||||
Rules and standards for NextJS and TypeScript development: architecture, typing, styles, components, API, and infrastructure.
|
|
||||||
|
|
||||||
## Documentation Structure
|
|
||||||
|
|
||||||
### Processes
|
|
||||||
|
|
||||||
**What to do** in a specific situation — step-by-step instructions.
|
|
||||||
|
|
||||||
| Section | Answers the question |
|
|
||||||
|---------|---------------------|
|
|
||||||
| Getting Started | What tools to install before starting development? |
|
|
||||||
| Creating an App | How to create a new project, where to get a template? |
|
|
||||||
| Creating Pages | How to add a page: routing and screen? |
|
|
||||||
| Creating Components | How to generate components using templates? |
|
|
||||||
| Styling | What to use: Mantine, tokens, or PostCSS? |
|
|
||||||
| Data Fetching | How to fetch data: SWR, codegen, sockets? |
|
|
||||||
| State Management | When and how to create a store (Zustand)? |
|
|
||||||
| Localization | How to add translations and work with i18next? |
|
|
||||||
|
|
||||||
### Basic Rules
|
|
||||||
|
|
||||||
**What the code should look like** — standards not tied to a specific technology.
|
|
||||||
|
|
||||||
| Section | Answers the question |
|
|
||||||
|---------|---------------------|
|
|
||||||
| Tech Stack | What stack do we use? |
|
|
||||||
| Architecture | How are FSD layers, dependencies, and public API structured? |
|
|
||||||
| Code Style | How to format code: indentation, quotes, imports, early return? |
|
|
||||||
| Naming | How to name files, variables, components, hooks? |
|
|
||||||
| Documentation | How to write JSDoc: what to document and what not? |
|
|
||||||
| Typing | How to type: type vs interface, any/unknown? |
|
|
||||||
|
|
||||||
### Applied Sections
|
|
||||||
|
|
||||||
**How a specific area works** — rules, structure, and code examples for specific technologies and tools.
|
|
||||||
|
|
||||||
| Section | Answers the question |
|
|
||||||
|---------|---------------------|
|
|
||||||
| Project Structure | How are folders and files organized by FSD? |
|
|
||||||
| Components | How is a component structured: files, props, clsx? |
|
|
||||||
| Page-level Components | How to define layout, page, loading, error, not-found? |
|
|
||||||
| Templates & Code Generation | How do templates work: syntax, variables, modifiers? |
|
|
||||||
| Styles | How to write CSS: PostCSS Modules, nesting, media, tokens? |
|
|
||||||
| Images | _(not filled)_ |
|
|
||||||
| SVG Sprites | _(not filled)_ |
|
|
||||||
| Video | _(not filled)_ |
|
|
||||||
| API | _(not filled)_ |
|
|
||||||
| Stores | _(not filled)_ |
|
|
||||||
| Hooks | _(not filled)_ |
|
|
||||||
| Fonts | _(not filled)_ |
|
|
||||||
| Localization | _(not filled)_ |
|
|
||||||
|
|
||||||
## For Assistants
|
|
||||||
|
|
||||||
Documentation map with links to all sections ([llmstxt.org](https://llmstxt.org) format):
|
|
||||||
https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/en/llms.txt
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Creating an App
|
|
||||||
---
|
|
||||||
|
|
||||||
# Creating an App
|
|
||||||
|
|
||||||
How to create a new application: choosing a project template and initialization.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Creating Components
|
|
||||||
---
|
|
||||||
|
|
||||||
# Creating Components
|
|
||||||
|
|
||||||
Generating components using templates, working with child components.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Creating Pages
|
|
||||||
---
|
|
||||||
|
|
||||||
# Creating Pages
|
|
||||||
|
|
||||||
Page creation pattern: routing (page.tsx) and screen.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Data Fetching
|
|
||||||
---
|
|
||||||
|
|
||||||
# Data Fetching
|
|
||||||
|
|
||||||
How to fetch data: SWR, API client codegen, sockets.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Getting Started
|
|
||||||
---
|
|
||||||
|
|
||||||
# Getting Started
|
|
||||||
|
|
||||||
Setting up the environment and installing tools before starting development.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Localization
|
|
||||||
---
|
|
||||||
|
|
||||||
# Localization
|
|
||||||
|
|
||||||
How to add translations and work with i18next.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: State Management
|
|
||||||
---
|
|
||||||
|
|
||||||
# State Management
|
|
||||||
|
|
||||||
When and how to create a store (Zustand), what to store locally vs globally.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Styling
|
|
||||||
---
|
|
||||||
|
|
||||||
# Styling
|
|
||||||
|
|
||||||
Styling tools priority and rules for their application.
|
|
||||||
264
docs/index.md
264
docs/index.md
@@ -3,96 +3,21 @@ layout: false
|
|||||||
---
|
---
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
|
|
||||||
const STORAGE_KEY = 'nsg-landing-lang'
|
|
||||||
const THEME_KEY = 'vitepress-theme-appearance'
|
const THEME_KEY = 'vitepress-theme-appearance'
|
||||||
|
|
||||||
// __BUILD_VERSION__ подставляется Vite-define из ENV `BUILD_VERSION`
|
// __BUILD_VERSION__ подставляется Vite-define из ENV `BUILD_VERSION`
|
||||||
// (см. .vitepress/config.ts). В dev и build всегда определена.
|
// (см. .vitepress/config.ts). В dev и build всегда определена.
|
||||||
const buildVersion = __BUILD_VERSION__
|
const buildVersion = __BUILD_VERSION__
|
||||||
|
|
||||||
const dict = {
|
|
||||||
ru: {
|
|
||||||
tagline: 'Готовые соглашения по архитектуре, коду, компонентам и инфраструктуре для Next.js + TypeScript-проектов — чтобы команда писала одинаково, а новые разработчики включались в проект быстрее.',
|
|
||||||
langLabel: 'Язык',
|
|
||||||
themeLabel: 'Тема',
|
|
||||||
themes: { auto: 'Авто', light: 'Светлая', dark: 'Тёмная' },
|
|
||||||
cards: {
|
|
||||||
docs: {
|
|
||||||
title: 'Документация',
|
|
||||||
desc: 'Все разделы: процессы разработки, базовые правила, прикладные руководства.',
|
|
||||||
href: './ru/',
|
|
||||||
cta: 'Открыть',
|
|
||||||
},
|
|
||||||
ai: {
|
|
||||||
title: 'Ассистенту',
|
|
||||||
desc: 'Карта документации в формате llms.txt для AI-агентов.',
|
|
||||||
href: './ru/llms.txt',
|
|
||||||
cta: 'Открыть',
|
|
||||||
},
|
|
||||||
zip: {
|
|
||||||
title: 'Скачать правила',
|
|
||||||
desc: 'Архив всех Markdown-файлов одним ZIP.',
|
|
||||||
href: './nextjs-style-guide-ru.zip',
|
|
||||||
cta: 'Скачать',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
en: {
|
|
||||||
tagline: 'Ready-made standards for architecture, code, components, and infrastructure in Next.js + TypeScript projects — so your team writes consistently and new developers ramp up faster.',
|
|
||||||
langLabel: 'Language',
|
|
||||||
themeLabel: 'Theme',
|
|
||||||
themes: { auto: 'Auto', light: 'Light', dark: 'Dark' },
|
|
||||||
cards: {
|
|
||||||
docs: {
|
|
||||||
title: 'Documentation',
|
|
||||||
desc: 'All sections: development processes, basic rules, applied guides.',
|
|
||||||
href: '#',
|
|
||||||
cta: 'Open',
|
|
||||||
badge: 'in development',
|
|
||||||
},
|
|
||||||
ai: {
|
|
||||||
title: 'For Assistant',
|
|
||||||
desc: 'Documentation map in llms.txt format for AI agents.',
|
|
||||||
href: '#',
|
|
||||||
cta: 'Open',
|
|
||||||
badge: 'in development',
|
|
||||||
},
|
|
||||||
zip: {
|
|
||||||
title: 'Download rules',
|
|
||||||
desc: 'Archive of all Markdown files and llms.txt in a single ZIP.',
|
|
||||||
href: '#',
|
|
||||||
cta: 'Download',
|
|
||||||
badge: 'soon',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const lang = ref('ru')
|
|
||||||
const theme = ref('auto')
|
const theme = ref('auto')
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const savedLang = localStorage.getItem(STORAGE_KEY)
|
|
||||||
if (savedLang === 'ru' || savedLang === 'en') {
|
|
||||||
lang.value = savedLang
|
|
||||||
} else {
|
|
||||||
const nav = (navigator.language || 'ru').toLowerCase()
|
|
||||||
lang.value = nav.startsWith('ru') ? 'ru' : 'en'
|
|
||||||
}
|
|
||||||
|
|
||||||
const savedTheme = localStorage.getItem(THEME_KEY)
|
const savedTheme = localStorage.getItem(THEME_KEY)
|
||||||
theme.value = savedTheme === 'dark' || savedTheme === 'light' ? savedTheme : 'auto'
|
theme.value = savedTheme === 'dark' || savedTheme === 'light' ? savedTheme : 'auto'
|
||||||
})
|
})
|
||||||
|
|
||||||
const t = computed(() => dict[lang.value])
|
|
||||||
|
|
||||||
function setLang(value) {
|
|
||||||
lang.value = value
|
|
||||||
localStorage.setItem(STORAGE_KEY, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTheme(value) {
|
function setTheme(value) {
|
||||||
theme.value = value
|
theme.value = value
|
||||||
if (value === 'auto') {
|
if (value === 'auto') {
|
||||||
@@ -118,31 +43,26 @@ function toggleTheme(value) {
|
|||||||
<section class="landing__hero">
|
<section class="landing__hero">
|
||||||
<h1 class="landing__title">NextJS Style Guide</h1>
|
<h1 class="landing__title">NextJS Style Guide</h1>
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<p class="landing__tagline">{{ t.tagline }}</p>
|
<p class="landing__tagline">Соглашения по разработке Next.js проектов: архитектура и слои приложения, структура кода, организация модулей, стилизация, типизация и инфраструктура.</p>
|
||||||
<div class="landing__controls">
|
<div class="landing__controls">
|
||||||
<div class="seg" role="group" :aria-label="t.langLabel">
|
<a
|
||||||
<button
|
class="landing__repo"
|
||||||
type="button"
|
href="https://gromlab.ru/docs/nextjs-style-guide"
|
||||||
class="seg__btn"
|
target="_blank"
|
||||||
:class="{ 'seg__btn--active': lang === 'ru' }"
|
rel="noopener"
|
||||||
:aria-pressed="lang === 'ru'"
|
>
|
||||||
@click="setLang('ru')"
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||||
>Русский</button>
|
<path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.83-.26.83-.57L9 21.07c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.08-.74.09-.73.09-.73 1.2.09 1.83 1.24 1.83 1.24 1.08 1.83 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.14-.3-.54-1.52.1-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.28-1.55 3.29-1.23 3.29-1.23.64 1.66.24 2.88.12 3.18a4.65 4.65 0 0 1 1.23 3.22c0 4.61-2.8 5.63-5.48 5.92.42.36.81 1.1.81 2.22l-.01 3.29c0 .31.2.69.82.57A12 12 0 0 0 12 .3Z"/>
|
||||||
<button
|
</svg>
|
||||||
type="button"
|
<span>Репозиторий</span>
|
||||||
class="seg__btn"
|
</a>
|
||||||
:class="{ 'seg__btn--active': lang === 'en' }"
|
<div class="seg seg--icons" role="group" aria-label="Тема">
|
||||||
:aria-pressed="lang === 'en'"
|
|
||||||
@click="setLang('en')"
|
|
||||||
>English</button>
|
|
||||||
</div>
|
|
||||||
<div class="seg seg--icons" role="group" :aria-label="t.themeLabel">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="seg__btn"
|
class="seg__btn"
|
||||||
:class="{ 'seg__btn--active': theme === 'light' }"
|
:class="{ 'seg__btn--active': theme === 'light' }"
|
||||||
:aria-pressed="theme === 'light'"
|
:aria-pressed="theme === 'light'"
|
||||||
:title="t.themes.light"
|
title="Светлая"
|
||||||
@click="toggleTheme('light')"
|
@click="toggleTheme('light')"
|
||||||
>
|
>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>
|
||||||
@@ -152,7 +72,7 @@ function toggleTheme(value) {
|
|||||||
class="seg__btn"
|
class="seg__btn"
|
||||||
:class="{ 'seg__btn--active': theme === 'dark' }"
|
:class="{ 'seg__btn--active': theme === 'dark' }"
|
||||||
:aria-pressed="theme === 'dark'"
|
:aria-pressed="theme === 'dark'"
|
||||||
:title="t.themes.dark"
|
title="Тёмная"
|
||||||
@click="toggleTheme('dark')"
|
@click="toggleTheme('dark')"
|
||||||
>
|
>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
|
||||||
@@ -162,25 +82,26 @@ function toggleTheme(value) {
|
|||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<ClientOnly>
|
|
||||||
<section class="landing__cards">
|
<section class="landing__cards">
|
||||||
<a
|
<a class="landing__card" href="./docs/">
|
||||||
v-for="key in ['docs', 'ai', 'zip']"
|
<h3>Документация</h3>
|
||||||
:key="key"
|
<p>Все разделы: процессы разработки, базовые правила, прикладные руководства.</p>
|
||||||
class="landing__card"
|
<span class="landing__cta">Открыть →</span>
|
||||||
:class="{ 'landing__card--soon': t.cards[key].badge }"
|
</a>
|
||||||
:href="t.cards[key].href"
|
<div class="landing__card landing__card--multi">
|
||||||
:aria-disabled="t.cards[key].badge ? 'true' : null"
|
<h3>Ассистенту</h3>
|
||||||
>
|
<p>Карта документации в формате llms.txt для AI-агентов.</p>
|
||||||
<h3>
|
<div class="landing__buttons">
|
||||||
{{ t.cards[key].title }}
|
<a class="landing__button" href="./llms.txt" target="_blank" rel="noopener">llms.txt</a>
|
||||||
<span v-if="t.cards[key].badge" class="landing__badge">{{ t.cards[key].badge }}</span>
|
<a class="landing__button" href="./llms-full.txt" target="_blank" rel="noopener">llms-full.txt</a>
|
||||||
</h3>
|
</div>
|
||||||
<p>{{ t.cards[key].desc }}</p>
|
</div>
|
||||||
<span class="landing__cta">{{ t.cards[key].cta }} →</span>
|
<a class="landing__card" href="./nextjs-style-guide.zip">
|
||||||
|
<h3>Скачать правила</h3>
|
||||||
|
<p>Архив всех Markdown-файлов одним ZIP.</p>
|
||||||
|
<span class="landing__cta">Скачать →</span>
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
||||||
</ClientOnly>
|
|
||||||
|
|
||||||
<p class="landing__version">v{{ buildVersion }}</p>
|
<p class="landing__version">v{{ buildVersion }}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -207,6 +128,35 @@ function toggleTheme(value) {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.landing__controls > * {
|
||||||
|
height: 36px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing__repo {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 0 14px;
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--vp-c-bg-soft);
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing__repo:hover {
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
border-color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing__repo svg {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.seg {
|
.seg {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
@@ -305,12 +255,6 @@ function toggleTheme(value) {
|
|||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.landing__card--soon {
|
|
||||||
opacity: 0.55;
|
|
||||||
cursor: not-allowed;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing__card h3 {
|
.landing__card h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@@ -332,6 +276,33 @@ function toggleTheme(value) {
|
|||||||
color: var(--vp-c-brand-1);
|
color: var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.landing__buttons {
|
||||||
|
margin-top: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing__button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: var(--vp-font-family-mono, monospace);
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
background: var(--vp-c-bg);
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: border-color 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing__button:hover {
|
||||||
|
border-color: var(--vp-c-brand-1);
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
.landing__version {
|
.landing__version {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 24px 0 0;
|
margin: 24px 0 0;
|
||||||
@@ -340,33 +311,58 @@ function toggleTheme(value) {
|
|||||||
font-family: var(--vp-font-family-mono, monospace);
|
font-family: var(--vp-font-family-mono, monospace);
|
||||||
}
|
}
|
||||||
|
|
||||||
.landing__badge {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 8px;
|
|
||||||
padding: 2px 8px;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 500;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
border-radius: 999px;
|
|
||||||
background: var(--vp-c-bg-mute);
|
|
||||||
color: var(--vp-c-text-3);
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.landing {
|
.landing {
|
||||||
padding: 16px 16px 48px;
|
padding: 48px 20px 56px;
|
||||||
gap: 32px;
|
gap: 40px;
|
||||||
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
.landing__title {
|
.landing__title {
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
}
|
}
|
||||||
|
.landing__tagline {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
.landing__cards {
|
.landing__cards {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
.landing__controls {
|
.landing__controls {
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
margin-top: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.landing {
|
||||||
|
padding: 44px 16px 48px;
|
||||||
|
gap: 36px;
|
||||||
|
}
|
||||||
|
.landing__title {
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
.landing__tagline {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.landing__controls {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
/* Репозиторий — только иконка, без текста, чтобы все контролы влезли в строку */
|
||||||
|
.landing__repo {
|
||||||
|
width: 36px;
|
||||||
|
padding: 0;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.landing__repo span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.seg__btn {
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.landing__card {
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
title: SVG-спрайты
|
|
||||||
---
|
|
||||||
|
|
||||||
# SVG-спрайты
|
|
||||||
365
generate-llms.ts
365
generate-llms.ts
@@ -11,7 +11,8 @@ const BUILD_DATE = new Date().toISOString();
|
|||||||
/** Корневая папка для генерируемой статики (попадает в build dist). */
|
/** Корневая папка для генерируемой статики (попадает в build dist). */
|
||||||
const PUBLIC_DIR = 'docs/public';
|
const PUBLIC_DIR = 'docs/public';
|
||||||
|
|
||||||
type Lang = 'ru' | 'en';
|
/** Префикс URL документации. Соответствует структуре `docs/docs/...`. */
|
||||||
|
const DOC_PREFIX = '/docs/';
|
||||||
|
|
||||||
interface SidebarItem {
|
interface SidebarItem {
|
||||||
text: string;
|
text: string;
|
||||||
@@ -76,13 +77,13 @@ const firstParagraphAfterH1 = (body: string): string | null => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Преобразовать sidebar `link` в относительный путь файла внутри
|
* Преобразовать sidebar `link` (например `/docs/foo`) в относительный
|
||||||
* `docs/{lang}/`. Sidebar links содержат полный префикс локали
|
* путь файла внутри `docs/docs/`. Префикс `/docs/` отрезается.
|
||||||
* (`/ru/...`, `/en/...`) — отрезаем его.
|
|
||||||
*/
|
*/
|
||||||
const linkToRel = (link: string, lang: Lang): string => {
|
const linkToRel = (link: string): string => {
|
||||||
const prefix = `/${lang}/`;
|
let rel = link.startsWith(DOC_PREFIX)
|
||||||
let rel = link.startsWith(prefix) ? link.slice(prefix.length) : link.replace(/^\//, '');
|
? link.slice(DOC_PREFIX.length)
|
||||||
|
: link.replace(/^\//, '');
|
||||||
if (rel === '' || rel.endsWith('/')) {
|
if (rel === '' || rel.endsWith('/')) {
|
||||||
rel += 'index.md';
|
rel += 'index.md';
|
||||||
} else {
|
} else {
|
||||||
@@ -91,15 +92,12 @@ const linkToRel = (link: string, lang: Lang): string => {
|
|||||||
return rel;
|
return rel;
|
||||||
};
|
};
|
||||||
|
|
||||||
const linkToFilePath = (link: string, lang: Lang): string =>
|
const linkToFilePath = (link: string): string =>
|
||||||
path.join('docs', lang, linkToRel(link, lang));
|
path.join('docs/docs', linkToRel(link));
|
||||||
|
|
||||||
/**
|
/** Абсолютный URL `.md`-копии страницы на сайте. */
|
||||||
* Абсолютный путь от корня сайта к `.md`-копии страницы.
|
const linkToSiteUrl = (link: string): string =>
|
||||||
* После build файлы лежат в `dist/{lang}/...md` (через `docs/public/`).
|
`${DOC_PREFIX}${linkToRel(link)}`;
|
||||||
*/
|
|
||||||
const linkToSiteUrl = (link: string, lang: Lang): string =>
|
|
||||||
`/${lang}/${linkToRel(link, lang)}`;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Развернуть sidebar в плоский список с сохранением группы и
|
* Развернуть sidebar в плоский список с сохранением группы и
|
||||||
@@ -153,34 +151,20 @@ const groupBySection = (entries: Entry[]): Map<string, Entry[]> => {
|
|||||||
return map;
|
return map;
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildLlms = (lang: Lang): void => {
|
interface SiteConfig {
|
||||||
const localeKey = lang;
|
|
||||||
// VitePress-конфиг типизирован как `UserConfig`, но обращаемся к
|
|
||||||
// фактически переданным значениям — сужаем тип через any.
|
|
||||||
const cfg = config as unknown as {
|
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
locales: Record<
|
themeConfig: { sidebar: SidebarItem[] };
|
||||||
string,
|
|
||||||
{
|
|
||||||
description?: string;
|
|
||||||
llmsBlockquote?: string;
|
llmsBlockquote?: string;
|
||||||
llmsContext?: string;
|
llmsContext?: string;
|
||||||
themeConfig?: { sidebar?: SidebarItem[] };
|
}
|
||||||
}
|
|
||||||
>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const locale = cfg.locales[localeKey];
|
const cfg = config as unknown as SiteConfig;
|
||||||
const sidebar = locale?.themeConfig?.sidebar;
|
|
||||||
if (!sidebar) {
|
const buildLlms = (): void => {
|
||||||
console.warn(`[${lang}] sidebar не найден в config`);
|
const sidebar = cfg.themeConfig.sidebar;
|
||||||
return;
|
const blockquote = cfg.llmsBlockquote ?? cfg.description;
|
||||||
}
|
const context = cfg.llmsContext;
|
||||||
// Для blockquote предпочитаем расширенный llms-текст; короткий
|
|
||||||
// description — fallback и используется для HTML meta-тега VitePress.
|
|
||||||
const blockquote = locale.llmsBlockquote ?? locale.description ?? cfg.description;
|
|
||||||
const context = locale.llmsContext;
|
|
||||||
|
|
||||||
const entries = flattenSidebar(sidebar);
|
const entries = flattenSidebar(sidebar);
|
||||||
const grouped = groupBySection(entries);
|
const grouped = groupBySection(entries);
|
||||||
@@ -200,19 +184,16 @@ const buildLlms = (lang: Lang): void => {
|
|||||||
lines.push('');
|
lines.push('');
|
||||||
|
|
||||||
for (const entry of items) {
|
for (const entry of items) {
|
||||||
const filePath = linkToFilePath(entry.link, lang);
|
const filePath = linkToFilePath(entry.link);
|
||||||
const url = linkToSiteUrl(entry.link, lang);
|
const url = linkToSiteUrl(entry.link);
|
||||||
|
|
||||||
// Текст ссылки берём из sidebar — он специально написан для навигации
|
|
||||||
// и точнее отражает иерархию (например "Обзор" внутри группы "Архитектура").
|
|
||||||
let description: string | null = null;
|
let description: string | null = null;
|
||||||
|
|
||||||
if (fs.existsSync(filePath)) {
|
if (fs.existsSync(filePath)) {
|
||||||
const raw = fs.readFileSync(filePath, 'utf8');
|
const raw = fs.readFileSync(filePath, 'utf8');
|
||||||
const { data, body } = parseFrontmatter(raw);
|
const { data, body } = parseFrontmatter(raw);
|
||||||
description = data.description || firstParagraphAfterH1(body);
|
description = data.description || firstParagraphAfterH1(body);
|
||||||
} else {
|
} else {
|
||||||
console.warn(`[${lang}] файл не найден: ${filePath}`);
|
console.warn(`файл не найден: ${filePath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const display = entry.prefix
|
const display = entry.prefix
|
||||||
@@ -225,40 +206,6 @@ const buildLlms = (lang: Lang): void => {
|
|||||||
lines.push('');
|
lines.push('');
|
||||||
}
|
}
|
||||||
|
|
||||||
const outDir = path.join(PUBLIC_DIR, lang);
|
|
||||||
fs.mkdirSync(outDir, { recursive: true });
|
|
||||||
const outFile = path.join(outDir, 'llms.txt');
|
|
||||||
fs.writeFileSync(outFile, lines.join('\n'), 'utf8');
|
|
||||||
console.log(`${outFile} создан`);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Корневой `/llms.txt` — роутер. По стандарту llmstxt.org это
|
|
||||||
* единственный файл в корне сайта; для двуязычного проекта он
|
|
||||||
* указывает LLM на локализованные карты документации.
|
|
||||||
*/
|
|
||||||
const buildRootIndex = (): void => {
|
|
||||||
const cfg = config as unknown as {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
locales: Record<string, { description?: string }>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ruDesc = cfg.locales.ru?.description ?? cfg.description;
|
|
||||||
const enDesc = cfg.locales.en?.description ?? cfg.description;
|
|
||||||
|
|
||||||
const lines: string[] = [
|
|
||||||
`# ${cfg.title}`,
|
|
||||||
'',
|
|
||||||
`> ${enDesc}.`,
|
|
||||||
'',
|
|
||||||
'## Documentation',
|
|
||||||
'',
|
|
||||||
`- [Русская версия (Russian)](/ru/llms.txt): ${ruDesc}.`,
|
|
||||||
'- English version: in development',
|
|
||||||
'',
|
|
||||||
];
|
|
||||||
|
|
||||||
fs.mkdirSync(PUBLIC_DIR, { recursive: true });
|
fs.mkdirSync(PUBLIC_DIR, { recursive: true });
|
||||||
const outFile = path.join(PUBLIC_DIR, 'llms.txt');
|
const outFile = path.join(PUBLIC_DIR, 'llms.txt');
|
||||||
fs.writeFileSync(outFile, lines.join('\n'), 'utf8');
|
fs.writeFileSync(outFile, lines.join('\n'), 'utf8');
|
||||||
@@ -288,41 +235,169 @@ const copyDirSync = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Скопировать все `.md`-файлы локали в `docs/public/{lang}/`,
|
* Скопировать все `.md`-файлы документации в `docs/public/docs/`,
|
||||||
* чтобы они попали в build `dist/` и были доступны по URL `/lang/path.md`.
|
* чтобы они попали в build `dist/` и были доступны по URL `/docs/path.md`.
|
||||||
*/
|
*/
|
||||||
const copyMdFiles = (lang: Lang): void => {
|
const copyMdFiles = (): void => {
|
||||||
const srcDir = path.join('docs', lang);
|
const srcDir = 'docs/docs';
|
||||||
const destDir = path.join(PUBLIC_DIR, lang);
|
const destDir = path.join(PUBLIC_DIR, 'docs');
|
||||||
if (!fs.existsSync(srcDir)) return;
|
if (!fs.existsSync(srcDir)) return;
|
||||||
|
|
||||||
const copied = copyDirSync(srcDir, destDir, (name) => name.endsWith('.md'));
|
const copied = copyDirSync(srcDir, destDir, (name) => name.endsWith('.md'));
|
||||||
console.log(`[${lang}] скопировано ${copied} .md-файлов в ${destDir}`);
|
console.log(`скопировано ${copied} .md-файлов в ${destDir}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Собрать `nextjs-style-guide-{lang}.zip` со всеми `.md` локали и `VERSION`.
|
* Преобразовать sidebar `link` в относительный путь файла внутри архива
|
||||||
* Внутри архива — единая папка `nextjs-style-guide/`.
|
* (от корня папки `nextjs-style-guide/`).
|
||||||
*
|
|
||||||
* `llms.txt` в архив не кладём: его ссылки указывают на сайт и локально
|
|
||||||
* не работают. Структура папки сама по себе является картой документации.
|
|
||||||
*/
|
*/
|
||||||
const buildZip = (lang: Lang): void => {
|
const linkToArchiveRel = (link: string): string => {
|
||||||
|
let rel = link.startsWith(DOC_PREFIX)
|
||||||
|
? link.slice(DOC_PREFIX.length)
|
||||||
|
: link.replace(/^\//, '');
|
||||||
|
if (rel === '' || rel.endsWith('/')) {
|
||||||
|
rel += 'index.md';
|
||||||
|
} else {
|
||||||
|
rel += '.md';
|
||||||
|
}
|
||||||
|
return rel;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Заменить во всех `.md` архива ссылки `[text](/docs/foo)` на относительные
|
||||||
|
* пути от расположения файла. Без этого внутренние ссылки в распакованной
|
||||||
|
* папке не работают.
|
||||||
|
*/
|
||||||
|
const transformLinksInDir = (rootDir: string): void => {
|
||||||
|
const linkRe = /\]\(\/docs\/([^)\s#]*)(#[^)]*)?\)/g;
|
||||||
|
|
||||||
|
const walk = (dir: string): void => {
|
||||||
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||||
|
const full = path.join(dir, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
walk(full);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
|
||||||
|
|
||||||
|
const content = fs.readFileSync(full, 'utf8');
|
||||||
|
const fileDir = path.dirname(full);
|
||||||
|
|
||||||
|
const updated = content.replace(linkRe, (_match, route, hash = '') => {
|
||||||
|
const targetRel = linkToArchiveRel(`${DOC_PREFIX}${route}`);
|
||||||
|
const targetAbs = path.join(rootDir, targetRel);
|
||||||
|
let rel = path.relative(fileDir, targetAbs);
|
||||||
|
if (!rel.startsWith('.')) rel = './' + rel;
|
||||||
|
return `](${rel}${hash})`;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updated !== content) {
|
||||||
|
fs.writeFileSync(full, updated, 'utf8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
walk(rootDir);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сгенерировать `README.md` — точка входа архива. Карта документации
|
||||||
|
* с относительными ссылками, описаниями из frontmatter/первого абзаца
|
||||||
|
* и метаинфо сборки.
|
||||||
|
*/
|
||||||
|
const buildArchiveReadme = (rootDir: string): void => {
|
||||||
|
const sidebar = cfg.themeConfig.sidebar;
|
||||||
|
const blockquote = cfg.llmsBlockquote ?? cfg.description ?? '';
|
||||||
|
const context = cfg.llmsContext;
|
||||||
|
|
||||||
|
const entries = flattenSidebar(sidebar).filter(
|
||||||
|
// «Главная» из sidebar — это страница раздела для веба, в архиве не нужна.
|
||||||
|
(e) => e.section !== 'Главная',
|
||||||
|
);
|
||||||
|
const grouped = groupBySection(entries);
|
||||||
|
|
||||||
|
const lines: string[] = [];
|
||||||
|
lines.push(`# ${cfg.title}`);
|
||||||
|
lines.push('');
|
||||||
|
if (blockquote) {
|
||||||
|
lines.push(`> ${blockquote}`);
|
||||||
|
lines.push('');
|
||||||
|
}
|
||||||
|
if (context) {
|
||||||
|
lines.push(context);
|
||||||
|
lines.push('');
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push('## Содержание');
|
||||||
|
lines.push('');
|
||||||
|
|
||||||
|
for (const [section, items] of grouped) {
|
||||||
|
lines.push(`### ${section}`);
|
||||||
|
lines.push('');
|
||||||
|
for (const entry of items) {
|
||||||
|
const targetRel = './' + linkToArchiveRel(entry.link);
|
||||||
|
const filePath = path.join(rootDir, linkToArchiveRel(entry.link));
|
||||||
|
|
||||||
|
let description: string | null = null;
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
const raw = fs.readFileSync(filePath, 'utf8');
|
||||||
|
const { data, body } = parseFrontmatter(raw);
|
||||||
|
description = data.description || firstParagraphAfterH1(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
const display = entry.prefix
|
||||||
|
? `${entry.prefix}: ${entry.text}`
|
||||||
|
: entry.text;
|
||||||
|
const descPart = description ? ` — ${description}` : '';
|
||||||
|
lines.push(`- [${display}](${targetRel})${descPart}`);
|
||||||
|
}
|
||||||
|
lines.push('');
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push('---');
|
||||||
|
lines.push('');
|
||||||
|
lines.push(`Версия: ${VERSION} · Сборка: ${BUILD_DATE}`);
|
||||||
|
lines.push('');
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(rootDir, 'README.md'), lines.join('\n'), 'utf8');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Собрать `nextjs-style-guide.zip`. Внутри — папка `nextjs-style-guide/`
|
||||||
|
* с `.md`-файлами, README, `llms-full.txt` и `VERSION`. Внутренние ссылки
|
||||||
|
* преобразуются в относительные.
|
||||||
|
*/
|
||||||
|
const buildZip = (): void => {
|
||||||
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nsg-'));
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nsg-'));
|
||||||
const stage = path.join(tmpRoot, 'nextjs-style-guide');
|
const stage = path.join(tmpRoot, 'nextjs-style-guide');
|
||||||
fs.mkdirSync(stage, { recursive: true });
|
fs.mkdirSync(stage, { recursive: true });
|
||||||
|
|
||||||
copyDirSync(path.join('docs', lang), stage, (name) => name.endsWith('.md'));
|
// 1. Копируем все .md в staging.
|
||||||
|
copyDirSync('docs/docs', stage, (name) => name.endsWith('.md'));
|
||||||
|
|
||||||
|
// 2. Удаляем веб-index.md — в архиве его роль выполняет README.md.
|
||||||
|
const indexPath = path.join(stage, 'index.md');
|
||||||
|
if (fs.existsSync(indexPath)) fs.unlinkSync(indexPath);
|
||||||
|
|
||||||
|
// 3. Преобразуем абсолютные ссылки `/docs/...` в относительные.
|
||||||
|
transformLinksInDir(stage);
|
||||||
|
|
||||||
|
// 4. Генерируем точку входа README.md.
|
||||||
|
buildArchiveReadme(stage);
|
||||||
|
|
||||||
|
// 5. Кладём llms-full.txt — удобно для одноразового чтения LLM.
|
||||||
|
const llmsFullSrc = path.join(PUBLIC_DIR, 'llms-full.txt');
|
||||||
|
if (fs.existsSync(llmsFullSrc)) {
|
||||||
|
fs.copyFileSync(llmsFullSrc, path.join(stage, 'llms-full.txt'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Метаинформация сборки.
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(stage, 'VERSION'),
|
path.join(stage, 'VERSION'),
|
||||||
`${VERSION}\n${BUILD_DATE}\n`,
|
`${VERSION}\n${BUILD_DATE}\n`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const outFile = path.resolve(
|
const outFile = path.resolve(PUBLIC_DIR, 'nextjs-style-guide.zip');
|
||||||
PUBLIC_DIR,
|
|
||||||
`nextjs-style-guide-${lang}.zip`,
|
|
||||||
);
|
|
||||||
fs.rmSync(outFile, { force: true });
|
fs.rmSync(outFile, { force: true });
|
||||||
|
|
||||||
execFileSync('zip', ['-rq', outFile, 'nextjs-style-guide'], {
|
execFileSync('zip', ['-rq', outFile, 'nextjs-style-guide'], {
|
||||||
@@ -333,21 +408,75 @@ const buildZip = (lang: Lang): void => {
|
|||||||
console.log(`${outFile} создан (${VERSION})`);
|
console.log(`${outFile} создан (${VERSION})`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Удалить YAML frontmatter из исходника `.md`. */
|
||||||
|
const stripFrontmatter = (content: string): string =>
|
||||||
|
content.replace(/^---\n[\s\S]*?\n---\n*/, '');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сдвинуть уровень заголовков на 1 вниз (h1→h2, h2→h3, ...).
|
||||||
|
* Игнорирует строки внутри блоков кода.
|
||||||
|
*/
|
||||||
|
const shiftHeadings = (content: string): string => {
|
||||||
|
const lines = content.split('\n');
|
||||||
|
let inCodeBlock = false;
|
||||||
|
return lines
|
||||||
|
.map((line) => {
|
||||||
|
if (line.startsWith('```')) inCodeBlock = !inCodeBlock;
|
||||||
|
if (inCodeBlock) return line;
|
||||||
|
if (/^#{1,5}\s/.test(line)) return '#' + line;
|
||||||
|
return line;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Собрать `llms-full.txt` — все страницы в одном файле.
|
||||||
|
* Порядок страниц повторяет порядок в sidebar.
|
||||||
|
*/
|
||||||
|
const buildLlmsFull = (): void => {
|
||||||
|
const sidebar = cfg.themeConfig.sidebar;
|
||||||
|
const entries = flattenSidebar(sidebar);
|
||||||
|
const blockquote = cfg.llmsBlockquote ?? cfg.description ?? '';
|
||||||
|
|
||||||
|
const parts: string[] = [];
|
||||||
|
parts.push(`# ${cfg.title}`);
|
||||||
|
parts.push('');
|
||||||
|
if (blockquote) parts.push(`> ${blockquote}`);
|
||||||
|
if (cfg.llmsContext) {
|
||||||
|
parts.push('');
|
||||||
|
parts.push(cfg.llmsContext);
|
||||||
|
}
|
||||||
|
parts.push('');
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const filePath = linkToFilePath(entry.link);
|
||||||
|
if (!fs.existsSync(filePath)) continue;
|
||||||
|
|
||||||
|
const raw = fs.readFileSync(filePath, 'utf8');
|
||||||
|
const content = shiftHeadings(stripFrontmatter(raw)).trim();
|
||||||
|
if (!content) continue;
|
||||||
|
|
||||||
|
// Мета-якорь: путь страницы для ориентации LLM
|
||||||
|
parts.push(`<!-- ${entry.link} -->`);
|
||||||
|
parts.push('');
|
||||||
|
parts.push(content);
|
||||||
|
parts.push('');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.mkdirSync(PUBLIC_DIR, { recursive: true });
|
||||||
|
const outFile = path.join(PUBLIC_DIR, 'llms-full.txt');
|
||||||
|
fs.writeFileSync(outFile, parts.join('\n'), 'utf8');
|
||||||
|
console.log(`${outFile} создан`);
|
||||||
|
};
|
||||||
|
|
||||||
/** Манифест сборки — для лендинга и внешних потребителей. */
|
/** Манифест сборки — для лендинга и внешних потребителей. */
|
||||||
const writeManifest = (): void => {
|
const writeManifest = (): void => {
|
||||||
const manifest = {
|
const manifest = {
|
||||||
version: VERSION,
|
version: VERSION,
|
||||||
buildDate: BUILD_DATE,
|
buildDate: BUILD_DATE,
|
||||||
languages: {
|
llms: '/llms.txt',
|
||||||
ru: {
|
llmsFull: '/llms-full.txt',
|
||||||
llms: '/ru/llms.txt',
|
zip: '/nextjs-style-guide.zip',
|
||||||
zip: '/nextjs-style-guide-ru.zip',
|
|
||||||
},
|
|
||||||
en: {
|
|
||||||
llms: '/en/llms.txt',
|
|
||||||
zip: '/nextjs-style-guide-en.zip',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
fs.mkdirSync(PUBLIC_DIR, { recursive: true });
|
fs.mkdirSync(PUBLIC_DIR, { recursive: true });
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
@@ -358,26 +487,22 @@ const writeManifest = (): void => {
|
|||||||
console.log(`${PUBLIC_DIR}/manifest.json создан`);
|
console.log(`${PUBLIC_DIR}/manifest.json создан`);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Скопировать `index.md` локали в корневой README без frontmatter */
|
/** Скопировать `index.md` документации в корневой README без frontmatter. */
|
||||||
const buildReadme = (lang: Lang, outFile: string): void => {
|
const buildReadme = (): void => {
|
||||||
const indexPath = path.join('docs', lang, 'index.md');
|
const indexPath = 'docs/docs/index.md';
|
||||||
if (!fs.existsSync(indexPath)) {
|
if (!fs.existsSync(indexPath)) {
|
||||||
console.warn(`Пропуск ${outFile}: ${indexPath} не найден`);
|
console.warn(`Пропуск README.md: ${indexPath} не найден`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const raw = fs.readFileSync(indexPath, 'utf8');
|
const raw = fs.readFileSync(indexPath, 'utf8');
|
||||||
const { body } = parseFrontmatter(raw);
|
const { body } = parseFrontmatter(raw);
|
||||||
fs.writeFileSync(outFile, body.trimStart(), 'utf8');
|
fs.writeFileSync('README.md', body.trimStart(), 'utf8');
|
||||||
console.log(`${outFile} обновлён из ${indexPath}`);
|
console.log(`README.md обновлён из ${indexPath}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
buildLlms('ru');
|
buildLlms();
|
||||||
buildLlms('en');
|
buildLlmsFull();
|
||||||
buildRootIndex();
|
copyMdFiles();
|
||||||
copyMdFiles('ru');
|
buildZip();
|
||||||
copyMdFiles('en');
|
|
||||||
buildZip('ru');
|
|
||||||
buildZip('en');
|
|
||||||
writeManifest();
|
writeManifest();
|
||||||
buildReadme('en', 'README.md');
|
buildReadme();
|
||||||
buildReadme('ru', 'README_RU.md');
|
|
||||||
|
|||||||
Reference in New Issue
Block a user