Обработка code blocks

Code blocks в Markdown представляют собой многострочные фрагменты кода, заключённые в тройные обратные кавычки (```). В контексте JavaScript и экосистемы Remark/Rehype их обработка требует понимания двух уровней: синтаксического анализа Markdown и трансформации AST в HTML.


Представление code blocks в AST

Remark использует MDAST (Markdown Abstract Syntax Tree). В MDAST code blocks представлены узлами типа code. Основные свойства узла:

  • type: "code" — указывает на тип узла.
  • lang: строка, указывающая язык (например, "js", "python"), может быть null.
  • value: содержимое блока кода.
  • meta: дополнительные данные, такие как подсветка или названия файлов.

Пример AST для блока кода:

{
  "type": "code",
  "lang": "js",
  "meta": "highlight-line=2",
  "value": "console.log('Hello World');"
}

Этот узел можно использовать для дальнейших трансформаций или подсветки синтаксиса.


Подключение Remark и Rehype

Remark отвечает за парсинг Markdown, Rehype — за работу с HTML. Для обработки code blocks стандартная цепочка выглядит так:

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';

const processor = unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeStringify);

const markdown = `
\`\`\`js
console.log('Hello World');
\`\`\`
`;

const html = await processor.process(markdown);
console.log(String(html));

В результате получаем HTML:

<pre><code class="language-js">console.log('Hello World');</code></pre>

Ключевой момент — class="language-js", который используется для интеграции с библиотеками подсветки синтаксиса, например Prism или Highlight.js.


Подсветка синтаксиса с помощью rehype

Для подсветки кода подключается rehype-highlight:

import rehypeHighlight from 'rehype-highlight';

const processor = unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeHighlight)
  .use(rehypeStringify);

rehype-highlight автоматически определяет язык по классу language-* и применяет HTML-классы для подсветки.

Дополнительно можно передавать настройки:

.use(rehypeHighlight, { ignoreMissing: true });
  • ignoreMissing: true позволяет пропускать блоки без указания языка, не генерируя ошибок.

Работа с метаданными code blocks

Remark поддерживает метаданные внутри тройных кавычек:

```js title="example.js" highlight-line=2
console.log('Hello World');
console.error('Error');

Узел MDAST:

```json
{
  "type": "code",
  "lang": "js",
  "meta": "title=\"example.js\" highlight-line=2",
  "value": "console.log('Hello World');\nconsole.error('Error');"
}

Метаданные можно парсить с помощью сторонних утилит, например parse-meta или самостоятельно:

function parseMeta(meta) {
  const result = {};
  meta?.split(' ').forEach(item => {
    const [key, value] = item.split('=');
    result[key] = value?.replace(/"/g, '');
  });
  return result;
}

После этого можно добавить к HTML атрибуты, например data-title или data-highlight.


Кастомизация HTML для code blocks

Remark + Rehype позволяет полностью кастомизировать рендеринг блоков кода через rehype plugins. Пример создания плагина для добавления заголовка к каждому блоку:

function rehypeCodeTitle() {
  return (tree) => {
    visit(tree, 'element', (node) => {
      if (node.tagName === 'pre' && node.children[0]?.tagName === 'code') {
        const meta = node.children[0].properties?.meta;
        if (meta) {
          const titleNode = {
            type: 'element',
            tagName: 'div',
            properties: { className: ['code-title'] },
            children: [{ type: 'text', value: meta }]
          };
          node.children.unshift(titleNode);
        }
      }
    });
  };
}

Этот подход позволяет создавать визуальные улучшения для блоков кода без вмешательства в сам Markdown.


Обработка инлайнового кода

Помимо многострочных блоков, MDAST поддерживает inline code (inlineCode):

  • type: "inlineCode".
  • value: содержимое кода.
  • Отображается в HTML как <code>...</code> без <pre>.

Пример использования:

Используйте `console.log()` для отладки.

В HTML:

<p>Используйте <code>console.log()</code> для отладки.</p>

Подсветка обычно не применяется к inline-коду, но метаданные можно обрабатывать аналогично.


Выводы по обработке code blocks

  • Узлы code и inlineCode в MDAST — основа для работы с кодом в Markdown.
  • remarkRehype преобразует AST в HTML с сохранением семантики <pre><code>.
  • Подключение rehype-highlight обеспечивает автоматическую подсветку синтаксиса.
  • Метаданные блоков кода позволяют добавлять заголовки, подсветку линий и другие визуальные улучшения.
  • Кастомные плагины Rehype дают полный контроль над HTML-структурой и атрибутами.

Эта комбинация инструментов обеспечивает гибкую и мощную обработку code blocks, поддерживая как базовые сценарии, так и сложные визуальные требования.