Большие монорепозитории в JavaScript обычно состоят из множества пакетов, объединённых единым управлением зависимостями и общими инструментами сборки. Типичная структура включает:
packages/*)apps/*)shared/*)node_modules через workspace-менеджерВ такой среде основная проблема сборки заключается не в единичной компиляции, а в экспоненциальном росте графа зависимостей. Даже небольшое изменение в одном пакете может инициировать пересборку десятков модулей.
Esbuild в этом контексте используется как высокоскоростной компилятор и бандлер, способный значительно сократить время сборки, но только при правильной организации графа модулей и кэширования.
Esbuild строит граф зависимостей начиная с entry-point и рекурсивно обходит импортируемые модули. В монорепозитории это означает:
Ключевая особенность: Esbuild не управляет монорепозиторием как системой, он работает только с графом импортов.
Эффективная работа начинается с чёткого разделения пакетов.
Использование Yarn Workspaces, pnpm или npm workspaces создаёт единое дерево зависимостей, но важно не полагаться на это автоматически.
Критично:
tsconfig.jsonsrc/index.ts)Одна из главных причин деградации производительности — попадание внутренних или внешних зависимостей в бандл.
Esbuild позволяет явно исключать зависимости:
external: [
'react',
'react-dom',
'@company/utils'
]
В монорепозитории особенно важно:
node_modulesКритический момент: если не пометить workspace-пакеты как external, Esbuild может собрать их несколько раз в разных частях графа.
Монорепозиторий формирует сложный DAG (directed acyclic graph), где пакеты зависят друг от друга.
Оптимальная стратегия:
distpackage.json
exportsПример структуры:
packages/
ui/
utils/
apps/
web/
Каждый пакет:
build через esbuildEsbuild поддерживает режим наблюдения, который критичен для монорепозиториев.
esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outdir: 'dist',
watch: true
})
Но в больших репозиториях этого недостаточно.
Оптимизация достигается через:
Esbuild сам по себе не имеет полноценного долговременного дискового кеша, поэтому в монорепозитории используются дополнительные стратегии:
Каждый пакет собирается отдельно:
Используются task-runner’ы (Turborepo, Nx), которые кэшируют результаты:
distКеш ломается при:
Esbuild компилирует TypeScript без type-checking, что создаёт важное разделение ответственности:
В монорепозитории это разделяется:
tsc --noEmitОптимизация достигается через:
incremental: true в TypeScriptreferences в
tsconfig)TypeScript project references позволяют разбить монорепозиторий на граф компиляции:
packages/utils -> packages/ui -> apps/web
Преимущества:
Часто используются paths в tsconfig:
{
"paths": {
"@utils/*": ["packages/utils/src/*"]
}
}
Проблема:
Решение:
esbuild-plugin-tsconfig-pathsalias в esbuild configОдно из ключевых преимуществ монорепозитория — возможность параллелизации.
Стратегия:
Пример архитектуры:
Чем меньше граф — тем быстрее сборка.
Практики:
index.ts с re-export всех
модулей)Esbuild поддерживает code splitting:
splitting: true,
format: 'esm'
В монорепозитории это позволяет:
Важно:
Esbuild умеет генерировать метаданные:
metafile: true
Это позволяет:
Типичный анализ включает:
На основе метафайла строятся оптимизации:
В монорепозитории важно разделять режимы:
Рекомендации:
watch только на уровне пакетовВозникает при:
Причины:
Причины:
При росте монорепозитория ключевым становится переход к многоуровневой архитектуре:
Esbuild используется на каждом уровне отдельно, без попытки собрать всё единым процессом.
Основной принцип: сборка должна следовать структуре репозитория, а не наоборот.