В esbuild разделение кода начинается не с автоматической оптимизации, а с явного определения структуры входных файлов. При использовании нескольких точек входа формируется набор независимых графов зависимостей, которые затем пересекаются через общие модули. Именно это пересечение и становится основой для выделения общих чанков.
esbuild поддерживает передачу массива входных файлов через параметр
entryPoints. Каждый элемент массива становится отдельным
корнем графа сборки.
import * as esbuild from 'esbuild';
await esbuild.build({
entryPoints: [
'src/admin.js',
'src/client.js',
'src/analytics.js'
],
outdir: 'dist',
bundle: true,
format: 'esm'
});
В этом случае формируются три независимых бандла. Однако без дополнительных настроек общие зависимости дублируются в каждом выходном файле, поскольку esbuild по умолчанию не выполняет агрессивного разделения общих модулей между точками входа.
Ключевым механизмом становится опция splitting. Она
активирует вынос общих частей кода в отдельные чанки, но работает только
в сочетании с форматом ESM.
await esbuild.build({
entryPoints: [
'src/admin.js',
'src/client.js',
'src/analytics.js'
],
outdir: 'dist',
bundle: true,
splitting: true,
format: 'esm'
});
При включённом splitting esbuild анализирует пересечение
графов зависимостей и выносит повторяющиеся модули в отдельные файлы,
которые затем импортируются из каждого entry chunk.
Использование outdir вместо outfile
принципиально важно. При нескольких точках входа esbuild обязан
формировать каталог, а не один файл.
Результирующая структура может выглядеть следующим образом:
dist/
admin.js
client.js
analytics.js
chunk-AB12CD.js
chunk-X9Y8Z7.js
Имена чанков генерируются хешированием содержимого и зависят от внутренней структуры графа зависимостей.
esbuild не использует заранее заданную стратегию разделения вроде manualChunks (как в других сборщиках). Вместо этого применяется алгоритм:
Если модуль используется только в одной точке входа, он остаётся внутри соответствующего бандла.
// src/utils/logger.js
export function log(message) {
console.log(message);
}
// src/admin.js
import { log } from './utils/logger';
log('admin');
// src/client.js
import { log } from './utils/logger';
log('client');
При сборке с несколькими entry points и splitting: true,
модуль logger.js будет вынесен в отдельный chunk, так как
он используется минимум в двух графах.
Несмотря на термин «ручное разделение», esbuild не предоставляет прямого управления чанками на уровне пользователя. Отсутствуют механизмы:
Разделение определяется исключительно структурой импорта и пересечением графов.
Поддержка разделения строго ограничена ESM. При использовании
format: 'cjs' или iife поведение меняется:
await esbuild.build({
entryPoints: ['src/a.js', 'src/b.js'],
outdir: 'dist',
bundle: true,
splitting: true,
format: 'cjs'
});
В этом случае результат не будет содержать отдельных чанков.
esbuild стремится к стабильной генерации имён чанков при неизменной структуре зависимостей. Однако изменение даже одного внутреннего модуля может привести к:
Это связано с тем, что чанки формируются на основе контента, а не фиксированной схемы.
Наличие нескольких entry points не исключает использование
import() внутри модулей. В этом случае:
// src/client.js
button.oncl ick = async () => {
const module = await import('./heavy-feature.js');
module.run();
};
Такой модуль будет вынесен в отдельный файл независимо от других entry points, если он не входит в их статический граф.
На практике несколько точек входа часто используются для изоляции разных частей приложения:
При этом общие библиотеки (HTTP-клиент, утилиты, форматирование данных) автоматически попадают в shared chunks.
Если два entry points имеют частично пересекающиеся зависимости, esbuild создаёт минимальный набор общих чанков. Например:
В этом случае формируются два независимых shared chunk, а не один общий для всех.
Использование нескольких entry points с splitting влияет на:
Разделение полезно только при стабильном повторном использовании общих модулей между точками входа. При слабом пересечении графов количество чанков может увеличиться без значимого выигрыша.
Для анализа структуры разделения можно использовать
metafile:
await esbuild.build({
entryPoints: ['src/admin.js', 'src/client.js'],
outdir: 'dist',
bundle: true,
splitting: true,
format: 'esm',
metafile: true
});
Метаданные позволяют увидеть:
Это особенно важно при сложных приложениях с несколькими уровнями входных точек.
Типичная конфигурация для нескольких entry points часто дополняется:
entryNames — управление именами выходных файловchunkNames — шаблон для имен shared chunksassetNames — управление статическими ресурсамиawait esbuild.build({
entryPoints: ['src/admin.js', 'src/client.js'],
outdir: 'dist',
bundle: true,
splitting: true,
format: 'esm',
entryNames: '[dir]/[name]-[hash]',
chunkNames: 'chunks/[name]-[hash]'
});
Это не влияет на логику разделения, но определяет структуру выходной директории.
При увеличении числа entry points поведение системы становится нелинейным:
Поэтому архитектура с множественными точками входа требует стабильного деления кода по доменным зонам, а не случайного дробления файлов.
esbuild оптимизирован для инкрементальной сборки. При изменении одного entry point:
Однако при изменении shared модуля может затронуться несколько entry points одновременно, что увеличивает стоимость пересборки.
Система разделения кода в esbuild строится на трёх принципах:
Результат определяется не декларативными правилами, а структурой импортов и реальными связями между модулями.