Больше чем просто пропущенные экспорты: создание раннего сборщика мусора

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

Интеграция плагина в большую кодовую базу — задача не из простых. Это превратилось в архитектурный вызов. Мне пришлось управлять манипуляциями с абстрактным синтаксическим деревом (AST), использованием памяти и рекурсивными циклами.

Проблема со стандартными плагинами

Чтобы найти решение, я провел три эксперимента.

Эксперимент 1: Плагин восстановил 135 типов. Однако он поместил их во внутренний модуль. Наш инструмент создавал для них отдельную папку. Нам приходилось сортировать их вручную. Структура документации также была неверной.

Эксперимент 2: Я включил настройку для скрытия внешних типов. Это сработало лишь частично. Объем данных сократился до 60 типов Webpack. Но TypeDoc

Больше, чем просто отсутствие экспортов: создание раннего сборщика мусора для AST TypeDoc в Webpack

При работе с документацией для крупных TypeScript-проектов часто возникает проблема: документация содержит символы, которые на самом деле не являются частью публичного API. Это происходит из-за того, что TypeDoc строит свое абстрактное синтаксическое дерево (AST), основываясь на анализе файлов, но не всегда учитывает, какие именно элементы действительно экспортируются и доступны через точку входа, особенно когда в цепочке участвует Webpack.

Проблема отсутствующих экспортов

Когда мы используем Webpack для сборки, он выполняет сложную работу по разрешению зависимостей и оптимизации кода. Однако TypeDoc работает на уровне анализа типов. Возникает разрыв: TypeDoc может видеть символы, которые Webpack «вырезает» или которые не экспортируются из основной точки входа. Это приводит к «раздутому» AST, наполненному неиспользуемыми узлами.

Обычно разработчики пытаются решить эту проблему, вручную настраивая поля exports в конфигурации TypeDoc. Но в масштабах огромных монорепозиториев это становится невозможным. Нам нужно решение, которое будет работать автоматически.

Решение: Ранний сборщик мусора

Вместо того чтобы пытаться очистить уже построенное дерево (что может быть ресурсозатратно и сложно из-за запутанных связей), мы можем внедрить механизм «сбора мусора» на раннем этапе — непосредственно в процессе построения AST.

Идея заключается в том, чтобы использовать алгоритм Mark-and-Sweep (пометить и очистить):

  1. Mark (Пометка): Начиная с точек входа (entry points), мы обходим дерево и помечаем все достижимые узлы.
  2. Sweep (Очистка): Все узлы, которые не были помечены как достижимые, удаляются из дерева.

Реализация

Чтобы реализовать это в контексте TypeDoc, нам нужно вмешаться в процесс создания узлов. Мы можем создать специальный Visitor, который будет проходить по дереву и отслеживать связи.

Вот концептуальный пример того, как это может выглядеть:

// Псевдокод реализации сборщика мусора для AST
class ASTGarbageCollector {
  private markedNodes = new Set<Node>();

  constructor(private entryPoints: Node[]) {}

  public collect(root: Node): void {
    this.mark();
    this.sweep(root);
  }

  private mark(): void {
    const queue = [...this.entryPoints];
    while (queue.length > 0) {
      const node = queue.shift();
      if (node && !this.markedNodes.has(node)) {
        this.markedNodes.add(node);
        queue.push(...node.getChildren());
      }
    }
  }

  private sweep(node: Node): void {
    const children = node.getChildren();
    for (const child of children) {
      if (!this.markedNodes.has(child)) {
        node.removeChild(child);
      } else {
        this.sweep(child);
      }
    }
  }
}

Почему это важно?

  1. Точность: Документация будет отражать только то, что реально доступно пользователю вашего пакета.
  2. Производительность: Меньший размер AST означает более быструю генерацию документации и меньшее потребление памяти.
  3. Чистота: Избавляет от «призрачных» экспортов, которые могут запутать разработчиков, использующих ваш API.

Заключение

Создание раннего сборщика мусора для AST — это способ синхронизировать то, как Webpack видит ваш код, с тем, как TypeDoc его документирует. Это превращает процесс генерации документации из простого «сканирования файлов» в интеллектуальный анализ графа зависимостей.