Сборка библиотек в Parcel опирается на систему targets, где каждый выходной артефакт описывает отдельный формат публикации. В отличие от сборки приложения, где результат ориентирован на конкретную среду исполнения (браузер или Node.js), библиотека требует одновременной генерации нескольких форматов модулей и строгого соответствия контракту публикации в npm.
Основная цель library target — формирование набора артефактов, которые могут быть использованы разными системами модулей без дополнительной трансформации.
В Parcel v2 конфигурация библиотеки строится через поле
targets в package.json. Каждый target
описывает отдельный вариант сборки:
Базовая структура:
{
"name": "my-lib",
"source": "src/index.js",
"targets": {
"default": {
"distDir": "dist",
"sourceMap": true
}
}
}
Однако для библиотек этого недостаточно, поскольку требуется публикация нескольких форматов.
Современные библиотеки почти всегда публикуются минимум в двух вариантах:
import/export)require/module.exports)Parcel позволяет явно задать разные targets под разные выходы:
{
"source": "src/index.js",
"targets": {
"module": {
"distDir": "dist/esm",
"outputFormat": "esmodule"
},
"main": {
"distDir": "dist/cjs",
"outputFormat": "commonjs"
}
}
}
Для библиотек важно явно указать, что сборка не является приложением.
В Parcel это выражается через контекст library:
{
"targets": {
"main": {
"context": "library",
"outputFormat": "commonjs",
"distDir": "dist"
}
}
}
Parcel использует поле source как единый вход:
{
"source": "src/index.js"
}
Файл входа должен формировать публичный API библиотеки:
export { sum } from "./math/sum.js";
export { multiply } from "./math/multiply.js";
При сборке Parcel анализирует граф модулей и формирует минимальный набор зависимостей для каждого target.
Для корректной интеграции с Node.js и bundlers важно синхронизировать Parcel targets с экспортами npm:
{
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/types/index.d.ts",
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
}
}
}
Parcel не заменяет эти поля, но использует их как часть контрактной модели публикации.
Одним из ключевых механизмов Parcel является повторное использование одного графа модулей для всех targets.
Процесс:
Это позволяет:
Для библиотек tree-shaking играет критическую роль, поскольку пользователи импортируют только части API.
Пример:
export function sum(a, b) {
return a + b;
}
export function debugLog(x) {
console.log(x);
}
Если пользователь импортирует только sum, Parcel:
debugLog из production bundleБиблиотеки часто не должны включать зависимости внутрь бандла.
Parcel позволяет управлять этим через package.json:
{
"dependencies": {
"lodash": "^4.17.21"
}
}
И в конфигурации target:
{
"targets": {
"main": {
"context": "library",
"outputFormat": "commonjs",
"external": ["lodash"]
}
}
}
require или importParcel автоматически генерирует типы при корректной конфигурации или
использует уже существующие .d.ts.
Типичная структура:
{
"source": "src/index.ts",
"targets": {
"main": {
"distDir": "dist",
"outputFormat": "esmodule"
}
}
}
Дополнительно:
tsc отдельноПолноценная библиотека обычно публикуется в трёх форматах:
Пример конфигурации:
{
"targets": {
"esm": {
"distDir": "dist/esm",
"outputFormat": "esmodule"
},
"cjs": {
"distDir": "dist/cjs",
"outputFormat": "commonjs"
},
"umd": {
"distDir": "dist/umd",
"outputFormat": "global",
"isLibrary": true
}
}
}
UMD-сборка используется для библиотек, которые должны быть доступны через глобальную переменную.
{
"targets": {
"umd": {
"distDir": "dist",
"outputFormat": "global",
"isLibrary": true,
"scopeHoist": false
}
}
}
Особенности:
window.MyLibParcel автоматически разделяет режимы:
Для библиотек это критично, поскольку:
Типичная структура после сборки:
dist/
esm/
index.js
cjs/
index.js
umd/
index.js
В продвинутых конфигурациях:
Parcel учитывает поле:
{
"sideEffects": false
}
Это позволяет:
Если библиотека содержит side effects (например, polyfills), поле должно быть настроено точечно:
{
"sideEffects": ["./src/polyfills.js"]
}
Ключевое требование library target — идентичность API во всех форматах.
Parcel обеспечивает это через:
Любое расхождение обычно возникает не в Parcel, а в: