Что такое esbuild и зачем он нужен

Современная JavaScript-разработка практически всегда связана с процессом сборки проекта. Исходный код редко доставляется в браузер или среду выполнения в том виде, в котором он написан. Обычно выполняется целый набор операций:

  • объединение модулей в единый пакет;
  • преобразование современного синтаксиса JavaScript в более старые версии;
  • обработка TypeScript;
  • минификация кода;
  • удаление неиспользуемых частей приложения;
  • обработка CSS и других ресурсов.

На протяжении многих лет для этих задач активно использовались инструменты вроде Webpack, Rollup и Parcel. Несмотря на богатые возможности, крупные проекты часто сталкивались с проблемой длительного времени сборки. С ростом объёмов кода запуск разработки и пересборка после изменений могли занимать десятки секунд или даже минуты.

Для решения этой проблемы был создан esbuild — сверхбыстрый инструмент сборки JavaScript и TypeScript, разработанный Эваном Уоллесом. Главной целью проекта стало достижение максимальной производительности без ущерба для основных возможностей современных сборщиков.


Что такое esbuild

esbuild — это инструмент для сборки JavaScript-приложений, который объединяет в себе несколько функций:

  • бандлер (Bundler);
  • транспилятор;
  • минификатор;
  • инструмент анализа зависимостей;
  • средство преобразования TypeScript;
  • компилятор JSX.

Проще говоря, esbuild способен выполнять большую часть задач, которые обычно требуются на этапе подготовки приложения к запуску или публикации.

Пример простейшей сборки:

esbuild src/index.js --bundle --outfile=dist/app.js

В результате:

  1. Находится файл src/index.js.
  2. Анализируются все импортируемые зависимости.
  3. Создаётся единый файл dist/app.js.
  4. Внутри него содержится весь необходимый код приложения.

Основная идея проекта

Главная особенность esbuild — экстремально высокая скорость работы.

Большинство традиционных сборщиков написаны на JavaScript и работают поверх движка V8. Esbuild реализован на языке Go, который компилируется в машинный код и обеспечивает высокий уровень производительности.

Кроме того, архитектура esbuild изначально строилась вокруг:

  • многопоточности;
  • параллельной обработки файлов;
  • минимизации лишних преобразований;
  • эффективного анализа модулей.

За счёт этого даже проекты с тысячами файлов могут собираться за доли секунды.


Почему скорость настолько важна

В небольших приложениях разница между сборкой за 2 секунды и за 200 миллисекунд может показаться несущественной. Однако в крупных проектах ситуация меняется.

Представим приложение со следующими характеристиками:

  • несколько сотен React-компонентов;
  • тысячи модулей;
  • TypeScript;
  • CSS-модули;
  • большое количество сторонних библиотек.

Если пересборка занимает 20–30 секунд, разработчик постоянно ожидает результата после каждого изменения.

Даже небольшие задержки накапливаются:

Время одной пересборки Пересборок в день Потери времени
2 секунды 300 10 минут
10 секунд 300 50 минут
20 секунд 300 100 минут

Поэтому современные инструменты всё чаще ориентируются на производительность, а esbuild стал одним из лидеров этого направления.


Основные возможности esbuild

Сборка модулей

Esbuild умеет объединять множество файлов в единый пакет.

Исходные файлы:

// math.js
export function sum(a, b) {
    return a + b;
}
// index.js
import { sum } from './math.js';

console.log(sum(5, 10));

Сборка:

esbuild index.js --bundle --outfile=bundle.js

Результат:

(() => {
    function sum(a, b) {
        return a + b;
    }

    console.log(sum(5, 10));
})();

Все зависимости оказываются внутри одного файла.


Транспиляция JavaScript

Esbuild способен преобразовывать современный синтаксис в код, совместимый со старыми браузерами.

Исходный код:

const getUserName = (user) => user?.name ?? 'Unknown';

Команда:

esbuild app.js --target=es2017 --outfile=out.js

После обработки синтаксис будет адаптирован под указанный стандарт ECMAScript.


Поддержка TypeScript

Отдельная настройка TypeScript-компилятора часто не требуется.

Файл:

type User = {
    name: string;
};

const user: User = {
    name: 'Alex'
};

console.log(user.name);

Сборка:

esbuild app.ts --bundle --outfile=app.js

Типы будут удалены, а результатом станет обычный JavaScript.


Поддержка JSX

Esbuild умеет работать с JSX без дополнительных транспиляторов.

Пример:

function App() {
    return <h1>Hello World</h1>;
}

Сборка:

esbuild app.jsx --bundle --outfile=app.js

Это особенно полезно при работе с React и другими библиотеками, использующими JSX-синтаксис.


Минификация

Для производственной версии приложения размер файлов имеет большое значение.

Исходный код:

function calculateTotal(price, tax) {
    return price + tax;
}

console.log(calculateTotal(100, 20));

Минификация:

esbuild app.js --minify --outfile=app.min.js

Результат:

function calculateTotal(o,t){return o+t}console.log(calculateTotal(100,20));

Размер файла становится меньше, а загрузка быстрее.


Архитектура работы esbuild

Внутри процесс сборки состоит из нескольких этапов.

Анализ входных файлов

Сначала esbuild считывает точку входа:

esbuild src/index.js

Далее определяется список импортируемых модулей:

import './utils.js';
import './api.js';
import './config.js';

Формируется граф зависимостей.


Построение графа модулей

Каждый файл рассматривается как узел графа.

Например:

index.js
 ├── api.js
 │    └── config.js
 └── utils.js

Esbuild определяет порядок обработки и включения файлов в итоговый пакет.


Преобразование кода

На данном этапе выполняются:

  • транспиляция;
  • обработка JSX;
  • преобразование TypeScript;
  • замена констант;
  • оптимизации.

Генерация результата

После завершения преобразований создаётся финальный набор файлов:

dist/
 ├── app.js
 ├── app.css
 └── app.js.map

При необходимости формируются карты исходников.


Почему esbuild работает быстрее конкурентов

Высокая скорость достигается благодаря нескольким решениям.

Реализация на Go

Большинство популярных инструментов написаны на JavaScript.

Esbuild реализован на Go:

JavaScript → V8 → Машинный код

Против:

Go → Машинный код

Это уменьшает накладные расходы выполнения.


Параллельная обработка

Если проект содержит тысячу файлов, esbuild может обрабатывать множество модулей одновременно.

Схематично:

Файл 1 ─┐
Файл 2 ─┼─> Поток 1
Файл 3 ─┘

Файл 4 ─┐
Файл 5 ─┼─> Поток 2
Файл 6 ─┘

Используются все доступные ядра процессора.


Минимальное количество проходов

Некоторые сборщики многократно проходят по одному и тому же дереву кода.

Esbuild старается выполнять несколько операций за один проход:

  • анализ;
  • преобразование;
  • оптимизацию.

Это дополнительно сокращает время работы.


Форматы модулей, поддерживаемые esbuild

Современная экосистема JavaScript использует несколько систем модулей.

ESM

Современный стандарт ECMAScript.

import { sum } from './math.js';

export default sum;

CommonJS

Традиционный формат Node.js.

const fs = require('fs');

module.exports = {};

IIFE

Самовызывающаяся функция для браузеров.

(() => {
    console.log('App');
})();

UMD

Универсальный формат, способный работать в разных окружениях.

Esbuild умеет преобразовывать код между различными форматами.

Пример:

esbuild app.js --format=esm

или

esbuild app.js --format=cjs

Работа с CSS

Хотя esbuild прежде всего ориентирован на JavaScript, он способен обрабатывать и CSS.

Пример импорта:

import './styles.css';

Во время сборки CSS будет включён в результирующий набор файлов.

Команда:

esbuild app.js --bundle --outfile=dist/app.js

Результат:

dist/
 ├── app.js
 └── app.css

Tree Shaking

Одной из важных возможностей является удаление неиспользуемого кода.

Модуль:

export function add() {}
export function subtract() {}
export function multiply() {}

Использование:

import { add } from './math.js';

add();

При сборке функции subtract и multiply могут быть удалены из итогового пакета.

Такой процесс называется Tree Shaking.

Преимущества:

  • уменьшение размера файлов;
  • ускорение загрузки;
  • снижение потребления памяти.

Source Maps

Во время разработки часто возникает проблема: браузер показывает ошибку в скомпилированном файле, а исходный код находится в другом месте.

Для решения используются карты исходников.

Создание:

esbuild app.js --sourcemap

Результат:

app.js
app.js.map

Теперь инструменты разработчика смогут связывать итоговый код с оригинальными файлами проекта.


API и CLI

Esbuild может использоваться двумя способами.

Через командную строку

Самый простой вариант:

esbuild src/index.js --bundle --outfile=dist/app.js

Подходит для небольших проектов и экспериментов.


Через JavaScript API

Пример:

const esbuild = require('esbuild');

esbuild.build({
    entryPoints: ['src/index.js'],
    bundle: true,
    outfile: 'dist/app.js'
});

Такой подход позволяет создавать собственные сценарии сборки и интегрировать esbuild в сложную инфраструктуру.


Роль esbuild в современной экосистеме

Сегодня esbuild используется не только как самостоятельный инструмент.

На его основе построены или частично используют его возможности многие современные решения:

  • Vite;
  • tsup;
  • Vitest;
  • Nx;
  • Astro.

Во многих случаях именно esbuild отвечает за быстрый запуск среды разработки, предварительную обработку зависимостей или часть процесса компиляции.


Когда esbuild особенно полезен

Наибольшую выгоду esbuild приносит в следующих сценариях:

  • крупные проекты с большим количеством модулей;
  • приложения на TypeScript;
  • React-проекты с JSX;
  • инструменты командной строки;
  • библиотеки для публикации в npm;
  • монорепозитории;
  • проекты, где критична скорость сборки.

Благодаря сочетанию функций бандлера, транспилятора и минификатора esbuild позволяет существенно сократить время подготовки кода, сохраняя при этом совместимость с современными стандартами JavaScript и существующей экосистемой фронтенд-разработки.