Все основные операции в esbuild построены вокруг асинхронной модели.
Функции build() и transform() возвращают
Promise, что делает библиотеку совместимой с современными механизмами
обработки асинхронности в JavaScript и позволяет интегрировать её в
серверные и CLI-пайплайны без блокировки потока выполнения.
Функция сборки имеет следующий базовый контракт:
import * as esbuild from "esbuild";
const result = await esbuild.build({
entryPoints: ["src/index.js"],
bundle: true,
outfile: "dist/bundle.js",
});
Promise резолвится объектом результата, содержащим:
outputFiles (при использовании
write: false)metafile (при включённой опции
metafile: true)warnings и errorsТакой подход позволяет строить цепочки обработки результата:
esbuild.build(options)
.then(result => {
if (result.warnings.length) {
console.warn(result.warnings);
}
return result;
})
.then(result => {
// дальнейшая обработка метаданных
})
.catch(err => {
console.error(err);
});
Promise-ориентированная архитектура делает esbuild удобным для интеграции в системы, где требуется последовательная обработка стадий сборки.
Ошибки в esbuild классифицируются на две группы:
Если сборка не может быть завершена, Promise отклоняется:
try {
await esbuild.build({
entryPoints: ["src/index.js"],
bundle: true,
outfile: "dist/bundle.js",
});
} catch (e) {
console.error("Ошибка сборки:", e);
}
Важно, что ошибки парсинга модулей, отсутствующих файлов или некорректных плагинов приводят именно к reject, а не к частичному результату.
Плагинная система esbuild полностью асинхронна. Все хуки могут возвращать Promise, что позволяет выполнять операции ввода-вывода без блокировки сборки.
const plugin = {
name: "async-loader",
setup(build) {
build.onLoad({ filter: /\.txt$/ }, async (args) => {
const content = await fs.promises.readFile(args.path, "utf8");
return {
contents: content,
loader: "text",
};
});
},
};
Асинхронность в onResolve и onLoad
обеспечивает:
Каждый такой хук может возвращать Promise, и esbuild автоматически включает его в граф выполнения.
С ростом размера проектов становится критически важной возможность остановки активной сборки. В esbuild существует несколько механизмов управления жизненным циклом процесса.
В современных версиях esbuild используется API контекста:
const ctx = await esbuild.context({
entryPoints: ["src/index.js"],
bundle: true,
outfile: "dist/bundle.js",
});
await ctx.rebuild();
Контекст позволяет:
Освобождение контекста:
await ctx.dispose();
dispose() завершает активные операции и освобождает
внутренние ресурсы. После вызова контекст становится невалидным для
дальнейших сборок.
Механизм отмены зависит от режима работы:
В некоторых конфигурациях поддерживается передача
AbortSignal, позволяющая прервать выполнение:
const controller = new AbortController();
esbuild.build({
entryPoints: ["src/index.js"],
bundle: true,
outfile: "dist/bundle.js",
signal: controller.signal,
});
controller.abort();
При вызове abort() текущая операция завершается с
ошибкой отмены, Promise отклоняется.
При использовании контекста отмена выполняется через его методы управления:
const ctx = await esbuild.context({
entryPoints: ["src/index.js"],
bundle: true,
outfile: "dist/bundle.js",
});
ctx.dispose();
В отличие от AbortSignal, dispose() ориентирован не
только на текущую операцию, но и на полное завершение жизненного цикла
сборщика.
Инкрементальная сборка изменяет модель выполнения: esbuild сохраняет состояние графа модулей между вызовами.
const ctx = await esbuild.context({
entryPoints: ["src/index.js"],
bundle: true,
outfile: "dist/bundle.js",
});
await ctx.rebuild();
await ctx.rebuild();
Если в момент выполнения rebuild() инициируется новая
операция или вызывается dispose(), текущий процесс может
быть прерван. В таких сценариях важно учитывать:
При работе с esbuild часто возникает ситуация параллельных сборок:
const build1 = esbuild.build(options);
const build2 = esbuild.build(options);
Каждая операция независима, но потребляет системные ресурсы. При высокой частоте вызовов возможны:
Контекстный API снижает риск таких гонок, так как сериализует операции внутри одного окружения.
Отмена операции не всегда интерпретируется как критическая ошибка. В практике обработки Promise используется разделение типов ошибок:
try {
await ctx.rebuild();
} catch (e) {
if (e.name === "CanceledError") {
// нормальное завершение при отмене
} else {
throw e;
}
}
Такая модель позволяет различать:
Если плагин выполняет длительные асинхронные операции, отмена сборки может происходить в момент их выполнения. В этом случае важно учитывать, что esbuild не всегда принудительно прерывает внешние Promise.
build.onLoad({ filter: /\.json$/ }, async (args) => {
const data = await fetchData(args.path);
return {
contents: JSON.stringify(data),
loader: "json",
};
});
Если сборка была отменена во время fetchData, результат
может быть проигнорирован, но сама операция может продолжить выполнение.
Это накладывает требования на:
В сложных конфигурациях с несколькими плагинами и асинхронными источниками данных возникает необходимость координации Promise:
onResolve и onLoadesbuild гарантирует детерминированность графа модулей, но не гарантирует порядок выполнения пользовательских асинхронных операций внутри плагинов, если они не синхронизированы явно.
Устойчивые схемы работы с отменой строятся на комбинации:
context.dispose() для завершения сборкиТакая модель позволяет избежать утечек ресурсов и неконтролируемого выполнения Promise после завершения сборки.