Изменение узлов

AST в SWC представляет собой строго типизированное дерево синтаксических узлов, в котором каждый элемент кода описывается отдельной структурой: идентификаторы, литералы, выражения, объявления функций, JSX-элементы и другие конструкции. Изменение узлов означает преобразование этих структур в процессе обхода дерева без изменения исходного текста напрямую.

Любая трансформация строится вокруг идеи неизменяемости входного дерева. Вместо редактирования узлов «на месте» создаются новые экземпляры, которые заменяют старые в результате обхода.

Каждый узел AST содержит:

  • тип узла (например, Identifier, CallExpression, BinaryExpression);
  • вложенные структуры (дочерние узлы);
  • метаданные (позиции в коде, комментарии, аннотации).

Изменение узлов заключается в том, чтобы:

  • определить нужный узел;
  • создать его модифицированную копию;
  • вернуть замену в процессе обхода.

Механизм обхода и Visitor

Основной способ работы с AST в SWC — паттерн Visitor. Он позволяет описывать функции, которые вызываются при входе или выходе из конкретных типов узлов.

Обход дерева реализуется через трансформеры, где каждый тип узла может быть обработан отдельно:

  • вход в узел (pre-order);
  • выход из узла (post-order);
  • замена узла;
  • удаление узла (возврат null или эквивалента).

Visitor обеспечивает предсказуемую структуру изменений, особенно при сложных вложенных выражениях.

Базовая модель трансформации

Трансформация в SWC строится через функцию, возвращающую новый AST:

  • вход: исходное дерево;
  • выход: модифицированное дерево;
  • промежуточный слой: обход и преобразование узлов.

В JavaScript API чаще всего используется пакет @swc/core, где трансформации задаются через plugin или transform.

Замена идентификаторов

Одной из самых частых операций является переименование переменных.

Принцип работы:

  • найти узлы типа Identifier;
  • проверить имя;
  • вернуть новый узел с изменённым значением.

Пример логики трансформации:

  • count → totalCount
  • temp → _temp

При этом важно учитывать контекст, чтобы не затронуть свойства объектов или ключи.

Особенность SWC заключается в том, что идентификатор может встречаться в разных ролях:

  • переменная;
  • параметр функции;
  • имя свойства объекта;
  • метка.

Поэтому простая замена строки без проверки типа контекста приводит к некорректному коду.

Изменение литералов

Литералы (строки, числа, булевы значения) изменяются аналогичным образом:

  • строковые литералы могут нормализоваться;
  • числовые — преобразовываться (например, округление);
  • булевы — инвертироваться или заменяться на выражения.

Пример типовой трансформации:

  • “debug” → ““
  • 0 → 1 (в тестовых режимах)
  • true → false

SWC требует создания нового узла литерала, а не изменения существующего поля.

Удаление и вставка узлов

Удаление узла осуществляется возвратом null в трансформере. Это применяется, например, для:

  • удаления console.log;
  • удаления dead-code блоков;
  • устранения условных веток.

Вставка узлов выполняется через замену текущего узла на новый составной узел, например:

  • блок { … } с дополнительными выражениями;
  • цепочка выражений через SequenceExpression.

Важно учитывать, что удаление узла может повлиять на синтаксическую корректность, поэтому часто требуется замена на «пустой» эквивалент, например undefined или пустой блок.

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

Выражения являются наиболее частым объектом трансформаций.

Типичные операции:

  • упрощение бинарных выражений;
  • инлайнинг констант;
  • замена вызовов функций;
  • оптимизация цепочек вызовов.

Пример:

  • 2 + 2 → 4
  • x * 1 → x
  • true && expr → expr

Такие преобразования требуют анализа вложенных узлов и их типов.

JSX и структурные изменения

При работе с JSX-деревом изменяются:

  • имена компонентов;
  • свойства (props);
  • вложенные элементы.

Пример трансформации:

  • <Button /> → <PrimaryButton />
  • className → cls

JSX-узлы в SWC представлены отдельной структурой, поэтому их обработка требует специализированных visitor-функций.

Сложные трансформации и композиция

SWC позволяет комбинировать несколько трансформеров:

  • один отвечает за переименование;
  • другой за оптимизацию;
  • третий за удаление отладочного кода.

Композиция выполняется последовательно, где результат одного этапа становится входом для следующего.

Порядок трансформаций критичен:

  • сначала нормализация;
  • затем оптимизация;
  • затем финальная чистка.

Scope и гигиенические преобразования

При изменении узлов важно учитывать область видимости:

  • локальные переменные не должны конфликтовать с глобальными;
  • переименование должно сохранять уникальность идентификаторов;
  • вставка новых переменных требует проверки коллизий.

SWC использует механизмы hygiene (гигиены), чтобы предотвращать случайные конфликты имён при генерации нового AST.

Работа с функциями трансформации Fold

Fold-трансформации позволяют не просто обходить дерево, а «сворачивать» узлы, заменяя их результатами вычислений.

Принцип:

  • узел обрабатывается снизу вверх;
  • дочерние выражения сначала трансформируются;
  • затем вычисляется родительский узел.

Это особенно полезно для:

  • оптимизации арифметики;
  • упрощения логических выражений;
  • статического анализа.

Практика изменения сложных структур

При работе с вложенными структурами важно:

  • сохранять неизменность промежуточных узлов;
  • аккуратно обрабатывать массивы узлов (например, body блока);
  • учитывать возможные null-узлы после удаления.

Пример типичной операции:

  • обход функции;
  • изменение всех return-выражений;
  • добавление логирования перед возвратом.

Типичные ошибки при трансформации

  • некорректная замена узла без пересоздания структуры;
  • потеря контекста области видимости;
  • нарушение синтаксиса JSX или выражений;
  • частичная модификация массива детей без копирования.

Изменение AST требует строгого соблюдения структурной целостности дерева: каждый возвращаемый узел должен быть валидным с точки зрения синтаксиса JavaScript/TypeScript.

Производительность трансформаций

Эффективность изменений узлов зависит от:

  • глубины дерева;
  • количества проходов;
  • сложности условий в visitor-функциях.

Оптимизация достигается через:

  • минимизацию повторных обходов;
  • использование кэширования результатов;
  • ранний выход из обработки узлов, не требующих изменений.