В экосистеме сборщиков JavaScript традиционно используются конфигурационные файлы, где описываются входные точки, выходные параметры, плагины и режимы работы. В Esbuild подход иной: конфигурация может быть полностью заменена программным скриптом на JavaScript, который использует API сборщика напрямую. Это делает процесс сборки более гибким, управляемым и расширяемым за счёт обычной логики языка.
Программная сборка в Esbuild основана на использовании функций
build(), context() и дополнительных API,
позволяющих управлять процессом без декларативных конфигураций.
Вместо статического объекта конфигурации используется исполняемый код, в котором параметры сборки формируются динамически.
Классическая конфигурация:
// esbuild.config.js
module.exports = {
entryPoints: ['src/index.js'],
bundle: true,
outdir: 'dist',
minify: true
};
Программный эквивалент:
import { build } from 'esbuild';
build({
entryPoints: ['src/index.js'],
bundle: true,
outdir: 'dist',
minify: true
});
Разница заключается не только в синтаксисе, но и в том, что параметры могут вычисляться во время выполнения.
Использование JavaScript позволяет формировать конфигурацию на основе условий окружения, аргументов командной строки или файловой структуры проекта.
import { build } from 'esbuild';
const isProd = process.env.NODE_ENV === 'production';
build({
entryPoints: ['src/index.js'],
bundle: true,
outdir: 'dist',
minify: isProd,
sourcemap: !isProd
});
В таком подходе логика сборки становится частью кода, а не внешним описанием.
Программный скрипт позволяет структурировать сборку на независимые части, что затруднительно в декларативных конфигурациях.
import { build } from 'esbuild';
const sharedConfig = {
bundle: true,
sourcemap: true
};
const client = build({
...sharedConfig,
entryPoints: ['src/client.js'],
outfile: 'dist/client.js'
});
const server = build({
...sharedConfig,
entryPoints: ['src/server.js'],
platform: 'node',
outfile: 'dist/server.js'
});
Такой подход позволяет явно описывать несколько целей сборки в одном файле без необходимости сложных конфигурационных структур.
Современный API Esbuild предоставляет context(), который
используется для инкрементальной сборки, наблюдения за файлами и
управления жизненным циклом процесса.
import { context } from 'esbuild';
const ctx = await context({
entryPoints: ['src/index.js'],
bundle: true,
outdir: 'dist'
});
Контекст позволяет запускать дополнительные режимы:
await ctx.watch();
Режим наблюдения отслеживает изменения файлов и автоматически пересобирает проект без необходимости внешних инструментов.
Esbuild способен поднимать встроенный dev-сервер, что часто заменяет отдельные инструменты разработки.
await ctx.serve({
port: 3000,
servedir: 'dist'
});
В этом режиме сборка и HTTP-сервер объединяются в одном процессе. Это исключает необходимость конфигурации отдельного dev-server инструмента.
Программный файл позволяет использовать любые источники данных для управления сборкой: переменные окружения, аргументы CLI, конфигурационные файлы JSON.
import { build } from 'esbuild';
const mode = process.argv.includes('--debug') ? 'debug' : 'release';
build({
entryPoints: ['src/index.js'],
bundle: true,
outdir: 'dist',
minify: mode === 'release',
define: {
__MODE__: JSON.stringify(mode)
}
});
Здесь сборка становится реакцией на входные параметры, а не фиксированной схемой.
Вместо нескольких конфигурационных файлов используется единый исполняемый файл с логическим разветвлением.
import { build } from 'esbuild';
const target = process.env.TARGET;
if (target === 'node') {
build({
entryPoints: ['src/node.js'],
platform: 'node',
outfile: 'dist/node.js'
});
} else {
build({
entryPoints: ['src/browser.js'],
platform: 'browser',
outfile: 'dist/browser.js'
});
}
Такой подход уменьшает дублирование конфигураций и централизует логику сборки.
Плагины в Esbuild подключаются как обычные JavaScript-объекты, что упрощает их композицию.
const examplePlugin = {
name: 'example',
setup(build) {
build.onResolve({ filter: /.*/ }, args => {
return { path: args.path, namespace: 'custom' };
});
}
};
Использование в сборке:
import { build } from 'esbuild';
build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/app.js',
plugins: [examplePlugin]
});
Логика плагина может динамически изменяться в зависимости от условий окружения.
Программный подход позволяет создавать фабрики конфигураций, что особенно полезно для больших проектов.
import { build } from 'esbuild';
function createConfig({ minify }) {
return {
entryPoints: ['src/index.js'],
bundle: true,
outdir: 'dist',
minify,
sourcemap: true
};
}
build(createConfig({ minify: true }));
Функции конфигурации позволяют переиспользовать общие параметры и избегать дублирования.
Так как build-функции возвращают Promise, несколько сборок могут выполняться одновременно.
import { build } from 'esbuild';
Promise.all([
build({
entryPoints: ['src/app.js'],
outfile: 'dist/app.js',
bundle: true
}),
build({
entryPoints: ['src/admin.js'],
outfile: 'dist/admin.js',
bundle: true
})
]);
Это ускоряет процесс сборки в монорепозиториях и многомодульных приложениях.
Программная модель позволяет использовать стандартные механизмы JavaScript для контроля ошибок.
import { build } from 'esbuild';
try {
await build({
entryPoints: ['src/index.js'],
bundle: true,
outdir: 'dist'
});
} catch (err) {
console.error('Ошибка сборки:', err);
process.exit(1);
}
Такой подход даёт полный контроль над поведением процесса в случае сбоя.
Esbuild-скрипты могут быть написаны как в формате ESM, так и CommonJS, что позволяет интегрировать их в разные типы проектов.
ESM:
import { build } from 'esbuild';
CommonJS:
const { build } = require('esbuild');
Выбор формата влияет только на способ импорта, не ограничивая функциональность API.
Программный API позволяет тонко управлять результатами сборки, включая формат модулей, имена файлов и структуру выходного каталога.
build({
entryPoints: ['src/index.js'],
bundle: true,
outdir: 'dist',
format: 'esm',
splitting: true,
chunkNames: 'chunks/[name]-[hash]'
});
Такая настройка особенно важна для приложений с динамическими импортами и код-сплиттингом.
Использование context() и rebuild()
позволяет строить систему сборки, работающую как долгоживущий
процесс.
import { context } from 'esbuild';
const ctx = await context({
entryPoints: ['src/index.js'],
bundle: true,
outdir: 'dist'
});
await ctx.watch();
process.on('SIGINT', async () => {
await ctx.dispose();
process.exit();
});
Такой подход уменьшает накладные расходы на пересборку и позволяет интегрировать сборку в сложные среды разработки.
Отказ от конфигурационного файла в пользу скрипта меняет саму модель управления сборкой. Конфигурация перестаёт быть статическим описанием и превращается в исполняемую программу, где доступны:
Это превращает процесс сборки в полноценный программируемый слой инфраструктуры, а не в декларативный набор настроек.