Трансформация кода в хуке transform

Хук transform является центральным элементом системы плагинов Rollup, отвечающим за модификацию исходного кода модулей в процессе сборки. Именно на этом этапе происходит основная работа по анализу, переписыванию и преобразованию кода до того, как он попадёт в граф модулей и далее в финальный бандл.

Пайплайн Rollup строится вокруг последовательной обработки модулей. После этапа разрешения импортов и чтения файлов каждый модуль проходит через цепочку плагинов. На этом этапе transform становится точкой, где код ещё представлен в исходной или почти исходной форме, но уже доступен для модификации.

Основная задача transform заключается в следующем:

  • изменение исходного кода модуля;
  • добавление или удаление кода;
  • анализ структуры для последующих стадий;
  • генерация source map для корректной отладки;
  • взаимодействие с метаданными модуля.

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

Сигнатура и базовое поведение

Хук transform вызывается для каждого модуля с двумя основными аргументами:

  • code — текст исходного модуля;
  • id — абсолютный путь или идентификатор модуля.

Типичная форма возврата:

  • строка (новый код);
  • объект { code, map };
  • null или undefined, если изменений нет.

При этом Rollup ожидает, что результат трансформации будет либо новым кодом, либо расширенной структурой с source map.

Порядок выполнения трансформаций

Каждый модуль проходит через цепочку плагинов последовательно. Важно учитывать следующие особенности:

  • плагины выполняются в порядке их объявления;
  • результат одного transform становится входом для следующего;
  • при возврате null код передаётся дальше без изменений;
  • асинхронные трансформации допускаются через Promise.

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

Работа с изменением кода

Основной сценарий использования transform — переписывание исходного кода. Это может включать:

Лексические изменения

Простейший вариант — текстовые замены:

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

Подобные операции часто реализуются через регулярные выражения или AST-обработку.

AST-трансформация

Более корректный подход предполагает разбор кода в AST (Abstract Syntax Tree). В этом случае transform используется как точка интеграции парсеров:

  • Babel;
  • Acorn;
  • SWC (через плагины).

После модификации AST код снова сериализуется в строку.

Инструментальная вставка кода

Хук позволяет внедрять дополнительную логику:

  • polyfill’ы;
  • runtime-хелперы;
  • instrumentation для логирования;
  • обёртки функций.

В таких случаях важно контролировать порядок вставок, чтобы не нарушить семантику исходного модуля.

Source map и корректная отладка

Одной из ключевых задач transform является поддержка source map. Rollup объединяет карты источников от всех плагинов, поэтому каждый трансформер должен корректно возвращать map.

Варианты возврата:

  • null — отсутствие карты;
  • объект map в формате Source Map v3;
  • генерация через утилиты вроде magic-string.

При неправильной работе с source map нарушается отладка, что особенно критично в production-сборках.

Контекст this в transform

Функция transform вызывается с контекстом плагина. Через this доступны вспомогательные методы Rollup:

  • this.getModuleInfo(id) — получение информации о модуле;
  • this.emitFile() — создание ассетов;
  • this.resolve() — ручное разрешение зависимостей;
  • this.addWatchFile() — добавление файлов в наблюдение;
  • this.warn() и this.error() — генерация сообщений.

Контекст позволяет не только изменять код, но и влиять на граф модулей.

Асинхронные трансформации

transform может возвращать Promise, что делает возможным:

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

Асинхронность увеличивает гибкость, но усложняет предсказуемость пайплайна. Rollup ожидает завершения всех трансформаций перед переходом к следующему этапу.

Управление пропуском трансформации

Если плагин не изменяет код, он может вернуть null. Это важный механизм оптимизации:

  • уменьшает количество промежуточных объектов;
  • ускоряет сборку;
  • снижает нагрузку на последующие плагины.

Часто используется условная логика:

  • проверка расширения файла;
  • фильтрация по id;
  • анализ содержимого кода.

Использование фильтров

Для ограничения области действия transform применяются фильтры:

  • @rollup/pluginutils createFilter;
  • собственные регулярные проверки;
  • условия по путям и namespace.

Фильтрация позволяет избегать обработки:

  • node_modules;
  • бинарных файлов;
  • сторонних библиотек.

Влияние transform на граф модулей

Хотя transform работает с кодом, он косвенно влияет на структуру графа:

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

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

Взаимодействие с другими хуками

transform находится между стадиями:

  • load — получение исходного кода;
  • transform — его модификация;
  • resolveId — разрешение зависимостей.

Часто плагины комбинируют несколько хуков:

  • load предоставляет виртуальный код;
  • transform адаптирует его под сборку.

Производительность transform

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

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

Оптимизации включают:

  • кеширование результатов;
  • ленивый парсинг;
  • ограничение области применения;
  • минимизацию сериализаций.

Типичные сценарии использования

Транспиляция синтаксиса

  • TypeScript в JavaScript;
  • JSX в createElement;
  • экспериментальные фичи ECMAScript.

Инструментирование кода

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

Оптимизация

  • удаление мёртвого кода;
  • инлайнинг констант;
  • упрощение выражений.

Генерация кода

  • создание обёрток модулей;
  • внедрение runtime-инициализации;
  • автоматическое подключение зависимостей.

Ограничения transform

Несмотря на гибкость, хук имеет ограничения:

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

Также важно учитывать, что чрезмерная модификация кода может привести к трудноотлаживаемым бандлам.

Детали выполнения в цепочке плагинов

Rollup применяет особую модель композиции:

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

Это делает transform предсказуемым, но чувствительным к порядку регистрации плагинов.

Работа с виртуальными модулями

transform часто используется совместно с виртуальными модулями:

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

В таких случаях id может не соответствовать реальному файлу, что требует дополнительной логики обработки.