Настройка бандлера esbuild для веб-проекта
esbuild написан на Go и работает в 10–100 раз быстрее аналогов на JavaScript. Холодный старт, который Webpack тратит 30 секунд, esbuild завершает за 300 мс. Это меняет не только время ожидания, но и подход к разработке: пересборка на каждом сохранении перестаёт быть проблемой.
Где esbuild применяется напрямую
Большинство проектов используют esbuild косвенно — через Vite, который берёт его для dev-сервера, или через Jest с esbuild-jest. Прямое использование esbuild оправдано когда:
- нужен минималистичный пайплайн без фреймворкового оверхеда
- собирается сервер на Node.js (бандлинг Lambda-функций, CLI-утилит)
- существующий билд слишком медленный и нужна точечная замена транспилятора
- проект в монорепо требует кастомного скрипта сборки
Установка и первый запуск
npm install --save-dev esbuild
Минимальный запуск без конфиг-файла:
./node_modules/.bin/esbuild src/index.ts \
--bundle \
--outfile=dist/bundle.js \
--target=es2020 \
--format=esm \
--sourcemap
JavaScript API
esbuild не имеет YAML/JSON-конфига — конфигурация через JS API или CLI. JS API даёт полный контроль:
// build.mjs
import * as esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outdir: 'dist',
format: 'esm',
target: ['es2020', 'chrome90', 'firefox88', 'safari14'],
splitting: true, // code splitting для ESM
sourcemap: true,
minify: process.env.NODE_ENV === 'production',
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
},
external: ['react', 'react-dom'],
metafile: true, // для анализа бандла
loader: {
'.png': 'file',
'.svg': 'dataurl',
'.woff2': 'file',
},
});
Dev-сервер с hot reload
esbuild имеет встроенный dev-сервер и watch-режим:
// dev.mjs
import * as esbuild from 'esbuild';
const ctx = await esbuild.context({
entryPoints: ['src/index.tsx'],
bundle: true,
outdir: 'dist',
format: 'esm',
sourcemap: 'inline',
define: { 'process.env.NODE_ENV': '"development"' },
});
await ctx.watch();
const { host, port } = await ctx.serve({
servedir: 'public', // статика
port: 3000,
});
console.log(`Dev server: http://${host}:${port}`);
Встроенный сервер поддерживает SSE для live reload, но не HMR в смысле React Fast Refresh. Для полноценного HMR лучше использовать Vite.
Плагины
Экосистема плагинов значительно меньше, чем у Webpack, но ключевые задачи закрыты:
import { sassPlugin } from 'esbuild-sass-plugin';
import { copy } from 'esbuild-plugin-copy';
await esbuild.build({
plugins: [
sassPlugin({
type: 'css', // или 'css-module' для CSS Modules
}),
copy({
assets: [
{ from: './public/**/*', to: './dist' },
],
}),
],
});
Написать собственный плагин несложно — API простой:
const envPlugin: esbuild.Plugin = {
name: 'env',
setup(build) {
// Перехватываем импорты вида `import env from 'env'`
build.onResolve({ filter: /^env$/ }, (args) => ({
path: args.path,
namespace: 'env-ns',
}));
build.onLoad({ filter: /.*/, namespace: 'env-ns' }, () => ({
contents: JSON.stringify(process.env),
loader: 'json',
}));
},
};
Сборка для Node.js
Lambda-функции, CLI-инструменты, серверные скрипты:
await esbuild.build({
entryPoints: ['src/handler.ts'],
bundle: true,
platform: 'node',
target: 'node18',
format: 'cjs', // Lambda ожидает CJS
outfile: 'dist/handler.js',
external: [
// AWS SDK не включаем — он уже в Lambda runtime
'@aws-sdk/*',
// Нативные модули
'bcrypt',
'sharp',
],
minify: true,
});
Для нативных addon'ов (.node файлы) esbuild не справится — нужен дополнительный шаг копирования.
Анализ метафайла
const result = await esbuild.build({ ..., metafile: true });
const text = await esbuild.analyzeMetafile(result.metafile!, {
verbose: true,
});
console.log(text);
Или выгрузить в JSON и загрузить на esbuild.github.io/analyze/ — интерактивная sunburst-диаграмма с размерами модулей.
Ограничения
esbuild намеренно не реализует ряд вещей:
-
TypeScript type checking — esbuild только транспилирует, не проверяет типы. Запускайте
tsc --noEmitотдельно в CI -
CSS Modules — поддержка базовая, нет
composesи кастомных имён классов в стиле[local]_[hash] - Декораторы — поддержка экспериментальная, старый синтаксис декораторов (emitDecoratorMetadata) не поддерживается
- Некоторые Babel-трансформы — если нужны специфические трансформы AST, потребуется плагин или предобработка через SWC/Babel
Сроки
Замена транспилятора в существующем проекте на esbuild (например, Jest → esbuild-jest): 2–4 часа. Настройка полноценного пайплайна сборки с нуля (dev-сервер, production-сборка, CSS, ассеты): 4–8 часов.







