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;