Современные JavaScript-проекты часто требуют одновременной сборки под разные среды выполнения. Один и тот же код может поставляться как для браузера, так и для Node.js, либо разделяться на несколько вариантов для различных браузерных наборов возможностей. В таких сценариях используется механизм многотаргетной сборки, позволяющий формировать несколько независимых выходных бандлов из одного исходного графа модулей.
В сборщике Parcel поддержка нескольких таргетов реализована на уровне конфигурации проекта и тесно интегрирована с системой определения окружений, трансформаций и оптимизаций.
В основе системы лежит концепция targets — именованных конфигураций сборки, каждая из которых описывает:
Каждый таргет рассматривается как самостоятельная единица сборки, но при этом использует общий граф зависимостей, что позволяет избежать дублирования вычислений и трансформаций.
Основной способ конфигурации — поле targets в
package.json.
{
"name": "multi-target-app",
"source": "src/index.js",
"targets": {
"modern": {
"engines": {
"browsers": [">0.25%", "not dead"]
},
"outputFormat": "esmodule",
"distDir": "dist/modern"
},
"legacy": {
"engines": {
"browsers": ["ie 11"]
},
"outputFormat": "global",
"distDir": "dist/legacy"
},
"node": {
"engines": {
"node": ">=18"
},
"outputFormat": "commonjs",
"distDir": "dist/node"
}
}
}
Каждый объект внутри targets формирует отдельный
пайплайн сборки.
При запуске команды сборки Parcel строит единый dependency graph, после чего выполняет:
Важно, что граф модулей строится один раз. Различия между таргетами применяются на стадии кодогенерации и оптимизации.
Каждый таргет может влиять на набор применяемых трансформаций:
Например, для legacy-таргета включается более агрессивная
транспиляция, включая преобразование async/await в
генераторы, а также добавление полифиллов. Для modern-таргета
сохраняется нативный ESM и минимальные трансформации.
При работе с браузерными таргетами используется интеграция с Browserslist:
{
"targets": {
"default": {
"engines": {
"browsers": ["> 1%", "last 2 versions"]
}
}
}
}
Каждый набор браузеров влияет на:
Parcel анализирует эти значения и формирует соответствующую стратегию генерации кода.
При сборке под Node.js учитываются следующие аспекты:
fs,
path, crypto);Конфигурация может выглядеть следующим образом:
{
"targets": {
"node": {
"engines": {
"node": ">=18"
},
"outputFormat": "commonjs",
"isLibrary": true,
"distDir": "dist/server"
}
}
}
Параметр isLibrary часто используется для предотвращения
лишней агрессивной оптимизации, характерной для frontend-сборок.
Каждый таргет может иметь собственный entry point:
{
"targets": {
"browser": {
"source": "src/browser.js",
"distDir": "dist/web"
},
"server": {
"source": "src/server.js",
"distDir": "dist/node"
}
}
}
Такой подход позволяет формировать полностью независимые приложения из одного репозитория, сохраняя единые утилиты и общие модули.
При многотаргетной сборке Parcel анализирует контекст импорта. Один и тот же модуль может:
Например:
{
"targets": {
"browser": {
"engines": {
"browsers": [">0.25%"]
},
"alias": {
"fs": false
}
}
}
}
Это позволяет избежать попадания серверных модулей в клиентский бандл.
Основная сложность многотаргетной сборки заключается в балансе между повторным использованием и изоляцией результатов.
Parcel применяет несколько уровней оптимизации:
Один и тот же модуль не трансформируется повторно для каждого таргета. Вместо этого используется промежуточное представление AST.
Различия между таргетами применяются только на этапе генерации кода, а не анализа.
При совпадении зависимостей Parcel может создавать shared chunks, если таргеты допускают совместимость.
Каждый таргет может иметь собственную стратегию разделения кода:
import());Однако важно, что чанки не всегда пересекаются между таргетами. Например, legacy-бандл может содержать собственные полифиллы, которые отсутствуют в modern-бандле.
Результатом многотаргетной сборки является структура:
dist/
modern/
index.js
vendor.js
legacy/
index.js
polyfills.js
node/
index.js
Каждый каталог полностью изолирован и может быть задеплоен независимо.
Многотаргетный подход часто используется в монорепозиториях:
Parcel позволяет держать всю конфигурацию в одном месте, минимизируя дублирование инфраструктурного кода.
При создании библиотек важно учитывать:
Типичная конфигурация:
{
"targets": {
"esm": {
"outputFormat": "esmodule",
"distDir": "dist/esm"
},
"cjs": {
"outputFormat": "commonjs",
"distDir": "dist/cjs"
},
"browser": {
"outputFormat": "global",
"distDir": "dist/browser"
}
}
}
Многотаргетная сборка усиливает эффективность tree-shaking, так как каждый таргет имеет собственный контекст использования кода.
Parcel анализирует доступность кода в каждом контексте отдельно, что позволяет уменьшить итоговый размер артефактов.
Несмотря на гибкость, существует ряд ограничений:
Эти ограничения компенсируются архитектурным разделением логики и корректной настройкой конфигурации.