Опция external в esbuild используется для управления
тем, какие модули должны быть исключены из бандла и оставлены как
внешние зависимости. В контексте peerDependencies эта
настройка становится ключевым механизмом корректного разделения
ответственности между библиотекой и приложением, предотвращая
дублирование пакетов и конфликты версий.
При сборке проекта esbuild анализирует граф зависимостей и по умолчанию пытается включить все импортируемые модули в итоговый бандл. Однако в реальных библиотеках часто требуется иной подход: некоторые зависимости не должны попадать внутрь выходного файла, поскольку они предполагаются уже установленными в окружении потребителя.
Опция:
require() или import в
результирующем коде;peerDependencies.external и
peerDependenciespeerDependencies в package.json описывает
зависимости, которые должны быть предоставлены конечным проектом.
Библиотека лишь объявляет совместимость, но не устанавливает их
самостоятельно.
Типичный пример:
{
"name": "my-ui-lib",
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
}
Если при сборке не использовать external, esbuild может
встроить react и react-dom внутрь бандла
библиотеки. Это приводит к проблемам:
externalОпция задаётся в конфигурации esbuild:
import esbuild from 'esbuild';
esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/index.js',
external: ['react', 'react-dom']
});
В этом случае любые импорты:
import React from 'react';
import { createRoot } from 'react-dom/client';
не будут встроены в сборку. Вместо этого они останутся внешними зависимостями, которые должны быть доступны в окружении потребителя.
peerDependencies на практикеЧасто external формируется автоматически на основе
package.json:
import pkg from './package.json' assert { type: 'json' };
const peerDeps = Object.keys(pkg.peerDependencies || {});
esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/index.js',
external: peerDeps
});
Такой подход гарантирует, что:
package.json и
сборкой.В зависимости от format (например, esm или
cjs) поведение внешних зависимостей сохраняется, но способ
подключения меняется:
const react = require('react');
import * as react from 'react';
Esbuild не внедряет код библиотеки, а оставляет ссылку на модуль для разрешения в рантайме.
Наиболее распространённый подход:
external: Object.keys(pkg.peerDependencies || {})
Используется в библиотеках UI, дизайн-системах, плагинах.
Иногда требуется исключить не только peerDependencies, но и крупные runtime-библиотеки:
external: [
'react',
'react-dom',
'vue',
'lodash'
]
Такой подход оправдан, когда библиотека позиционируется как надстройка над экосистемой.
esbuild поддерживает паттерны через external с
использованием regex-совместимых строк:
external: [
/^react/,
/^@mui\//
]
Это позволяет исключить целые семейства пакетов:
react/*@mui/*external напрямую влияет на структуру итоговой
сборки:
При неправильной конфигурации возможны проблемы:
Типичный сценарий для библиотеки:
import esbuild from 'esbuild';
import pkg from './package.json' assert { type: 'json' };
const external = [
...Object.keys(pkg.peerDependencies || {}),
...Object.keys(pkg.dependencies || {})
];
esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/index.js',
format: 'esm',
external
});
Однако включение dependencies в external
требует осторожности: это превращает библиотеку в тонкую обёртку над
внешними модулями.
dependencies и peerDependencies в
контексте externaldependencies — устанавливаются автоматически вместе с
библиотекойpeerDependencies — должны быть предоставлены
потребителемСледовательно:
peerDependencies почти всегда должны быть
externaldependencies — только если есть архитектурная причина
не включать их в бандл// ошибка: react попадёт в бандл
esbuild.build({
bundle: true,
external: []
});
Последствия:
external: ['*']
Последствия:
При использовании внешних зависимостей esbuild не трансформирует их содержимое, но корректно сохраняет синтаксис импорта. Это важно для библиотек, которые поддерживают двойной формат:
Часто используется отдельный модуль конфигурации:
export function createExternal(pkg) {
return Object.keys(pkg.peerDependencies || {});
}
И применение:
external: createExternal(pkg)
Это обеспечивает:
При использовании external esbuild:
Это важно учитывать при использовании кастомных плагинов для алиасов или виртуальных модулей.
В монорепозиториях часто встречается комбинация:
Пример:
external: [
...Object.keys(pkg.peerDependencies || {}),
/^@my-org\//
]
Это предотвращает дублирование внутренних пакетов монорепозитория.
external в архитектуре сборкиМеханизм external формирует границу между:
В связке с peerDependencies он обеспечивает
предсказуемость, совместимость и отсутствие дублирования, позволяя
библиотекам оставаться лёгкими и независимыми от конкретного
runtime-окружения.