Сборка библиотеки: library target

Сборка библиотек в Parcel опирается на систему targets, где каждый выходной артефакт описывает отдельный формат публикации. В отличие от сборки приложения, где результат ориентирован на конкретную среду исполнения (браузер или Node.js), библиотека требует одновременной генерации нескольких форматов модулей и строгого соответствия контракту публикации в npm.

Основная цель library target — формирование набора артефактов, которые могут быть использованы разными системами модулей без дополнительной трансформации.


Targets как основа библиотечной сборки

В Parcel v2 конфигурация библиотеки строится через поле targets в package.json. Каждый target описывает отдельный вариант сборки:

  • формат модулей (ESM, CommonJS, UMD)
  • назначение (браузер, Node.js, библиотека)
  • директорию вывода
  • дополнительные ограничения окружения

Базовая структура:

{
  "name": "my-lib",
  "source": "src/index.js",
  "targets": {
    "default": {
      "distDir": "dist",
      "sourceMap": true
    }
  }
}

Однако для библиотек этого недостаточно, поскольку требуется публикация нескольких форматов.


Разделение ESM и CommonJS

Современные библиотеки почти всегда публикуются минимум в двух вариантах:

  • ESM (import/export)
  • CommonJS (require/module.exports)

Parcel позволяет явно задать разные targets под разные выходы:

{
  "source": "src/index.js",
  "targets": {
    "module": {
      "distDir": "dist/esm",
      "outputFormat": "esmodule"
    },
    "main": {
      "distDir": "dist/cjs",
      "outputFormat": "commonjs"
    }
  }
}

Поведение Parcel при сборке

  • каждый target компилируется независимо
  • tree-shaking применяется отдельно для каждого формата
  • кодовая база не дублируется на уровне исходников, но результат может отличаться

Library context и семантика публикации

Для библиотек важно явно указать, что сборка не является приложением. В Parcel это выражается через контекст library:

{
  "targets": {
    "main": {
      "context": "library",
      "outputFormat": "commonjs",
      "distDir": "dist"
    }
  }
}

Эффекты library context

  • отключение браузер-ориентированных оптимизаций
  • корректная обработка side effects
  • сохранение экспортной структуры без агрессивного бандлинга
  • более строгая работа с внешними зависимостями

Управление точкой входа библиотеки

Parcel использует поле source как единый вход:

{
  "source": "src/index.js"
}

Файл входа должен формировать публичный API библиотеки:

export { sum } from "./math/sum.js";
export { multiply } from "./math/multiply.js";

При сборке Parcel анализирует граф модулей и формирует минимальный набор зависимостей для каждого target.


Экспортные поля package.json

Для корректной интеграции с 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.

Процесс:

  1. строится единый dependency graph
  2. применяется tree-shaking
  3. генерируются независимые бандлы под каждый target

Это позволяет:

  • избежать расхождений между форматами
  • сократить время полной пересборки
  • сохранить консистентность API

Tree-shaking в библиотечном режиме

Для библиотек tree-shaking играет критическую роль, поскольку пользователи импортируют только части API.

Пример:

export function sum(a, b) {
  return a + b;
}

export function debugLog(x) {
  console.log(x);
}

Если пользователь импортирует только sum, Parcel:

  • исключает debugLog из production bundle
  • удаляет несвязанный код
  • сохраняет минимальный размер пакета

External зависимости

Библиотеки часто не должны включать зависимости внутрь бандла.

Parcel позволяет управлять этим через package.json:

{
  "dependencies": {
    "lodash": "^4.17.21"
  }
}

И в конфигурации target:

{
  "targets": {
    "main": {
      "context": "library",
      "outputFormat": "commonjs",
      "external": ["lodash"]
    }
  }
}

Поведение external

  • зависимость не попадает в bundle
  • остаётся импортом require или import
  • ответственность за установку лежит на потребителе

Поддержка TypeScript в library target

Parcel автоматически генерирует типы при корректной конфигурации или использует уже существующие .d.ts.

Типичная структура:

{
  "source": "src/index.ts",
  "targets": {
    "main": {
      "distDir": "dist",
      "outputFormat": "esmodule"
    }
  }
}

Дополнительно:

  • декларации могут собираться через tsc отдельно
  • Parcel корректно сохраняет структуру модулей
  • типы синхронизируются с ESM-выходом

Мультиформатная публикация

Полноценная библиотека обычно публикуется в трёх форматах:

  • ESM (для современных bundlers)
  • CJS (для Node.js и legacy tooling)
  • UMD/IIFE (для CDN и прямого подключения в браузере)

Пример конфигурации:

{
  "targets": {
    "esm": {
      "distDir": "dist/esm",
      "outputFormat": "esmodule"
    },
    "cjs": {
      "distDir": "dist/cjs",
      "outputFormat": "commonjs"
    },
    "umd": {
      "distDir": "dist/umd",
      "outputFormat": "global",
      "isLibrary": true
    }
  }
}

Глобальные библиотеки и UMD режим

UMD-сборка используется для библиотек, которые должны быть доступны через глобальную переменную.

{
  "targets": {
    "umd": {
      "distDir": "dist",
      "outputFormat": "global",
      "isLibrary": true,
      "scopeHoist": false
    }
  }
}

Особенности:

  • экспорт попадает в window.MyLib
  • отсутствует зависимость от модульной системы
  • важно контролировать имена глобальных переменных

Разделение dev и production сборки

Parcel автоматически разделяет режимы:

  • development — быстрые сборки, читаемый код
  • production — минификация, оптимизация, tree-shaking

Для библиотек это критично, поскольку:

  • dev-сборка используется при локальной разработке пакета
  • production — при публикации в npm

Организация выходной структуры

Типичная структура после сборки:

dist/
  esm/
    index.js
  cjs/
    index.js
  umd/
    index.js

В продвинутых конфигурациях:

  • добавляются sourcemaps
  • генерируются отдельные chunks для крупных библиотек
  • сохраняется структура исходных модулей

Side effects и корректная оптимизация

Parcel учитывает поле:

{
  "sideEffects": false
}

Это позволяет:

  • агрессивно удалять неиспользуемый код
  • улучшать tree-shaking
  • уменьшать размер конечного пакета

Если библиотека содержит side effects (например, polyfills), поле должно быть настроено точечно:

{
  "sideEffects": ["./src/polyfills.js"]
}

Согласованность API между форматами

Ключевое требование library target — идентичность API во всех форматах.

Parcel обеспечивает это через:

  • единый AST-пайплайн
  • одинаковую систему резолва модулей
  • синхронное применение трансформаций

Любое расхождение обычно возникает не в Parcel, а в:

  • ручных условиях export
  • неправильной конфигурации external
  • различиях runtime (Node vs browser)