Инициализация плагина

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

Система плагинов в Parcel организована вокруг нескольких ключевых типов расширений:

  • Resolver — отвечает за разрешение путей модулей
  • Transformer — выполняет преобразование содержимого файлов
  • Bundler — управляет формированием итоговых бандлов
  • Optimizer — оптимизирует уже собранный код
  • Reporter — обрабатывает события сборки и выводит информацию

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

Структура плагина и экспортируемый контракт

Плагин в Parcel представляет собой JavaScript-модуль, который экспортирует объект с методами, соответствующими жизненному циклу.

export default {
  resolve({ dependency, options }) {
    return {
      filePath: null
    };
  },

  load({ filePath }) {
    return null;
  },

  transform({ asset }) {
    return [asset];
  }
};

Каждый метод является точкой инициализации логики, связанной с конкретной стадией обработки ресурсов. При загрузке Parcel регистрирует эти методы в внутреннем планировщике задач.

Конфигурационная привязка через .parcelrc

Инициализация плагина невозможна без объявления в конфигурационном файле .parcelrc. Этот файл определяет пайплайн плагинов и порядок их выполнения.

{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.txt": ["parcel-transformer-custom"]
  },
  "resolvers": ["parcel-resolver-custom"]
}

При запуске сборки Parcel считывает .parcelrc, формирует граф плагинов и выполняет их инициализацию в соответствии с типами ресурсов.

Важно, что порядок объявления влияет на цепочку обработки: каждый следующий плагин получает результат предыдущего, если не происходит прерывания цепочки.

Механизм инициализации и загрузки

Инициализация плагина происходит в несколько этапов:

  1. Чтение конфигурации .parcelrc
  2. Разрешение модулей плагинов через Node.js resolution algorithm
  3. Импорт модулей плагинов
  4. Регистрация экспортируемых хендлеров
  5. Привязка к внутренним хукам Parcel

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

import myPlugin from 'parcel-plugin-example';

// Parcel выполняет регистрацию функций без немедленного вызова логики

Инициализационные параметры и контекст

Каждый плагин получает контекст исполнения, который содержит служебные API Parcel. Контекст передаётся при вызове функций плагина, а не при его создании, что позволяет избегать глобального состояния.

export default {
  transform({ asset, options, logger }) {
    logger.info({
      message: 'Transform started'
    });

    asset.setCode(asset.getCode().toUpperCase());

    return [asset];
  }
};

Параметр options формируется на этапе инициализации сборщика и содержит объединённую конфигурацию проекта и плагинов.

Регистрация зависимостей плагина

Parcel поддерживает автоматическую регистрацию зависимостей внутри плагинов. При инициализации анализируется импортируемый граф модулей, что позволяет включать дополнительные runtime-зависимости без ручной настройки.

export default {
  load() {
    const helper = require('./helper');
    return helper.getData();
  }
};

Такие зависимости попадают в общий граф и обрабатываются стандартным механизмом Parcel.

Инициализация Transformer-плагинов

Transformer-плагины являются наиболее часто используемым типом расширений. Их инициализация происходит при совпадении паттерна файла с правилом в .parcelrc.

{
  "transformers": {
    "*.css": ["parcel-transformer-css"]
  }
}

После сопоставления расширения файла с правилом Parcel:

  • импортирует модуль трансформера
  • вызывает его функции обработки
  • передаёт объект asset с метаданными
export default {
  async transform({ asset }) {
    const code = await asset.getCode();
    asset.setCode(`/* processed */\n${code}`);
    return [asset];
  }
};

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

Resolver-плагины и ранняя инициализация

Resolver-плагины инициализируются на самой ранней стадии, до построения графа зависимостей. Их задача — определить физическое расположение модулей.

export default {
  resolve({ dependency, options }) {
    if (dependency.specifier === 'special-module') {
      return {
        filePath: '/src/special/index.js'
      };
    }
  }
};

Инициализация resolver-плагина критична, поскольку ошибки на этом этапе блокируют построение графа модулей.

Передача конфигурации в момент инициализации

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

{
  "transformers": {
    "*.md": [
      "parcel-transformer-markdown",
      {
        "highlight": true
      }
    ]
  }
}

Плагин получает эти данные через объект options при каждом вызове метода.

export default {
  transform({ asset, options }) {
    if (options.highlight) {
      // дополнительная обработка
    }

    return [asset];
  }
};

Жизненный цикл и повторная инициализация

Parcel не создаёт новый экземпляр плагина для каждого файла. Вместо этого используется один загруженный модуль, а инициализация сводится к регистрации функций.

Повторная инициализация возможна только при:

  • перезапуске процесса сборки
  • изменении .parcelrc
  • горячем обновлении конфигурации

В остальных случаях используется уже загруженный контекст.

Асинхронная инициализация и отложенные операции

Некоторые плагины требуют асинхронной подготовки, например загрузки внешних конфигураций или инициализации SDK. Parcel допускает асинхронные хуки.

let cache;

export default {
  async initialize() {
    cache = await fetch('https://example.com/config').then(r => r.json());
  },

  transform({ asset }) {
    if (cache?.enabled) {
      asset.setCode(asset.getCode() + '\n// enabled');
    }
    return [asset];
  }
};

Хотя метод initialize не является обязательным стандартом всех плагинов, он часто используется как соглашение внутри экосистемы.

Кэширование и влияние на инициализацию

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

Кэшируются:

  • результат трансформаций
  • состояние модулей
  • результаты резолвинга

Это означает, что логика инициализации должна быть детерминированной и не зависеть от внешнего изменяемого состояния без явного контроля.

Ошибки инициализации и их обработка

Ошибки, возникающие на этапе инициализации плагина, разделяются на несколько категорий:

  • ошибки импорта модуля
  • ошибки конфигурации .parcelrc
  • ошибки выполнения хука при первом вызове
  • ошибки несовместимости версий API

Parcel прекращает выполнение соответствующей стадии сборки при фатальных ошибках resolver- или transformer-плагинов, но может продолжить работу в degraded-режиме для reporter-плагинов.

Совместимость версий API

Инициализация плагина зависит от версии Parcel API. Несовместимость может приводить к тому, что плагин импортируется, но его хуки не регистрируются.

export default function (api) {
  api.transform(/* ... */);
}

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