API плагинов является одной из наиболее активно развивающихся частей Rollup. По мере появления новых возможностей сборщика менялись интерфейсы хуков, способы взаимодействия с графом модулей, механизмы генерации ресурсов и система контекста плагина. Разработчикам собственных плагинов важно понимать различия между версиями, поскольку код, корректно работавший в одной версии, может требовать адаптации после обновления.
Изменения происходили постепенно, однако наиболее заметные модификации коснулись следующих областей:
PluginContext);На ранних этапах развития Rollup API был относительно компактным. Многие операции выполнялись через небольшое количество хуков:
export default function myPlugin() {
return {
name: 'my-plugin',
resolveId(id) {
return null;
},
load(id) {
return null;
},
transform(code, id) {
return code;
}
};
}
Со временем появились новые требования:
В результате API стало значительно богаче.
В старых версиях контекст плагина содержал относительно ограниченный набор методов:
transform(code, id) {
this.warn('Debug message');
return code;
}
Наиболее часто использовались:
this.warn(...)
this.error(...)
this.parse(...)
Количество служебных методов было невелико.
Позже контекст получил множество новых возможностей:
this.resolve(...)
this.emitFile(...)
this.getFileName(...)
this.addWatchFile(...)
this.getModuleInfo(...)
this.getModuleIds()
Пример:
async transform(code, id) {
const resolved = await this.resolve('./helper.js', id);
return code;
}
Ранее подобная операция часто требовала самостоятельной логики поиска файлов.
Появление this.resolve() позволило использовать
существующую цепочку плагинов для разрешения зависимостей.
Изначально хук выглядел достаточно просто:
resolveId(importee, importer) {
return null;
}
Параметры:
importee — импортируемый путь;importer — модуль-источник.Позже сигнатура была расширена:
resolveId(source, importer, options) {
return null;
}
Появился объект options.
Пример:
resolveId(source, importer, options) {
console.log(options.isEntry);
return null;
}
Новые свойства позволяют определить:
Это дало значительно больше контроля над процессом резолвинга.
Классический вариант:
load(id) {
if (id === 'virtual') {
return 'export default 123';
}
return null;
}
Современные версии поддерживают более сложные объекты:
load(id) {
return {
code: 'export const value = 123;',
map: null
};
}
Плагин может сразу предоставить:
Долгое время было достаточно вернуть строку:
transform(code) {
return code.replace('__DEV__', 'true');
}
Позже основным вариантом стал объект:
transform(code) {
return {
code: code.replace('__DEV__', 'true'),
map: null
};
}
Это позволило корректно передавать карты исходников.
В более новых версиях появились пользовательские метаданные:
transform(code) {
return {
code,
meta: {
transformed: true
}
};
}
Информация может использоваться последующими хуками.
Одним из наиболее важных изменений стало внедрение механизма генерации файлов.
Плагин фактически не имел официального способа создавать дополнительные ресурсы.
Разработчики прибегали к обходным решениям:
import fs from 'fs';
generateBundle() {
fs.writeFileSync(
'dist/data.json',
'{}'
);
}
Такой подход нарушал внутреннюю модель Rollup.
const ref = this.emitFile({
type: 'asset',
fileName: 'data.json',
source: '{}'
});
Rollup самостоятельно управляет ресурсом.
Преимущества:
После появления emitFile() API продолжило
расширяться.
this.emitFile({
type: 'asset',
name: 'style.css',
source: css
});
Позже появился метод:
const referenceId = this.emitFile({
type: 'asset',
source: css
});
const fileName =
this.getFileName(referenceId);
Особенно полезно при использовании хешей:
assets/style-[hash].css
Плагину больше не требуется угадывать итоговое имя файла.
Появилась возможность создавать дополнительные чанки напрямую.
Пример:
this.emitFile({
type: 'chunk',
id: './runtime.js'
});
Rollup самостоятельно включает модуль в граф зависимостей.
Это особенно важно для:
В старых версиях получение сведений о модуле было ограниченным.
Плагин часто вынужден был самостоятельно вести внутренние структуры данных.
const info =
this.getModuleInfo(id);
Пример:
const info = this.getModuleInfo(id);
console.log(info.importedIds);
console.log(info.isEntry);
Доступны сведения:
Для обхода графа появился специальный итератор:
for (const id of this.getModuleIds()) {
console.log(id);
}
Ранее подобные задачи были намного сложнее.
Это открыло возможности для:
this.warn('Something happened');
this.warn({
code: 'CUSTOM_WARNING',
message: 'Something happened'
});
Преимущества:
Раньше:
this.error('Invalid syntax');
Теперь:
this.error({
message: 'Invalid syntax',
id,
pos: 120
});
Rollup способен показать:
В ранних версиях многие плагины вообще возвращали:
{
code,
map: null
}
Позже система source maps стала обязательной частью экосистемы.
Распространённый вариант:
return {
code: result.code,
map: result.map
};
Многие официальные плагины были обновлены именно ради корректной цепочки карт преобразований.
Ранее плагины нередко использовали собственные механизмы слежения.
Появился официальный способ регистрации зависимостей:
this.addWatchFile(
'config/settings.json'
);
Теперь изменение файла автоматически вызывает пересборку.
Это особенно полезно для:
Одним из крупнейших улучшений API стало появление метода:
await this.resolve(...)
Пример:
const resolved =
await this.resolve(
'./utils.js',
importer
);
Результат:
{
id: '/src/utils.js'
}
Плагин может использовать всю существующую цепочку резолвинга Rollup вместо дублирования логики.
Со временем появился дополнительный этап обработки уже разобранного модуля.
moduleParsed(moduleInfo) {
console.log(moduleInfo.id);
}
Плагин получает доступ к AST после завершения анализа.
Это удобно для:
generateBundle(options, bundle) {
}
Обычно использовалось только чтение содержимого сборки.
generateBundle(options, bundle) {
for (const file of Object.values(bundle)) {
console.log(file.fileName);
}
}
Плагин способен:
По мере развития Rollup улучшалась типизация.
Старый код часто использовал:
export default function plugin(): any {
}
Современный вариант:
import type { Plugin } from 'rollup';
export default function plugin(): Plugin {
return {
name: 'example'
};
}
Появились отдельные типы:
Plugin
OutputChunk
OutputAsset
ModuleInfo
PluginContext
TransformResult
ResolvedId
Это значительно повысило надёжность разработки.
Старые плагины часто публиковались как CommonJS-модули:
module.exports = function () {
return {};
};
Современная экосистема Rollup всё чаще использует ESM:
export default function () {
return {};
}
Соответственно изменились:
Наиболее распространённые изменения при обновлении старого плагина:
this.resolve() вместо ручного поиска
модулей.emitFile() для создания ресурсов.map в результатах load() и
transform().getModuleInfo() вместо собственных
реестров зависимостей.addWatchFile() для внешних файлов.Большинство изменений в API плагинов направлено на унификацию работы с графом модулей, улучшение диагностики, поддержку сложных сценариев сборки и снижение количества неофициальных обходных решений. В результате современные плагины получают значительно больше возможностей для интеграции во внутренние механизмы Rollup, чем плагины первых поколений.