Хук transform

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

Этап transform располагается после получения исходного содержимого модуля и до построения графа зависимостей в окончательном виде. Последовательность обработки модуля в Rollup можно описать следующим образом:

  1. Определение входной точки
  2. Разрешение идентификатора (resolveId)
  3. Загрузка содержимого модуля (load)
  4. Трансформация содержимого (transform)
  5. Анализ импортов и построение графа

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

Сигнатура и параметры хука

Хук transform может быть синхронным или асинхронным и принимает два основных параметра:

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

Дополнительно может возвращаться объект с полями:

  • code — изменённый код
  • map — sourcemap для корректного отображения исходного кода

Пример логической структуры:

transform(code, id) {
  return {
    code: modifiedCode,
    map: sourceMap
  };
}

Асинхронный вариант:

async transform(code, id) {
  const result = await process(code);
  return {
    code: result.code,
    map: result.map
  };
}

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

Трансформация синтаксиса

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

  • преобразование JSX
  • поддержку TypeScript
  • обработку нестандартных языковых расширений

На этом этапе Rollup не ограничивает тип трансформации, поэтому хук часто используется как основа для компиляторов.

Внедрение дополнительного кода

transform позволяет модифицировать модули путем добавления или изменения логики. Примеры:

  • автоматическое добавление импортов
  • внедрение логирования
  • вставка полифиллов
  • инструментирование кода для профилирования
transform(code) {
  return {
    code: `console.log("module loaded");\n${code}`
  };
}

Переписывание зависимостей

Хотя основная работа с импортами происходит в resolveId, в transform можно изменять строки импортов до анализа, что позволяет:

  • перенаправлять импорты на другие модули
  • внедрять виртуальные зависимости
  • подменять библиотеки

Возвращаемые значения и управление потоком

Хук может возвращать несколько типов значений:

1. Строка

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

transform(code) {
  return code.toUpperCase();
}

2. Объект

Используется при необходимости передать source map:

transform(code) {
  return {
    code: transformed,
    map: mapObject
  };
}

3. null

Возвращение null означает отсутствие изменений и передачу кода дальше по цепочке.

Контекст выполнения и this

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

  • this.emitFile — генерация новых файлов
  • this.warn — вывод предупреждений
  • this.error — генерация ошибок сборки
  • this.getModuleInfo — получение информации о модуле

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

transform(code, id) {
  if (code.includes('eval')) {
    this.warn(`Использование eval в ${id}`);
  }

  return code;
}

Управление sourcemap

Поддержка source map является важной частью transform, так как любые изменения кода должны сохранять корректное отображение исходных строк.

Основные режимы:

  • true — автоматическая генерация
  • false — отключение
  • "inline" — встроенная карта
  • объект — ручное управление

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

Кэширование результатов трансформации

Rollup может кэшировать результаты transform для ускорения последующих сборок. Кэширование зависит от:

  • идентификатора модуля
  • содержимого кода
  • конфигурации плагина

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

Влияние на граф зависимостей

Хотя transform не предназначен напрямую для управления зависимостями, он влияет на их формирование, поскольку Rollup анализирует импорт-выражения уже после трансформации кода. Это означает:

  • добавленные импорты становятся частью графа
  • удалённые импорты исключаются
  • динамически изменённые строки влияют на структуру сборки

Ограничения и особенности

Хук имеет ряд архитектурных ограничений:

  • не предназначен для разрешения путей модулей (для этого используется resolveId)
  • не должен выполнять тяжёлые операции без необходимости
  • изменение кода должно учитывать корректность AST и синтаксиса
  • последовательность плагинов влияет на итоговый результат

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

Композиция нескольких transform-плагинов

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

  1. Первый плагин получает исходный код
  2. Второй — результат первого
  3. И так далее

Это позволяет строить цепочки трансформаций, но также создаёт риск конфликтов при несовместимых изменениях.

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

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

  • id может не соответствовать файловой системе
  • код создаётся на лету
  • трансформация становится основным источником логики

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

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

Поскольку transform вызывается для каждого модуля, его производительность напрямую влияет на скорость сборки. Основные рекомендации архитектурного характера:

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

В крупных проектах именно transform становится основной точкой оптимизации сборки.

Связь с AST-парсингом

Хотя Rollup сам по себе не требует работы с AST, большинство трансформаций используют его. Типичный сценарий:

  1. Получение code
  2. Парсинг в AST
  3. Модификация дерева
  4. Генерация нового кода
  5. Возврат результата через transform

Этот подход обеспечивает точечные изменения без разрушения структуры модуля.