Внутри Esbuild существует два фундаментальных режима обработки
исходного кода: transform и bundle.
Оба режима используют один и тот же парсер, систему плагинов и быстрый
движок компиляции на Go, но решают принципиально разные задачи.
Ключевое различие заключается в уровне работы:
- transform работает с отдельным модулем
(файлом)
- bundle работает с графом зависимостей
проекта
Это различие определяет поведение всей системы сборки, оптимизации и
итогового результата.
Режим transform предназначен для преобразования одного файла без
анализа его зависимостей. Он не строит граф импортов и не знает ничего о
других модулях проекта.
Основные задачи transform:
- транспиляция TypeScript → JavaScript
- преобразование JSX / TSX
- минимизация одного файла
- применение синтаксических плагинов (например, макросов)
- генерация sourcemap для одного модуля
import { transform } from "esbuild";
const result = await transform(`
const App = () => <div>Hello</div>;
`, {
loader: "jsx",
jsx: "automatic",
});
console.log(result.code);
Transform работает строго локально:
- отсутствует разрешение импортов
- отсутствует анализ зависимостей
- отсутствует объединение файлов
- каждый файл обрабатывается независимо
Это делает transform чрезвычайно быстрым и предсказуемым инструментом
для изолированных преобразований.
Bundle: работа с графом
модулей
Bundle — это режим, в котором Esbuild строит полный граф зависимостей
приложения, начиная с entry-point и рекурсивно подключая все
импортируемые модули.
Основные задачи bundle:
- объединение модулей в один или несколько файлов
- разрешение импортов (node_modules, относительные пути)
- tree-shaking (удаление неиспользуемого кода)
- код-сплиттинг
- управление внешними зависимостями
- генерация production-ready бандла
Пример bundle через API
import { build } from "esbuild";
await build({
entryPoints: ["src/index.js"],
bundle: true,
outfile: "dist/app.js",
});
Ключевая архитектурная
разница
1. Область видимости
Transform:
- один файл
- отсутствует контекст проекта
Bundle:
- весь граф зависимостей
- глобальный контекст приложения
2. Работа с импортами
Transform:
// import игнорируется как зависимость системы сборки
import { sum } from "./math.js";
Esbuild в transform-режиме не пытается загрузить
math.js. Он просто преобразует код внутри файла.
Bundle:
import { sum } from "./math.js";
Esbuild:
- находит
math.js
- анализирует его зависимости
- добавляет в граф
- может исключить при tree-shaking
3. Tree-shaking
Transform:
- невозможен
- нет информации о том, что используется в других модулях
Bundle:
- доступен на уровне графа
- удаляет неиспользуемые экспорты
Пример:
// math.js
export const a = 1;
export const b = 2;
// index.js
import { a } from "./math.js";
console.log(a);
В bundle-режиме b будет удалён из финального
результата.
4. Производительность
Transform:
- O(1) относительно числа файлов проекта
- масштабируется линейно по количеству файлов при параллельной
обработке
Bundle:
- зависит от размера графа зависимостей
- требует построения дерева модулей
- включает анализ импортов и резолвинг
Несмотря на это, Esbuild остаётся одним из самых быстрых bundler’ов
благодаря реализации на Go и агрессивному кешированию.
Практическое
различие сценариев использования
- встроенные редакторы кода (on-the-fly трансформация)
- обработка отдельных файлов перед загрузкой
- плагины, модифицирующие AST одного файла
- интеграция в пайплайны без сборки проекта
Пример CLI-подхода:
esbuild input.ts --loader=ts --outfile=out.js --transform
Bundle применяется в случаях:
- сборка SPA/SSR приложений
- подготовка production-бандла
- оптимизация зависимостей
- код-сплиттинг для динамической загрузки
Bundle-режим получает дополнительные параметры, которые transform не
использует в таком виде.
external зависимости
build({
entryPoints: ["src/index.js"],
bundle: true,
external: ["react", "react-dom"],
});
Эти модули:
- не включаются в бандл
- остаются как
import в итоговом коде
build({
platform: "node",
bundle: true,
});
Влияет на:
- формат резолвинга модулей
- поведение встроенных Node.js API
- формат вывода (CommonJS vs ESM)
Transform не использует эти правила, так как не выполняет
резолвинг.
Минимизация: различие в
контексте
Transform:
transform(code, {
minify: true,
});
Минификация применяется только к одному файлу без учёта внешних
ссылок.
Bundle:
build({
entryPoints: ["src/index.js"],
bundle: true,
minify: true,
});
Здесь минификация происходит после:
- объединения модулей
- tree-shaking
- устранения дубликатов кода
Итоговый результат обычно значительно компактнее.
Sourcemap: различие уровней
Transform:
- sourcemap привязан к одному файлу
- нет цепочки исходников
Bundle:
- sourcemap отражает весь граф модулей
- поддерживает mapping до исходных файлов проекта
- может включать inline и external режимы
Код-сплиттинг
как исключительная функция bundle
Transform не поддерживает разделение кода, так как не имеет графа
зависимостей.
Bundle поддерживает:
build({
entryPoints: ["src/app.js"],
bundle: true,
splitting: true,
format: "esm",
outdir: "dist",
});
Результат:
- несколько файлов chunks
- динамические import() становятся отдельными чанками
- оптимизация загрузки
Плагинная система:
различие поведения
Transform-плагины:
- работают на уровне одного файла
- могут изменять содержимое AST
- не могут влиять на резолвинг зависимостей проекта
Bundle-плагины:
- участвуют в процессе резолвинга
- могут подменять модули
- влияют на граф зависимостей
- могут предотвращать или перенаправлять импорт
В bundle-режиме transform используется как внутренний этап обработки
каждого модуля.
Процесс выглядит так:
- построение графа зависимостей
- загрузка модуля
- transform для каждого файла
- объединение результата
- оптимизация
Таким образом transform — это базовый строительный блок bundle, но не
наоборот.
Сравнение поведения на
уровне результата
Transform:
- 1 вход → 1 выход
- структура проекта не изменяется
- отсутствует оптимизация между файлами
Bundle:
- N входов → 1 или несколько выходов
- структура проекта трансформируется
- применяется межмодульная оптимизация
Типичные ошибки при выборе
режима
- попытка обработать проект с импортами через transform
- ожидание, что зависимости будут автоматически подтянуты
Результат: отсутствующие модули в runtime
Ошибка:
использование bundle для единичных файлов
- лишняя нагрузка на граф зависимостей
- медленнее, чем transform
- избыточная работа резолвера
Итоговое функциональное
разделение
Transform:
- локальная трансформация
- синтаксическая обработка
- минимальная стоимость вычислений
Bundle:
- системная сборка приложения
- анализ зависимостей
- оптимизация структуры проекта
- генерация production-артефактов