В Rollup плагин представляет собой функцию, которая возвращает объект с набором хуков (hooks). Именно эта форма является базовой и наиболее часто используемой, так как позволяет создавать изолированные, конфигурируемые и переиспользуемые расширения сборщика.
Минимальный плагин в Rollup выглядит как функция без параметров или с параметрами конфигурации, которая возвращает объект:
export default function myPlugin(options = {}) {
return {
name: 'my-plugin'
};
}
Ключевой момент заключается в том, что Rollup не требует классов или специальных API-конструкторов. Достаточно вернуть объект с определёнными полями.
Функция, возвращающая объект плагина, выполняет несколько задач:
Пример с параметрами:
export default function myPlugin({ debug = false } = {}) {
let startTime = Date.now();
return {
name: 'my-plugin',
buildStart() {
if (debug) {
console.log('Сборка началась');
}
startTime = Date.now();
},
buildEnd() {
if (debug) {
console.log('Сборка завершилась за', Date.now() - startTime, 'мс');
}
}
};
}
Здесь функция выступает как фабрика плагинов, создающая независимые экземпляры с собственным состоянием.
Объект, возвращаемый функцией, описывает поведение плагина через следующие элементы:
name — обязательное имя плагина (используется Rollup
для диагностики и отладки)Простейший объект:
return {
name: 'example-plugin'
};
Расширенный объект:
return {
name: 'example-plugin',
resolveId(source) {
return null;
},
load(id) {
return null;
},
transform(code, id) {
return null;
}
};
Использование функции как обёртки плагина решает несколько архитектурных задач:
Каждый вызов функции создаёт новую область видимости:
export default function plugin() {
let counter = 0;
return {
name: 'counter-plugin',
buildStart() {
counter++;
},
generateBundle() {
console.log(counter);
}
};
}
Переменная counter не разделяется между разными сборками
или экземплярами.
Плагин может адаптироваться под входные параметры:
export default function replacePlugin(find, replace) {
return {
name: 'replace-plugin',
transform(code) {
return code.replaceAll(find, replace);
}
};
}
Такой подход делает плагин универсальным и переиспользуемым.
Объект плагина создаётся только при вызове функции, что позволяет отложить вычисления:
export default function expensivePlugin() {
const heavyData = computeHeavyData();
return {
name: 'expensive-plugin',
transform(code) {
return code + heavyData;
}
};
}
Rollup ожидает, что функция вернёт объект, соответствующий определённому контракту:
name обязательно для корректной
диагностикиПример корректного контракта:
export default function plugin() {
return {
name: 'valid-plugin',
async load(id) {
const result = await fetchModule(id);
return result;
}
};
}
На практике базовый скелет часто включает заготовку всех основных хуков:
export default function plugin(options = {}) {
let config = options;
return {
name: 'full-skeleton-plugin',
options(inputOptions) {
return inputOptions;
},
buildStart() {
// инициализация
},
resolveId(source, importer) {
return null;
},
load(id) {
return null;
},
transform(code, id) {
return null;
},
buildEnd() {
// завершение сборки
}
};
}
Такой каркас используется как основа для более сложных плагинов, где каждый хук добавляется по мере необходимости.
Функциональный подход позволяет хранить состояние прямо в замыкании:
export default function plugin() {
const filesProcessed = new Set();
return {
name: 'stateful-plugin',
transform(code, id) {
filesProcessed.add(id);
return code;
},
generateBundle() {
console.log('Обработано файлов:', filesProcessed.size);
}
};
}
Состояние остаётся доступным на всех этапах сборки.
Функция, возвращающая объект, даёт несколько важных свойств архитектуры:
В реальных проектах скелет обычно выглядит как комбинация конфигурации, состояния и хуков:
export default function plugin({ include = [], exclude = [] } = {}) {
const cache = new Map();
function shouldProcess(id) {
if (exclude.some(p => id.includes(p))) return false;
if (include.length && !include.some(p => id.includes(p))) return false;
return true;
}
return {
name: 'production-plugin',
transform(code, id) {
if (!shouldProcess(id)) return null;
if (cache.has(id)) return cache.get(id);
const result = code; // обработка
cache.set(id, result);
return result;
}
};
}
Такой подход показывает, что функция-обёртка — это не просто формальность, а фундаментальный элемент архитектуры плагинов Rollup.