Сборка приложения ориентирована на конечный исполняемый продукт: веб-страницу, SPA, серверный бандл или desktop-обёртку. В центре внимания — минимальный размер, оптимальная загрузка, код-сплиттинг, кэширование и производительность выполнения.
Сборка библиотеки ориентирована на распространение кода как зависимости. Главная цель — предсказуемая интеграция в чужие проекты, совместимость с различными сборщиками, отсутствие дублирования зависимостей и корректная работа модульной системы потребителя.
Эти различия напрямую влияют на конфигурацию Esbuild и стратегию упаковки кода.
Приложение:
Библиотека:
В приложении зависимости обычно включаются в итоговый бандл:
import { format } from "date-fns";
Esbuild объединяет date-fns внутрь итогового файла.
В библиотеке такое поведение недопустимо по умолчанию. Библиотека должна не дублировать зависимости у потребителя.
Конфигурационно это выражается через external:
esbuild.build({
entryPoints: ["src/index.ts"],
outfile: "dist/index.js",
bundle: true,
external: ["react", "lodash"]
});
Чаще всего используется один формат:
iife для браузераesm для современного фронтендаcjs для Node.jsОбычно достаточно одного целевого бандла:
esbuild.build({
entryPoints: ["src/main.ts"],
bundle: true,
outfile: "dist/app.js",
platform: "browser"
});
Требует мультиформатной публикации:
esm)cjs)Пример:
esbuild.build({
entryPoints: ["src/index.ts"],
outdir: "dist",
bundle: true,
format: "esm",
splitting: true,
sourcemap: true
});
Затем отдельные сборки:
format: "cjs"
format: "esm"
format: "iife"
Обычно один файл или несколько чанков:
dist/
app.js
chunk-1.js
chunk-2.js
Чанки управляются исключительно внутренней логикой приложения.
Структура должна отражать публичный API:
dist/
index.js
index.mjs
index.cjs
utils/
format.js
math.js
или даже:
dist/
esm/
cjs/
types/
Ключевая идея — контролируемая экспортируемая поверхность.
В приложении code splitting — стандартная практика:
import()Esbuild автоматически разбивает бандл:
splitting: true,
format: "esm"
В библиотеке code splitting используется ограниченно.
Причины:
Поэтому чаще применяется:
import { x } from "lib/submodule")Tree-shaking работает внутри одного проекта:
Библиотека должна помогать tree-shaking потребителю:
sideEffects: false)Пример package.json:
{
"type": "module",
"sideEffects": false,
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
Минификация обязательна:
minify: true
Минификация — спорный выбор:
Иногда публикуют два варианта:
dist/index.js (readable)dist/index.min.js (production)Зависит от продукта:
es2020, es2022target: "es2019"
Должна быть более консервативной:
В библиотечном режиме критично избегать дублирования runtime:
external: ["react", "react-dom"]
Если этого не сделать:
В приложении подобная проблема отсутствует — всё контролируется одним бандлом.
Обычно один entry point:
src/main.ts
Множественные входные точки:
entryPoints: [
"src/index.ts",
"src/utils.ts",
"src/components/button.ts"
]
Это позволяет:
Source maps используются для:
sourcemap: true
Source maps важны ещё сильнее:
Иногда публикуют:
.map файлы для productionEsbuild может:
Часто избегают:
Предпочтение:
package.json в библиотечной сборкеКлючевые поля:
main — CommonJS entrymodule — ESM entry (legacy, но используется)exports — современный контрактtypes — TypeScript декларацииEsbuild не генерирует .d.ts, поэтому обычно используется
отдельный инструмент.
Типичные проблемы:
external → дублирование зависимостейexports → некорректные импортыСборка приложения в Esbuild — это процесс формирования оптимизированного runtime-артефакта, максимально сжатого и специализированного под конкретную среду.
Сборка библиотеки — это процесс формирования переносимого кода, который должен корректно встроиться в чужую архитектуру без конфликтов, дублирования зависимостей и нарушения модульных контрактов.