Настройка TypeScript для работы с unified

Unified — это экосистема инструментов для работы с синтаксическими деревьями (AST) текста, которая включает библиотеки Remark для Markdown и Rehype для HTML. Для полноценной работы с этими библиотеками в TypeScript важно правильно настроить типизацию, конфигурацию проекта и поддержку плагинов.


Установка зависимостей

Для работы с unified, Remark и Rehype в проекте TypeScript необходимо установить основные пакеты и их типы:

npm install unified remark remark-parse remark-stringify rehype rehype-parse rehype-stringify
npm install -D typescript @types/node

Если используются плагины, важно также установить соответствующие типы, если они доступны:

npm install remark-gfm rehype-highlight
npm install -D @types/remark-gfm @types/rehype-highlight

Конфигурация TypeScript

TypeScript требует корректной настройки tsconfig.json для работы с модулями ES и import/export:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Node",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "declaration": true,
    "outDir": "./dist"
  },
  "include": ["src"]
}

Ключевые параметры:

  • esModuleInterop и allowSyntheticDefaultImports — обеспечивают корректный импорт CommonJS-пакетов, к которым относятся некоторые плагины unified.
  • skipLibCheck — предотвращает ошибки типизации при несовершенной типизации плагинов.
  • strict — включает строгую проверку типов, что критично для сложных цепочек плагинов.

Работа с плагинами Remark и Rehype

Unified использует цепочки плагинов через метод use. В TypeScript важно типизировать плагины, чтобы избежать ошибок при передаче параметров.

Пример цепочки плагинов для Markdown → HTML:

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

const processor = unified()
  .use(remarkParse)           // Парсинг Markdown
  .use(remarkGfm)             // Расширения GitHub Flavored Markdown
  .use(remarkRehype)          // Конвертация в HTML AST
  .use(rehypeStringify);      // Генерация HTML

async function parseMarkdown(md: string): Promise<string> {
  const file = await processor.process(md);
  return String(file);
}

Здесь важно учитывать:

  • process возвращает объект типа VFile, который нужно преобразовать в строку.
  • Для TypeScript необходимо импортировать плагины через import, а не require, иначе могут возникнуть проблемы с типами.

Типизация AST

Unified использует unist (Universal Syntax Tree) для представления дерева документа. Типизация AST позволяет безопасно обращаться к узлам дерева:

import { Root, Content } from 'mdast';

function getHeadings(tree: Root): string[] {
  return tree.children
    .filter((node): node is { type: 'heading', depth: number, children: Content[] } => node.type === 'heading')
    .map(node => node.children.map(child => 'value' in child ? child.value : '').join(''));
}

Особенности:

  • TypeScript требует явного указания типов узлов при фильтрации.
  • Типы Root и Content импортируются из mdast для Markdown и hast для HTML.

Совместимость с ESM и CommonJS

Поскольку многие плагины Remark/Rehype пишутся на CommonJS, а TypeScript с ESM использует синтаксис import, часто возникает необходимость комбинировать импорты:

import remarkGfm from 'remark-gfm';
// vs
// const remarkGfm = require('remark-gfm');

Использование esModuleInterop: true позволяет обращаться к плагинам как к default-экспорту, что упрощает типизацию и предотвращает ошибки.


Настройка типов для собственных плагинов

Если создается собственный плагин, его типизация оформляется следующим образом:

import { Plugin } from 'unified';
import { Root } from 'mdast';

const myRemarkPlugin: Plugin<[string?], Root> = (options = {}) => {
  return (tree, file) => {
    // манипуляции с tree
  };
};

Параметры:

  • [string?] — тип опций плагина (необязательный).
  • Root — тип дерева Markdown.
  • file имеет тип VFile, автоматически типизированный unified.

Интеграция с редакторами и линтерами

Для полноценного TypeScript-проекта рекомендуется подключить:

  • ESLint с плагинами eslint-plugin-unicorn и eslint-plugin-mdx для проверок AST.
  • Prettier для форматирования Markdown и HTML.
  • Настройки typeRoots и paths в tsconfig.json для корректного поиска типов плагинов.

Советы по типовой безопасности

  1. Всегда импортировать плагины через import при esModuleInterop: true.
  2. Использовать строгую типизацию AST (mdast и hast) вместо any.
  3. Типизировать собственные плагины через Plugin<Options, Tree>.
  4. Проверять типы возвращаемых значений методов process, run и runSync.
  5. Включить skipLibCheck: true для совместимости с несовершенно типизированными сторонними плагинами.

Эта конфигурация позволяет TypeScript корректно работать с unified, Remark и Rehype, обеспечивая строгую проверку типов, поддержку современных модулей и удобную интеграцию с пользовательскими и сторонними плагинами.