Markdown-it поддерживает систему плагинов, позволяющую расширять функциональность рендерера. Для подсветки кода чаще всего используют плагин markdown-it-highlightjs, который интегрируется с библиотекой highlight.js. Установка выполняется через npm:
npm install markdown-it markdown-it-highlightjs highlight.js
Импорт и базовая настройка:
const MarkdownIt = require('markdown-it');
const hljs = require('highlight.js');
const markdownItHighlightjs = require('markdown-it-highlightjs');
const md = new MarkdownIt({
html: true,
linkify: true,
typographer: true
});
md.use(markdownItHighlightjs);
После подключения, при рендеринге Markdown-кода все блоки с указанием языка автоматически подсвечиваются:
const source = `
\`\`\`javascript
function hello() {
console.log("Hello, world!");
}
\`\`\`
`;
console.log(md.render(source));
Результат будет содержать HTML с классами hljs и
конкретным языком (language-javascript), что позволяет
CSS-стилям highlight.js применяться корректно.
Markdown-it позволяет настраивать подсветку кода вручную через
параметр highlight при инициализации:
const md = new MarkdownIt({
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return `<pre class="hljs"><code>` +
hljs.highlight(str, { language: lang }).value +
`</code></pre>`;
} catch (__) {}
}
return `<pre class="hljs"><code>` + md.utils.escapeHtml(str) + `</code></pre>`;
}
});
Особенности такого подхода:
Markdown-it автоматически выделяет инлайновый код с использованием
тега <code>. Подсветка с highlight.js применяется
только к многострочным блокам кода, но можно расширить функциональность,
если требуется подсветка инлайнового кода. Для этого создается
пользовательский плагин:
function inlineCodeHighlight(md) {
const defaultRender = md.renderer.rules.code_inline || function(tokens, idx, options, env, self) {
return self.renderToken(tokens, idx, options);
};
md.renderer.rules.code_inline = function(tokens, idx, options, env, self) {
const content = tokens[idx].content;
return `<code class="hljs-inline">${md.utils.escapeHtml(content)}</code>`;
};
}
md.use(inlineCodeHighlight);
Классы hljs-inline можно стилизовать отдельно через
CSS.
highlight.js позволяет добавлять свои языки или модифицировать
существующие. Например, можно зарегистрировать кастомный язык
myLang:
hljs.registerLanguage('myLang', function(hljs) {
return {
keywords: 'if else for while return function',
contains: [
hljs.C_NUMBER_MODE,
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
{
className: 'string',
begin: /"/, end: /"/
}
]
};
});
После регистрации блоки с \```myLang будут
подсвечиваться согласно определенным правилам.
highlight.js поставляется с набором CSS-тем, которые можно подключить отдельно. Пример подключения темы:
<link rel="stylesheet" href="node_modules/highlight.js/styles/github-dark.css">
Стили применяются автоматически к блокам
<pre><code class="hljs">.
Для динамической смены темы можно менять класс на родительском контейнере и подгружать соответствующий CSS, что особенно полезно при реализации светлой и тёмной темы на сайте.
При рендеринге больших документов стоит учитывать производительность. highlight.js может быть дорогим для множества блоков кода. Для оптимизации можно кешировать результат подсветки:
const codeCache = new Map();
function highlightCached(str, lang) {
const key = lang + ':' + str;
if (codeCache.has(key)) return codeCache.get(key);
const result = lang && hljs.getLanguage(lang)
? hljs.highlight(str, { language: lang }).value
: md.utils.escapeHtml(str);
codeCache.set(key, result);
return result;
}
const mdOptimized = new MarkdownIt({
highlight: highlightCached
});
Это снижает нагрузку при повторных рендерах одинаковых фрагментов кода.
Markdown-it поддерживает цепочку плагинов. Подсветка кода хорошо сочетается с:
Правильное подключение плагинов в нужной последовательности обеспечивает корректное отображение и подсветку всех элементов документа:
md.use(markdownItHighlightjs)
.use(require('markdown-it-anchor'))
.use(require('markdown-it-table-of-contents'));
Если язык не поддерживается, важно не ломать рендер. Для этого используется fallback:
md.set({
highlight: function(str, lang) {
try {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(str, { language: lang }).value;
}
} catch (err) {
console.error(err);
}
return md.utils.escapeHtml(str);
}
});
Такой подход предотвращает падение рендеринга при неизвестных языках и позволяет безопасно отображать исходный текст кода.