refactor: перенести сборку в проекты
All checks were successful
CI/CD Pipeline / build (push) Successful in 39s
CI/CD Pipeline / docker (push) Successful in 1m30s
CI/CD Pipeline / deploy (push) Successful in 8s

- перенесены каноны и VitePress-конфиги в projects/<slug>

- добавлены корневой и проектные build.ts для сборки артефактов

- добавлены shared-библиотеки сборки в projects/_shared/lib

- обновлены CI, Dockerfile, package.json, gitignore и README

- удалена сборка frontend-агента
This commit is contained in:
2026-05-22 19:07:10 +03:00
parent a53c5fc1b1
commit bdb99ade62
117 changed files with 442 additions and 568 deletions

View File

@@ -20,22 +20,7 @@ jobs:
- name: Установка зависимостей
run: npm ci
- name: Сборка документации SLM Design
run: npm run docs:build:slm-design
- name: Сборка документации NextJS Style Guide
run: npm run docs:build:nextjs-style-guide
- name: Сборка документации Figma Adaptive Standards
run: npm run docs:build:figma-adaptive-standards
- name: Генерация корневых артефактов
run: npm run site:generate
- name: Сборка агентов
run: npm run agents:build
- name: Сборка лендинга
- name: Сборка артефактов проекта
run: npm run build
docker:

5
.gitignore vendored
View File

@@ -13,14 +13,13 @@ dist-ssr
*.local
# Generated docs
docs/*/content/
docs/*/.vitepress/cache/
projects/*/docs/content/
projects/*/docs/.vitepress/cache/
public/llms.txt
public/slm-design/
public/nextjs-style-guide/
public/figma-adaptive-standards/
public/template-sync-strategy/
public/agents/
# Editor directories and files
.vscode/*

View File

@@ -6,7 +6,7 @@ COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run docs:build:slm-design && npm run docs:build:nextjs-style-guide && npm run docs:build:figma-adaptive-standards && npm run docs:build:template-sync-strategy && npm run agents:build && npm run build
RUN npm run build
FROM caddy:2-alpine

View File

@@ -13,6 +13,7 @@
- VitePress-сборка для `Template Sync Strategy`.
- Корневой `llms.txt` как карта всех документаций.
- Собственные `llms.txt` и `llms-full.txt` внутри каждой документации.
- ZIP-архивы Markdown-контента для каждой документации.
- Docker/Caddy-конфигурация для публикации статической сборки.
- Gitea CI/CD для ветки `master`.
@@ -27,15 +28,16 @@
## Структура
```text
canons/ исходные материалы и черновики
docs/slm-design/ VitePress-сайт SLM Design
docs/nextjs-style-guide/ VitePress-сайт NextJS Style Guide
docs/figma-adaptive-standards/ VitePress-сайт Figma Adaptive Standards
docs/template-sync-strategy/ VitePress-сайт Template Sync Strategy
scripts/docs/ подготовка контента для документаций
scripts/site/ генерация корневых артефактов сайта
build.ts сборка всего репозитория
projects/<slug>/build.ts сборка конкретного проекта
projects/<slug>/canons/ исходные Markdown-материалы проекта
projects/<slug>/docs/ VitePress-конфигурация проекта
projects/<slug>/scripts/ уникальные вспомогательные скрипты проекта
projects/_shared/lib/ общие библиотечные функции сборки
projects/_shared/docs/ общая VitePress-тема
src/ React-лендинг
public/ статические файлы и сгенерированные документации
dist/ итоговая статическая сборка
```
## Команды
@@ -46,25 +48,39 @@ npm run dev
```
```bash
npm run docs:build:slm-design
npm run docs:build:nextjs-style-guide
npm run docs:build:figma-adaptive-standards
npm run docs:build:template-sync-strategy
npm run site:generate
npm run build
```
Основные скрипты:
- `npm run dev` — запускает Vite dev server.
- `npm run docs:build:slm-design`подготавливает и собирает VitePress-документацию SLM Design.
- `npm run docs:build:nextjs-style-guide` — подготавливает и собирает VitePress-документацию NextJS Style Guide.
- `npm run docs:build:figma-adaptive-standards` — подготавливает и собирает VitePress-документацию Figma Adaptive Standards.
- `npm run docs:build:template-sync-strategy` — подготавливает и собирает VitePress-документацию Template Sync Strategy.
- `npm run site:generate`генерирует корневой `public/llms.txt` из `src/config/docs.config.ts` и хардкод-секций.
- `npm run build`генерирует корневые артефакты и собирает лендинг.
- `npm run build` — одной командой собирает проектные документации, ZIP-архивы, корневой `llms.txt`, агентов и лендинг.
- `npm run build:slm-design` — собирает только проект `SLM Design`.
- `npm run build:nextjs-style-guide` — собирает только проект `NextJS Style Guide`.
- `npm run build:figma-adaptive-standards` — собирает только проект `Figma Adaptive Standards`.
- `npm run build:template-sync-strategy`собирает только проект `Template Sync Strategy`.
- `npm run app:build`собирает React/Vite-лендинг без проектных артефактов.
- `npm run lint` — запускает ESLint.
## Проекты
Каждая документация живёт в собственной папке `projects/<slug>` и сама владеет исходниками, конфигами и сборкой.
```text
projects/slm-design/
build.ts
project.config.ts
canons/
docs/
docs.config.ts
.vitepress/
scripts/
```
Общая команда `npm run build` запускает корневой `build.ts`. Он последовательно вызывает `projects/<slug>/build.ts`, затем собирает общие артефакты репозитория.
Если скрипт является библиотечной функцией сборки, он лежит в `projects/_shared/lib`. Если скрипт уникален для проекта, он лежит в `projects/<slug>/scripts`.
## LLM-артефакты
Корневой файл:
@@ -86,6 +102,10 @@ npm run build
/figma-adaptive-standards/llms-full.txt
/template-sync-strategy/llms.txt
/template-sync-strategy/llms-full.txt
/slm-design/slm-design.zip
/nextjs-style-guide/nextjs-style-guide.zip
/figma-adaptive-standards/figma-adaptive-standards.zip
/template-sync-strategy/template-sync-strategy.zip
```
Корневой `llms-full.txt` намеренно не создаётся. Полные bundles остаются внутри конкретных документаций.
@@ -114,10 +134,6 @@ docker build -t all-docs:test .
Docker-сборка выполняет:
```bash
npm run docs:build:slm-design
npm run docs:build:nextjs-style-guide
npm run docs:build:figma-adaptive-standards
npm run docs:build:template-sync-strategy
npm run build
```

View File

@@ -1,88 +0,0 @@
# Frontend Architect
## Роль
Ты frontend-архитектор проекта. К тебе обращаются, когда нужно понять, как правильно спроектировать frontend-часть задачи: где создать код, какие модули затронуть, как разделить ответственность, как связать части приложения и не сломать архитектурные границы.
В этом проекте архитектурным стандартом является SLM Design. Используй его как рабочую модель для всех решений: слой, модуль, сегмент, публичный API, направление зависимостей, business factory и композиция.
## Граница ответственности
- По умолчанию не пиши production-код.
- Не реализуй фичи как frontend-разработчик без явного запроса пользователя.
- Проектируй реализацию задачи: какие слои, модули, сегменты, public API и factory нужны.
- Точно указывай, где создавать или изменять файлы: слой, модуль, сегмент, назначение файла.
- Объясняй, почему код должен жить именно там, а не в другом месте.
- Проводь архитектурное ревью существующего или предлагаемого кода.
- Переходи к правкам кода только если пользователь явно попросил внести изменения.
## Архитектурная основа
Единственная архитектура проекта — SLM Design. Все frontend-решения принимай только в терминах SLM: слой, модуль, сегмент, публичный API, направление зависимостей, business factory.
Не вводи альтернативные архитектурные модели и не объясняй решения через них.
## Рабочий процесс
1. Пойми пользовательскую задачу как frontend-изменение: экран, фича, домен, UI-блок, сервис, состояние, данные или инфраструктура.
2. Найди существующую структуру проекта, если решение зависит от текущего кода.
3. Определи слой SLM, где должна жить основная ответственность.
4. Определи модуль или модули, которые нужно создать или изменить.
5. Определи сегменты и конкретные файлы внутри модулей.
6. Определи публичные API, допустимые импорты и runtime-зависимости.
7. Если решение спорное, открой relevant reference и принимай решение по нему.
8. Сформулируй итог: как реализовать задачу по SLM, куда что положить, какие границы не нарушать.
9. Если пользователь попросил код, сначала зафиксируй архитектурное решение, потом выполняй минимальные правки.
## Инварианты SLM
- Импорты между модулями идут только через публичный API.
- Deep imports во внутренние сегменты чужого модуля запрещены.
- Зависимости идут по направлению SLM-слоёв.
- Runtime-связи между business-доменами идут через factory.
- `import type` не считается runtime-зависимостью.
- Доменные типы не выносятся в `shared`.
- `shared` не знает о продукте и доменах.
- `ui` не содержит бизнес-логику, сценарную логику и обращения к API.
## Архитектурные развилки
При спорных решениях не пересказывай правила из памяти. Открой нужный reference и прими решение по нему.
- Слой и направление зависимостей → `references/slm-layers.md`.
- Модуль, компонент, публичный API, factory → `references/slm-modules.md`.
- Размещение файлов внутри модуля → `references/slm-segments.md`.
- Monorepo и `packages/*``references/slm-monorepo.md`.
- Business factory → `references/slm-react-factory.md`.
- Композиция business factories → `references/slm-react-factory-composition.md`.
- Композиция через Provider → `references/slm-react-composition-provider.md`.
## Что проектировать
- Новый экран: определить `screens/{name}`, локальные `parts/`, нужные business/widget/ui зависимости.
- Новый UI-блок: решить, это локальный `parts/`, `widgets`, `business/{domain}/parts` или `ui`.
- Новая фича: определить доменную принадлежность, business-модуль, factory API и точку композиции.
- Новый business-домен: определить factory, types, hooks/services/mappers/ui и public API.
- Новый сервис: определить, это `infra` или локальная часть модуля.
- Общая утилита: проверить, действительно ли это `shared`, а не локальный `lib/` модуля.
- Monorepo-вынос: проверить, допустим ли `packages/ui`, `packages/infra` или `packages/shared`.
## Формат ответа
Если пользователь просит архитектурное решение, отвечай коротко:
```text
Решение: ...
Почему: ...
Куда: ...
Что создать/изменить: ...
Зависимости: ...
Ограничения: ...
Reference: ...
```
Если пользователь просит ревью, сначала перечисли нарушения и риски, затем предложи SLM-совместимое исправление.
## Поведение при конфликте
Если запрос пользователя требует нарушить SLM, не делай это молча. Коротко объясни конфликт и предложи SLM-совместимый вариант. Если правил недостаточно для решения, задай один уточняющий вопрос.

View File

@@ -1,17 +0,0 @@
{
"name": "frontend-architect",
"title": "Frontend Architect",
"version": "0.1.0",
"description": "Frontend-архитектор для работы по SLM Design.",
"entry": "AGENT.md",
"references": [
"references/slm-overview.md",
"references/slm-layers.md",
"references/slm-modules.md",
"references/slm-segments.md",
"references/slm-monorepo.md",
"references/slm-react-factory.md",
"references/slm-react-factory-composition.md",
"references/slm-react-composition-provider.md"
]
}

View File

@@ -1,36 +0,0 @@
{
"references": [
{
"source": "canons/slm-design/architecture/index.md",
"target": "references/slm-overview.md"
},
{
"source": "canons/slm-design/architecture/layers.md",
"target": "references/slm-layers.md"
},
{
"source": "canons/slm-design/architecture/modules.md",
"target": "references/slm-modules.md"
},
{
"source": "canons/slm-design/architecture/segments.md",
"target": "references/slm-segments.md"
},
{
"source": "canons/slm-design/architecture/monorepo.md",
"target": "references/slm-monorepo.md"
},
{
"source": "canons/slm-design/examples/react/factory.md",
"target": "references/slm-react-factory.md"
},
{
"source": "canons/slm-design/examples/react/factory-composition.md",
"target": "references/slm-react-factory-composition.md"
},
{
"source": "canons/slm-design/examples/react/composition-provider.md",
"target": "references/slm-react-composition-provider.md"
}
]
}

9
build.ts Normal file
View File

@@ -0,0 +1,9 @@
import { generateRootLlms } from './projects/_shared/lib/root-llms';
import { run } from './projects/_shared/lib/run';
run('npm', ['run', 'build:slm-design']);
run('npm', ['run', 'build:nextjs-style-guide']);
run('npm', ['run', 'build:figma-adaptive-standards']);
run('npm', ['run', 'build:template-sync-strategy']);
generateRootLlms();
run('npm', ['run', 'app:build']);

View File

@@ -1 +0,0 @@
export { default } from '../../../shared/vitepress/theme';

View File

@@ -1 +0,0 @@
export { default } from '../../../shared/vitepress/theme';

View File

@@ -1 +0,0 @@
export { default } from '../../../shared/vitepress/theme';

View File

@@ -1 +0,0 @@
export { default } from '../../../shared/vitepress/theme';

View File

@@ -8,8 +8,8 @@ import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores([
'dist',
'docs/*/content',
'docs/*/.vitepress/cache',
'projects/*/docs/content',
'projects/*/docs/.vitepress/cache',
'public',
]),
{

View File

@@ -5,18 +5,12 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "npm run site:generate && tsc -b && vite build",
"docs:prepare:slm-design": "tsx scripts/docs/prepare.ts slm-design",
"docs:build:slm-design": "npm run docs:prepare:slm-design && vitepress build docs/slm-design",
"docs:prepare:nextjs-style-guide": "tsx scripts/docs/prepare.ts nextjs-style-guide",
"docs:build:nextjs-style-guide": "npm run docs:prepare:nextjs-style-guide && vitepress build docs/nextjs-style-guide",
"docs:prepare:figma-adaptive-standards": "tsx scripts/docs/prepare.ts figma-adaptive-standards",
"docs:build:figma-adaptive-standards": "npm run docs:prepare:figma-adaptive-standards && vitepress build docs/figma-adaptive-standards",
"docs:prepare:template-sync-strategy": "tsx scripts/docs/prepare.ts template-sync-strategy",
"docs:build:template-sync-strategy": "npm run docs:prepare:template-sync-strategy && vitepress build docs/template-sync-strategy",
"site:generate": "tsx scripts/site/generate-artifacts.ts",
"agent:build:frontend-architect": "tsx agents/frontend-architect/scripts/build.ts",
"agents:build": "npm run agent:build:frontend-architect",
"build": "tsx build.ts",
"app:build": "tsc -b && vite build",
"build:slm-design": "tsx projects/slm-design/build.ts",
"build:nextjs-style-guide": "tsx projects/nextjs-style-guide/build.ts",
"build:figma-adaptive-standards": "tsx projects/figma-adaptive-standards/build.ts",
"build:template-sync-strategy": "tsx projects/template-sync-strategy/build.ts",
"lint": "eslint .",
"preview": "vite preview"
},

View File

@@ -0,0 +1,112 @@
import fs from 'node:fs';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
type Page = {
source: string;
target: string;
};
type RouteRewrite = {
from: string;
to: string;
};
type DocsConfig = {
mounts: Page[];
routeRewrites?: RouteRewrite[];
};
type ProjectConfig = {
slug: string;
docsDir: string;
};
const MD_LINK_RE = /\]\((?!#|[a-z][a-z0-9+.-]*:|\/\/)([^)\s]+)(#[^)]*)?\)/gi;
function normalizePath(value: string) {
return value.split(path.sep).join('/').replace(/^\.\//, '');
}
function formatRelativeMarkdownPath(fromTarget: string, toTarget: string) {
const relative = path.relative(path.dirname(fromTarget), toTarget).split(path.sep).join('/');
return relative.startsWith('.') ? relative : `./${relative}`;
}
async function loadDocsConfig(configPath: string) {
return (await import(`${pathToFileURL(configPath).href}?t=${Date.now()}`)) as DocsConfig;
}
export async function prepareDocs(projectDir: string, config: ProjectConfig) {
const docsDir = path.join(projectDir, config.docsDir);
const contentDir = path.join(docsDir, 'content');
const docsConfig = await loadDocsConfig(path.join(docsDir, 'docs.config.ts'));
const targetBySource = new Map(
docsConfig.mounts.map((page) => [normalizePath(path.resolve(projectDir, page.source)), normalizePath(page.target)]),
);
const routeRewrites = [...(docsConfig.routeRewrites ?? [])].sort((a, b) => b.from.length - a.from.length);
function applyRouteRewrites(route: string) {
for (const rewrite of routeRewrites) {
if (route === rewrite.from || route.startsWith(`${rewrite.from}/`) || route.startsWith(`${rewrite.from}#`)) {
return `${rewrite.to}${route.slice(rewrite.from.length)}`;
}
}
return undefined;
}
function formatDocsRoute(route: string) {
const rewritten = applyRouteRewrites(route);
if (rewritten) return rewritten;
if (route === '/docs') return '/';
if (route.startsWith('/docs/')) return route.slice('/docs'.length);
return undefined;
}
function formatRelativeRoute(hrefPath: string, sourceDir: string) {
const sourcePath = normalizePath(path.relative(projectDir, path.resolve(sourceDir, hrefPath)));
if (sourcePath.startsWith('canons/')) return formatDocsRoute(`/docs/${sourcePath.slice('canons/'.length)}`);
return undefined;
}
function transformMarkdownLinks(content: string, page: Page) {
const sourceDir = path.dirname(path.resolve(projectDir, page.source));
return content.replace(MD_LINK_RE, (match, href: string, hash = '') => {
const [hrefPath, query = ''] = href.split('?');
const queryPart = query ? `?${query}` : '';
if (hrefPath.startsWith('/')) {
const route = formatDocsRoute(hrefPath) ?? applyRouteRewrites(hrefPath);
return route ? `](${route}${queryPart}${hash})` : match;
}
if (!hrefPath.endsWith('.md')) {
const route = formatRelativeRoute(hrefPath, sourceDir);
return route ? `](${route}${queryPart}${hash})` : match;
}
const target = targetBySource.get(normalizePath(path.resolve(sourceDir, hrefPath)));
if (!target) return match;
return `](${formatRelativeMarkdownPath(page.target, `${target}${queryPart}`)}${hash})`;
});
}
fs.rmSync(contentDir, { recursive: true, force: true });
fs.mkdirSync(contentDir, { recursive: true });
for (const page of docsConfig.mounts) {
const sourcePath = path.resolve(projectDir, page.source);
const targetPath = path.join(contentDir, page.target);
if (!fs.existsSync(sourcePath)) throw new Error(`Не найден канон: ${sourcePath}`);
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.writeFileSync(targetPath, transformMarkdownLinks(fs.readFileSync(sourcePath, 'utf8'), page), 'utf8');
console.log(`${page.target} -> ${path.relative(projectDir, sourcePath)}`);
}
console.log(`Подготовлен VitePress content для ${config.slug}: ${docsConfig.mounts.length} страниц`);
}

View File

@@ -0,0 +1,51 @@
import fs from 'node:fs';
import path from 'node:path';
import { docs } from '../../../src/config/docs.config';
const siteTitle = 'Документация';
const siteDescription = 'Единое пространство для идей, черновиков и первых версий документаций, которые ещё формируются и постепенно становятся самостоятельными материалами.';
function formatMarkdownLink(label: string, href: string, description: string) {
return `- [${label}](${href}): ${description}`;
}
function findDocLink(doc: (typeof docs)[number], label: string) {
return doc.links.find((link) => link.label === label);
}
export function generateRootLlms(rootDir = process.cwd()) {
const publicDir = path.join(rootDir, 'public');
const llmsPath = path.join(publicDir, 'llms.txt');
const content = [
`# ${siteTitle}`,
'',
`> ${siteDescription}`,
'',
'Этот файл является корневой картой документаций. Для работы с конкретным направлением используйте его собственный `llms.txt`.',
'',
'## Documentation',
'',
...docs
.map((doc) => {
const link = findDocLink(doc, 'llms.txt');
return link ? formatMarkdownLink(doc.title, link.href, doc.description) : undefined;
})
.filter(Boolean),
'',
'## Optional',
'',
...docs
.map((doc) => {
const link = findDocLink(doc, 'llms-full.txt');
return link ? formatMarkdownLink(`${doc.title} full`, link.href, `Полный bundle документации: ${doc.label.toLowerCase()}.`) : undefined;
})
.filter(Boolean),
'',
].join('\n');
fs.mkdirSync(publicDir, { recursive: true });
fs.writeFileSync(llmsPath, content, 'utf8');
console.log(`Сгенерирован ${path.relative(rootDir, llmsPath)}`);
}

View File

@@ -0,0 +1,12 @@
import { spawnSync } from 'node:child_process';
export function run(command: string, args: string[], cwd = process.cwd()) {
const result = spawnSync(command, args, {
cwd,
stdio: 'inherit',
shell: process.platform === 'win32',
});
if (result.error) throw result.error;
if (result.status !== 0) throw new Error(`Command failed: ${[command, ...args].join(' ')}`);
}

View File

@@ -1,53 +1,24 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
type ReferenceConfig = {
references: Array<{
source: string;
target: string;
}>;
};
type ZipEntry = {
name: string;
content: Buffer;
};
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const agentDir = path.resolve(scriptDir, '..');
const rootDir = path.resolve(agentDir, '../..');
const sourceDir = path.join(agentDir, 'source');
const tempDir = path.join(agentDir, '.tmp');
const packageName = 'frontend-architect';
const packageDir = path.join(tempDir, packageName);
const publicAgentsDir = path.join(rootDir, 'public', 'agents');
const zipPath = path.join(publicAgentsDir, `${packageName}.zip`);
function readJson<T>(filePath: string): T {
return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T;
}
function copyFile(sourcePath: string, targetPath: string) {
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.copyFileSync(sourcePath, targetPath);
}
function collectFiles(dir: string, baseDir = dir): ZipEntry[] {
function collectFiles(dir: string, baseDir = dir, archiveRoot = path.basename(dir)): ZipEntry[] {
return fs
.readdirSync(dir, { withFileTypes: true })
.flatMap((entry) => {
const entryPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
return collectFiles(entryPath, baseDir);
}
if (entry.isDirectory()) return collectFiles(entryPath, baseDir, archiveRoot);
const relativePath = path.relative(baseDir, entryPath).split(path.sep).join('/');
return [
{
name: `${packageName}/${relativePath}`,
name: `${archiveRoot}/${relativePath}`,
content: fs.readFileSync(entryPath),
},
];
@@ -120,7 +91,7 @@ function createZip(entries: ZipEntry[]) {
localParts.push(localHeader, entry.content);
const centralHeader = Buffer.concat([
centralParts.push(Buffer.concat([
writeUInt32(0x02014b50),
writeUInt16(20),
writeUInt16(20),
@@ -139,9 +110,8 @@ function createZip(entries: ZipEntry[]) {
writeUInt32(0),
writeUInt32(offset),
fileName,
]);
]));
centralParts.push(centralHeader);
offset += localHeader.length + size;
}
@@ -160,30 +130,7 @@ function createZip(entries: ZipEntry[]) {
return Buffer.concat([...localParts, centralDirectory, endOfCentralDirectory]);
}
function buildAgent() {
const referencesConfig = readJson<ReferenceConfig>(path.join(sourceDir, 'references.json'));
fs.rmSync(tempDir, { recursive: true, force: true });
fs.mkdirSync(packageDir, { recursive: true });
copyFile(path.join(sourceDir, 'AGENT.md'), path.join(packageDir, 'AGENT.md'));
copyFile(path.join(sourceDir, 'manifest.json'), path.join(packageDir, 'manifest.json'));
for (const reference of referencesConfig.references) {
const sourcePath = path.join(rootDir, reference.source);
if (!fs.existsSync(sourcePath)) {
throw new Error(`Reference not found: ${reference.source}`);
export function writeZipFromDirectory(sourceDir: string, zipPath: string, archiveRoot = path.basename(sourceDir)) {
fs.mkdirSync(path.dirname(zipPath), { recursive: true });
fs.writeFileSync(zipPath, createZip(collectFiles(sourceDir, sourceDir, archiveRoot)));
}
copyFile(sourcePath, path.join(packageDir, reference.target));
}
fs.mkdirSync(publicAgentsDir, { recursive: true });
fs.writeFileSync(zipPath, createZip(collectFiles(packageDir)));
fs.rmSync(tempDir, { recursive: true, force: true });
console.log(`Built ${path.relative(rootDir, zipPath)}`);
}
buildAgent();

View File

@@ -0,0 +1,20 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { prepareDocs } from '../_shared/lib/prepare-docs';
import { run } from '../_shared/lib/run';
import { writeZipFromDirectory } from '../_shared/lib/zip';
import config from './project.config';
const projectDir = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(projectDir, '../..');
const docsDir = path.join(projectDir, config.docsDir);
await prepareDocs(projectDir, config);
run('npx', ['vitepress', 'build', docsDir], rootDir);
if (config.archive) {
const zipPath = path.join(rootDir, 'public', config.slug, `${config.slug}.zip`);
writeZipFromDirectory(path.join(docsDir, 'content'), zipPath, config.slug);
console.log(`Собран ${path.relative(rootDir, zipPath)}`);
}

View File

@@ -1,7 +1,7 @@
import { defineConfig } from 'vitepress';
import taskLists from 'markdown-it-task-lists';
import llmstxt from 'vitepress-plugin-llms';
import { themeSyncHead } from '../../shared/vitepress/themeHead';
import { themeSyncHead } from '../../../_shared/docs/vitepress/themeHead';
import { sidebar, site } from '../docs.config';
export default defineConfig({

View File

@@ -0,0 +1 @@
export { default } from '../../../../_shared/docs/vitepress/theme';

View File

@@ -2,22 +2,22 @@ export const site = {
title: 'Figma Adaptive Standards',
description: 'Стандарты подготовки адаптивных макетов в Figma',
base: '/figma-adaptive-standards/',
outDir: '../../public/figma-adaptive-standards',
outDir: '../../../public/figma-adaptive-standards',
};
/**
* Карта монтирования исходных канонов в VitePress-документацию.
*
* `source` указывает на markdown-файл внутри `canons/`.
* `target` задаёт путь, по которому этот файл попадёт в `docs/figma-adaptive-standards/content/`
* `target` задаёт путь, по которому этот файл попадёт в `docs/content/`
* и станет страницей итоговой документации.
*/
export const mounts = [
{ target: 'index.md', source: 'figma/index.md' },
{ target: 'overview.md', source: 'figma/index.md' },
{ target: 'adaptive-layout-requirements/short.md', source: 'figma/adaptive-layout-requirements.short.md' },
{ target: 'adaptive-layout-requirements/full.md', source: 'figma/adaptive-layout-requirements.full.md' },
{ target: 'adaptive-layout-requirements/checklist.md', source: 'figma/adaptive-layout-requirements.checklist.md' },
{ target: 'index.md', source: 'canons/index.md' },
{ target: 'overview.md', source: 'canons/index.md' },
{ target: 'adaptive-layout-requirements/short.md', source: 'canons/adaptive-layout-requirements.short.md' },
{ target: 'adaptive-layout-requirements/full.md', source: 'canons/adaptive-layout-requirements.full.md' },
{ target: 'adaptive-layout-requirements/checklist.md', source: 'canons/adaptive-layout-requirements.checklist.md' },
];
export const sidebar = [

View File

@@ -0,0 +1,5 @@
export default {
slug: 'figma-adaptive-standards',
docsDir: 'docs',
archive: true,
} as const;

View File

@@ -0,0 +1,20 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { prepareDocs } from '../_shared/lib/prepare-docs';
import { run } from '../_shared/lib/run';
import { writeZipFromDirectory } from '../_shared/lib/zip';
import config from './project.config';
const projectDir = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(projectDir, '../..');
const docsDir = path.join(projectDir, config.docsDir);
await prepareDocs(projectDir, config);
run('npx', ['vitepress', 'build', docsDir], rootDir);
if (config.archive) {
const zipPath = path.join(rootDir, 'public', config.slug, `${config.slug}.zip`);
writeZipFromDirectory(path.join(docsDir, 'content'), zipPath, config.slug);
console.log(`Собран ${path.relative(rootDir, zipPath)}`);
}

View File

@@ -16,7 +16,7 @@ description: Практический стайлгайд для разработ
**Для проекта:**
- [nextjs-style-guide.zip](/nextjs-style-guide.zip) — Набор Markdown-файлов для распаковки в `./ai/nextjs-style-guide/` или другую папку проекта.
- [nextjs-style-guide.zip](/nextjs-style-guide/nextjs-style-guide.zip) — Набор Markdown-файлов для распаковки в `./ai/nextjs-style-guide/` или другую папку проекта.
## Структура документации

View File

@@ -1,7 +1,7 @@
import { defineConfig } from 'vitepress';
import taskLists from 'markdown-it-task-lists';
import llmstxt from 'vitepress-plugin-llms';
import { themeSyncHead } from '../../shared/vitepress/themeHead';
import { themeSyncHead } from '../../../_shared/docs/vitepress/themeHead';
import { sidebar, site } from '../docs.config';
export default defineConfig({
@@ -11,6 +11,7 @@ export default defineConfig({
outDir: site.outDir,
srcDir: 'content',
cleanUrls: true,
ignoreDeadLinks: [/^\/slm-design\//],
head: [...themeSyncHead],
vite: {
plugins: [llmstxt()],

View File

@@ -0,0 +1 @@
export { default } from '../../../../_shared/docs/vitepress/theme';

View File

@@ -2,71 +2,71 @@ export const site = {
title: 'NextJS Style Guide',
description: 'Практический стайлгайд для разработки frontend-приложений на Next.js и TypeScript',
base: '/nextjs-style-guide/',
outDir: '../../public/nextjs-style-guide',
outDir: '../../../public/nextjs-style-guide',
};
/**
* Карта монтирования исходных канонов в VitePress-документацию.
*
* SLM-разделы берутся только из корневого `canons/slm-design/`, чтобы
* NextJS-гайд не содержал собственных дублей архитектурного канона.
* SLM-разделы берутся из проекта `slm-design`, чтобы NextJS-гайд не содержал
* собственных дублей архитектурного канона.
*/
export const mounts = [
{ target: 'index.md', source: 'style-guide/index.md' },
{ target: 'workflow.md', source: 'style-guide/workflow.md' },
{ target: 'index.md', source: 'canons/index.md' },
{ target: 'workflow.md', source: 'canons/workflow.md' },
{ target: 'slm-design/architecture/index.md', source: 'slm-design/architecture/index.md' },
{ target: 'slm-design/architecture/layers.md', source: 'slm-design/architecture/layers.md' },
{ target: 'slm-design/architecture/modules.md', source: 'slm-design/architecture/modules.md' },
{ target: 'slm-design/architecture/segments.md', source: 'slm-design/architecture/segments.md' },
{ target: 'slm-design/architecture/monorepo.md', source: 'slm-design/architecture/monorepo.md' },
{ target: 'slm-design/examples/react/factory.md', source: 'slm-design/examples/react/factory.md' },
{ target: 'slm-design/examples/react/factory-composition.md', source: 'slm-design/examples/react/factory-composition.md' },
{ target: 'slm-design/examples/react/composition-provider.md', source: 'slm-design/examples/react/composition-provider.md' },
{ target: 'slm-design/architecture/index.md', source: '../slm-design/canons/architecture/index.md' },
{ target: 'slm-design/architecture/layers.md', source: '../slm-design/canons/architecture/layers.md' },
{ target: 'slm-design/architecture/modules.md', source: '../slm-design/canons/architecture/modules.md' },
{ target: 'slm-design/architecture/segments.md', source: '../slm-design/canons/architecture/segments.md' },
{ target: 'slm-design/architecture/monorepo.md', source: '../slm-design/canons/architecture/monorepo.md' },
{ target: 'slm-design/examples/react/factory.md', source: '../slm-design/canons/examples/react/factory.md' },
{ target: 'slm-design/examples/react/factory-composition.md', source: '../slm-design/canons/examples/react/factory-composition.md' },
{ target: 'slm-design/examples/react/composition-provider.md', source: '../slm-design/canons/examples/react/composition-provider.md' },
{ target: 'basics/tech-stack.md', source: 'style-guide/basics/tech-stack.md' },
{ target: 'basics/naming.md', source: 'style-guide/basics/naming.md' },
{ target: 'basics/code-style.md', source: 'style-guide/basics/code-style.md' },
{ target: 'basics/documentation.md', source: 'style-guide/basics/documentation.md' },
{ target: 'basics/typing.md', source: 'style-guide/basics/typing.md' },
{ target: 'basics/tech-stack.md', source: 'canons/basics/tech-stack.md' },
{ target: 'basics/naming.md', source: 'canons/basics/naming.md' },
{ target: 'basics/code-style.md', source: 'canons/basics/code-style.md' },
{ target: 'basics/documentation.md', source: 'canons/basics/documentation.md' },
{ target: 'basics/typing.md', source: 'canons/basics/typing.md' },
{ target: 'applied/creating-project/from-template.md', source: 'style-guide/applied/creating-project/from-template.md' },
{ target: 'applied/creating-project/manual.md', source: 'style-guide/applied/creating-project/manual.md' },
{ target: 'applied/creating-project/nextjs.md', source: 'style-guide/applied/creating-project/nextjs.md' },
{ target: 'applied/project-structure.md', source: 'style-guide/applied/project-structure.md' },
{ target: 'applied/page-level.md', source: 'style-guide/applied/page-level.md' },
{ target: 'applied/component.md', source: 'style-guide/applied/component.md' },
{ target: 'applied/module.md', source: 'style-guide/applied/module.md' },
{ target: 'applied/rest-client/index.md', source: 'style-guide/applied/rest-client/index.md' },
{ target: 'applied/rest-client/setup/index.md', source: 'style-guide/applied/rest-client/setup/index.md' },
{ target: 'applied/rest-client/setup/auto.md', source: 'style-guide/applied/rest-client/setup/auto.md' },
{ target: 'applied/rest-client/setup/manual.md', source: 'style-guide/applied/rest-client/setup/manual.md' },
{ target: 'applied/rest-client/setup/hooks.md', source: 'style-guide/applied/rest-client/setup/hooks.md' },
{ target: 'applied/rest-client/usage.md', source: 'style-guide/applied/rest-client/usage.md' },
{ target: 'applied/data-fetch/index.md', source: 'style-guide/applied/data-fetch/index.md' },
{ target: 'applied/data-fetch/server-await.md', source: 'style-guide/applied/data-fetch/server-await.md' },
{ target: 'applied/data-fetch/parallel-server-requests.md', source: 'style-guide/applied/data-fetch/parallel-server-requests.md' },
{ target: 'applied/data-fetch/pass-promise-down.md', source: 'style-guide/applied/data-fetch/pass-promise-down.md' },
{ target: 'applied/data-fetch/client-hooks-initial-data.md', source: 'style-guide/applied/data-fetch/client-hooks-initial-data.md' },
{ target: 'applied/data-fetch/client-get-hook.md', source: 'style-guide/applied/data-fetch/client-get-hook.md' },
{ target: 'applied/data-fetch/business-composition.md', source: 'style-guide/applied/data-fetch/business-composition.md' },
{ target: 'applied/styles/styles-setup.md', source: 'style-guide/applied/styles/styles-setup.md' },
{ target: 'applied/styles/styles-usage.md', source: 'style-guide/applied/styles/styles-usage.md' },
{ target: 'applied/svg-sprites/svg-sprites-intro.md', source: 'style-guide/applied/svg-sprites/svg-sprites-intro.md' },
{ target: 'applied/svg-sprites/svg-sprites-setup.md', source: 'style-guide/applied/svg-sprites/svg-sprites-setup.md' },
{ target: 'applied/svg-sprites/svg-sprites-usage.md', source: 'style-guide/applied/svg-sprites/svg-sprites-usage.md' },
{ target: 'applied/images.md', source: 'style-guide/applied/images.md' },
{ target: 'applied/fonts.md', source: 'style-guide/applied/fonts.md' },
{ target: 'applied/aliases.md', source: 'style-guide/applied/aliases.md' },
{ target: 'applied/templates/templates-intro.md', source: 'style-guide/applied/templates/templates-intro.md' },
{ target: 'applied/templates/templates-setup.md', source: 'style-guide/applied/templates/templates-setup.md' },
{ target: 'applied/templates/templates-create.md', source: 'style-guide/applied/templates/templates-create.md' },
{ target: 'applied/templates/templates-usage.md', source: 'style-guide/applied/templates/templates-usage.md' },
{ target: 'applied/biome.md', source: 'style-guide/applied/biome.md' },
{ target: 'applied/postcss.md', source: 'style-guide/applied/postcss.md' },
{ target: 'applied/vscode.md', source: 'style-guide/applied/vscode.md' },
{ target: 'applied/localization.md', source: 'style-guide/applied/localization.md' },
{ target: 'applied/stores.md', source: 'style-guide/applied/stores.md' },
{ target: 'applied/creating-project/from-template.md', source: 'canons/applied/creating-project/from-template.md' },
{ target: 'applied/creating-project/manual.md', source: 'canons/applied/creating-project/manual.md' },
{ target: 'applied/creating-project/nextjs.md', source: 'canons/applied/creating-project/nextjs.md' },
{ target: 'applied/project-structure.md', source: 'canons/applied/project-structure.md' },
{ target: 'applied/page-level.md', source: 'canons/applied/page-level.md' },
{ target: 'applied/component.md', source: 'canons/applied/component.md' },
{ target: 'applied/module.md', source: 'canons/applied/module.md' },
{ target: 'applied/rest-client/index.md', source: 'canons/applied/rest-client/index.md' },
{ target: 'applied/rest-client/setup/index.md', source: 'canons/applied/rest-client/setup/index.md' },
{ target: 'applied/rest-client/setup/auto.md', source: 'canons/applied/rest-client/setup/auto.md' },
{ target: 'applied/rest-client/setup/manual.md', source: 'canons/applied/rest-client/setup/manual.md' },
{ target: 'applied/rest-client/setup/hooks.md', source: 'canons/applied/rest-client/setup/hooks.md' },
{ target: 'applied/rest-client/usage.md', source: 'canons/applied/rest-client/usage.md' },
{ target: 'applied/data-fetch/index.md', source: 'canons/applied/data-fetch/index.md' },
{ target: 'applied/data-fetch/server-await.md', source: 'canons/applied/data-fetch/server-await.md' },
{ target: 'applied/data-fetch/parallel-server-requests.md', source: 'canons/applied/data-fetch/parallel-server-requests.md' },
{ target: 'applied/data-fetch/pass-promise-down.md', source: 'canons/applied/data-fetch/pass-promise-down.md' },
{ target: 'applied/data-fetch/client-hooks-initial-data.md', source: 'canons/applied/data-fetch/client-hooks-initial-data.md' },
{ target: 'applied/data-fetch/client-get-hook.md', source: 'canons/applied/data-fetch/client-get-hook.md' },
{ target: 'applied/data-fetch/business-composition.md', source: 'canons/applied/data-fetch/business-composition.md' },
{ target: 'applied/styles/styles-setup.md', source: 'canons/applied/styles/styles-setup.md' },
{ target: 'applied/styles/styles-usage.md', source: 'canons/applied/styles/styles-usage.md' },
{ target: 'applied/svg-sprites/svg-sprites-intro.md', source: 'canons/applied/svg-sprites/svg-sprites-intro.md' },
{ target: 'applied/svg-sprites/svg-sprites-setup.md', source: 'canons/applied/svg-sprites/svg-sprites-setup.md' },
{ target: 'applied/svg-sprites/svg-sprites-usage.md', source: 'canons/applied/svg-sprites/svg-sprites-usage.md' },
{ target: 'applied/images.md', source: 'canons/applied/images.md' },
{ target: 'applied/fonts.md', source: 'canons/applied/fonts.md' },
{ target: 'applied/aliases.md', source: 'canons/applied/aliases.md' },
{ target: 'applied/templates/templates-intro.md', source: 'canons/applied/templates/templates-intro.md' },
{ target: 'applied/templates/templates-setup.md', source: 'canons/applied/templates/templates-setup.md' },
{ target: 'applied/templates/templates-create.md', source: 'canons/applied/templates/templates-create.md' },
{ target: 'applied/templates/templates-usage.md', source: 'canons/applied/templates/templates-usage.md' },
{ target: 'applied/biome.md', source: 'canons/applied/biome.md' },
{ target: 'applied/postcss.md', source: 'canons/applied/postcss.md' },
{ target: 'applied/vscode.md', source: 'canons/applied/vscode.md' },
{ target: 'applied/localization.md', source: 'canons/applied/localization.md' },
{ target: 'applied/stores.md', source: 'canons/applied/stores.md' },
];
export const routeRewrites = [

View File

@@ -0,0 +1,5 @@
export default {
slug: 'nextjs-style-guide',
docsDir: 'docs',
archive: true,
} as const;

View File

@@ -0,0 +1,20 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { prepareDocs } from '../_shared/lib/prepare-docs';
import { run } from '../_shared/lib/run';
import { writeZipFromDirectory } from '../_shared/lib/zip';
import config from './project.config';
const projectDir = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(projectDir, '../..');
const docsDir = path.join(projectDir, config.docsDir);
await prepareDocs(projectDir, config);
run('npx', ['vitepress', 'build', docsDir], rootDir);
if (config.archive) {
const zipPath = path.join(rootDir, 'public', config.slug, `${config.slug}.zip`);
writeZipFromDirectory(path.join(docsDir, 'content'), zipPath, config.slug);
console.log(`Собран ${path.relative(rootDir, zipPath)}`);
}

View File

@@ -1,7 +1,7 @@
import { defineConfig } from 'vitepress';
import taskLists from 'markdown-it-task-lists';
import llmstxt from 'vitepress-plugin-llms';
import { themeSyncHead } from '../../shared/vitepress/themeHead';
import { themeSyncHead } from '../../../_shared/docs/vitepress/themeHead';
import { sidebar, site } from '../docs.config';
export default defineConfig({

View File

@@ -0,0 +1 @@
export { default } from '../../../../_shared/docs/vitepress/theme';

View File

@@ -2,26 +2,26 @@ export const site = {
title: 'SLM Design',
description: 'Каноны архитектуры SLM Design',
base: '/slm-design/',
outDir: '../../public/slm-design',
outDir: '../../../public/slm-design',
};
/**
* Карта монтирования исходных канонов в VitePress-документацию.
*
* `source` указывает на markdown-файл внутри `canons/`.
* `target` задаёт путь, по которому этот файл попадёт в `docs/slm-design/content/`
* `target` задаёт путь, по которому этот файл попадёт в `docs/content/`
* и станет страницей итоговой документации.
*/
export const mounts = [
{ target: 'index.md', source: 'slm-design/architecture/index.md' },
{ target: 'architecture/index.md', source: 'slm-design/architecture/index.md' },
{ target: 'architecture/layers.md', source: 'slm-design/architecture/layers.md' },
{ target: 'architecture/modules.md', source: 'slm-design/architecture/modules.md' },
{ target: 'architecture/segments.md', source: 'slm-design/architecture/segments.md' },
{ target: 'architecture/monorepo.md', source: 'slm-design/architecture/monorepo.md' },
{ target: 'examples/react/factory.md', source: 'slm-design/examples/react/factory.md' },
{ target: 'examples/react/factory-composition.md', source: 'slm-design/examples/react/factory-composition.md' },
{ target: 'examples/react/composition-provider.md', source: 'slm-design/examples/react/composition-provider.md' },
{ target: 'index.md', source: 'canons/architecture/index.md' },
{ target: 'architecture/index.md', source: 'canons/architecture/index.md' },
{ target: 'architecture/layers.md', source: 'canons/architecture/layers.md' },
{ target: 'architecture/modules.md', source: 'canons/architecture/modules.md' },
{ target: 'architecture/segments.md', source: 'canons/architecture/segments.md' },
{ target: 'architecture/monorepo.md', source: 'canons/architecture/monorepo.md' },
{ target: 'examples/react/factory.md', source: 'canons/examples/react/factory.md' },
{ target: 'examples/react/factory-composition.md', source: 'canons/examples/react/factory-composition.md' },
{ target: 'examples/react/composition-provider.md', source: 'canons/examples/react/composition-provider.md' },
];
export const sidebar = [

View File

@@ -0,0 +1,5 @@
export default {
slug: 'slm-design',
docsDir: 'docs',
archive: true,
} as const;

View File

View File

@@ -0,0 +1,20 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { prepareDocs } from '../_shared/lib/prepare-docs';
import { run } from '../_shared/lib/run';
import { writeZipFromDirectory } from '../_shared/lib/zip';
import config from './project.config';
const projectDir = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(projectDir, '../..');
const docsDir = path.join(projectDir, config.docsDir);
await prepareDocs(projectDir, config);
run('npx', ['vitepress', 'build', docsDir], rootDir);
if (config.archive) {
const zipPath = path.join(rootDir, 'public', config.slug, `${config.slug}.zip`);
writeZipFromDirectory(path.join(docsDir, 'content'), zipPath, config.slug);
console.log(`Собран ${path.relative(rootDir, zipPath)}`);
}

Some files were not shown because too many files have changed in this diff Show More