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: Установка зависимостей - name: Установка зависимостей
run: npm ci run: npm ci
- name: Сборка документации SLM Design - name: Сборка артефактов проекта
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: Сборка лендинга
run: npm run build run: npm run build
docker: docker:

5
.gitignore vendored
View File

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

View File

@@ -6,7 +6,7 @@ COPY package*.json ./
RUN npm ci RUN npm ci
COPY . . 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 FROM caddy:2-alpine

View File

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

View File

@@ -5,18 +5,12 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "npm run site:generate && tsc -b && vite build", "build": "tsx build.ts",
"docs:prepare:slm-design": "tsx scripts/docs/prepare.ts slm-design", "app:build": "tsc -b && vite build",
"docs:build:slm-design": "npm run docs:prepare:slm-design && vitepress build docs/slm-design", "build:slm-design": "tsx projects/slm-design/build.ts",
"docs:prepare:nextjs-style-guide": "tsx scripts/docs/prepare.ts nextjs-style-guide", "build:nextjs-style-guide": "tsx projects/nextjs-style-guide/build.ts",
"docs:build:nextjs-style-guide": "npm run docs:prepare:nextjs-style-guide && vitepress build docs/nextjs-style-guide", "build:figma-adaptive-standards": "tsx projects/figma-adaptive-standards/build.ts",
"docs:prepare:figma-adaptive-standards": "tsx scripts/docs/prepare.ts figma-adaptive-standards", "build:template-sync-strategy": "tsx projects/template-sync-strategy/build.ts",
"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",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview" "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 fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url';
type ReferenceConfig = {
references: Array<{
source: string;
target: string;
}>;
};
type ZipEntry = { type ZipEntry = {
name: string; name: string;
content: Buffer; content: Buffer;
}; };
const scriptDir = path.dirname(fileURLToPath(import.meta.url)); function collectFiles(dir: string, baseDir = dir, archiveRoot = path.basename(dir)): ZipEntry[] {
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[] {
return fs return fs
.readdirSync(dir, { withFileTypes: true }) .readdirSync(dir, { withFileTypes: true })
.flatMap((entry) => { .flatMap((entry) => {
const entryPath = path.join(dir, entry.name); const entryPath = path.join(dir, entry.name);
if (entry.isDirectory()) { if (entry.isDirectory()) return collectFiles(entryPath, baseDir, archiveRoot);
return collectFiles(entryPath, baseDir);
}
const relativePath = path.relative(baseDir, entryPath).split(path.sep).join('/'); const relativePath = path.relative(baseDir, entryPath).split(path.sep).join('/');
return [ return [
{ {
name: `${packageName}/${relativePath}`, name: `${archiveRoot}/${relativePath}`,
content: fs.readFileSync(entryPath), content: fs.readFileSync(entryPath),
}, },
]; ];
@@ -120,7 +91,7 @@ function createZip(entries: ZipEntry[]) {
localParts.push(localHeader, entry.content); localParts.push(localHeader, entry.content);
const centralHeader = Buffer.concat([ centralParts.push(Buffer.concat([
writeUInt32(0x02014b50), writeUInt32(0x02014b50),
writeUInt16(20), writeUInt16(20),
writeUInt16(20), writeUInt16(20),
@@ -139,9 +110,8 @@ function createZip(entries: ZipEntry[]) {
writeUInt32(0), writeUInt32(0),
writeUInt32(offset), writeUInt32(offset),
fileName, fileName,
]); ]));
centralParts.push(centralHeader);
offset += localHeader.length + size; offset += localHeader.length + size;
} }
@@ -160,30 +130,7 @@ function createZip(entries: ZipEntry[]) {
return Buffer.concat([...localParts, centralDirectory, endOfCentralDirectory]); return Buffer.concat([...localParts, centralDirectory, endOfCentralDirectory]);
} }
function buildAgent() { export function writeZipFromDirectory(sourceDir: string, zipPath: string, archiveRoot = path.basename(sourceDir)) {
const referencesConfig = readJson<ReferenceConfig>(path.join(sourceDir, 'references.json')); fs.mkdirSync(path.dirname(zipPath), { recursive: true });
fs.writeFileSync(zipPath, createZip(collectFiles(sourceDir, sourceDir, archiveRoot)));
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}`);
}
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 { defineConfig } from 'vitepress';
import taskLists from 'markdown-it-task-lists'; import taskLists from 'markdown-it-task-lists';
import llmstxt from 'vitepress-plugin-llms'; 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'; import { sidebar, site } from '../docs.config';
export default defineConfig({ 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', title: 'Figma Adaptive Standards',
description: 'Стандарты подготовки адаптивных макетов в Figma', description: 'Стандарты подготовки адаптивных макетов в Figma',
base: '/figma-adaptive-standards/', base: '/figma-adaptive-standards/',
outDir: '../../public/figma-adaptive-standards', outDir: '../../../public/figma-adaptive-standards',
}; };
/** /**
* Карта монтирования исходных канонов в VitePress-документацию. * Карта монтирования исходных канонов в VitePress-документацию.
* *
* `source` указывает на markdown-файл внутри `canons/`. * `source` указывает на markdown-файл внутри `canons/`.
* `target` задаёт путь, по которому этот файл попадёт в `docs/figma-adaptive-standards/content/` * `target` задаёт путь, по которому этот файл попадёт в `docs/content/`
* и станет страницей итоговой документации. * и станет страницей итоговой документации.
*/ */
export const mounts = [ export const mounts = [
{ target: 'index.md', source: 'figma/index.md' }, { target: 'index.md', source: 'canons/index.md' },
{ target: 'overview.md', source: 'figma/index.md' }, { target: 'overview.md', source: 'canons/index.md' },
{ target: 'adaptive-layout-requirements/short.md', source: 'figma/adaptive-layout-requirements.short.md' }, { target: 'adaptive-layout-requirements/short.md', source: 'canons/adaptive-layout-requirements.short.md' },
{ target: 'adaptive-layout-requirements/full.md', source: 'figma/adaptive-layout-requirements.full.md' }, { target: 'adaptive-layout-requirements/full.md', source: 'canons/adaptive-layout-requirements.full.md' },
{ target: 'adaptive-layout-requirements/checklist.md', source: 'figma/adaptive-layout-requirements.checklist.md' }, { target: 'adaptive-layout-requirements/checklist.md', source: 'canons/adaptive-layout-requirements.checklist.md' },
]; ];
export const sidebar = [ 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 { defineConfig } from 'vitepress';
import taskLists from 'markdown-it-task-lists'; import taskLists from 'markdown-it-task-lists';
import llmstxt from 'vitepress-plugin-llms'; 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'; import { sidebar, site } from '../docs.config';
export default defineConfig({ export default defineConfig({
@@ -11,6 +11,7 @@ export default defineConfig({
outDir: site.outDir, outDir: site.outDir,
srcDir: 'content', srcDir: 'content',
cleanUrls: true, cleanUrls: true,
ignoreDeadLinks: [/^\/slm-design\//],
head: [...themeSyncHead], head: [...themeSyncHead],
vite: { vite: {
plugins: [llmstxt()], 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', title: 'NextJS Style Guide',
description: 'Практический стайлгайд для разработки frontend-приложений на Next.js и TypeScript', description: 'Практический стайлгайд для разработки frontend-приложений на Next.js и TypeScript',
base: '/nextjs-style-guide/', base: '/nextjs-style-guide/',
outDir: '../../public/nextjs-style-guide', outDir: '../../../public/nextjs-style-guide',
}; };
/** /**
* Карта монтирования исходных канонов в VitePress-документацию. * Карта монтирования исходных канонов в VitePress-документацию.
* *
* SLM-разделы берутся только из корневого `canons/slm-design/`, чтобы * SLM-разделы берутся из проекта `slm-design`, чтобы NextJS-гайд не содержал
* NextJS-гайд не содержал собственных дублей архитектурного канона. * собственных дублей архитектурного канона.
*/ */
export const mounts = [ export const mounts = [
{ target: 'index.md', source: 'style-guide/index.md' }, { target: 'index.md', source: 'canons/index.md' },
{ target: 'workflow.md', source: 'style-guide/workflow.md' }, { target: 'workflow.md', source: 'canons/workflow.md' },
{ target: 'slm-design/architecture/index.md', source: 'slm-design/architecture/index.md' }, { target: 'slm-design/architecture/index.md', source: '../slm-design/canons/architecture/index.md' },
{ target: 'slm-design/architecture/layers.md', source: 'slm-design/architecture/layers.md' }, { target: 'slm-design/architecture/layers.md', source: '../slm-design/canons/architecture/layers.md' },
{ target: 'slm-design/architecture/modules.md', source: 'slm-design/architecture/modules.md' }, { target: 'slm-design/architecture/modules.md', source: '../slm-design/canons/architecture/modules.md' },
{ target: 'slm-design/architecture/segments.md', source: 'slm-design/architecture/segments.md' }, { target: 'slm-design/architecture/segments.md', source: '../slm-design/canons/architecture/segments.md' },
{ target: 'slm-design/architecture/monorepo.md', source: 'slm-design/architecture/monorepo.md' }, { target: 'slm-design/architecture/monorepo.md', source: '../slm-design/canons/architecture/monorepo.md' },
{ target: 'slm-design/examples/react/factory.md', source: 'slm-design/examples/react/factory.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/examples/react/factory-composition.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/examples/react/composition-provider.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/tech-stack.md', source: 'canons/basics/tech-stack.md' },
{ target: 'basics/naming.md', source: 'style-guide/basics/naming.md' }, { target: 'basics/naming.md', source: 'canons/basics/naming.md' },
{ target: 'basics/code-style.md', source: 'style-guide/basics/code-style.md' }, { target: 'basics/code-style.md', source: 'canons/basics/code-style.md' },
{ target: 'basics/documentation.md', source: 'style-guide/basics/documentation.md' }, { target: 'basics/documentation.md', source: 'canons/basics/documentation.md' },
{ target: 'basics/typing.md', source: 'style-guide/basics/typing.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/from-template.md', source: 'canons/applied/creating-project/from-template.md' },
{ target: 'applied/creating-project/manual.md', source: 'style-guide/applied/creating-project/manual.md' }, { target: 'applied/creating-project/manual.md', source: 'canons/applied/creating-project/manual.md' },
{ target: 'applied/creating-project/nextjs.md', source: 'style-guide/applied/creating-project/nextjs.md' }, { target: 'applied/creating-project/nextjs.md', source: 'canons/applied/creating-project/nextjs.md' },
{ target: 'applied/project-structure.md', source: 'style-guide/applied/project-structure.md' }, { target: 'applied/project-structure.md', source: 'canons/applied/project-structure.md' },
{ target: 'applied/page-level.md', source: 'style-guide/applied/page-level.md' }, { target: 'applied/page-level.md', source: 'canons/applied/page-level.md' },
{ target: 'applied/component.md', source: 'style-guide/applied/component.md' }, { target: 'applied/component.md', source: 'canons/applied/component.md' },
{ target: 'applied/module.md', source: 'style-guide/applied/module.md' }, { target: 'applied/module.md', source: 'canons/applied/module.md' },
{ target: 'applied/rest-client/index.md', source: 'style-guide/applied/rest-client/index.md' }, { target: 'applied/rest-client/index.md', source: 'canons/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/index.md', source: 'canons/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/auto.md', source: 'canons/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/manual.md', source: 'canons/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/setup/hooks.md', source: 'canons/applied/rest-client/setup/hooks.md' },
{ target: 'applied/rest-client/usage.md', source: 'style-guide/applied/rest-client/usage.md' }, { target: 'applied/rest-client/usage.md', source: 'canons/applied/rest-client/usage.md' },
{ target: 'applied/data-fetch/index.md', source: 'style-guide/applied/data-fetch/index.md' }, { target: 'applied/data-fetch/index.md', source: 'canons/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/server-await.md', source: 'canons/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/parallel-server-requests.md', source: 'canons/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/pass-promise-down.md', source: 'canons/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-hooks-initial-data.md', source: 'canons/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/client-get-hook.md', source: 'canons/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/data-fetch/business-composition.md', source: 'canons/applied/data-fetch/business-composition.md' },
{ target: 'applied/styles/styles-setup.md', source: 'style-guide/applied/styles/styles-setup.md' }, { target: 'applied/styles/styles-setup.md', source: 'canons/applied/styles/styles-setup.md' },
{ target: 'applied/styles/styles-usage.md', source: 'style-guide/applied/styles/styles-usage.md' }, { target: 'applied/styles/styles-usage.md', source: 'canons/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-intro.md', source: 'canons/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-setup.md', source: 'canons/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/svg-sprites/svg-sprites-usage.md', source: 'canons/applied/svg-sprites/svg-sprites-usage.md' },
{ target: 'applied/images.md', source: 'style-guide/applied/images.md' }, { target: 'applied/images.md', source: 'canons/applied/images.md' },
{ target: 'applied/fonts.md', source: 'style-guide/applied/fonts.md' }, { target: 'applied/fonts.md', source: 'canons/applied/fonts.md' },
{ target: 'applied/aliases.md', source: 'style-guide/applied/aliases.md' }, { target: 'applied/aliases.md', source: 'canons/applied/aliases.md' },
{ target: 'applied/templates/templates-intro.md', source: 'style-guide/applied/templates/templates-intro.md' }, { target: 'applied/templates/templates-intro.md', source: 'canons/applied/templates/templates-intro.md' },
{ target: 'applied/templates/templates-setup.md', source: 'style-guide/applied/templates/templates-setup.md' }, { target: 'applied/templates/templates-setup.md', source: 'canons/applied/templates/templates-setup.md' },
{ target: 'applied/templates/templates-create.md', source: 'style-guide/applied/templates/templates-create.md' }, { target: 'applied/templates/templates-create.md', source: 'canons/applied/templates/templates-create.md' },
{ target: 'applied/templates/templates-usage.md', source: 'style-guide/applied/templates/templates-usage.md' }, { target: 'applied/templates/templates-usage.md', source: 'canons/applied/templates/templates-usage.md' },
{ target: 'applied/biome.md', source: 'style-guide/applied/biome.md' }, { target: 'applied/biome.md', source: 'canons/applied/biome.md' },
{ target: 'applied/postcss.md', source: 'style-guide/applied/postcss.md' }, { target: 'applied/postcss.md', source: 'canons/applied/postcss.md' },
{ target: 'applied/vscode.md', source: 'style-guide/applied/vscode.md' }, { target: 'applied/vscode.md', source: 'canons/applied/vscode.md' },
{ target: 'applied/localization.md', source: 'style-guide/applied/localization.md' }, { target: 'applied/localization.md', source: 'canons/applied/localization.md' },
{ target: 'applied/stores.md', source: 'style-guide/applied/stores.md' }, { target: 'applied/stores.md', source: 'canons/applied/stores.md' },
]; ];
export const routeRewrites = [ 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 { defineConfig } from 'vitepress';
import taskLists from 'markdown-it-task-lists'; import taskLists from 'markdown-it-task-lists';
import llmstxt from 'vitepress-plugin-llms'; 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'; import { sidebar, site } from '../docs.config';
export default defineConfig({ 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', title: 'SLM Design',
description: 'Каноны архитектуры SLM Design', description: 'Каноны архитектуры SLM Design',
base: '/slm-design/', base: '/slm-design/',
outDir: '../../public/slm-design', outDir: '../../../public/slm-design',
}; };
/** /**
* Карта монтирования исходных канонов в VitePress-документацию. * Карта монтирования исходных канонов в VitePress-документацию.
* *
* `source` указывает на markdown-файл внутри `canons/`. * `source` указывает на markdown-файл внутри `canons/`.
* `target` задаёт путь, по которому этот файл попадёт в `docs/slm-design/content/` * `target` задаёт путь, по которому этот файл попадёт в `docs/content/`
* и станет страницей итоговой документации. * и станет страницей итоговой документации.
*/ */
export const mounts = [ export const mounts = [
{ target: 'index.md', source: 'slm-design/architecture/index.md' }, { target: 'index.md', source: 'canons/architecture/index.md' },
{ target: 'architecture/index.md', source: 'slm-design/architecture/index.md' }, { target: 'architecture/index.md', source: 'canons/architecture/index.md' },
{ target: 'architecture/layers.md', source: 'slm-design/architecture/layers.md' }, { target: 'architecture/layers.md', source: 'canons/architecture/layers.md' },
{ target: 'architecture/modules.md', source: 'slm-design/architecture/modules.md' }, { target: 'architecture/modules.md', source: 'canons/architecture/modules.md' },
{ target: 'architecture/segments.md', source: 'slm-design/architecture/segments.md' }, { target: 'architecture/segments.md', source: 'canons/architecture/segments.md' },
{ target: 'architecture/monorepo.md', source: 'slm-design/architecture/monorepo.md' }, { target: 'architecture/monorepo.md', source: 'canons/architecture/monorepo.md' },
{ target: 'examples/react/factory.md', source: 'slm-design/examples/react/factory.md' }, { target: 'examples/react/factory.md', source: 'canons/examples/react/factory.md' },
{ target: 'examples/react/factory-composition.md', source: 'slm-design/examples/react/factory-composition.md' }, { target: 'examples/react/factory-composition.md', source: 'canons/examples/react/factory-composition.md' },
{ target: 'examples/react/composition-provider.md', source: 'slm-design/examples/react/composition-provider.md' }, { target: 'examples/react/composition-provider.md', source: 'canons/examples/react/composition-provider.md' },
]; ];
export const sidebar = [ 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