Skip to content

Поиск циклов

Цикл — группа модулей, импортирующих друг друга по кругу, напрямую или через цепочку. Циклы — самая частая причина «я тронул один файл, и три других фичи отвалились»: порядок загрузки модулей становится load-bearing, type-ошибки распространяются неожиданным образом, тесты начинают зависеть от порядка запуска.

Алгоритм

Archora гоняет алгоритм Тарьяна (strongly-connected-components) по графу импортов. Type-only рёбра по умолчанию исключены — TypeScript стирает их при сборке, и они не участвуют в runtime-циклах.

Каждый SCC с ≥ 2 модулями — цикл. Self-import (SCC длины 1) выводится отдельно как warning self-import, не как цикл.

Сложность — O(V + E), линейная по модулям + импортам. На 5000 модулях — единицы миллисекунд.

Паттерны циклов

После детектирования Archora делает второй проход и классифицирует каждый цикл по типичной форме:

ПаттернВыглядит какТипичное решение
mutual-pairРовно 2 модуля импортируют друг друга.Вынести общий модуль или инвертировать одно направление.
barrel-cycleindex.ts папки импортирует соседа, который импортирует обратно через barrel.Обходить barrel изнутри папки.
hub-feedbackМного модулей указывают на «хаб», который указывает обратно на один или несколько.Разбить хаб или инвертировать back-edge.
long-chainДлинная цепочка, замкнутая 1–2 рёбрами.Перевести одно замыкающее ребро в import type, если применимо.
no-shapeНе удалось уверенно классифицировать.Использовать feedback-arc-set подсказку.

Распознавание паттернов чисто описательное — оно не меняет цикл, оно даёт словарь, чтобы говорить о нём и выбирать правильный фикс.

Severity

SeverityЗначение
directДлина 2 — два модуля импортируют друг друга.
indirectДлина ≥ 3 — есть хотя бы один промежуточный hop.

Десктоп показывает direct-циклы красным, indirect — оранжевым. CLI считает оба одинаково — оба попадают в --fail-on cycles:N.

Что насчёт type-only циклов?

Если цикл закрывается только type-импортом, такое ребро стирается при сборке, и цикла на runtime фактически нет. Archora находит такие случаи как рекомендацию type-only candidate: меняете import на import type, сохраняете — цикл исчезает.

Ограничения

  • По умолчанию мы не моделируем import.meta.glob или кастомные dynamic-loader-ы. Сконфигурируйте их в archora.config, если они есть в проекте — иначе часть «реальных» runtime-рёбер будет отсутствовать в графе.
  • Циклические типовые графы (type Foo = { bar: Bar } и наоборот) не репортятся. TypeScript с ними справляется, и архитектурного долга в них нет.

См. также

Выпущено под лицензией BUSL-1.1.