feat: добавить документацию NextJS Style Guide

- добавлен отдельный VitePress-сайт для NextJS Style Guide
- удалены дубли SLM-канонов из style-guide
- обновлены ссылки, сборочные скрипты, CI, Docker и README
- разблокирована карточка NextJS Style Guide на главной
This commit is contained in:
2026-05-13 17:12:18 +03:00
parent ab72c06fd5
commit 53aa01199d
26 changed files with 336 additions and 2266 deletions

View File

@@ -7,11 +7,17 @@ type Page = {
target: string;
};
type DocsConfig = {
mounts: Page[];
type RouteRewrite = {
from: string;
to: string;
};
const MD_LINK_RE = /\]\((?!#|[a-z][a-z0-9+.-]*:)([^)\s]+\.md)(#[^)]*)?\)/gi;
type DocsConfig = {
mounts: Page[];
routeRewrites?: RouteRewrite[];
};
const MD_LINK_RE = /\]\((?!#|[a-z][a-z0-9+.-]*:|\/\/)([^)\s]+)(#[^)]*)?\)/gi;
const siteName = process.argv[2];
@@ -33,6 +39,7 @@ 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(/^\.\//, '');
@@ -47,17 +54,71 @@ function formatRelativeMarkdownPath(fromTarget: string, toTarget: string) {
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}${query ? `?${query}` : ''}`);
const nextHref = formatRelativeMarkdownPath(page.target, `${target}${queryPart}`);
return `](${nextHref}${hash})`;
});