Markdown Service
Markdown Service отвечает за обработку markdown-контента в Diplodoc. Этот сервис позволяет трансформировать контент, добавлять пользовательские блоки и валидировать документацию.
Основные возможности
- Обработка markdown-файлов с поддержкой включений и переменных
- Управление метаданными и frontmatter
- Поддержка шаблонов с условиями и подстановками
- Сбор и анализ зависимостей и ассетов
- Работа с заголовками и их якорями
Получение доступа к сервису
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('MyExtension', (run) => {
// Получение сервиса из контекста
const {markdown} = run;
// Получение хуков сервиса
const markdownHooks = getMarkdownHooks(markdown);
});
}
}
Доступные хуки
Plugins
Хук для регистрации плагинов обработки markdown-файлов. Вызывается при инициализации сервиса.
markdownHooks.Plugins.tapPromise('MyExtension', async (plugins) => {
// plugins: Массив существующих плагинов
// Добавляем новый плагин
return [
...plugins,
{
name: 'my-plugin',
transform: (content) => {
// Трансформация контента
return content;
}
}
];
});
Collects
Хук для регистрации анализаторов контента. Вызывается при инициализации сервиса.
markdownHooks.Collects.tapPromise('MyExtension', async (collects) => {
// collects: Массив существующих анализаторов
// Добавляем новый анализатор
return [
...collects,
{
name: 'my-collector',
collect: (content, path) => {
// Анализ контента
return {
// Результаты анализа
};
}
}
];
});
Loaded
Хук вызывается после загрузки и начальной обработки markdown-файла.
markdownHooks.Loaded.tapPromise('MyProcessor', async (raw, meta, path) => {
// raw: Исходный контент файла
// meta: Метаданные файла
// path: Путь к файлу
// Обработка загруженного контента
return raw;
});
Resolved
Хук вызывается после полного разрешения контента (включения, переменные).
markdownHooks.Resolved.tapPromise('MyProcessor', async (content, path) => {
// content: Разрешенный markdown-контент
// path: Путь к файлу
// Трансформация контента
return content;
});
Dump
Хук вызывается перед сохранением markdown-файла.
markdownHooks.Dump.tapPromise('MyProcessor', async (vfile) => {
// vfile: VFile с контентом и метаданными
// Модификация перед сохранением
return vfile;
});
API сервиса
Метод init
Инициализирует сервис. Вычисляет финальный набор анализаторов и плагинов для обработки файлов.
await markdownService.init();
Метод load
Загружает и обрабатывает markdown-файл.
Параметры:
path: RelativePath
- относительный путь к файлуfrom: NormalizedPath[]
- массив путей исходных файлов (для включений)
Возвращает:
Promise<string>
- промис с обработанным контентом
Вызывает хуки:
Loaded
- после загрузки файлаResolved
- после полного разрешения контента
const content = await markdownService.load('path/to/file.md');
Метод dump
Сохраняет markdown-файл.
Параметры:
file: NormalizedPath
- путь к файлуmarkdown?: string
- контент для сохранения (если не указан, загружается из файла)
Возвращает:
Promise<VFile>
- промис с VFile
Вызывает хуки:
Dump
- перед сохранением файла
const vfile = await markdownService.dump('path/to/file.md', content);
Метод meta
Получает метаданные файла.
Параметры:
path: RelativePath
- путь к файлу
Возвращает:
Promise<Meta>
- промис с метаданными
const meta = await markdownService.meta('path/to/file.md');
Метод graph
Получает граф зависимостей файла.
Параметры:
path: RelativePath
- путь к файлу
Возвращает:
Promise<EntryGraph>
- промис с графом зависимостей
const graph = await markdownService.graph('path/to/file.md');
Метод assets
Получает список ассетов файла.
Параметры:
path: RelativePath
- путь к файлу
Возвращает:
Promise<NormalizedPath[]>
- промис с массивом путей к ассетам
const assets = await markdownService.assets('path/to/file.md');
Метод headings
Получает информацию о заголовках файла.
Параметры:
path: RelativePath
- путь к файлу
Возвращает:
Promise<HeadingInfo[]>
- промис с массивом информации о заголовках
const headings = await markdownService.headings('path/to/file.md');
Метод titles
Получает словарь заголовков и их якорей.
Параметры:
path: RelativePath
- путь к файлу
Возвращает:
Promise<Hash<string>>
- промис со словарем заголовков
const titles = await markdownService.titles('path/to/file.md');
Метод inspect
Анализирует контент без сохранения состояния.
Параметры:
path: RelativePath
- путь к файлуraw: string
- исходный контентvars: Hash
- переменные для подстановки
Возвращает:
Promise<{content: string, deps: IncludeInfo[], assets: NormalizedPath[]}>
- промис с результатами анализа
const {content, deps, assets} = await markdownService.inspect('path/to/file.md', raw, vars);
Метод remap
Преобразует номер строки с учетом sourcemap.
Параметры:
path: RelativePath
- путь к файлуline: number
- номер строки
Возвращает:
number
- преобразованный номер строки
const mappedLine = markdownService.remap('path/to/file.md', 10);
Примеры использования
Добавление пользовательских блоков
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('CustomBlocks', (run) => {
const markdownHooks = getMarkdownHooks(run.markdown);
markdownHooks.Resolved.tapPromise('CustomBlocks', async (content) => {
// Добавляем пользовательский блок
return content.replace(
/:::custom-block([\s\S]*?):::/g,
(_, body) => `<div class="custom-block">${body}</div>`
);
});
});
}
}
Валидация контента
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('ContentValidator', (run) => {
const markdownHooks = getMarkdownHooks(run.markdown);
markdownHooks.Resolved.tapPromise('ContentValidator', async (content, path) => {
// Проверяем наличие заголовка
if (!content.match(/^#\s/)) {
run.logger.warn(`Missing title in ${path}`);
}
// Проверяем длину разделов
validateSectionLengths(content);
// Проверяем корректность ссылок
await validateLinks(content, path);
return content;
});
});
}
}
Обработка включений
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('IncludeProcessor', (run) => {
const markdownHooks = getMarkdownHooks(run.markdown);
markdownHooks.Resolved.tapPromise('IncludeProcessor', async (content, path, from) => {
// Если это включенный контент
if (from) {
// Добавляем информацию об источнике
return `<!-- Included from: ${from} -->\n${content}`;
}
return content;
});
});
}
}
Интеграция с внешними сервисами
export class Extension {
constructor(private apiKey: string) {}
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('ExternalIntegration', (run) => {
const markdownHooks = getMarkdownHooks(run.markdown);
markdownHooks.Resolved.tapPromise('ExternalIntegration', async (content) => {
// Обрабатываем специальные теги
return content.replace(
/{external-data\sid="([^"]+)"}/g,
async (_, id) => {
const data = await this.fetchExternalData(id);
return this.formatExternalData(data);
}
);
});
});
}
private async fetchExternalData(id: string) {
// Получение данных из внешнего API
}
private formatExternalData(data: any) {
// Форматирование данных в markdown
}
}