Compare commits
5 Commits
v3
...
90bf360c06
| Author | SHA1 | Date | |
|---|---|---|---|
| 90bf360c06 | |||
| 5cf0f0f8ba | |||
| 464c709859 | |||
| 64db18917b | |||
| ae103e962e |
@@ -1,6 +1,10 @@
|
||||
import { defineConfig } from 'vitepress';
|
||||
|
||||
const ruSidebar = [
|
||||
{
|
||||
text: 'Главная',
|
||||
link: '/ru/',
|
||||
},
|
||||
{
|
||||
text: 'Workflow',
|
||||
link: '/ru/workflow',
|
||||
@@ -47,6 +51,10 @@ const ruSidebar = [
|
||||
];
|
||||
|
||||
const enSidebar = [
|
||||
{
|
||||
text: 'Home',
|
||||
link: '/en/',
|
||||
},
|
||||
{
|
||||
text: 'Processes',
|
||||
items: [
|
||||
@@ -137,6 +145,9 @@ export default defineConfig({
|
||||
description: 'Стандарты разработки на Next.js + TypeScript с архитектурой SLM',
|
||||
themeConfig: {
|
||||
sidebar: ruSidebar,
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://gromlab.ru/docs/nextjs-style-guide' },
|
||||
],
|
||||
},
|
||||
// Расширенный блок описания для llms.txt — даёт LLM полный
|
||||
// технический контекст: стек, методология, охват тем.
|
||||
@@ -152,6 +163,9 @@ export default defineConfig({
|
||||
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).',
|
||||
|
||||
@@ -15,3 +15,14 @@
|
||||
max-width: 100%;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
/*
|
||||
* Скрыть root-локаль (лендинг с layout: false) из переключателя языка.
|
||||
* VitePress не даёт исключить root через config — она всегда попадает
|
||||
* в lang-switcher. Идентифицируем по href="/" — другие локали используют
|
||||
* href="/ru/" и href="/en/".
|
||||
*/
|
||||
.VPNavBarTranslations a[href="/"],
|
||||
.VPNavScreenTranslations a[href="/"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -185,6 +185,50 @@ title: Название раздела
|
||||
- Подсекции внутри `h2` — `h3`.
|
||||
- `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 `.
|
||||
|
||||
18
README.md
18
README.md
@@ -1,6 +1,17 @@
|
||||
# NextJS Style Guide
|
||||
|
||||
Rules and standards for NextJS and TypeScript development: architecture, typing, styles, components, API, and infrastructure.
|
||||
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
|
||||
|
||||
@@ -26,7 +37,7 @@ Rules and standards for NextJS and TypeScript development: architecture, typing,
|
||||
| Section | Answers the question |
|
||||
|---------|---------------------|
|
||||
| Tech Stack | What stack do we use? |
|
||||
| Architecture | How are FSD layers, dependencies, and public API structured? |
|
||||
| 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? |
|
||||
@@ -52,7 +63,4 @@ Rules and standards for NextJS and TypeScript development: architecture, typing,
|
||||
| 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
|
||||
|
||||
16
README_RU.md
16
README_RU.md
@@ -1,11 +1,17 @@
|
||||
# NextJS Style Guide
|
||||
|
||||
Правила и стандарты разработки на NextJS и TypeScript: архитектура, типизация, стили, компоненты, API и инфраструктурные разделы.
|
||||
Соглашения по разработке Next.js проектов: архитектура и слои приложения, структура кода, организация модулей, стилизация, типизация и инфраструктура.
|
||||
|
||||
## Для ассистентов
|
||||
## Использование
|
||||
|
||||
Карта документации со ссылками на все разделы (формат [llmstxt.org](https://llmstxt.org)):
|
||||
https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/llms.txt
|
||||
**Для 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/` или другую папку проекта.
|
||||
|
||||
## Структура документации
|
||||
|
||||
@@ -59,3 +65,5 @@ https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/llms.txt
|
||||
| Шрифты | _(не заполнен)_ |
|
||||
| Локализация | _(не заполнен)_ |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
# NextJS Style Guide
|
||||
|
||||
Rules and standards for NextJS and TypeScript development: architecture, typing, styles, components, API, and infrastructure.
|
||||
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
|
||||
|
||||
@@ -26,7 +37,7 @@ Rules and standards for NextJS and TypeScript development: architecture, typing,
|
||||
| Section | Answers the question |
|
||||
|---------|---------------------|
|
||||
| Tech Stack | What stack do we use? |
|
||||
| Architecture | How are FSD layers, dependencies, and public API structured? |
|
||||
| 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? |
|
||||
@@ -52,7 +63,4 @@ Rules and standards for NextJS and TypeScript development: architecture, typing,
|
||||
| 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
|
||||
|
||||
181
docs/index.md
181
docs/index.md
@@ -14,9 +14,10 @@ const buildVersion = __BUILD_VERSION__
|
||||
|
||||
const dict = {
|
||||
ru: {
|
||||
tagline: 'Готовые соглашения по архитектуре, коду, компонентам и инфраструктуре для Next.js + TypeScript-проектов — чтобы команда писала одинаково, а новые разработчики включались в проект быстрее.',
|
||||
tagline: 'Соглашения по разработке Next.js проектов: архитектура и слои приложения, структура кода, организация модулей, стилизация, типизация и инфраструктура.',
|
||||
langLabel: 'Язык',
|
||||
themeLabel: 'Тема',
|
||||
repoLabel: 'Репозиторий',
|
||||
themes: { auto: 'Авто', light: 'Светлая', dark: 'Тёмная' },
|
||||
cards: {
|
||||
docs: {
|
||||
@@ -28,8 +29,10 @@ const dict = {
|
||||
ai: {
|
||||
title: 'Ассистенту',
|
||||
desc: 'Карта документации в формате llms.txt для AI-агентов.',
|
||||
href: './ru/llms.txt',
|
||||
cta: 'Открыть',
|
||||
buttons: [
|
||||
{ label: 'llms.txt', href: './ru/llms.txt' },
|
||||
{ label: 'llms-full.txt', href: './ru/llms-full.txt' },
|
||||
],
|
||||
},
|
||||
zip: {
|
||||
title: 'Скачать правила',
|
||||
@@ -40,9 +43,10 @@ const dict = {
|
||||
},
|
||||
},
|
||||
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.',
|
||||
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: {
|
||||
@@ -55,9 +59,11 @@ const dict = {
|
||||
ai: {
|
||||
title: 'For Assistant',
|
||||
desc: 'Documentation map in llms.txt format for AI agents.',
|
||||
href: '#',
|
||||
cta: 'Open',
|
||||
badge: 'in development',
|
||||
buttons: [
|
||||
{ label: 'llms.txt', href: '#' },
|
||||
{ label: 'llms-full.txt', href: '#' },
|
||||
],
|
||||
},
|
||||
zip: {
|
||||
title: 'Download rules',
|
||||
@@ -120,6 +126,17 @@ function toggleTheme(value) {
|
||||
<ClientOnly>
|
||||
<p class="landing__tagline">{{ t.tagline }}</p>
|
||||
<div class="landing__controls">
|
||||
<a
|
||||
class="landing__repo"
|
||||
href="https://gromlab.ru/docs/nextjs-style-guide"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<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"/>
|
||||
</svg>
|
||||
<span>{{ t.repoLabel }}</span>
|
||||
</a>
|
||||
<div class="seg" role="group" :aria-label="t.langLabel">
|
||||
<button
|
||||
type="button"
|
||||
@@ -164,21 +181,44 @@ function toggleTheme(value) {
|
||||
|
||||
<ClientOnly>
|
||||
<section class="landing__cards">
|
||||
<a
|
||||
v-for="key in ['docs', 'ai', 'zip']"
|
||||
:key="key"
|
||||
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 v-for="key in ['docs', 'ai', 'zip']" :key="key">
|
||||
<div
|
||||
v-if="t.cards[key].buttons"
|
||||
class="landing__card landing__card--multi"
|
||||
:class="{ 'landing__card--soon': t.cards[key].badge }"
|
||||
: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>
|
||||
<div class="landing__buttons">
|
||||
<a
|
||||
v-for="btn in t.cards[key].buttons"
|
||||
:key="btn.label"
|
||||
class="landing__button"
|
||||
:href="btn.href"
|
||||
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>
|
||||
|
||||
@@ -207,6 +247,35 @@ function toggleTheme(value) {
|
||||
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 {
|
||||
display: inline-flex;
|
||||
align-items: stretch;
|
||||
@@ -332,6 +401,33 @@ function toggleTheme(value) {
|
||||
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 {
|
||||
text-align: center;
|
||||
margin: 24px 0 0;
|
||||
@@ -356,17 +452,56 @@ function toggleTheme(value) {
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.landing {
|
||||
padding: 16px 16px 48px;
|
||||
gap: 32px;
|
||||
padding: 48px 20px 56px;
|
||||
gap: 40px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.landing__title {
|
||||
font-size: 36px;
|
||||
}
|
||||
.landing__tagline {
|
||||
font-size: 16px;
|
||||
}
|
||||
.landing__cards {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
.landing__controls {
|
||||
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>
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Структура проекта
|
||||
|
||||
# Структура проекта
|
||||
|
||||
Раздел описывает расположение файлов и папок в проекте Next.js (App Router).
|
||||
Файловая организация Next.js-проекта по архитектуре SLM.
|
||||
|
||||
## Корень репозитория
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Стили
|
||||
|
||||
# Стили
|
||||
|
||||
Раздел описывает правила написания CSS: PostCSS Modules, вложенность, медиа-запросы, переменные, форматирование.
|
||||
Правила написания CSS: PostCSS Modules, форматирование, переменные.
|
||||
|
||||
## Общие правила
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Архитектура
|
||||
description: "Раздел описывает архитектуру проекта: из каких слоёв состоит приложение, как организован код внутри слоёв и какие правила управляют зависимостями."
|
||||
---
|
||||
|
||||
# SLM Design
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Слои
|
||||
|
||||
# Слои
|
||||
|
||||
Раздел описывает слои SLM: что такое слой, какие бывают, как между ними направлены зависимости и какие правила действуют на каждом.
|
||||
Слои SLM: назначение, классификация, направление зависимостей, правила.
|
||||
|
||||
## Определение
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Модули
|
||||
|
||||
# Модули
|
||||
|
||||
Раздел описывает модули SLM: что такое модуль, из чего он состоит и как взаимодействует с остальным кодом.
|
||||
Модули SLM: состав, границы, взаимодействие с остальным кодом.
|
||||
|
||||
## Определение
|
||||
|
||||
|
||||
@@ -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, архитектура, данные, состояние, локализация, тестирование, стили, генерация кода.
|
||||
|
||||
## Что используем
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Типизация
|
||||
|
||||
# Типизация
|
||||
|
||||
Этот раздел описывает правила типизации: как типизировать компоненты, функции и работу с `any`/`unknown`.
|
||||
Правила типизации в TypeScript: общие принципы и работа с динамическими типами.
|
||||
|
||||
## Общие правила
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
# NextJS Style Guide
|
||||
|
||||
Правила и стандарты разработки на NextJS и TypeScript: архитектура, типизация, стили, компоненты, API и инфраструктурные разделы.
|
||||
Соглашения по разработке Next.js проектов: архитектура и слои приложения, структура кода, организация модулей, стилизация, типизация и инфраструктура.
|
||||
|
||||
## Для ассистентов
|
||||
## Использование
|
||||
|
||||
Карта документации со ссылками на все разделы (формат [llmstxt.org](https://llmstxt.org)):
|
||||
https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/llms.txt
|
||||
**Для 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/` или другую папку проекта.
|
||||
|
||||
## Структура документации
|
||||
|
||||
@@ -59,3 +65,5 @@ https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/llms.txt
|
||||
| Шрифты | _(не заполнен)_ |
|
||||
| Локализация | _(не заполнен)_ |
|
||||
|
||||
|
||||
|
||||
|
||||
244
generate-llms.ts
244
generate-llms.ts
@@ -301,19 +301,173 @@ const copyMdFiles = (lang: Lang): void => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Собрать `nextjs-style-guide-{lang}.zip` со всеми `.md` локали и `VERSION`.
|
||||
* Внутри архива — единая папка `nextjs-style-guide/`.
|
||||
* Преобразовать sidebar `link` в относительный путь файла внутри архива
|
||||
* (от корня папки `nextjs-style-guide/`). Это путь, по которому файл лежит
|
||||
* в распакованной папке, без расширения добавляется `.md`.
|
||||
*/
|
||||
const linkToArchiveRel = (link: string, lang: Lang): string => {
|
||||
const prefix = `/${lang}/`;
|
||||
let rel = link.startsWith(prefix) ? link.slice(prefix.length) : link.replace(/^\//, '');
|
||||
if (rel === '' || rel.endsWith('/')) {
|
||||
rel += 'index.md';
|
||||
} else {
|
||||
rel += '.md';
|
||||
}
|
||||
return rel;
|
||||
};
|
||||
|
||||
/**
|
||||
* Заменить во всех `.md` архива ссылки `[text](/ru/foo)` на относительные
|
||||
* пути от расположения файла. Без этого внутренние ссылки в распакованной
|
||||
* папке не работают.
|
||||
*/
|
||||
const transformLinksInDir = (rootDir: string, lang: Lang): void => {
|
||||
const linkRe = /\]\(\/([a-z]{2})\/([^)\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, urlLang, route, hash = '') => {
|
||||
// Ссылки на другую локаль не трогаем — её в архиве нет.
|
||||
if (urlLang !== lang) return match;
|
||||
|
||||
const fakeLink = `/${urlLang}/${route}${route.endsWith('/') ? '' : ''}`;
|
||||
const targetRel = linkToArchiveRel(fakeLink, lang);
|
||||
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 = (lang: Lang, rootDir: string): void => {
|
||||
const cfg = config as unknown as {
|
||||
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 blockquote = locale.llmsBlockquote ?? locale.description ?? '';
|
||||
const context = locale.llmsContext;
|
||||
const entries = flattenSidebar(sidebar).filter(
|
||||
// «Главная» из sidebar — это страница локали для веба, в архиве не нужна.
|
||||
(e) => !(e.section === 'Главная' || e.section === 'Home'),
|
||||
);
|
||||
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('');
|
||||
}
|
||||
|
||||
const heading = lang === 'ru' ? 'Содержание' : 'Contents';
|
||||
lines.push(`## ${heading}`);
|
||||
lines.push('');
|
||||
|
||||
for (const [section, items] of grouped) {
|
||||
lines.push(`### ${section}`);
|
||||
lines.push('');
|
||||
for (const entry of items) {
|
||||
const targetRel = './' + linkToArchiveRel(entry.link, lang);
|
||||
const filePath = path.join(rootDir, linkToArchiveRel(entry.link, lang));
|
||||
|
||||
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-{lang}.zip`. Внутри архива — единая папка
|
||||
* `nextjs-style-guide/` с `.md`-файлами локали, README-точкой входа,
|
||||
* `llms-full.txt` и `VERSION`. Внутренние ссылки в `.md` преобразуются
|
||||
* в относительные.
|
||||
*
|
||||
* `llms.txt` в архив не кладём: его ссылки указывают на сайт и локально
|
||||
* не работают. Структура папки сама по себе является картой документации.
|
||||
* Веб-`index.md` локали из архива удаляется — его роль выполняет README.md.
|
||||
*/
|
||||
const buildZip = (lang: Lang): void => {
|
||||
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nsg-'));
|
||||
const stage = path.join(tmpRoot, 'nextjs-style-guide');
|
||||
fs.mkdirSync(stage, { recursive: true });
|
||||
|
||||
// 1. Копируем все .md локали в staging.
|
||||
copyDirSync(path.join('docs', lang), stage, (name) => name.endsWith('.md'));
|
||||
|
||||
// 2. Удаляем веб-index.md локали — в архиве он избыточен.
|
||||
const indexPath = path.join(stage, 'index.md');
|
||||
if (fs.existsSync(indexPath)) fs.unlinkSync(indexPath);
|
||||
|
||||
// 3. Преобразуем абсолютные ссылки `/ru/...` в относительные.
|
||||
transformLinksInDir(stage, lang);
|
||||
|
||||
// 4. Генерируем точку входа README.md.
|
||||
buildArchiveReadme(lang, stage);
|
||||
|
||||
// 5. Кладём llms-full.txt — удобно для одноразового чтения LLM.
|
||||
const llmsFullSrc = path.join(PUBLIC_DIR, lang, 'llms-full.txt');
|
||||
if (fs.existsSync(llmsFullSrc)) {
|
||||
fs.copyFileSync(llmsFullSrc, path.join(stage, 'llms-full.txt'));
|
||||
}
|
||||
|
||||
// 6. Метаинформация сборки.
|
||||
fs.writeFileSync(
|
||||
path.join(stage, 'VERSION'),
|
||||
`${VERSION}\n${BUILD_DATE}\n`,
|
||||
@@ -333,6 +487,84 @@ const buildZip = (lang: Lang): void => {
|
||||
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 = (lang: Lang): void => {
|
||||
const cfg = config as unknown as {
|
||||
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 blockquote = locale.llmsBlockquote ?? locale.description ?? '';
|
||||
|
||||
const parts: string[] = [];
|
||||
parts.push(`# ${cfg.title}`);
|
||||
parts.push('');
|
||||
if (blockquote) parts.push(`> ${blockquote}`);
|
||||
if (locale.llmsContext) {
|
||||
parts.push('');
|
||||
parts.push(locale.llmsContext);
|
||||
}
|
||||
parts.push('');
|
||||
|
||||
for (const entry of entries) {
|
||||
const filePath = linkToFilePath(entry.link, lang);
|
||||
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('');
|
||||
}
|
||||
|
||||
const outDir = path.join(PUBLIC_DIR, lang);
|
||||
fs.mkdirSync(outDir, { recursive: true });
|
||||
const outFile = path.join(outDir, 'llms-full.txt');
|
||||
fs.writeFileSync(outFile, parts.join('\n'), 'utf8');
|
||||
console.log(`${outFile} создан`);
|
||||
};
|
||||
|
||||
/** Манифест сборки — для лендинга и внешних потребителей. */
|
||||
const writeManifest = (): void => {
|
||||
const manifest = {
|
||||
@@ -341,10 +573,12 @@ const writeManifest = (): void => {
|
||||
languages: {
|
||||
ru: {
|
||||
llms: '/ru/llms.txt',
|
||||
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',
|
||||
},
|
||||
},
|
||||
@@ -373,6 +607,8 @@ const buildReadme = (lang: Lang, outFile: string): void => {
|
||||
|
||||
buildLlms('ru');
|
||||
buildLlms('en');
|
||||
buildLlmsFull('ru');
|
||||
buildLlmsFull('en');
|
||||
buildRootIndex();
|
||||
copyMdFiles('ru');
|
||||
copyMdFiles('en');
|
||||
|
||||
Reference in New Issue
Block a user