Определение языков программирования

Remark и Rehype — это два мощных инструмента для работы с текстами в формате Markdown и HTML в экосистеме JavaScript. Remark отвечает за разбор, модификацию и генерацию Markdown, а Rehype — за HTML. Оба инструмента строятся на основе дерева синтаксического анализа (AST — Abstract Syntax Tree), что позволяет осуществлять точечные трансформации содержимого и интегрировать сторонние плагины для расширения функционала.

  • Remark преобразует Markdown в AST (MDAST), позволяя анализировать структуру документа на уровне заголовков, списков, ссылок, кода и других элементов.
  • Rehype работает с HTML, создавая HAST (HTML AST), что даёт возможность модифицировать узлы, атрибуты и содержимое HTML без прямого парсинга текста.

AST и его структура

AST (Abstract Syntax Tree) — это иерархическая структура данных, представляющая исходный текст как набор узлов. Каждый узел содержит тип (type), свойства (data, attributes) и дочерние элементы (children). В Remark и Rehype выделяются основные типы узлов:

  • Root — корневой узел, содержащий весь документ.
  • Paragraph — параграф текста.
  • Heading — заголовок с указанием уровня (depth).
  • List / ListItem — нумерованные или маркированные списки и их элементы.
  • Code — блоки кода с возможностью указания языка (lang).
  • Text — простой текстовый узел.
  • HTML — вставка HTML в Markdown (для Remark) или HTML узел в Rehype.

Пример создания узла кода с языком программирования:

const codeNode = {
  type: 'code',
  lang: 'javascript',
  value: 'console.log("Hello, world!");'
};

Определение языков программирования в код-блоках

В Markdown часто используются код-блоки, обозначаемые тройными обратными кавычками:

```javascript
console.log("Пример кода");
```

Remark сохраняет язык программирования в поле lang узла типа code. Это позволяет выполнять:

  • Подсветку синтаксиса с помощью плагинов (например, rehype-prism).
  • Фильтрацию блоков по языку для дальнейшей обработки.
  • Генерацию документации с разделением кода по категориям языков.

Пример обхода AST для извлечения всех узлов с языком javascript:

import { visit } from 'unist-util-visit';

visit(tree, 'code', (node) => {
  if (node.lang === 'javascript') {
    console.log(node.value);
  }
});

Интеграция Remark и Rehype

Remark может быть использован совместно с Rehype через плагин remark-rehype, который преобразует MDAST в HAST. Это необходимо для рендеринга Markdown в HTML с последующей обработкой:

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 html = await processor.process('```javascript\nconsole.log("Hello");\n```');
console.log(String(html));

Ключевые моменты:

  • remark-parse преобразует Markdown в MDAST.
  • remark-rehype конвертирует MDAST в HAST.
  • rehype-stringify генерирует HTML из HAST.

Расширенные возможности обработки языков

  1. Автоопределение языка: некоторые плагины могут пытаться определить язык по содержимому кода, если поле lang пустое.
  2. Синтаксическая подсветка: интеграция с Prism или Shiki позволяет рендерить блоки кода с подсветкой на стороне сервера или клиента.
  3. Фильтрация по языку: AST можно обходить и генерировать отдельные разделы документации для каждого языка.

Пример фильтрации и подсветки JavaScript-кода:

import { visit } from 'unist-util-visit';
import { highlight } from 'rehype-prism-plus';

visit(tree, 'code', (node) => {
  if (node.lang === 'javascript') {
    node.type = 'html';
    node.value = highlight(node.value, { lang: 'javascript' });
  }
});

Советы по организации больших документов

  • Использовать Heading с разными уровнями для логической структуры.
  • Кодовые блоки с указанием lang облегчают интеграцию с системами документации.
  • Разделение этапов: сначала парсинг Markdown через Remark, затем обработка и рендеринг HTML через Rehype.
  • Хранение AST в промежуточной переменной позволяет выполнять дополнительные трансформации, такие как вставка ссылок, автоматическая нумерация списков, или модификация заголовков.

Практическая структура AST для документации с кодом

Root
 ├─ Heading (depth: 1)
 ├─ Paragraph
 ├─ Code (lang: javascript)
 ├─ Heading (depth: 2)
 ├─ List
 │   ├─ ListItem
 │   └─ ListItem
 └─ Code (lang: python)

Такое представление позволяет:

  • Разделять документацию по языкам.
  • Генерировать разные форматы (HTML, PDF, JSON).
  • Автоматизировать проверку наличия примеров для каждого языка.

Поддержка мульти-языковых проектов

Для проектов, включающих несколько языков:

  • Сохранять язык в lang каждого блока кода.
  • Создавать фильтрующие плагины для генерации учебных материалов по конкретному языку.
  • Подключать линтеры и компиляторы кода на этапе трансформации AST для проверки синтаксиса.