Diplodoc services
Services are the main components of Diplodoc, responsible for various aspects of documentation processing. Each service provides its own set of hooks for extending functionality.
Available services
- TOC Service — managing the documentation structure.
- Leading Service — processing leading pages.
- Markdown Service — transforming markdown content.
- Meta Service — working with documentation metadata.
- Vars Service — managing variables and templates.
- VCS Service — working with the version control system.
- Search Service — organizing search across documentation.
- Logger Service — managing logging.
Working with Services
Accessing services
All services are available through the execution context run, which is passed to hooks:
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('MyExtension', (run) => {
// Доступ к сервисам
const {toc, markdown, leading} = run;
});
}
}
Getting service hooks
To work with a service, you need to get its hooks using the corresponding function:
import {getHooks as getBaseHooks} from '@diplodoc/cli/lib/program';
import {getHooks as getTocHooks} from '@diplodoc/cli/lib/toc';
import {getHooks as getMarkdownHooks} from '@diplodoc/cli/lib/markdown';
import {getHooks as getLeadingHooks} from '@diplodoc/cli/lib/leading';
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('MyExtension', (run) => {
// Получение хуков сервисов
const tocHooks = getTocHooks(run.toc);
const markdownHooks = getMarkdownHooks(run.markdown);
const leadingHooks = getLeadingHooks(run.leading);
});
}
}
Using hooks
Service hooks follow a common usage pattern:
serviceHooks.HookName.tap('ExtensionName', (data) => {
// Синхронная обработка
return modifiedData;
});
// или
serviceHooks.HookName.tapPromise('ExtensionName', async (data) => {
// Асинхронная обработка
return modifiedData;
});
Service API
Each service, in addition to hooks, provides a set of methods for working with data:
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('MyExtension', (run) => {
// TOC Service API
const tocPath = run.toc.dir(entry); // Получение директории TOC
const tocData = run.toc.data(entry); // Получение данных TOC
const entries = run.toc.entries; // Список всех entry в TOC
// Leading Service API
const hasLeading = run.leading.has(path); // Проверка наличия leading-страницы
const leadingData = run.leading.get(path); // Получение данных leading-страницы
// Markdown Service API
const content = run.markdown.read(path); // Чтение markdown-файла
await run.markdown.save(path, content); // Сохранение markdown-файла
});
}
}
Using service APIs instead of working directly with the file system allows you to:
- Get data in the correct build context
- Use internal caching
- Support various data formats and sources
- Ensure data consistency
Best practices
1. Logic isolation
Separate processing logic across the appropriate services:
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('MyExtension', (run) => {
// TOC-специфичная логика
const tocHooks = getTocHooks(run.toc);
tocHooks.Item.tap('MyExtension', (item) => {
// Работа со структурой
return item;
});
// Markdown-специфичная логика
const markdownHooks = getMarkdownHooks(run.markdown);
markdownHooks.Resolved.tap('MyExtension', (content) => {
// Работа с контентом
return content;
});
});
}
}
2. Error handling
Always handle possible errors in hooks:
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('MyExtension', (run) => {
const tocHooks = getTocHooks(run.toc);
tocHooks.Item.tapPromise('MyExtension', async (item) => {
try {
const processedItem = await processItem(item);
return processedItem;
} catch (error) {
run.logger.error('Failed to process TOC item:', error);
return item;
}
});
});
}
}
3. Hook execution order
The hook execution order is determined by two factors:
- Program lifecycle: the main hook execution order is determined by the Diplodoc lifecycle.
- Hook configuration: the capabilities of the tapable library are used to control the execution order of hooks with the same name:
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap({
name: 'MyExtension',
stage: 10, // Более высокий stage выполняется позже
before: ['OtherExtension'], // Выполнить до указанных расширений
after: ['AnotherExtension'], // Выполнить после указанных расширений
}, (run) => {
// Логика расширения
});
}
}
You can also use different hook types for specific scenarios:
tap- synchronous hooktapAsync- asynchronous hook with callbacktapPromise- asynchronous hook with Promiseintercept- for intercepting hook calls