Одной из ключевых особенностей Parcel является возможность создавать несколько вариантов сборки одного и того же проекта. Для решения этой задачи используется концепция targets — целей сборки, определяющих характеристики итогового набора файлов.
Target описывает, для какой среды предназначается результат сборки, какие браузеры или платформы должны поддерживаться, какой формат модулей необходимо использовать, требуется ли минификация, выполняется ли оптимизация, а также куда должны быть помещены итоговые файлы.
Один исходный код может одновременно собираться для различных сценариев:
Parcel способен запускать все такие сборки параллельно, используя единый набор исходников.
Target — это конфигурационный объект, описывающий конкретный результат сборки.
Простейшая схема выглядит следующим образом:
Исходный код
│
▼
Parcel
│
┌─────┼─────┐
▼ ▼ ▼
Web Node Library
Каждая цель сборки может иметь собственные настройки:
| Параметр | Назначение |
|---|---|
| context | Среда выполнения |
| distDir | Каталог вывода |
| engines | Поддерживаемые платформы |
| outputFormat | Формат модулей |
| optimize | Оптимизация |
| sourceMap | Карты исходников |
| isLibrary | Режим библиотеки |
Parcel автоматически анализирует некоторые поля файла package.json и на их основе создает цели сборки.
Например:
{
"main": "dist/main.js",
"module": "dist/module.js",
"browser": "dist/browser.js"
}
В этом случае Parcel формирует несколько целей:
| Поле | Target |
|---|---|
| main | CommonJS-сборка |
| module | ES Modules-сборка |
| browser | браузерная версия |
Команда:
parcel build src/index.js
может автоматически создать все перечисленные варианты.
Поле main обычно используется для публикации пакетов в npm.
Пример:
{
"main": "dist/index.js"
}
Parcel воспринимает данный файл как основной экспорт библиотеки.
После сборки:
dist/
└── index.js
Обычно для main генерируется формат CommonJS.
Пример использования собранного пакета:
const lib = require("my-library");
Поле module предназначено для современных сборщиков и браузеров, поддерживающих ES Modules.
{
"module": "dist/index.mjs"
}
После сборки:
dist/
└── index.mjs
Использование:
import lib from "my-library";
Преимущества:
Target browser описывает браузерную сборку.
{
"browser": "dist/browser.js"
}
Parcel учитывает особенности веб-платформы:
Пример:
process.env.NODE_ENV
может быть преобразован в константу на этапе сборки.
В большинстве реальных проектов используются собственные цели сборки.
Пример:
{
"targets": {
"modern": {},
"legacy": {}
}
}
Теперь доступны две независимые сборки.
Запуск:
parcel build src/index.js
создаст обе версии одновременно.
Каждый target может использовать собственный каталог результатов.
{
"targets": {
"modern": {
"distDir": "dist/modern"
},
"legacy": {
"distDir": "dist/legacy"
}
}
}
Результат:
dist/
├── modern/
│ └── index.js
│
└── legacy/
└── index.js
Такой подход особенно удобен при публикации разных версий приложения.
Свойство context определяет среду выполнения.
Возможные значения:
{
"context": "browser"
}
{
"context": "node"
}
{
"context": "web-worker"
}
{
"context": "service-worker"
}
Parcel использует данную информацию для выбора трансформаций и оптимизаций.
{
"context": "browser"
}
Предназначено для веб-приложений.
Особенности:
{
"context": "node"
}
Предназначено для серверного кода.
Особенности:
Пример:
const fs = require("fs");
Такой код не требует браузерных полифиллов.
{
"context": "web-worker"
}
Используется для фоновых потоков браузера.
Пример:
self.onmess age = event => {
console.log(event.data);
};
Parcel учитывает ограничения среды Worker.
{
"context": "service-worker"
}
Применяется для:
Свойство engines определяет список поддерживаемых платформ.
Пример:
{
"targets": {
"modern": {
"engines": {
"browsers": [
"last 2 Chrome versions"
]
}
}
}
}
Parcel использует эту информацию для:
Parcel интегрируется с Browserslist.
Пример:
{
"targets": {
"legacy": {
"engines": {
"browsers": [
"> 0.5%",
"not dead"
]
}
}
}
}
Parcel автоматически определяет:
Очень распространённый сценарий.
{
"targets": {
"modern": {
"distDir": "dist/modern",
"engines": {
"browsers": [
"last 2 Chrome versions"
]
}
},
"legacy": {
"distDir": "dist/legacy",
"engines": {
"browsers": [
"> 0.5%",
"IE 11"
]
}
}
}
}
Результат:
dist/
├── modern/
└── legacy/
Современная версия содержит меньше транспиляции и работает быстрее.
Legacy-версия включает дополнительные преобразования для старых браузеров.
Свойство outputFormat задаёт формат модулей.
Поддерживаются:
{
"outputFormat": "commonjs"
}
Пример результата:
module.exports = myFunction;
Чаще всего используется для Node.js.
{
"outputFormat": "esmodule"
}
Пример:
export default myFunction;
Используется современными инструментами сборки.
{
"outputFormat": "global"
}
Экспорт выполняется через глобальный объект.
Например:
window.MyLibrary = MyLibrary;
Такой вариант удобен для подключения через тег script.
Для npm-пакетов существует специальный параметр:
{
"isLibrary": true
}
Когда target является библиотекой:
Пример:
{
"targets": {
"library": {
"isLibrary": true
}
}
}
Каждая цель может иметь собственные настройки оптимизации.
{
"targets": {
"production": {
"optimize": true
},
"debug": {
"optimize": false
}
}
}
Результат:
Карты исходников можно включать или отключать независимо.
{
"targets": {
"dev": {
"sourceMap": true
},
"prod": {
"sourceMap": false
}
}
}
Подход позволяет получать удобную отладку в процессе разработки и компактные файлы в продакшене.
При наличии нескольких целей можно собирать только одну из них.
Пример:
parcel build src/index.js --target modern
Либо:
parcel build src/index.js --target legacy
Это сокращает время сборки и удобно в CI/CD-процессах.
Часто пакет публикуется сразу в нескольких форматах.
Конфигурация:
{
"targets": {
"cjs": {
"distDir": "dist/cjs",
"outputFormat": "commonjs",
"isLibrary": true
},
"esm": {
"distDir": "dist/esm",
"outputFormat": "esmodule",
"isLibrary": true
}
}
}
Структура:
dist/
├── cjs/
│ └── index.js
│
└── esm/
└── index.js
Преимущества:
Каждая цель сборки может получать собственные значения переменных окружения.
Пример:
if (process.env.BUILD_TARGET === "modern") {
console.log("Modern build");
}
В процессе сборки Parcel способен исключать неиспользуемые ветки кода благодаря механизму dead code elimination.
Parcel анализирует каждый target отдельно.
Предположим, имеется код:
import feature from "./feature.js";
Для современного target:
feature?.run();
может остаться без изменений.
Для старого браузера тот же код будет транспилирован в более совместимую форму.
Таким образом один и тот же исходник приводит к разным результатам сборки.
{
"targets": {
"modern": {
"distDir": "dist/modern",
"context": "browser",
"outputFormat": "esmodule",
"optimize": true,
"sourceMap": false,
"engines": {
"browsers": [
"last 2 Chrome versions"
]
}
},
"legacy": {
"distDir": "dist/legacy",
"context": "browser",
"outputFormat": "global",
"optimize": true,
"sourceMap": false,
"engines": {
"browsers": [
"> 0.5%",
"IE 11"
]
}
},
"node": {
"distDir": "dist/node",
"context": "node",
"outputFormat": "commonjs",
"optimize": false,
"sourceMap": true
}
}
}
После выполнения сборки:
dist/
├── modern/
├── legacy/
└── node/
Каждая директория содержит вариант приложения, оптимизированный под свою платформу и требования совместимости.
Концепция targets превращает Parcel из обычного сборщика в систему генерации нескольких артефактов из единой кодовой базы. Вместо поддержки отдельных конфигураций для браузера, Node.js, библиотеки или устаревших платформ все варианты описываются через независимые цели сборки.
Каждый target формирует собственный конвейер обработки:
Благодаря этому один проект может одновременно выпускать:
Именно эта модель делает targets одним из важнейших механизмов Parcel при разработке универсальных приложений и библиотек.