diff --git a/docs/05-plugins.md b/docs/05-plugins.md index 44c3097170c..a7deaaeaf42 100644 --- a/docs/05-plugins.md +++ b/docs/05-plugins.md @@ -149,6 +149,12 @@ Kind: `sync, sequential` Reads and replaces or manipulates the options object passed to `rollup.rollup`. Returning `null` does not replace anything. This is the only hook that does not have access to most [plugin context](guide/en#plugin-context) utility functions as it is run before rollup is fully configured. +#### `outputOptions` +Type: `(outputOptions: OutputOptions) => OutputOptions | null`
+Kind: `sync, sequential` + +Reads and replaces or manipulates the output options object passed to `bundle.generate`. Returning `null` does not replace anything. + #### `outro` Type: `string | (() => string)`
Kind: `async, parallel` diff --git a/src/rollup/index.ts b/src/rollup/index.ts index 6ad366c878e..20d82f14808 100644 --- a/src/rollup/index.ts +++ b/src/rollup/index.ts @@ -11,6 +11,7 @@ import { writeFile } from '../utils/fs'; import getExportMode from '../utils/getExportMode'; import mergeOptions, { GenericConfigObject } from '../utils/mergeOptions'; import { basename, dirname, isAbsolute, resolve } from '../utils/path'; +import { PluginDriver } from '../utils/pluginDriver'; import { SOURCEMAPPING_URL } from '../utils/sourceMappingURL'; import { getTimings, initialiseTimers, timeEnd, timeStart } from '../utils/timers'; import { @@ -174,7 +175,8 @@ export default function rollup(rawInputOptions: GenericConfigObject): Promise 1 + chunks.length > 1, + graph.pluginDriver ); timeStart('GENERATE', 1); @@ -413,7 +415,8 @@ function writeOutputFile( function normalizeOutputOptions( inputOptions: GenericConfigObject, rawOutputOptions: GenericConfigObject, - hasMultipleChunks: boolean + hasMultipleChunks: boolean, + pluginDriver: PluginDriver ): OutputOptions { if (!rawOutputOptions) { throw new Error('You must supply an options object'); @@ -427,7 +430,15 @@ function normalizeOutputOptions( if (mergedOptions.optionError) throw new Error(mergedOptions.optionError); // now outputOptions is an array, but rollup.rollup API doesn't support arrays - const outputOptions = mergedOptions.outputOptions[0]; + const mergedOutputOptions = mergedOptions.outputOptions[0]; + const outputOptionsReducer = (outputOptions: OutputOptions, result: OutputOptions) => { + return result || outputOptions; + }; + const outputOptions = pluginDriver.hookReduceArg0Sync( + 'outputOptions', + [mergedOutputOptions], + outputOptionsReducer + ); checkOutputOptions(outputOptions); diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index 4248afa4703..2e64f739804 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -237,6 +237,7 @@ export interface Plugin { chunk: OutputChunk ) => void | Promise; options?: (this: MinimalPluginContext, options: InputOptions) => InputOptions | void | null; + outputOptions?: (this: PluginContext, options: OutputOptions) => OutputOptions | void | null; outro?: AddonHook; renderChunk?: RenderChunkHook; renderError?: (this: PluginContext, err?: Error) => Promise | void; diff --git a/src/utils/pluginDriver.ts b/src/utils/pluginDriver.ts index b7dcd08aed1..be80d7e3cff 100644 --- a/src/utils/pluginDriver.ts +++ b/src/utils/pluginDriver.ts @@ -29,6 +29,12 @@ export interface PluginDriver { reduce: Reduce, hookContext?: HookContext ): Promise; + hookReduceArg0Sync( + hook: string, + args: any[], + reduce: Reduce, + hookContext?: HookContext + ): T; hookReduceValue( hook: string, value: T | Promise, @@ -307,6 +313,14 @@ export function createPluginDriver( } return promise; }, + // chains, synchronically reduces returns of type R, to type T, handling the reduced value as the first hook argument + hookReduceArg0Sync(name, [arg0, ...args], reduce, hookContext) { + for (let i = 0; i < plugins.length; i++) { + const result = runHookSync(name, [arg0, ...args], i, false, hookContext); + arg0 = reduce.call(pluginContexts[i], arg0, result, plugins[i]); + } + return arg0; + }, // chains, reduces returns of type R, to type T, handling the reduced value separately. permits hooks as values. hookReduceValue(name, initial, args, reduce, hookContext) { let promise = Promise.resolve(initial); diff --git a/test/hooks/index.js b/test/hooks/index.js index ef6907f57cf..99234a6b18f 100644 --- a/test/hooks/index.js +++ b/test/hooks/index.js @@ -29,6 +29,38 @@ describe('hooks', () => { .then(bundle => {}); }); + it('allows to read and modify output options in the outputOptions hook', () => { + return rollup + .rollup({ + input: 'input', + treeshake: false, + plugins: [ + loader({ input: `alert('hello')` }), + { + renderChunk(code, chunk, options) { + assert.strictEqual(options.banner, 'new banner'); + assert.strictEqual(options.format, 'cjs'); + }, + outputOptions(options) { + assert.strictEqual(options.banner, 'banner'); + assert.strictEqual(options.format, 'cjs'); + assert.ok(/^\d+\.\d+\.\d+/.test(this.meta.rollupVersion)); + return Object.assign({}, options, { banner: 'new banner' }); + } + } + ] + }) + .then(bundle => { + return bundle.generate({ + format: 'cjs', + banner: 'banner' + }); + }) + .then(({ output }) => { + assert.equal(output[0].code, `new banner\n'use strict';\n\nalert('hello');\n`); + }); + }); + it('supports buildStart and buildEnd hooks', () => { let buildStartCnt = 0; let buildEndCnt = 0;