Files
docs/scripts/docs/prepare.ts
S.Gromov 53aa01199d feat: добавить документацию NextJS Style Guide
- добавлен отдельный VitePress-сайт для NextJS Style Guide
- удалены дубли SLM-канонов из style-guide
- обновлены ссылки, сборочные скрипты, CI, Docker и README
- разблокирована карточка NextJS Style Guide на главной
2026-05-13 17:12:18 +03:00

147 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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[];
};
const MD_LINK_RE = /\]\((?!#|[a-z][a-z0-9+.-]*:|\/\/)([^)\s]+)(#[^)]*)?\)/gi;
const siteName = process.argv[2];
if (!siteName) {
throw new Error('Укажите имя сайта: tsx scripts/docs/prepare.ts slm-design');
}
const rootDir = process.cwd();
const canonsDir = path.join(rootDir, 'canons');
const siteDir = path.join(rootDir, 'docs', siteName);
const contentDir = path.join(siteDir, 'content');
const configPath = path.join(siteDir, 'docs.config.ts');
if (!fs.existsSync(configPath)) {
throw new Error(`Не найден конфиг сайта: ${configPath}`);
}
const config = (await import(pathToFileURL(configPath).href)) as DocsConfig;
const targetBySource = new Map(
config.mounts.map((page) => [normalizePath(page.source), normalizePath(page.target)]),
);
const routeRewrites = [...(config.routeRewrites ?? [])].sort((a, b) => b.from.length - a.from.length);
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}`;
}
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.posix.normalize(path.posix.join(sourceDir, hrefPath)));
if (sourcePath.startsWith('style-guide/')) {
const route = `/docs/${sourcePath.slice('style-guide/'.length)}`;
return applyRouteRewrites(route);
}
return undefined;
}
function transformMarkdownLinks(content: string, page: Page) {
const sourceDir = path.posix.dirname(normalizePath(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);
if (route) return `](${route}${queryPart}${hash})`;
const rewritten = applyRouteRewrites(hrefPath);
if (rewritten) return `](${rewritten}${queryPart}${hash})`;
return match;
}
if (!hrefPath.endsWith('.md')) {
const route = formatRelativeRoute(hrefPath, sourceDir);
if (route) return `](${route}${queryPart}${hash})`;
return match;
}
const sourcePath = normalizePath(path.posix.normalize(path.posix.join(sourceDir, hrefPath)));
const target = targetBySource.get(sourcePath);
if (!target) return match;
const nextHref = formatRelativeMarkdownPath(page.target, `${target}${queryPart}`);
return `](${nextHref}${hash})`;
});
}
fs.rmSync(contentDir, { recursive: true, force: true });
fs.mkdirSync(contentDir, { recursive: true });
for (const page of config.mounts) {
const sourcePath = path.join(canonsDir, page.source);
const targetPath = path.join(contentDir, page.target);
if (!fs.existsSync(sourcePath)) {
throw new Error(`Не найден канон: ${sourcePath}`);
}
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
const content = transformMarkdownLinks(fs.readFileSync(sourcePath, 'utf8'), page);
fs.writeFileSync(targetPath, content, 'utf8');
console.log(`${page.target} -> canons/${page.source}`);
}
console.log(`Подготовлен VitePress content для ${siteName}: ${config.mounts.length} страниц`);