Next-generation ES module bundler that compiles small pieces of code into larger, more complex applications or libraries
—
Comprehensive hook-based plugin architecture for extending bundling behavior, with lifecycle hooks for input processing, transformation, and output generation. Rollup's plugin system provides fine-grained control over every aspect of the bundling process.
Core plugin interface for creating Rollup plugins with lifecycle hooks and metadata.
/**
* Plugin interface for extending Rollup functionality
*/
interface Plugin<A = any> extends OutputPlugin, Partial<PluginHooks> {
/** Plugin identifier (required) */
name: string;
/** Plugin version */
version?: string;
/** Inter-plugin communication API */
api?: A;
}
interface OutputPlugin extends Partial<Record<OutputPluginHooks, PluginHooks[OutputPluginHooks]>>,
Partial<Record<AddonHooks, ObjectHook<AddonHook>>> {
/** Cache key for plugin output */
cacheKey?: string;
/** Plugin name (required) */
name: string;
/** Plugin version */
version?: string;
}
type OutputPluginHooks =
| 'augmentChunkHash'
| 'generateBundle'
| 'outputOptions'
| 'renderChunk'
| 'renderDynamicImport'
| 'renderError'
| 'renderStart'
| 'resolveFileUrl'
| 'resolveImportMeta'
| 'writeBundle';
type AddonHooks = 'banner' | 'footer' | 'intro' | 'outro';Usage Examples:
// Basic plugin
const myPlugin = () => ({
name: 'my-plugin',
version: '1.0.0',
buildStart(options) {
console.log('Build starting...');
},
load(id) {
if (id.endsWith('?inline')) {
return `export default ${JSON.stringify(
fs.readFileSync(id.slice(0, -7), 'utf8')
)}`;
}
}
});
// Plugin with API
const utilityPlugin = () => ({
name: 'utility-plugin',
api: {
getUtilityPath: () => '/path/to/utility',
processData: (data) => processUtilityData(data)
},
buildStart() {
this.api.initialize();
}
});
// Using plugin APIs
const consumerPlugin = () => ({
name: 'consumer-plugin',
buildStart() {
const utilPlugin = this.resolve('utility-plugin');
if (utilPlugin?.api) {
const path = utilPlugin.api.getUtilityPath();
console.log('Utility path:', path);
}
}
});Context object provided to plugin hooks with utilities for bundle manipulation and communication.
/**
* Context object provided to plugin hooks
*/
interface PluginContext extends MinimalPluginContext {
/** Add file to watch list */
addWatchFile: (id: string) => void;
/** Plugin-specific cache */
cache: PluginCache;
/** Emit additional files (assets/chunks) */
emitFile: EmitFile;
/** Get emitted file name by reference ID */
getFileName: (fileReferenceId: string) => string;
/** Get iterator of all module IDs */
getModuleIds: () => IterableIterator<string>;
/** Get module information */
getModuleInfo: GetModuleInfo;
/** Get list of watched files */
getWatchFiles: () => string[];
/** Load module with options */
load: (options: LoadOptions & Partial<PartialNull<ModuleOptions>>) => Promise<ModuleInfo>;
/** Parse code to AST */
parse: ParseAst;
/** Resolve module path */
resolve: (
source: string,
importer?: string,
options?: {
attributes?: Record<string, string>;
custom?: CustomPluginOptions;
isEntry?: boolean;
skipSelf?: boolean;
}
) => Promise<ResolvedId | null>;
/** Set asset source content */
setAssetSource: (assetReferenceId: string, source: string | Uint8Array) => void;
/** File system interface */
fs: RollupFsModule;
}
interface TransformPluginContext extends PluginContext {
/** Debug logging with position support */
debug: LoggingFunctionWithPosition;
/** Error with position support */
error: (error: RollupError | string, pos?: number | { column: number; line: number }) => never;
/** Get combined source map for transformations */
getCombinedSourcemap: () => SourceMap;
/** Info logging with position support */
info: LoggingFunctionWithPosition;
/** Warning with position support */
warn: LoggingFunctionWithPosition;
}
interface LoadOptions {
id: string;
resolveDependencies?: boolean;
}
type LoggingFunctionWithPosition = (
log: RollupLog | string | (() => RollupLog | string),
pos?: number | { column: number; line: number }
) => void;
interface MinimalPluginContext {
/** Debug logging */
debug: LoggingFunction;
/** Throw build error */
error: (error: RollupError | string) => never;
/** Info logging */
info: LoggingFunction;
/** Plugin metadata */
meta: PluginContextMeta;
/** Warning logging */
warn: LoggingFunction;
}
interface PluginContextMeta {
/** Current Rollup version */
rollupVersion: string;
/** Whether running in watch mode */
watchMode: boolean;
}Lifecycle hooks for processing input modules during the build phase.
/**
* Input plugin hooks for build processing
*/
interface InputPluginHooks {
/** Build initialization */
buildStart: (this: PluginContext, options: NormalizedInputOptions) => void;
/** Build completion */
buildEnd: (this: PluginContext, error?: Error) => void;
/** Bundle closure */
closeBundle: (this: PluginContext, error?: Error) => void;
/** Watcher closure */
closeWatcher: (this: PluginContext) => void;
/** Module loading */
load: LoadHook;
/** Module parsing completion */
moduleParsed: ModuleParsedHook;
/** Log filtering */
onLog: (this: MinimalPluginContext, level: LogLevel, log: RollupLog) => boolean | null;
/** Options processing */
options: (this: MinimalPluginContext, options: InputOptions) => InputOptions | null;
/** Module resolution */
resolveId: ResolveIdHook;
/** Dynamic import resolution */
resolveDynamicImport: ResolveDynamicImportHook;
/** Cache validation */
shouldTransformCachedModule: ShouldTransformCachedModuleHook;
/** Code transformation */
transform: TransformHook;
/** File change handling */
watchChange: WatchChangeHook;
}
type LoadHook = (this: PluginContext, id: string) => LoadResult;
type ResolveIdHook = (
this: PluginContext,
source: string,
importer: string | undefined,
options: { attributes: Record<string, string>; custom?: CustomPluginOptions; isEntry: boolean }
) => ResolveIdResult;
type TransformHook = (this: TransformPluginContext, code: string, id: string) => TransformResult;
type LoadResult = SourceDescription | string | null;
type ResolveIdResult = string | null | false | PartialResolvedId;
type TransformResult = string | null | Partial<SourceDescription>;Usage Examples:
// Module resolution plugin
const resolverPlugin = () => ({
name: 'resolver-plugin',
resolveId(source, importer) {
if (source.startsWith('virtual:')) {
return source; // Handle virtual modules
}
if (source.startsWith('~')) {
return path.resolve('src', source.slice(1));
}
return null; // Let other plugins handle
},
load(id) {
if (id.startsWith('virtual:')) {
return `export default "Virtual module: ${id}";`;
}
return null;
}
});
// Transform plugin
const transformPlugin = () => ({
name: 'transform-plugin',
transform(code, id) {
if (id.endsWith('.special')) {
return {
code: processSpecialFile(code),
map: generateSourceMap(code, id)
};
}
return null;
}
});
// Build lifecycle plugin
const lifecyclePlugin = () => ({
name: 'lifecycle-plugin',
buildStart(options) {
console.log('Build started with options:', options.input);
this.addWatchFile('config.json'); // Watch additional files
},
moduleParsed(moduleInfo) {
console.log(`Parsed module: ${moduleInfo.id}`);
},
buildEnd(error) {
if (error) {
console.error('Build failed:', error.message);
} else {
console.log('Build completed successfully');
}
}
});Hooks for processing generated output during the render and write phases.
/**
* Output plugin hooks for render processing
*/
interface OutputPluginHooks {
/** Add hash contribution */
augmentChunkHash: (this: PluginContext, chunk: RenderedChunk) => string | void;
/** Bundle generation */
generateBundle: (
this: PluginContext,
options: NormalizedOutputOptions,
bundle: OutputBundle,
isWrite: boolean
) => void;
/** Output options processing */
outputOptions: (this: PluginContext, options: OutputOptions) => OutputOptions | null;
/** Chunk rendering */
renderChunk: RenderChunkHook;
/** Dynamic import rendering */
renderDynamicImport: (this: PluginContext, options: RenderDynamicImportOptions) => RenderDynamicImportResult;
/** Render error handling */
renderError: (this: PluginContext, error?: Error) => void;
/** Render initialization */
renderStart: (
this: PluginContext,
outputOptions: NormalizedOutputOptions,
inputOptions: NormalizedInputOptions
) => void;
/** File URL resolution */
resolveFileUrl: ResolveFileUrlHook;
/** Import meta resolution */
resolveImportMeta: ResolveImportMetaHook;
/** Bundle writing completion */
writeBundle: (
this: PluginContext,
options: NormalizedOutputOptions,
bundle: OutputBundle
) => void;
}
type RenderChunkHook = (
this: PluginContext,
code: string,
chunk: RenderedChunk,
options: NormalizedOutputOptions,
meta: { chunks: Record<string, RenderedChunk> }
) => { code: string; map?: SourceMapInput } | string | null;Usage Examples:
// Output processing plugin
const outputPlugin = () => ({
name: 'output-plugin',
renderStart(outputOptions, inputOptions) {
console.log(`Rendering for format: ${outputOptions.format}`);
},
renderChunk(code, chunk) {
if (chunk.isEntry) {
// Add header to entry chunks
return {
code: `/* Entry: ${chunk.name} */\n${code}`,
map: null
};
}
return null;
},
generateBundle(options, bundle) {
// Add manifest file
this.emitFile({
type: 'asset',
fileName: 'manifest.json',
source: JSON.stringify({
files: Object.keys(bundle),
timestamp: Date.now()
})
});
},
writeBundle(options, bundle) {
console.log(`Written ${Object.keys(bundle).length} files to ${options.dir || options.file}`);
}
});
// Code transformation plugin
const codeProcessorPlugin = () => ({
name: 'code-processor',
renderChunk(code, chunk, options) {
if (options.format === 'umd') {
// Add UMD-specific modifications
return addUmdPolyfills(code);
}
return null;
},
augmentChunkHash(chunk) {
// Include custom data in hash
return JSON.stringify(chunk.exports);
}
});Utility functions and types for advanced plugin development.
/**
* File emission utilities
*/
type EmitFile = (emittedFile: EmittedFile) => string;
type EmittedFile = EmittedAsset | EmittedChunk | EmittedPrebuiltChunk;
interface EmittedAsset {
type: 'asset';
name?: string;
fileName?: string;
source?: string | Uint8Array;
needsCodeReference?: boolean;
}
interface EmittedChunk {
type: 'chunk';
id: string;
name?: string;
fileName?: string;
implicitlyLoadedAfterOneOf?: string[];
importer?: string;
preserveSignature?: PreserveEntrySignaturesOption;
}
/**
* Plugin cache interface
*/
interface PluginCache {
delete(id: string): boolean;
get<T = any>(id: string): T;
has(id: string): boolean;
set<T = any>(id: string, value: T): void;
}
/**
* Module information interface
*/
interface ModuleInfo extends ModuleOptions {
id: string;
code: string | null;
ast: ProgramNode | null;
isEntry: boolean;
isExternal: boolean;
isIncluded: boolean | null;
importers: readonly string[];
dynamicImporters: readonly string[];
importedIds: readonly string[];
importedIdResolutions: readonly ResolvedId[];
dynamicallyImportedIds: readonly string[];
dynamicallyImportedIdResolutions: readonly ResolvedId[];
implicitlyLoadedAfterOneOf: readonly string[];
implicitlyLoadedBefore: readonly string[];
exports: string[] | null;
exportedBindings: Record<string, string[]> | null;
hasDefaultExport: boolean | null;
}const virtualModulesPlugin = (modules) => {
const virtualModules = new Map(Object.entries(modules));
return {
name: 'virtual-modules',
resolveId(id) {
if (virtualModules.has(id)) {
return id;
}
return null;
},
load(id) {
if (virtualModules.has(id)) {
return virtualModules.get(id);
}
return null;
}
};
};
// Usage
rollup({
input: 'src/main.js',
plugins: [
virtualModulesPlugin({
'virtual:config': 'export default { version: "1.0.0" };',
'virtual:env': `export default ${JSON.stringify(process.env)};`
})
]
});const assetPlugin = () => ({
name: 'asset-plugin',
load(id) {
if (id.endsWith('?asset')) {
const filePath = id.slice(0, -6);
const source = fs.readFileSync(filePath);
const assetId = this.emitFile({
type: 'asset',
name: path.basename(filePath),
source
});
return `export default import.meta.ROLLUP_FILE_URL_${assetId};`;
}
return null;
},
generateBundle() {
// Post-process emitted assets
for (const [fileName, asset] of Object.entries(this.bundle)) {
if (asset.type === 'asset' && fileName.endsWith('.svg')) {
asset.source = optimizeSvg(asset.source);
}
}
}
});const devPlugin = () => ({
name: 'dev-plugin',
buildStart() {
if (this.meta.watchMode) {
console.log('🔄 Development mode active');
}
},
watchChange(id, { event }) {
console.log(`📁 ${event.toUpperCase()}: ${path.relative(process.cwd(), id)}`);
},
buildEnd(error) {
if (error) {
console.error('❌ Build failed:', error.message);
} else {
console.log('✅ Build successful');
}
}
});Install with Tessl CLI
npx tessl i tessl/npm-rollup