Любой современный JavaScript-проект состоит не только из исходного кода приложения, но и из множества внешних модулей. Библиотеки пользовательского интерфейса, инструменты работы с датами, средства маршрутизации, системы управления состоянием, утилиты форматирования и сотни других пакетов формируют граф зависимостей проекта.
Parcel автоматически анализирует этот граф, определяет связи между модулями и выполняет разрешение зависимостей (dependency resolution), находя конкретные файлы, которые должны быть включены в итоговую сборку.
В отличие от традиционных сборщиков, требующих значительного объёма конфигурации, Parcel реализует интеллектуальную систему разрешения модулей практически без участия разработчика.
Разрешение зависимостей — это процесс определения того, какой именно файл должен быть подключён при встрече инструкции импорта.
Например:
import React from "react";
Parcel должен определить:
react.После анализа формируется полный граф зависимостей проекта.
Parcel рассматривает приложение как ориентированный граф.
Пример структуры:
index.js
├── app.js
│ ├── router.js
│ └── api.js
└── utils.js
Код:
// index.js
import "./app";
import "./utils";
// app.js
import "./router";
import "./api";
Каждый импорт создаёт новую связь внутри графа.
Parcel рекурсивно обходит все импорты и строит полную карту проекта.
Схематично:
index.js
|
+---- app.js
| |
| +---- router.js
| |
| +---- api.js
|
+---- utils.js
Полученный граф используется для:
Parcel во многом использует алгоритм разрешения модулей Node.js.
Импорт:
import lodash from "lodash";
Запускает поиск:
project/
├── node_modules/
│ └── lodash/
Если пакет не найден, Parcel поднимается вверх по дереву каталогов:
project/
└── src/
└── app/
Поиск будет происходить следующим образом:
src/app/node_modules
src/node_modules
project/node_modules
Такой механизм позволяет использовать вложенные структуры пакетов и монорепозитории.
Относительные импорты начинаются с:
./
../
Пример:
import Button from "./Button";
Parcel выполняет поиск:
Button.js
Button.jsx
Button.ts
Button.tsx
Button/index.js
Button/index.ts
...
Если файл найден, он становится узлом графа зависимостей.
Импорт без относительного пути считается пакетом.
Пример:
import axios from "axios";
Parcel ищет пакет внутри:
node_modules/axios
Затем анализирует файл:
{
"main": "index.js"
}
или
{
"module": "dist/axios.esm.js"
}
или
{
"source": "src/index.js"
}
В зависимости от контекста сборки Parcel выбирает наиболее подходящую точку входа.
Файл package.json играет ключевую роль в разрешении
зависимостей.
Пример:
{
"name": "my-library",
"main": "dist/main.js",
"module": "dist/module.js",
"source": "src/index.js"
}
Parcel анализирует поля:
| Поле | Назначение |
|---|---|
| main | Основная точка входа CommonJS |
| module | ES Module версия |
| source | Исходный код библиотеки |
| browser | Замены для браузерной среды |
| exports | Современная система экспорта |
| types | TypeScript-описания |
На основе этих данных выбирается оптимальный вариант подключения.
Одной из важных особенностей Parcel является поддержка поля:
{
"source": "src/index.js"
}
Если пакет содержит исходный код, Parcel способен работать непосредственно с ним.
Преимущества:
Например:
{
"source": "src/index.ts"
}
Parcel сначала скомпилирует TypeScript, а затем включит результат в сборку.
Многие пакеты имеют разные реализации для Node.js и браузера.
Пример:
{
"browser": {
"./server.js": "./browser.js"
}
}
Когда выполняется браузерная сборка:
import "./server.js";
Parcel автоматически заменяет зависимость:
import "./browser.js";
Это позволяет создавать универсальные библиотеки.
Современные версии Node.js используют механизм
exports.
Пример:
{
"exports": {
".": "./dist/index.js",
"./utils": "./dist/utils.js"
}
}
Допустимые импорты:
import lib from "my-lib";
import utils from "my-lib/utils";
Недопустимый импорт:
import test from "my-lib/internal/test";
Parcel соблюдает ограничения, указанные в exports.
Современные пакеты могут публиковать различные версии модулей.
Пример:
{
"exports": {
".": {
"import": "./esm/index.js",
"require": "./cjs/index.js",
"browser": "./browser/index.js"
}
}
}
Parcel определяет среду выполнения и выбирает нужный вариант.
Для браузера:
browser/index.js
Для ES-модулей:
esm/index.js
Для CommonJS:
cjs/index.js
Parcel полностью поддерживает ES Modules.
Пример:
import { format } from "date-fns";
При анализе:
Это позволяет выполнять эффективное удаление неиспользуемого кода.
Многие библиотеки всё ещё используют CommonJS.
Пример:
const express = require("express");
Parcel умеет анализировать:
require(...)
и включать такие зависимости в граф проекта.
Поддерживаются конструкции:
module.exports
exports.foo
require()
Некоторые библиотеки одновременно поддерживают CommonJS и ES Modules.
Пример:
{
"main": "dist/index.cjs",
"module": "dist/index.mjs"
}
Parcel автоматически выбирает наиболее подходящий формат.
Обычно предпочтение отдаётся ES Module версии, поскольку она предоставляет больше возможностей для оптимизации.
Импорт:
import User from "./User";
может быть разрешён в:
User.ts
или:
User.tsx
Parcel учитывает расширения TypeScript и корректно интегрирует их в общий граф зависимостей.
Поддерживаются:
.ts
.tsx
.cts
.mts
Импорт:
import App from "./App";
может ссылаться на:
App.jsx
или
App.tsx
Parcel автоматически определяет тип файла и запускает соответствующий трансформер.
В Parcel зависимостью является не только JavaScript.
Пример:
import "./styles.css";
CSS-файл становится отдельным узлом графа.
Аналогично:
import logo from "./logo.svg";
import image from "./photo.png";
import data from "./users.json";
Все подобные ресурсы проходят через систему разрешения зависимостей.
Файл:
@import "./variables.css";
создаёт новую связь в графе.
Также анализируются конструкции:
background-image: url("./bg.png");
Parcel автоматически находит ресурс:
bg.png
и включает его в процесс сборки.
Parcel умеет отслеживать ресурсы через URL.
Пример:
new URL("./worker.js", import.meta.url);
или
new URL("./image.png", import.meta.url);
Такие ссылки рассматриваются как полноценные зависимости проекта.
Создание Worker:
new Worker(
new URL("./worker.js", import.meta.url)
);
Приводит к созданию отдельного графа зависимостей.
Parcel:
Динамический импорт:
import("./settings.js");
создаёт асинхронную зависимость.
Parcel выделяет её в отдельный пакет:
main.bundle.js
settings.bundle.js
Связь между ними сохраняется в графе проекта.
Типичная структура:
packages/
├── ui
├── core
└── app
Пакет:
import { Button } from "@company/ui";
может ссылаться на локальную библиотеку внутри монорепозитория.
Parcel корректно работает с:
При необходимости используются исходные файлы пакетов вместо предварительно собранных артефактов.
Многие менеджеры пакетов создают символьные ссылки.
Например:
node_modules/ui
-> ../. ./packages/ui
Parcel разрешает такие ссылки и рассматривает реальное расположение файлов при построении графа зависимостей.
Поиск зависимостей является дорогой операцией.
Parcel сохраняет результаты:
При повторной сборке информация берётся из кэша, что значительно ускоряет работу.
Типичная ошибка:
import api from "./api";
при отсутствии файла:
api.js
Parcel выдаёт подробную диагностику:
Cannot resolve dependency './api'
Часто дополнительно показываются:
В проекте могут существовать разные версии одного пакета.
Пример:
react@18
react@19
Parcel учитывает фактическую структуру node_modules и
подключает именно ту версию, которая соответствует конкретному узлу
графа.
Благодаря этому корректно работают даже сложные деревья зависимостей.
После построения графа Parcel анализирует используемые экспорты.
Исходный код:
import { add } from "./math";
Модуль:
export function add() {}
export function sub() {}
export function mul() {}
Если используются только:
add()
Parcel может удалить:
sub()
mul()
из финальной сборки.
Качество tree shaking напрямую зависит от точности разрешения зависимостей.
После построения полного графа Parcel определяет:
Например:
import("./admin");
import("./profile");
может привести к созданию:
main.js
admin.js
profile.js
shared.js
Для принятия подобных решений используется информация, полученная на этапе разрешения зависимостей.
Архитектура Parcel поддерживает плагины-резолверы.
Схема работы:
Import
↓
Resolver
↓
Asset
↓
Transformer
Резолвер может:
Это позволяет адаптировать механизм разрешения практически под любую инфраструктуру проекта.
Упрощённая последовательность действий Parcel выглядит следующим образом:
Импорт найден
↓
Определение типа зависимости
↓
Поиск файла или пакета
↓
Чтение package.json
↓
Определение точки входа
↓
Проверка условий exports
↓
Создание узла графа
↓
Рекурсивный обход новых зависимостей
↓
Формирование полного Dependency Graph
Именно этот механизм лежит в основе всей системы сборки Parcel, обеспечивая автоматическое обнаружение модулей, корректную работу различных форматов пакетов, поддержку современных возможностей экосистемы JavaScript и эффективную оптимизацию итогового приложения.