Система плагинов построена вокруг модульной архитектуры, в которой каждый плагин реализует строго определённый набор контрактов. Основная идея заключается в том, что сборщик не «знает» о конкретных трансформациях заранее — он вызывает зарегистрированные хуки, а плагины определяют поведение на каждом этапе обработки модулей.
Плагин в Parcel представляет собой JavaScript-модуль, экспортирующий функции-хуки. Эти хуки группируются по типам задач:
Каждый тип плагина подключается к определённой фазе пайплайна сборки.
Плагин в Parcel экспортируется как объект или набор функций, где ключи соответствуют хукам API.
Типичный вариант ESM-экспорта:
export default {
name: "custom-plugin",
async loadConfig({ config }) {
return {};
},
transform({ asset }) {
return [asset];
}
};
Допускается также экспорт функций напрямую, если плагин реализует единственный хук:
export default function transformer({ asset }) {
return [asset];
}
Parcel определяет тип плагина по наличию соответствующих методов.
Каждый плагин обязан предоставлять уникальное имя через свойство
name. Это имя используется:
export default {
name: "parcel-transformer-svg",
};
Имя не влияет на функциональность, но критично для отладки и экосистемной совместимости.
Плагин API строится на строгих контрактах функций. Каждый хук принимает объект контекста и возвращает строго определённый результат.
Resolver отвечает за определение пути модуля.
export async function resolve({ dependency }) {
if (dependency.specifier === "my-lib") {
return {
filePath: "/absolute/path/to/my-lib/index.js",
};
}
}
Основные свойства контекста:
dependency.specifier — строка импортаdependency.resolveFrom — исходный модульoptions — конфигурация резолвераРезультат всегда должен содержать filePath, иначе резолв
считается неуспешным.
Transformer — центральный тип плагина. Он изменяет содержимое модулей.
export async function transform({ asset }) {
const code = await asset.getCode();
const transformed = code.replace("const", "let");
asset.setCode(transformed);
return [asset];
}
Ключевые принципы:
assetassetGenerator отвечает за финальную генерацию кода.
export function generate({ asset }) {
return {
code: asset.generatedCode,
map: asset.map,
};
}
Используется для:
Optimizer применяется после генерации бандла.
export function optimize({ bundle }) {
return {
contents: minify(bundle.contents),
};
}
Основные задачи:
Полный плагин может содержать несколько типов хуков одновременно.
export default {
name: "complex-plugin",
async resolve() {},
async transform() {},
async generate() {},
async optimize() {},
};
Parcel агрегирует все методы и регистрирует их в соответствующих стадиях pipeline.
Плагин может загружать конфигурацию проекта через специальный хук:
export async function loadConfig({ config }) {
const result = await config.getConfig([
"my-plugin.config.json",
]);
return result.contents;
}
Используется для:
Все основные хуки поддерживают async/await. Parcel
строит граф зависимостей и параллелит выполнение там, где это
возможно.
Особенности:
asset — ключевая абстракция внутри трансформеров.
Основные методы:
asset.getCode()
asset.setCode(code)
asset.getMap()
asset.setMap(map)
asset.addDependency(dep)
Пример модификации:
export async function transform({ asset }) {
const code = await asset.getCode();
if (code.includes("debug")) {
asset.setCode(code.replace("debug", ""));
}
return [asset];
}
Плагин может динамически добавлять зависимости:
asset.addDependency({
specifier: "./utils.js",
kind: "esm",
});
Типы зависимостей:
esm — ES modulescommonjsurlworkerdynamicЭто влияет на граф сборки и код-сплиттинг.
Один пакет может содержать несколько плагинов через именованные экспорты:
export const resolver = {
name: "resolver-plugin",
resolve() {},
};
export const transformer = {
name: "transformer-plugin",
transform() {},
};
Parcel регистрирует каждый экспорт как отдельный плагин.
Поддерживаются оба формата:
module.exports = {
name: "cjs-plugin",
transform() {},
};
или
export default {
name: "esm-plugin",
transform() {},
};
В современных конфигурациях предпочтителен ESM.
Каждый хук получает контекст:
logger — логированиеoptions — параметры сборкиfilePath — путь к модулюenv — переменные окруженияПример:
export function transform({ logger, asset }) {
logger.info({ message: "transforming asset" });
return [asset];
}
Ошибки должны выбрасываться явно:
throw new Error("Unsupported syntax");
Parcel оборачивает их в диагностический формат:
Также поддерживаются предупреждения:
logger.warn({ message: "deprecated API used" });
Плагины должны соблюдать ряд правил:
Нарушение этих правил приводит к некорректному кэшированию и нестабильной сборке.
parcel-plugin-example/
package.json
src/
index.js
resolver.js
transformer.js
package.json:
{
"name": "parcel-plugin-example",
"main": "src/index.js",
"peerDependencies": {
"parcel": "^2.0.0"
}
}
Parcel автоматически регистрирует хуки по их именам. Нет необходимости вручную описывать тип плагина.
Система распознаёт:
resolve → resolvertransform → transformergenerate → generatoroptimize → optimizerЭто позволяет минимизировать конфигурацию и снижает вероятность ошибок интеграции.
Плагины не вызывают друг друга напрямую. Вместо этого используется общий pipeline:
Такой подход исключает жёсткую связанность и облегчает масштабирование экосистемы.