fix: исправить ссылки markdown-артефактов
All checks were successful
CI/CD Pipeline / build (push) Successful in 18s
CI/CD Pipeline / version (push) Successful in 6s
CI/CD Pipeline / docker (push) Successful in 1m18s
CI/CD Pipeline / deploy (push) Successful in 6s

- исправлена генерация ссылок для architecture и examples
- сохранена структура папок в архиве slm-design.zip
- обновлён сгенерированный ARCHITECTURE.md
This commit is contained in:
2026-05-11 21:26:55 +03:00
parent 5874d3604c
commit 4b29e175bc
3 changed files with 32 additions and 46 deletions

View File

@@ -1,6 +1,6 @@
---
title: Монорепозитории
description: "Правила применения SLM Design для frontend-проектов, находящихся в монорепозитории"
description: Правила применения SLM Design для frontend-проектов, находящихся в монорепозитории
---
# Монорепозитории

View File

@@ -65,32 +65,37 @@ function fileRelToMdUrl(file: string): string {
return `${DOC_ROUTE_PREFIX}/${file}`;
}
const ARCHITECTURE_LINK_RE = /\]\((\/architecture(?:\/[^)\s#]*)?)(#[^)\s]*)?\)/g;
const DOC_LINK_RE = /\]\((\/(?:architecture|examples)(?:\/[^)\s#]*)?)(#[^)\s]*)?\)/g;
function architectureRouteToFileRel(route: string): string {
function docRouteToFileRel(route: string): string {
if (route.replace(/\/$/, "") === "/architecture") return "architecture/index.md";
if (route.replace(/\/$/, "") === "/examples") return "examples/index.md";
return linkToFileRel(route);
}
function transformArchitectureLinks(
function transformDocLinks(
content: string,
toHref: (route: string, hash: string) => string,
): string {
return content.replace(ARCHITECTURE_LINK_RE, (_match, route: string, hash = "") => {
return content.replace(DOC_LINK_RE, (_match, route: string, hash = "") => {
return `](${toHref(route, hash)})`;
});
}
function transformArchiveLinks(content: string): string {
return transformArchitectureLinks(content, (route, hash) => {
const fileName = path.basename(architectureRouteToFileRel(route));
return `./${fileName}${hash}`;
function formatRelativeMarkdownPath(fromFile: string, toFile: string): string {
const relative = path.relative(path.dirname(fromFile), toFile).split(path.sep).join("/");
return relative.startsWith(".") ? relative : `./${relative}`;
}
function transformArchiveLinks(content: string, fromFile: string): string {
return transformDocLinks(content, (route, hash) => {
return `${formatRelativeMarkdownPath(fromFile, docRouteToFileRel(route))}${hash}`;
});
}
function transformSiteMarkdownLinks(content: string): string {
return transformArchitectureLinks(content, (route, hash) => {
return `${fileRelToMdUrl(architectureRouteToFileRel(route))}${hash}`;
return transformDocLinks(content, (route, hash) => {
return `${fileRelToMdUrl(docRouteToFileRel(route))}${hash}`;
});
}
@@ -104,35 +109,15 @@ const stripFrontmatter = (content: string) =>
const stripRulesLink = (content: string) =>
content.replace(/<!-- rules-link -->[\s\S]*?<!-- \/rules-link -->\n*/g, "");
function slugifyHeading(heading: string): string {
return heading
.trim()
.replace(/[`*_~[\]()]/g, "")
.toLowerCase()
.replace(/[^\p{L}\p{N}\s-]/gu, "")
.trim()
.replace(/\s+/g, "-");
}
function fileRelToSingleFileAnchor(file: string): string {
const filePath = path.join(SRC_DIR, file);
if (!fs.existsSync(filePath)) return slugifyHeading(path.basename(file, ".md"));
const raw = stripFrontmatter(fs.readFileSync(filePath, "utf8"));
const title = raw.match(/^#\s+(.+)$/m)?.[1];
return slugifyHeading(title ?? path.basename(file, ".md"));
}
function transformSingleFileLinks(content: string): string {
return transformArchitectureLinks(content, (route, hash) => {
if (hash) return hash;
return `#${fileRelToSingleFileAnchor(architectureRouteToFileRel(route))}`;
return transformDocLinks(content, (route, hash) => {
return `${fileRelToMdUrl(docRouteToFileRel(route))}${hash}`;
});
}
function transformReadmeLinks(content: string): string {
return transformArchitectureLinks(content, (route, hash) => {
return `docs/${architectureRouteToFileRel(route)}${hash}`;
return transformDocLinks(content, (route, hash) => {
return `docs/${docRouteToFileRel(route)}${hash}`;
});
}
@@ -242,9 +227,10 @@ function buildZip() {
if (!fs.existsSync(src)) continue;
let content = fs.readFileSync(src, "utf8");
content = stripRulesLink(stripFrontmatter(content)).trim();
content = transformArchiveLinks(content);
const destName = path.basename(file);
fs.writeFileSync(path.join(tmpDir, destName), content, "utf8");
content = transformArchiveLinks(content, file);
const dest = path.join(tmpDir, file);
fs.mkdirSync(path.dirname(dest), { recursive: true });
fs.writeFileSync(dest, content, "utf8");
}
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf8"));

View File

@@ -10,10 +10,10 @@ Scoped Layered Module Design — модульная архитектура фр
Спецификация SLM Design состоит из нескольких связанных разделов. Этот обзор даёт общий контекст, а детальные правила описаны дальше:
- [Слои](#слои) — уровни организации `src/`, направление зависимостей и зона ответственности каждого слоя.
- [Модули](#модули) — границы ответственности, публичный API, типы модулей и отличие модуля от компонента.
- [Сегменты](#сегменты) — внутренние папки модуля (`ui/`, `parts/`, `hooks/`, `types/` и другие) и правила размещения файлов.
- [Монорепозитории](#монорепозитории) — применение SLM в `apps/` и `packages/`, правила выноса общих слоёв и ограничения для business.
- [Слои](/docs/architecture/layers.md) — уровни организации `src/`, направление зависимостей и зона ответственности каждого слоя.
- [Модули](/docs/architecture/modules.md) — границы ответственности, публичный API, типы модулей и отличие модуля от компонента.
- [Сегменты](/docs/architecture/segments.md) — внутренние папки модуля (`ui/`, `parts/`, `hooks/`, `types/` и другие) и правила размещения файлов.
- [Монорепозитории](/docs/architecture/monorepo.md) — применение SLM в `apps/` и `packages/`, правила выноса общих слоёв и ограничения для business.
Рекомендуемый порядок чтения: обзор → слои → модули → сегменты → монорепозитории.
@@ -513,7 +513,7 @@ backend-api/
└── index.ts # публичный API
```
Подробное описание сегментов — в разделе [Сегменты](#сегменты).
Подробное описание сегментов — в разделе [Сегменты](/docs/architecture/segments.md).
### Публичный API
@@ -558,9 +558,9 @@ Business-модуль всегда экспортирует фабрику. Фа
#### Примеры
Пример реализации фабрики в React см. в [Создание фабрики](/examples/react/factory).
Пример реализации фабрики в React см. в [Создание фабрики](/docs/examples/react/factory.md).
Пример композиции фабрик в React screen-модуле см. в [Композиция фабрик](/examples/react/factory-composition).
Пример композиции фабрик в React screen-модуле см. в [Композиция фабрик](/docs/examples/react/factory-composition.md).
### Жизненный цикл
@@ -614,7 +614,7 @@ Business-модуль всегда экспортирует фабрику. Фа
- Не получает данные самостоятельно, не выбирает источник данных и не композирует данные.
- Не содержит бизнес-логику или сценарную логику.
Если UI-сущности нужно что-то за пределами этих ограничений, она должна быть оформлена как модуль. Полная граница описана в разделе [Компонент](#компонент).
Если UI-сущности нужно что-то за пределами этих ограничений, она должна быть оформлена как модуль. Полная граница описана в разделе [Компонент](/docs/architecture/modules.md#компонент).
Корневой файл модуля в `ui/` не размещается. Он лежит в корне модуля: `{module-name}.tsx`.