esbuild.context() представляет собой переход от
одноразовой сборки к управляемому жизненному циклу процесса компиляции.
Вместо вызова esbuild.build() на каждое изменение входных
файлов создаётся долговременный контекст, который инкапсулирует
конфигурацию, состояние кэша, файловые наблюдатели и механизм повторных
сборок.
Контекст устраняет необходимость вручную управлять:
Основная идея заключается в том, что сборка превращается в объект с жизненным циклом, а не в разовую операцию.
Контекст создаётся через асинхронный вызов:
import * as esbuild from 'esbuild';
const ctx = await esbuild.context({
entryPoints: ['src/index.js'],
outdir: 'dist',
bundle: true,
platform: 'node',
sourcemap: true
});
Возвращаемое значение — объект контекста, содержащий методы управления:
ctx.rebuild() — ручной запуск сборки;ctx.watch() — включение режима наблюдения за
файлами;ctx.serve() — встроенный dev-сервер;ctx.dispose() — завершение жизненного цикла.Контекст существует в памяти процесса и удерживает внутренние структуры esbuild до явного освобождения.
Жизненный цикл можно разделить на несколько фаз:
При вызове esbuild.context() происходит:
На этом этапе сборка ещё не выполняется автоматически.
Первый вызов происходит либо явно через rebuild(), либо
автоматически при watch()/serve().
await ctx.rebuild();
В этот момент:
После первичной сборки контекст может находиться в одном из режимов:
rebuild);serve.Контекст сохраняет:
При изменении файлов (в режиме watch):
Ключевая особенность: esbuild не пересобирает всё приложение, а пересчитывает только изменённые ветки графа.
Завершение работы выполняется через:
await ctx.dispose();
Это критически важный этап управления ресурсами:
Игнорирование dispose() приводит к утечкам ресурсов в
долгоживущих процессах (dev-server, CLI watchers, тестовые среды).
Режим наблюдения включается явно:
await ctx.watch();
После активации:
Особенность архитектуры заключается в том, что watch не является «надстройкой» над build, а встроен в контекстный слой.
При изменении файла происходит следующая цепочка:
Это обеспечивает стабильную задержку обновления независимо от размера проекта.
Метод rebuild() используется в сценариях:
const result = await ctx.rebuild();
console.log(result.errors, result.warnings);
Особенность rebuild() в контексте — он использует уже
созданный граф зависимостей, а не строит его заново.
Метод serve() объединяет сборку и HTTP-сервер:
await ctx.serve({
port: 3000,
servedir: 'public'
});
В этом режиме:
Архитектурно это расширение жизненного цикла контекста до уровня runtime-сервиса.
Контекст esbuild опирается на несколько уровней кеша:
Хранит:
Содержит:
Фиксирует:
Эти кеши живут внутри контекста и сбрасываются только при
dispose().
Плагины в контексте работают в рамках долгоживущего процесса.
const ctx = await esbuild.context({
entryPoints: ['src/index.js'],
bundle: true,
plugins: [
{
name: 'logger',
setup(build) {
build.onStart(() => {
console.log('build started');
});
build.onEnd(() => {
console.log('build finished');
});
}
}
]
});
Особенности:
onStart вызывается перед каждой пересборкой;onEnd вызывается после завершения;Это превращает плагины в часть реактивного жизненного цикла, а не разовой обработки.
Контекст сериализует сборки:
Это исключает race conditions при частых изменениях файлов.
Контекст хранит значительный объём данных в памяти. Основные источники:
Поэтому жизненный цикл должен быть строго ограничен:
dispose() после
завершения.В некоторых сценариях требуется полное обновление конфигурации:
В таких случаях:
await ctx.dispose();
const newCtx = await esbuild.context({
entryPoints: ['src/app.js'],
bundle: true,
platform: 'browser'
});
Пересоздание дешевле и предсказуемее, чем попытка мутировать существующий контекст.
Происходит при отсутствии dispose() в:
Запуск watch() без завершения предыдущего контекста
приводит к:
Частые изменения файлов без debounce на уровне внешней системы могут перегружать очередь сборки.
esbuild.context() формирует модель, в которой:
Такой подход превращает esbuild из инструмента компиляции в управляемую runtime-систему сборки с чётко определённым жизненным циклом.