Resolver: разрешение путей и алиасов

В процессе сборки Parcel анализирует каждую импортную директиву и преобразует строковые пути в конкретные файлы файловой системы или виртуальные ресурсы. Этот процесс называется разрешением модулей (module resolution). Он включает несколько уровней поиска, учитывающих тип импорта, контекст файла, конфигурацию проекта и правила платформы.

Основная цель — определить точный файл, который соответствует выражению вида:

import Button from "./components/Button";
import React from "react";

Первый случай относится к относительному пути, второй — к внешнему пакету. В обоих случаях Parcel применяет разные стратегии поиска.


Базовые типы путей

Разрешение в Parcel начинается с классификации импортируемой строки:

Относительные пути

import utils from "../utils/math";

Обрабатываются относительно текущего файла. Parcel вычисляет абсолютный путь, добавляет расширения и ищет файл в файловой системе.

Абсолютные или алиасные пути

import utils from "@/utils/math";

Обрабатываются через систему алиасов или пользовательских правил.

Импорты пакетов

import lodash from "lodash";

Разрешаются через node_modules с учётом package.json.


Алгоритм разрешения относительных импортов

Для относительных путей Parcel выполняет последовательность шагов:

  1. Нормализация пути относительно текущего файла
  2. Проверка существования файла с указанным расширением
  3. Попытка добавления расширений из списка поддерживаемых (js, jsx, ts, tsx, json, css и др.)
  4. Поиск директории с индексным файлом
  5. Обработка условий платформы (browser/node/worker)

Пример:

import config from "./config";

Parcel последовательно проверяет:

  • ./config
  • ./config.js
  • ./config.ts
  • ./config/index.js
  • ./config/index.ts

Разрешение пакетов из node_modules

При импорте стороннего пакета Parcel использует алгоритм, совместимый с Node.js, но расширенный поддержкой современных полей package.json.

Базовый поиск

import express from "express";

Поиск происходит по цепочке директорий:

  • node_modules в текущей папке
  • node_modules в родительских папках
  • глобальные fallback-пути проекта

Анализ package.json

После нахождения пакета Parcel анализирует его package.json, учитывая поля:

  • module
  • main
  • browser
  • exports

Приоритет определяется конфигурацией среды сборки.


Поле exports и современная резолюция

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

{
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    },
    "./utils": "./dist/utils.mjs"
  }
}

Parcel интерпретирует это как строгую карту доступных импортов. Попытка обратиться к неэкспортированному пути приводит к ошибке разрешения.

Пример:

import { helper } from "package/utils";

Если путь не описан в exports, модуль не будет найден, даже если файл физически существует.


Алиасы (aliases) в Parcel

Parcel поддерживает систему алиасов, позволяющую заменять длинные или сложные пути на короткие логические идентификаторы.

Конфигурация через package.json

{
  "alias": {
    "@components": "./src/components",
    "@utils": "./src/utils"
  }
}

Использование:

import Button from "@components/Button";
import formatDate from "@utils/date";

Алиасы через .parcelrc

Более гибкая настройка возможна через конфигурационные плагины и резолверы:

{
  "resolvers": ["@parcel/resolver-default"]
}

Собственные резолверы могут перехватывать импорт и возвращать альтернативные пути.


Взаимодействие алиасов и относительных путей

При наличии алиасов Parcel проверяет их до обработки файловой системы.

Порядок разрешения:

  1. Проверка алиасов
  2. Проверка встроенных пакетов
  3. Относительные пути
  4. node_modules

Это позволяет избежать конфликтов между пользовательскими именами и реальными пакетами.


Интеграция с TypeScript paths

Parcel поддерживает paths из tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@app/*": ["src/app/*"],
      "@shared/*": ["src/shared/*"]
    }
  }
}

Импорт:

import store from "@app/store";

Parcel синхронизирует эти правила с собственной системой резолвинга, обеспечивая единое поведение между TypeScript и сборкой.


Разрешение расширений файлов

Parcel автоматически определяет расширения, даже если они не указаны явно.

Приоритет обычно следующий:

  1. .ts
  2. .tsx
  3. .js
  4. .jsx
  5. .json
  6. .node
  7. ассеты (css, scss, png и др. в зависимости от пайплайна)

Пример:

import config from "./config";

Файл может быть:

  • config.ts
  • config.js
  • config.json

Платформенные условия (browser / node)

Parcel учитывает целевую платформу сборки.

browser-режим

Приоритет отдается полю browser в package.json:

{
  "browser": "./dist/browser.js"
}

node-режим

Используется main или module, игнорируя browser-специфичные реализации.


Динамическое разрешение и ленивые импорты

При использовании динамических импортов:

const module = await import("./feature");

Parcel выполняет тот же алгоритм резолвинга, но дополнительно создает отдельные чанки для код-сплиттинга.

Если путь содержит переменные, Parcel пытается ограничить набор возможных модулей через статический анализ.


Обработка ошибок разрешения

Ошибки возникают при невозможности сопоставить импорт с физическим или виртуальным ресурсом.

Типичные причины:

  • отсутствие файла
  • неэкспортированный путь в exports
  • конфликт алиасов
  • некорректный baseUrl или paths
  • отсутствие пакета в node_modules

Parcel возвращает детализированное сообщение, указывающее цепочку поиска.


Виртуальные модули и плагины резолвинга

Parcel позволяет подключать кастомные резолверы, которые могут создавать виртуальные модули:

  • генерация кода на лету
  • импорт ресурсов из API
  • переопределение существующих модулей

Такие резолверы интегрируются в общий pipeline и участвуют в ранней стадии сборки, до трансформации кода.


Кэширование результатов резолвинга

Для ускорения сборки Parcel кэширует результаты разрешения:

  • путь импорта
  • итоговый файл
  • примененные алиасы
  • результат анализа package.json

Кэш привязан к содержимому зависимостей и автоматически инвалидируется при изменениях файловой системы или конфигурации.