incremental режим в esbuild относится к устаревшему API, которое использовалось для ускорения повторных сборок за счёт сохранения внутреннего состояния графа модулей между вызовами. Его ключевая идея — избегать полной пересборки проекта при каждом изменении, ограничиваясь пересчётом только затронутых файлов.
При первой сборке esbuild строит полный граф зависимостей: анализирует входные точки, разрешает импорты, трансформирует модули и формирует промежуточное представление. В режиме incremental этот граф сохраняется в памяти и повторно используется при последующих сборках.
Основной результат первой сборки содержит дополнительный объект, который позволяет инициировать повторную сборку без повторного анализа всей файловой структуры.
import * as esbuild from 'esbuild';
const result = await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
incremental: true
});
// повторная сборка с учётом изменений
await result.rebuild();
Метод rebuild() использует ранее построенный граф и
пересчитывает только изменившиеся модули. Это существенно ускоряет цикл
разработки в проектах с большим количеством файлов.
При включении incremental: true результат
build() расширяется дополнительными возможностями:
rebuild() — повторная сборка на основе сохранённого
состоянияdispose() — освобождение памяти, занятой графом
модулейawait result.dispose();
dispose() критически важен, поскольку
incremental-контекст удерживает значительный объём памяти. При
длительной работе без очистки возможно накопление утечек памяти,
особенно в процессах с частыми пересборками или в тестовых средах.
Incremental режим опирается на несколько уровней кэширования:
При вызове rebuild() esbuild старается переиспользовать
эти структуры, пересчитывая только изменённые узлы графа. Если изменения
затрагивают входные точки или конфигурацию плагинов, часть кэша
инвалидируется.
Incremental режим имеет ряд ограничений:
rebuild() без
создания нового контекстаОсобенно важно ограничение на конфигурацию: любые изменения опций,
влияющих на граф сборки (например, define,
jsxFactory, platform), требуют новой полной
сборки.
Incremental API был промежуточным решением до появления более
универсального механизма — контекста сборки. Основная проблема
incremental заключалась в том, что он был «надстройкой» над обычным
build(), а не полноценной абстракцией управления жизненным
циклом сборки.
Это приводило к:
Современная альтернатива incremental — context API,
который предоставляет более структурированное управление сборкой.
Эквивалент incremental через context:
import * as esbuild from 'esbuild';
const ctx = await esbuild.context({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js'
});
// первая сборка
await ctx.rebuild();
// повторные сборки
await ctx.rebuild();
await ctx.dispose();
В отличие от incremental, context API:
Incremental:
build()Context:
esbuild.context()И incremental, и context используют одинаковую стратегию отслеживания изменений файловой системы. При изменении исходного файла:
Разница заключается в том, как хранится и управляется этот граф.
Incremental часто использовался как основа для ручной реализации watch:
import * as esbuild from 'esbuild';
const result = await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
incremental: true
});
require('fs').watch('src', async () => {
await result.rebuild();
});
Однако такой подход был низкоуровневым и требовал самостоятельного управления debounce, обработкой ошибок и очисткой ресурсов.
Context API решает это более системно через встроенный
watch режим:
const ctx = await esbuild.context({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js'
});
await ctx.watch();
Incremental-контекст удерживает:
При частых rebuild без dispose() память может расти
линейно. Это особенно критично в CI-средах и долгоживущих
dev-серверах.
Контекстный API делает управление ресурсами более предсказуемым:
жизненный цикл явно завершается вызовом dispose(),
освобождая все внутренние структуры.
Incremental API сохраняет значение только в поддержке старых кодовых баз и библиотек, где переход на context требует изменения архитектуры инструментария. В новых проектах он не используется, так как context полностью перекрывает его функциональность и расширяет её.
Миграция обычно сводится к замене:
esbuild.build({ incremental: true }) наesbuild.context() + rebuild()с последующим добавлением явного управления жизненным циклом.