Markdown Service
The Markdown Service is responsible for processing markdown content in Diplodoc. This service allows transforming content, adding custom blocks, and validating documentation.
Main features
- Processing markdown files with support for includes and variables.
- Managing metadata and frontmatter.
- Support for templates with conditions and substitutions.
- Collecting and analyzing dependencies and assets.
- Working with headings and their anchors.
Accessing the service
export class Extension {
apply(program: Build) {
getBaseHooks(program).BeforeAnyRun.tap('MyExtension', (run) => {
// Получение сервиса из контекста
const {markdown} = run;
// Получение хуков сервиса
const markdownHooks = getMarkdownHooks(markdown);
});
}
}
Available hooks
Plugins
Hook for registering markdown file processing plugins. Called during service initialization.
markdownHooks.Plugins.tapPromise('MyExtension', async (plugins) => {
// plugins: Массив существующих плагинов
// Добавляем новый плагин
return [
...plugins,
{
name: 'my-plugin',
transform: (content) => {
// Трансформация контента
return content;
}
}
];
});
Collects
Hook for registering content analyzers. Called during service initialization.
markdownHooks.Collects.tapPromise('MyExtension', async (collects) => {
// collects: Массив существующих анализаторов
// Добавляем новый анализатор
return [
...collects,
{
name: 'my-collector',
collect: (content, path) => {
// Анализ контента
return {
// Результаты анализа
};
}
}
];
});
Loaded
The hook is called after loading and initial processing of a markdown file.
markdownHooks.Loaded.tapPromise('MyProcessor', async (raw, meta, path) => {
// raw: Исходный контент файла
// meta: Метаданные файла
// path: Путь к файлу
// Обработка загруженного контента
return raw;
});
Resolved
The hook is called after full content resolution (includes, variables).
markdownHooks.Resolved.tapPromise('MyProcessor', async (content, path) => {
// content: Разрешенный markdown-контент
// path: Путь к файлу
// Трансформация контента
return content;
});
Dump
The hook is called before saving a markdown file.
markdownHooks.Dump.tapPromise('MyProcessor', async (vfile) => {
// vfile: VFile с контентом и метаданными
// Модификация перед сохранением
return vfile;
});
Service API
Method init
Initializes the service. Computes the final set of analyzers and plugins for file processing.
await markdownService.init();
Method load
Loads and processes a markdown file.
Parameters:
path: RelativePath— relative path to the filefrom: NormalizedPath[]— array of source file paths (for includes)
Returns:
Promise<string>— promise with processed content
Calls hooks:
Loaded— after file loadingResolved— after full content resolution
const content = await markdownService.load('path/to/file.md');
Method dump
Saves a markdown file.
Parameters:
file: NormalizedPath— path to the filemarkdown?: string— content to save (if not specified, loaded from the file)
Returns:
Promise<VFile>— promise with VFile
Calls hooks:
Dump— before saving the file
const vfile = await markdownService.dump('path/to/file.md', content);
Method meta
Gets file metadata.
Parameters:
path: RelativePath— path to the file
Returns:
Promise<Meta>— promise with metadata
const meta = await markdownService.meta('path/to/file.md');
Method graph
Gets the file dependency graph.
Parameters:
path: RelativePath— path to the file
Returns:
Promise<EntryGraph>— promise with the dependency graph
const graph = await markdownService.graph('path/to/file.md');
Method assets
Gets the list of file assets.
Parameters:
path: RelativePath— path to the file
Returns:
Promise<NormalizedPath[]>— promise with an array of asset paths
const assets = await markdownService.assets('path/to/file.md');
Method headings
Gets information about file headings.
Parameters:
path: RelativePath— path to the file
Returns:
Promise<HeadingInfo[]>— promise with an array of heading information
const headings = await markdownService.headings('path/to/file.md');
Method titles
Gets a dictionary of headings and their anchors.
Parameters:
path: RelativePath— path to the file
Returns:
Promise<Hash<string>>— promise with a dictionary of headings
const titles = await markdownService.titles('path/to/file.md');
Method inspect
Analyzes content without saving state.
Parameters:
path: RelativePath— path to the fileraw: string— raw contentvars: Hash— variables for substitution
Returns:
Promise<{content: string, deps: IncludeInfo[], assets: NormalizedPath[]}>— promise with analysis results
const {content, deps, assets} = await markdownService.inspect('path/to/file.md', raw, vars);
Method remap
Transforms a line number taking into account the sourcemap.
Parameters:
path: RelativePath— path to the fileline: number— line number
Returns:
number— transformed line number
const mappedLine = markdownService.remap('path/to/file.md', 10);
Usage examples
Adding custom blocks
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>`
);
});
});
}
}
Content validation
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;
});
});
}
}
Processing includes
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;
});
});
}
}
Integration with external services
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
}
}