refactor: удалить английскую локаль и упростить структуру
- Удалена английская версия документации (docs/en/) и артефакты en - Контент перенесён docs/ru/ → docs/docs/, URL /ru/ заменён на /docs/ - Из .vitepress/config.ts убраны locales и enSidebar, оставлен один sidebar - Из лендинга удалён переключатель языка ru/en и en-словарь - generate-llms.ts переписан без параметра lang; llms.txt, llms-full.txt и nextjs-style-guide.zip генерируются в корень docs/public/ - README_RU.md занял место корневого README.md - Обновлены CONTRIBUTING.md, custom.css, комментарий в Dockerfile
This commit is contained in:
@@ -1,101 +1,51 @@
|
|||||||
import { defineConfig } from 'vitepress';
|
import { defineConfig } from 'vitepress';
|
||||||
|
|
||||||
const ruSidebar = [
|
const sidebar = [
|
||||||
{
|
{
|
||||||
text: 'Главная',
|
text: 'Главная',
|
||||||
link: '/ru/',
|
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/components' },
|
||||||
{ text: 'Страницы (App Router)', link: '/ru/applied/page-level' },
|
{ text: 'Страницы (App Router)', link: '/docs/applied/page-level' },
|
||||||
{ text: 'Шаблоны и генерация кода', link: '/ru/applied/templates-generation' },
|
{ text: 'Шаблоны и генерация кода', link: '/docs/applied/templates-generation' },
|
||||||
{ text: 'Стили', link: '/ru/applied/styles' },
|
{ text: 'Стили', link: '/docs/applied/styles' },
|
||||||
{ text: 'Изображения', link: '/ru/applied/images-sprites' },
|
{ text: 'Изображения', link: '/docs/applied/images-sprites' },
|
||||||
{ text: 'SVG-спрайты', link: '/ru/applied/svg-sprites' },
|
{ text: 'SVG-спрайты', link: '/docs/applied/svg-sprites' },
|
||||||
{ text: 'Видео', link: '/ru/applied/video' },
|
{ text: 'Видео', link: '/docs/applied/video' },
|
||||||
{ text: 'API', link: '/ru/applied/api' },
|
{ text: 'API', link: '/docs/applied/api' },
|
||||||
{ text: 'Stores', link: '/ru/applied/stores' },
|
{ text: 'Stores', link: '/docs/applied/stores' },
|
||||||
{ text: 'Хуки', link: '/ru/applied/hooks' },
|
{ text: 'Хуки', link: '/docs/applied/hooks' },
|
||||||
{ text: 'Шрифты', link: '/ru/applied/fonts' },
|
{ text: 'Шрифты', link: '/docs/applied/fonts' },
|
||||||
{ text: 'Локализация', link: '/ru/applied/localization' },
|
{ text: 'Локализация', link: '/docs/applied/localization' },
|
||||||
{ text: 'Настройка VS Code', link: '/ru/applied/vscode' },
|
{ text: 'Настройка VS Code', link: '/docs/applied/vscode' },
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const enSidebar = [
|
|
||||||
{
|
|
||||||
text: 'Home',
|
|
||||||
link: '/en/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Processes',
|
|
||||||
items: [
|
|
||||||
{ text: 'Getting Started', link: '/en/workflow/getting-started' },
|
|
||||||
{ text: 'Creating an App', link: '/en/workflow/creating-app' },
|
|
||||||
{ 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: 'Basic Rules',
|
|
||||||
items: [
|
|
||||||
{ text: 'Tech Stack', link: '/en/basics/tech-stack' },
|
|
||||||
{ text: 'Architecture', link: '/en/basics/architecture' },
|
|
||||||
{ text: 'Code Style', link: '/en/basics/code-style' },
|
|
||||||
{ text: 'Naming', link: '/en/basics/naming' },
|
|
||||||
{ text: 'Documentation', link: '/en/basics/documentation' },
|
|
||||||
{ 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' },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -123,8 +73,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],
|
||||||
@@ -133,44 +84,18 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
locales: {
|
themeConfig: {
|
||||||
root: {
|
sidebar,
|
||||||
label: 'Languages',
|
socialLinks: [
|
||||||
lang: 'en',
|
{ icon: 'github', link: 'https://gromlab.ru/docs/nextjs-style-guide' },
|
||||||
},
|
],
|
||||||
ru: {
|
|
||||||
label: 'Русский',
|
|
||||||
lang: 'ru-RU',
|
|
||||||
link: '/ru/',
|
|
||||||
description: 'Стандарты разработки на Next.js + TypeScript с архитектурой SLM',
|
|
||||||
themeConfig: {
|
|
||||||
sidebar: ruSidebar,
|
|
||||||
socialLinks: [
|
|
||||||
{ icon: 'github', link: 'https://gromlab.ru/docs/nextjs-style-guide' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// Расширенный блок описания для llms.txt — даёт LLM полный
|
|
||||||
// технический контекст: стек, методология, охват тем.
|
|
||||||
llmsBlockquote:
|
|
||||||
'Стандарты разработки frontend-приложений на Next.js (App Router) + TypeScript + React с архитектурой SLM (Scoped Layered Module Design — модульная архитектура со слоями ответственности, где каждый модуль содержит всё необходимое: компоненты, хуки, сторы, типы, стили).',
|
|
||||||
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.',
|
|
||||||
},
|
|
||||||
en: {
|
|
||||||
label: 'English',
|
|
||||||
lang: 'en-US',
|
|
||||||
link: '/en/',
|
|
||||||
description: 'Next.js + TypeScript development standards with SLM architecture',
|
|
||||||
themeConfig: {
|
|
||||||
sidebar: enSidebar,
|
|
||||||
socialLinks: [
|
|
||||||
{ icon: 'github', link: 'https://gromlab.ru/docs/nextjs-style-guide' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
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.',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
|
||||||
|
// Расширенный блок описания для llms.txt — даёт LLM полный
|
||||||
|
// технический контекст: стек, методология, охват тем.
|
||||||
|
// Используется в generate-llms.ts.
|
||||||
|
llmsBlockquote:
|
||||||
|
'Стандарты разработки frontend-приложений на Next.js (App Router) + TypeScript + React с архитектурой SLM (Scoped Layered Module Design — модульная архитектура со слоями ответственности, где каждый модуль содержит всё необходимое: компоненты, хуки, сторы, типы, стили).',
|
||||||
|
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.',
|
||||||
|
} as any);
|
||||||
|
|||||||
@@ -16,13 +16,4 @@
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Скрыть root-локаль (лендинг с layout: false) из переключателя языка.
|
|
||||||
* VitePress не даёт исключить root через config — она всегда попадает
|
|
||||||
* в lang-switcher. Идентифицируем по href="/" — другие локали используют
|
|
||||||
* href="/ru/" и href="/en/".
|
|
||||||
*/
|
|
||||||
.VPNavBarTranslations a[href="/"],
|
|
||||||
.VPNavScreenTranslations a[href="/"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
## Два типа документации
|
## Два типа документации
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
97
README.md
97
README.md
@@ -1,66 +1,69 @@
|
|||||||
# NextJS Style Guide
|
# NextJS Style Guide
|
||||||
|
|
||||||
Conventions for Next.js project development: application architecture and layers, code structure, module organization, styling, typing, and infrastructure.
|
Соглашения по разработке Next.js проектов: архитектура и слои приложения, структура кода, организация модулей, стилизация, типизация и инфраструктура.
|
||||||
|
|
||||||
## Usage
|
## Использование
|
||||||
|
|
||||||
**For AI agents:**
|
**Для AI-агентов:**
|
||||||
|
|
||||||
- [Sections map](https://nextjs-style-guide.gromlab.ru/en/llms.txt) — `llms.txt`, table of contents with links to sections.
|
- [Карта разделов](https://nextjs-style-guide.gromlab.ru/llms.txt) — `llms.txt`, оглавление со ссылками на разделы.
|
||||||
- [Full text](https://nextjs-style-guide.gromlab.ru/en/llms-full.txt) — `llms-full.txt`, the entire documentation in a single file.
|
- [Полный текст](https://nextjs-style-guide.gromlab.ru/llms-full.txt) — `llms-full.txt`, вся документация одним файлом.
|
||||||
|
|
||||||
**For projects:**
|
**Для проекта:**
|
||||||
|
|
||||||
- [Rules archive](https://nextjs-style-guide.gromlab.ru/nextjs-style-guide-en.zip) — `nextjs-style-guide-en.zip`, a set of Markdown files to unpack into `./ai/nextjs-style-guide/` or another project folder.
|
- [Архив с правилами](https://nextjs-style-guide.gromlab.ru/nextjs-style-guide.zip) — `nextjs-style-guide.zip`, набор Markdown-файлов для распаковки в `./ai/nextjs-style-guide/` или другую папку проекта.
|
||||||
|
|
||||||
## Documentation Structure
|
## Структура документации
|
||||||
|
|
||||||
### Processes
|
### Workflow
|
||||||
|
|
||||||
**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? |
|
| Добавление UI-модуля | Как создать компонент, бизнес-модуль, виджет или layout? |
|
||||||
| 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 SLM layers, dependencies, and public API structured? |
|
| Архитектура | Как устроены слои SLM, зависимости, публичный API? |
|
||||||
| Code Style | How to format code: indentation, quotes, imports, early return? |
|
| Стиль кода | Как оформлять код: отступы, кавычки, импорты, early return? |
|
||||||
| Naming | How to name files, variables, components, hooks? |
|
| Именование | Как называть файлы, переменные, компоненты, хуки? |
|
||||||
| Documentation | How to write JSDoc: what to document and what not? |
|
| Документирование | Как писать JSDoc: что документировать, а что нет? |
|
||||||
| Typing | How to type: type vs interface, any/unknown? |
|
| Типизация | Как типизировать: type vs interface, any/unknown? |
|
||||||
|
|
||||||
### Applied Sections
|
### Прикладные разделы
|
||||||
|
|
||||||
**How a specific area works** — rules, structure, and code examples for specific technologies and tools.
|
**Как это настроить и использовать** — конфигурация, структура и примеры кода для конкретных областей.
|
||||||
|
|
||||||
|
| Раздел | Отвечает на вопрос |
|
||||||
|
|--------|-------------------|
|
||||||
|
| Настройка 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)_ |
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
69
README_RU.md
69
README_RU.md
@@ -1,69 +0,0 @@
|
|||||||
# NextJS Style Guide
|
|
||||||
|
|
||||||
Соглашения по разработке Next.js проектов: архитектура и слои приложения, структура кода, организация модулей, стилизация, типизация и инфраструктура.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
**Для AI-агентов:**
|
|
||||||
|
|
||||||
- [Карта разделов](https://nextjs-style-guide.gromlab.ru/ru/llms.txt) — `llms.txt`, оглавление со ссылками на разделы.
|
|
||||||
- [Полный текст](https://nextjs-style-guide.gromlab.ru/ru/llms-full.txt) — `llms-full.txt`, вся документация одним файлом.
|
|
||||||
|
|
||||||
**Для проекта:**
|
|
||||||
|
|
||||||
- [Архив с правилами](https://nextjs-style-guide.gromlab.ru/nextjs-style-guide-ru.zip) — `nextjs-style-guide-ru.zip`, набор Markdown-файлов для распаковки в `./ai/nextjs-style-guide/` или другую папку проекта.
|
|
||||||
|
|
||||||
## Структура документации
|
|
||||||
|
|
||||||
### 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 | _(не заполнен)_ |
|
|
||||||
| Хуки | _(не заполнен)_ |
|
|
||||||
| Шрифты | _(не заполнен)_ |
|
|
||||||
| Локализация | _(не заполнен)_ |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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).
|
||||||
|
|
||||||
## Реализация
|
## Реализация
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ auth/
|
|||||||
└── index.ts # публичный API
|
└── index.ts # публичный API
|
||||||
```
|
```
|
||||||
|
|
||||||
Подробное описание каждого сегмента — в разделе [Сегменты](/ru/basics/architecture/reference/segments).
|
Подробное описание каждого сегмента — в разделе [Сегменты](/docs/basics/architecture/reference/segments).
|
||||||
|
|
||||||
## Публичный API
|
## Публичный API
|
||||||
|
|
||||||
@@ -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-компоненты.
|
||||||
@@ -6,12 +6,12 @@
|
|||||||
|
|
||||||
**Для AI-агентов:**
|
**Для AI-агентов:**
|
||||||
|
|
||||||
- [Карта разделов](https://nextjs-style-guide.gromlab.ru/ru/llms.txt) — `llms.txt`, оглавление со ссылками на разделы.
|
- [llms.txt](/llms.txt) — Карта разделов, оглавление со ссылками на разделы.
|
||||||
- [Полный текст](https://nextjs-style-guide.gromlab.ru/ru/llms-full.txt) — `llms-full.txt`, вся документация одним файлом.
|
- [llms-full.txt](/llms-full.txt) — Вся документация одним файлом.
|
||||||
|
|
||||||
**Для проекта:**
|
**Для проекта:**
|
||||||
|
|
||||||
- [Архив с правилами](https://nextjs-style-guide.gromlab.ru/nextjs-style-guide-ru.zip) — `nextjs-style-guide-ru.zip`, набор Markdown-файлов для распаковки в `./ai/nextjs-style-guide/` или другую папку проекта.
|
- [nextjs-style-guide.zip](/nextjs-style-guide.zip) — Набор Markdown-файлов для распаковки в `./ai/nextjs-style-guide/` или другую папку проекта.
|
||||||
|
|
||||||
## Структура документации
|
## Структура документации
|
||||||
|
|
||||||
@@ -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).
|
||||||
@@ -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,66 +0,0 @@
|
|||||||
# NextJS Style Guide
|
|
||||||
|
|
||||||
Conventions for Next.js project development: application architecture and layers, code structure, module organization, styling, typing, and infrastructure.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
**For AI agents:**
|
|
||||||
|
|
||||||
- [Sections map](https://nextjs-style-guide.gromlab.ru/en/llms.txt) — `llms.txt`, table of contents with links to sections.
|
|
||||||
- [Full text](https://nextjs-style-guide.gromlab.ru/en/llms-full.txt) — `llms-full.txt`, the entire documentation in a single file.
|
|
||||||
|
|
||||||
**For projects:**
|
|
||||||
|
|
||||||
- [Rules archive](https://nextjs-style-guide.gromlab.ru/nextjs-style-guide-en.zip) — `nextjs-style-guide-en.zip`, a set of Markdown files to unpack into `./ai/nextjs-style-guide/` or another project folder.
|
|
||||||
|
|
||||||
## 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 SLM 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)_ |
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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.
|
|
||||||
191
docs/index.md
191
docs/index.md
@@ -3,102 +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 проектов: архитектура и слои приложения, структура кода, организация модулей, стилизация, типизация и инфраструктура.',
|
|
||||||
langLabel: 'Язык',
|
|
||||||
themeLabel: 'Тема',
|
|
||||||
repoLabel: 'Репозиторий',
|
|
||||||
themes: { auto: 'Авто', light: 'Светлая', dark: 'Тёмная' },
|
|
||||||
cards: {
|
|
||||||
docs: {
|
|
||||||
title: 'Документация',
|
|
||||||
desc: 'Все разделы: процессы разработки, базовые правила, прикладные руководства.',
|
|
||||||
href: './ru/',
|
|
||||||
cta: 'Открыть',
|
|
||||||
},
|
|
||||||
ai: {
|
|
||||||
title: 'Ассистенту',
|
|
||||||
desc: 'Карта документации в формате llms.txt для AI-агентов.',
|
|
||||||
buttons: [
|
|
||||||
{ label: 'llms.txt', href: './ru/llms.txt' },
|
|
||||||
{ label: 'llms-full.txt', href: './ru/llms-full.txt' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
zip: {
|
|
||||||
title: 'Скачать правила',
|
|
||||||
desc: 'Архив всех Markdown-файлов одним ZIP.',
|
|
||||||
href: './nextjs-style-guide-ru.zip',
|
|
||||||
cta: 'Скачать',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
en: {
|
|
||||||
tagline: 'Conventions for Next.js project development: application architecture and layers, code structure, module organization, styling, typing, and infrastructure.',
|
|
||||||
langLabel: 'Language',
|
|
||||||
themeLabel: 'Theme',
|
|
||||||
repoLabel: 'Repository',
|
|
||||||
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.',
|
|
||||||
badge: 'in development',
|
|
||||||
buttons: [
|
|
||||||
{ label: 'llms.txt', href: '#' },
|
|
||||||
{ label: 'llms-full.txt', href: '#' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
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') {
|
||||||
@@ -124,7 +43,7 @@ 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">
|
||||||
<a
|
<a
|
||||||
class="landing__repo"
|
class="landing__repo"
|
||||||
@@ -135,31 +54,15 @@ function toggleTheme(value) {
|
|||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||||
<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"/>
|
<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"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span>{{ t.repoLabel }}</span>
|
<span>Репозиторий</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="seg" role="group" :aria-label="t.langLabel">
|
<div class="seg seg--icons" role="group" aria-label="Тема">
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="seg__btn"
|
|
||||||
:class="{ 'seg__btn--active': lang === 'ru' }"
|
|
||||||
:aria-pressed="lang === 'ru'"
|
|
||||||
@click="setLang('ru')"
|
|
||||||
>Русский</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="seg__btn"
|
|
||||||
:class="{ 'seg__btn--active': lang === 'en' }"
|
|
||||||
: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>
|
||||||
@@ -169,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>
|
||||||
@@ -179,48 +82,26 @@ function toggleTheme(value) {
|
|||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<ClientOnly>
|
<section class="landing__cards">
|
||||||
<section class="landing__cards">
|
<a class="landing__card" href="./docs/">
|
||||||
<template v-for="key in ['docs', 'ai', 'zip']" :key="key">
|
<h3>Документация</h3>
|
||||||
<div
|
<p>Все разделы: процессы разработки, базовые правила, прикладные руководства.</p>
|
||||||
v-if="t.cards[key].buttons"
|
<span class="landing__cta">Открыть →</span>
|
||||||
class="landing__card landing__card--multi"
|
</a>
|
||||||
:class="{ 'landing__card--soon': t.cards[key].badge }"
|
<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>
|
||||||
<div class="landing__buttons">
|
<a class="landing__card" href="./nextjs-style-guide.zip">
|
||||||
<a
|
<h3>Скачать правила</h3>
|
||||||
v-for="btn in t.cards[key].buttons"
|
<p>Архив всех Markdown-файлов одним ZIP.</p>
|
||||||
:key="btn.label"
|
<span class="landing__cta">Скачать →</span>
|
||||||
class="landing__button"
|
</a>
|
||||||
:href="btn.href"
|
</section>
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>{{ btn.label }}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
v-else
|
|
||||||
class="landing__card"
|
|
||||||
:class="{ 'landing__card--soon': t.cards[key].badge }"
|
|
||||||
:href="t.cards[key].href"
|
|
||||||
:aria-disabled="t.cards[key].badge ? 'true' : null"
|
|
||||||
>
|
|
||||||
<h3>
|
|
||||||
{{ t.cards[key].title }}
|
|
||||||
<span v-if="t.cards[key].badge" class="landing__badge">{{ t.cards[key].badge }}</span>
|
|
||||||
</h3>
|
|
||||||
<p>{{ t.cards[key].desc }}</p>
|
|
||||||
<span class="landing__cta">{{ t.cards[key].cta }} →</span>
|
|
||||||
</a>
|
|
||||||
</template>
|
|
||||||
</section>
|
|
||||||
</ClientOnly>
|
|
||||||
|
|
||||||
<p class="landing__version">v{{ buildVersion }}</p>
|
<p class="landing__version">v{{ buildVersion }}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -374,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;
|
||||||
@@ -436,20 +311,6 @@ 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: 48px 20px 56px;
|
padding: 48px 20px 56px;
|
||||||
|
|||||||
289
generate-llms.ts
289
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;
|
title: string;
|
||||||
// VitePress-конфиг типизирован как `UserConfig`, но обращаемся к
|
description: string;
|
||||||
// фактически переданным значениям — сужаем тип через any.
|
themeConfig: { sidebar: SidebarItem[] };
|
||||||
const cfg = config as unknown as {
|
llmsBlockquote?: string;
|
||||||
title: string;
|
llmsContext?: string;
|
||||||
description: string;
|
}
|
||||||
locales: Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
description?: string;
|
|
||||||
llmsBlockquote?: 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,26 +235,26 @@ 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}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Преобразовать sidebar `link` в относительный путь файла внутри архива
|
* Преобразовать sidebar `link` в относительный путь файла внутри архива
|
||||||
* (от корня папки `nextjs-style-guide/`). Это путь, по которому файл лежит
|
* (от корня папки `nextjs-style-guide/`).
|
||||||
* в распакованной папке, без расширения добавляется `.md`.
|
|
||||||
*/
|
*/
|
||||||
const linkToArchiveRel = (link: string, lang: Lang): string => {
|
const linkToArchiveRel = (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 {
|
||||||
@@ -317,12 +264,12 @@ const linkToArchiveRel = (link: string, lang: Lang): string => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Заменить во всех `.md` архива ссылки `[text](/ru/foo)` на относительные
|
* Заменить во всех `.md` архива ссылки `[text](/docs/foo)` на относительные
|
||||||
* пути от расположения файла. Без этого внутренние ссылки в распакованной
|
* пути от расположения файла. Без этого внутренние ссылки в распакованной
|
||||||
* папке не работают.
|
* папке не работают.
|
||||||
*/
|
*/
|
||||||
const transformLinksInDir = (rootDir: string, lang: Lang): void => {
|
const transformLinksInDir = (rootDir: string): void => {
|
||||||
const linkRe = /\]\(\/([a-z]{2})\/([^)\s#]*)(#[^)]*)?\)/g;
|
const linkRe = /\]\(\/docs\/([^)\s#]*)(#[^)]*)?\)/g;
|
||||||
|
|
||||||
const walk = (dir: string): void => {
|
const walk = (dir: string): void => {
|
||||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||||
@@ -336,12 +283,8 @@ const transformLinksInDir = (rootDir: string, lang: Lang): void => {
|
|||||||
const content = fs.readFileSync(full, 'utf8');
|
const content = fs.readFileSync(full, 'utf8');
|
||||||
const fileDir = path.dirname(full);
|
const fileDir = path.dirname(full);
|
||||||
|
|
||||||
const updated = content.replace(linkRe, (match, urlLang, route, hash = '') => {
|
const updated = content.replace(linkRe, (_match, route, hash = '') => {
|
||||||
// Ссылки на другую локаль не трогаем — её в архиве нет.
|
const targetRel = linkToArchiveRel(`${DOC_PREFIX}${route}`);
|
||||||
if (urlLang !== lang) return match;
|
|
||||||
|
|
||||||
const fakeLink = `/${urlLang}/${route}${route.endsWith('/') ? '' : ''}`;
|
|
||||||
const targetRel = linkToArchiveRel(fakeLink, lang);
|
|
||||||
const targetAbs = path.join(rootDir, targetRel);
|
const targetAbs = path.join(rootDir, targetRel);
|
||||||
let rel = path.relative(fileDir, targetAbs);
|
let rel = path.relative(fileDir, targetAbs);
|
||||||
if (!rel.startsWith('.')) rel = './' + rel;
|
if (!rel.startsWith('.')) rel = './' + rel;
|
||||||
@@ -362,29 +305,14 @@ const transformLinksInDir = (rootDir: string, lang: Lang): void => {
|
|||||||
* с относительными ссылками, описаниями из frontmatter/первого абзаца
|
* с относительными ссылками, описаниями из frontmatter/первого абзаца
|
||||||
* и метаинфо сборки.
|
* и метаинфо сборки.
|
||||||
*/
|
*/
|
||||||
const buildArchiveReadme = (lang: Lang, rootDir: string): void => {
|
const buildArchiveReadme = (rootDir: string): void => {
|
||||||
const cfg = config as unknown as {
|
const sidebar = cfg.themeConfig.sidebar;
|
||||||
title: string;
|
const blockquote = cfg.llmsBlockquote ?? cfg.description ?? '';
|
||||||
locales: Record<
|
const context = cfg.llmsContext;
|
||||||
string,
|
|
||||||
{
|
|
||||||
description?: string;
|
|
||||||
llmsBlockquote?: string;
|
|
||||||
llmsContext?: string;
|
|
||||||
themeConfig?: { sidebar?: SidebarItem[] };
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const locale = cfg.locales[lang];
|
|
||||||
const sidebar = locale?.themeConfig?.sidebar;
|
|
||||||
if (!sidebar) return;
|
|
||||||
|
|
||||||
const blockquote = locale.llmsBlockquote ?? locale.description ?? '';
|
|
||||||
const context = locale.llmsContext;
|
|
||||||
const entries = flattenSidebar(sidebar).filter(
|
const entries = flattenSidebar(sidebar).filter(
|
||||||
// «Главная» из sidebar — это страница локали для веба, в архиве не нужна.
|
// «Главная» из sidebar — это страница раздела для веба, в архиве не нужна.
|
||||||
(e) => !(e.section === 'Главная' || e.section === 'Home'),
|
(e) => e.section !== 'Главная',
|
||||||
);
|
);
|
||||||
const grouped = groupBySection(entries);
|
const grouped = groupBySection(entries);
|
||||||
|
|
||||||
@@ -400,16 +328,15 @@ const buildArchiveReadme = (lang: Lang, rootDir: string): void => {
|
|||||||
lines.push('');
|
lines.push('');
|
||||||
}
|
}
|
||||||
|
|
||||||
const heading = lang === 'ru' ? 'Содержание' : 'Contents';
|
lines.push('## Содержание');
|
||||||
lines.push(`## ${heading}`);
|
|
||||||
lines.push('');
|
lines.push('');
|
||||||
|
|
||||||
for (const [section, items] of grouped) {
|
for (const [section, items] of grouped) {
|
||||||
lines.push(`### ${section}`);
|
lines.push(`### ${section}`);
|
||||||
lines.push('');
|
lines.push('');
|
||||||
for (const entry of items) {
|
for (const entry of items) {
|
||||||
const targetRel = './' + linkToArchiveRel(entry.link, lang);
|
const targetRel = './' + linkToArchiveRel(entry.link);
|
||||||
const filePath = path.join(rootDir, linkToArchiveRel(entry.link, lang));
|
const filePath = path.join(rootDir, linkToArchiveRel(entry.link));
|
||||||
|
|
||||||
let description: string | null = null;
|
let description: string | null = null;
|
||||||
if (fs.existsSync(filePath)) {
|
if (fs.existsSync(filePath)) {
|
||||||
@@ -436,33 +363,30 @@ const buildArchiveReadme = (lang: Lang, rootDir: string): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Собрать `nextjs-style-guide-{lang}.zip`. Внутри архива — единая папка
|
* Собрать `nextjs-style-guide.zip`. Внутри — папка `nextjs-style-guide/`
|
||||||
* `nextjs-style-guide/` с `.md`-файлами локали, README-точкой входа,
|
* с `.md`-файлами, README, `llms-full.txt` и `VERSION`. Внутренние ссылки
|
||||||
* `llms-full.txt` и `VERSION`. Внутренние ссылки в `.md` преобразуются
|
* преобразуются в относительные.
|
||||||
* в относительные.
|
|
||||||
*
|
|
||||||
* Веб-`index.md` локали из архива удаляется — его роль выполняет README.md.
|
|
||||||
*/
|
*/
|
||||||
const buildZip = (lang: Lang): void => {
|
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 });
|
||||||
|
|
||||||
// 1. Копируем все .md локали в staging.
|
// 1. Копируем все .md в staging.
|
||||||
copyDirSync(path.join('docs', lang), stage, (name) => name.endsWith('.md'));
|
copyDirSync('docs/docs', stage, (name) => name.endsWith('.md'));
|
||||||
|
|
||||||
// 2. Удаляем веб-index.md локали — в архиве он избыточен.
|
// 2. Удаляем веб-index.md — в архиве его роль выполняет README.md.
|
||||||
const indexPath = path.join(stage, 'index.md');
|
const indexPath = path.join(stage, 'index.md');
|
||||||
if (fs.existsSync(indexPath)) fs.unlinkSync(indexPath);
|
if (fs.existsSync(indexPath)) fs.unlinkSync(indexPath);
|
||||||
|
|
||||||
// 3. Преобразуем абсолютные ссылки `/ru/...` в относительные.
|
// 3. Преобразуем абсолютные ссылки `/docs/...` в относительные.
|
||||||
transformLinksInDir(stage, lang);
|
transformLinksInDir(stage);
|
||||||
|
|
||||||
// 4. Генерируем точку входа README.md.
|
// 4. Генерируем точку входа README.md.
|
||||||
buildArchiveReadme(lang, stage);
|
buildArchiveReadme(stage);
|
||||||
|
|
||||||
// 5. Кладём llms-full.txt — удобно для одноразового чтения LLM.
|
// 5. Кладём llms-full.txt — удобно для одноразового чтения LLM.
|
||||||
const llmsFullSrc = path.join(PUBLIC_DIR, lang, 'llms-full.txt');
|
const llmsFullSrc = path.join(PUBLIC_DIR, 'llms-full.txt');
|
||||||
if (fs.existsSync(llmsFullSrc)) {
|
if (fs.existsSync(llmsFullSrc)) {
|
||||||
fs.copyFileSync(llmsFullSrc, path.join(stage, 'llms-full.txt'));
|
fs.copyFileSync(llmsFullSrc, path.join(stage, 'llms-full.txt'));
|
||||||
}
|
}
|
||||||
@@ -473,10 +397,7 @@ const buildZip = (lang: Lang): void => {
|
|||||||
`${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'], {
|
||||||
@@ -509,42 +430,26 @@ const shiftHeadings = (content: string): string => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Собрать `llms-full.txt` — все страницы локали в одном файле.
|
* Собрать `llms-full.txt` — все страницы в одном файле.
|
||||||
* Порядок страниц повторяет порядок в sidebar.
|
* Порядок страниц повторяет порядок в sidebar.
|
||||||
*/
|
*/
|
||||||
const buildLlmsFull = (lang: Lang): void => {
|
const buildLlmsFull = (): void => {
|
||||||
const cfg = config as unknown as {
|
const sidebar = cfg.themeConfig.sidebar;
|
||||||
title: string;
|
|
||||||
locales: Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
description?: string;
|
|
||||||
llmsBlockquote?: string;
|
|
||||||
llmsContext?: string;
|
|
||||||
themeConfig?: { sidebar?: SidebarItem[] };
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const locale = cfg.locales[lang];
|
|
||||||
const sidebar = locale?.themeConfig?.sidebar;
|
|
||||||
if (!sidebar) return;
|
|
||||||
|
|
||||||
const entries = flattenSidebar(sidebar);
|
const entries = flattenSidebar(sidebar);
|
||||||
const blockquote = locale.llmsBlockquote ?? locale.description ?? '';
|
const blockquote = cfg.llmsBlockquote ?? cfg.description ?? '';
|
||||||
|
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
parts.push(`# ${cfg.title}`);
|
parts.push(`# ${cfg.title}`);
|
||||||
parts.push('');
|
parts.push('');
|
||||||
if (blockquote) parts.push(`> ${blockquote}`);
|
if (blockquote) parts.push(`> ${blockquote}`);
|
||||||
if (locale.llmsContext) {
|
if (cfg.llmsContext) {
|
||||||
parts.push('');
|
parts.push('');
|
||||||
parts.push(locale.llmsContext);
|
parts.push(cfg.llmsContext);
|
||||||
}
|
}
|
||||||
parts.push('');
|
parts.push('');
|
||||||
|
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const filePath = linkToFilePath(entry.link, lang);
|
const filePath = linkToFilePath(entry.link);
|
||||||
if (!fs.existsSync(filePath)) continue;
|
if (!fs.existsSync(filePath)) continue;
|
||||||
|
|
||||||
const raw = fs.readFileSync(filePath, 'utf8');
|
const raw = fs.readFileSync(filePath, 'utf8');
|
||||||
@@ -558,9 +463,8 @@ const buildLlmsFull = (lang: Lang): void => {
|
|||||||
parts.push('');
|
parts.push('');
|
||||||
}
|
}
|
||||||
|
|
||||||
const outDir = path.join(PUBLIC_DIR, lang);
|
fs.mkdirSync(PUBLIC_DIR, { recursive: true });
|
||||||
fs.mkdirSync(outDir, { recursive: true });
|
const outFile = path.join(PUBLIC_DIR, 'llms-full.txt');
|
||||||
const outFile = path.join(outDir, 'llms-full.txt');
|
|
||||||
fs.writeFileSync(outFile, parts.join('\n'), 'utf8');
|
fs.writeFileSync(outFile, parts.join('\n'), 'utf8');
|
||||||
console.log(`${outFile} создан`);
|
console.log(`${outFile} создан`);
|
||||||
};
|
};
|
||||||
@@ -570,18 +474,9 @@ 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',
|
||||||
llmsFull: '/ru/llms-full.txt',
|
|
||||||
zip: '/nextjs-style-guide-ru.zip',
|
|
||||||
},
|
|
||||||
en: {
|
|
||||||
llms: '/en/llms.txt',
|
|
||||||
llmsFull: '/en/llms-full.txt',
|
|
||||||
zip: '/nextjs-style-guide-en.zip',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
fs.mkdirSync(PUBLIC_DIR, { recursive: true });
|
fs.mkdirSync(PUBLIC_DIR, { recursive: true });
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
@@ -592,28 +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();
|
||||||
buildLlmsFull('ru');
|
copyMdFiles();
|
||||||
buildLlmsFull('en');
|
buildZip();
|
||||||
buildRootIndex();
|
|
||||||
copyMdFiles('ru');
|
|
||||||
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