Skip to content

Commit

Permalink
Add hook for modifying OutputOptions (#2736)
Browse files Browse the repository at this point in the history
* Add test for outputOptions hook.

* Add type for outputOptions hook.

* Implement outputOptions hook.

* Update unit test.

* Implement hookReduceArg0Sync.

* Update outputOptions hook to use pluginDriver.hookReduceArg0Sync.

* Add docs for outputOptions hook.

* Simplify replace options.
  • Loading branch information
Comandeer authored and lukastaegert committed Mar 8, 2019
1 parent ae000c9 commit 00f414d
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 3 deletions.
6 changes: 6 additions & 0 deletions docs/05-plugins.md
Expand Up @@ -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`<br>
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)`<br>
Kind: `async, parallel`
Expand Down
17 changes: 14 additions & 3 deletions src/rollup/index.ts
Expand Up @@ -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 {
Expand Down Expand Up @@ -174,7 +175,8 @@ export default function rollup(rawInputOptions: GenericConfigObject): Promise<Ro
const outputOptions = normalizeOutputOptions(
inputOptions,
rawOutputOptions,
chunks.length > 1
chunks.length > 1,
graph.pluginDriver
);

timeStart('GENERATE', 1);
Expand Down Expand Up @@ -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');
Expand All @@ -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);

Expand Down
1 change: 1 addition & 0 deletions src/rollup/types.d.ts
Expand Up @@ -237,6 +237,7 @@ export interface Plugin {
chunk: OutputChunk
) => void | Promise<void>;
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> | void;
Expand Down
14 changes: 14 additions & 0 deletions src/utils/pluginDriver.ts
Expand Up @@ -29,6 +29,12 @@ export interface PluginDriver {
reduce: Reduce<R, T>,
hookContext?: HookContext
): Promise<T>;
hookReduceArg0Sync<R = any, T = any>(
hook: string,
args: any[],
reduce: Reduce<R, T>,
hookContext?: HookContext
): T;
hookReduceValue<R = any, T = any>(
hook: string,
value: T | Promise<T>,
Expand Down Expand Up @@ -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);
Expand Down
32 changes: 32 additions & 0 deletions test/hooks/index.js
Expand Up @@ -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;
Expand Down

0 comments on commit 00f414d

Please sign in to comment.