From 595db7edd825201d22ee4a7e758f45c8c3458a04 Mon Sep 17 00:00:00 2001 From: Comandeer Date: Sat, 2 Mar 2019 00:41:33 +0100 Subject: [PATCH 1/8] Add test for outputOptions hook. --- test/hooks/index.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/hooks/index.js b/test/hooks/index.js index ef6907f57cf..77c10e357ea 100644 --- a/test/hooks/index.js +++ b/test/hooks/index.js @@ -29,6 +29,34 @@ describe('hooks', () => { .then(bundle => {}); }); + it('allows to read and modify output options in the outputOptions hook', () => { + return rollup + .rollup({ + input: 'input', + treeshake: false, + output: { + format: 'esm', + banner: 'banner' + }, + plugins: [ + loader({ input: `alert('hello')` }), + { + renderChunk(code, chunk, options) { + assert.strictEqual(options.format, 'cjs'); + assert.strictEqual(options.banner, 'banner'); + }, + outputOptions(options) { + assert.strictEqual(options.format, 'esm'); + assert.strictEqual(options.banner, 'banner'); + assert.ok(/^\d+\.\d+\.\d+/.test(this.meta.rollupVersion)); + return Object.assign({}, options, { format: 'cjs' }); + } + } + ] + }) + .then(bundle => {}); + }); + it('supports buildStart and buildEnd hooks', () => { let buildStartCnt = 0; let buildEndCnt = 0; From 05a2c889c0ff79a897d110a18ae87c6e96115af5 Mon Sep 17 00:00:00 2001 From: Comandeer Date: Sat, 2 Mar 2019 00:41:57 +0100 Subject: [PATCH 2/8] Add type for outputOptions hook. --- src/rollup/types.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index 4248afa4703..fbaa0cd08b8 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -237,6 +237,10 @@ export interface Plugin { chunk: OutputChunk ) => void | Promise; options?: (this: MinimalPluginContext, options: InputOptions) => InputOptions | void | null; + outputOptions?: ( + this: MinimalPluginContext, + options: OutputOptions + ) => OutputOptions | void | null; outro?: AddonHook; renderChunk?: RenderChunkHook; renderError?: (this: PluginContext, err?: Error) => Promise | void; From da999655adcff9ca33ae04e9d715855c288b1cf7 Mon Sep 17 00:00:00 2001 From: Comandeer Date: Sat, 2 Mar 2019 00:42:12 +0100 Subject: [PATCH 3/8] Implement outputOptions hook. --- src/rollup/index.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/rollup/index.ts b/src/rollup/index.ts index 6ad366c878e..af3c96d1c1f 100644 --- a/src/rollup/index.ts +++ b/src/rollup/index.ts @@ -66,6 +66,13 @@ function applyOptionHook(inputOptions: InputOptions, plugin: Plugin) { return inputOptions; } +function applyOutputOptionHook(outputOptions: OutputOptions, plugin: Plugin) { + if (plugin.outputOptions) + return plugin.outputOptions.call({ meta: { rollupVersion } }, outputOptions) || outputOptions; + + return outputOptions; +} + function getInputOptions(rawInputOptions: GenericConfigObject): any { if (!rawInputOptions) { throw new Error('You must supply an options object to rollup'); @@ -427,7 +434,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 plugins = inputOptions.plugins; + inputOptions.plugins = Array.isArray(plugins) + ? plugins.filter(Boolean) + : plugins + ? [plugins] + : []; + const outputOptions = inputOptions.plugins.reduce(applyOutputOptionHook, mergedOutputOptions); checkOutputOptions(outputOptions); From ffb3565c30e81b0814a5273265c2ae156587c6d0 Mon Sep 17 00:00:00 2001 From: Comandeer Date: Sun, 3 Mar 2019 16:50:30 +0100 Subject: [PATCH 4/8] Update unit test. --- test/hooks/index.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/hooks/index.js b/test/hooks/index.js index 77c10e357ea..99234a6b18f 100644 --- a/test/hooks/index.js +++ b/test/hooks/index.js @@ -34,27 +34,31 @@ describe('hooks', () => { .rollup({ input: 'input', treeshake: false, - output: { - format: 'esm', - banner: 'banner' - }, plugins: [ loader({ input: `alert('hello')` }), { renderChunk(code, chunk, options) { + assert.strictEqual(options.banner, 'new banner'); assert.strictEqual(options.format, 'cjs'); - assert.strictEqual(options.banner, 'banner'); }, outputOptions(options) { - assert.strictEqual(options.format, 'esm'); assert.strictEqual(options.banner, 'banner'); + assert.strictEqual(options.format, 'cjs'); assert.ok(/^\d+\.\d+\.\d+/.test(this.meta.rollupVersion)); - return Object.assign({}, options, { format: 'cjs' }); + return Object.assign({}, options, { banner: 'new banner' }); } } ] }) - .then(bundle => {}); + .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', () => { From c212cfa22b04f13d0a14fd6c8b407fa28fdd87f7 Mon Sep 17 00:00:00 2001 From: Comandeer Date: Thu, 7 Mar 2019 00:02:26 +0100 Subject: [PATCH 5/8] Implement hookReduceArg0Sync. --- src/utils/pluginDriver.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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); From 52abafd08ba465dc1be1956a0cc8c7fd6027cc5b Mon Sep 17 00:00:00 2001 From: Comandeer Date: Thu, 7 Mar 2019 00:07:25 +0100 Subject: [PATCH 6/8] Update outputOptions hook to use pluginDriver.hookReduceArg0Sync. --- src/rollup/index.ts | 30 ++++++++++++++---------------- src/rollup/types.d.ts | 5 +---- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/rollup/index.ts b/src/rollup/index.ts index af3c96d1c1f..665d7af05ff 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 { @@ -66,13 +67,6 @@ function applyOptionHook(inputOptions: InputOptions, plugin: Plugin) { return inputOptions; } -function applyOutputOptionHook(outputOptions: OutputOptions, plugin: Plugin) { - if (plugin.outputOptions) - return plugin.outputOptions.call({ meta: { rollupVersion } }, outputOptions) || outputOptions; - - return outputOptions; -} - function getInputOptions(rawInputOptions: GenericConfigObject): any { if (!rawInputOptions) { throw new Error('You must supply an options object to rollup'); @@ -181,7 +175,8 @@ export default function rollup(rawInputOptions: GenericConfigObject): Promise 1 + chunks.length > 1, + graph.pluginDriver ); timeStart('GENERATE', 1); @@ -420,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'); @@ -435,14 +431,16 @@ function normalizeOutputOptions( // now outputOptions is an array, but rollup.rollup API doesn't support arrays const mergedOutputOptions = mergedOptions.outputOptions[0]; + const outputOptionsReducer = (outputOptions: OutputOptions, result: OutputOptions) => { + if (!result) return outputOptions; - const plugins = inputOptions.plugins; - inputOptions.plugins = Array.isArray(plugins) - ? plugins.filter(Boolean) - : plugins - ? [plugins] - : []; - const outputOptions = inputOptions.plugins.reduce(applyOutputOptionHook, mergedOutputOptions); + return {...outputOptions, ...result}; + }; + const outputOptions = pluginDriver.hookReduceArg0Sync( + 'outputOptions', + [mergedOutputOptions], + outputOptionsReducer + ); checkOutputOptions(outputOptions); diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index fbaa0cd08b8..2e64f739804 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -237,10 +237,7 @@ export interface Plugin { chunk: OutputChunk ) => void | Promise; options?: (this: MinimalPluginContext, options: InputOptions) => InputOptions | void | null; - outputOptions?: ( - this: MinimalPluginContext, - options: OutputOptions - ) => OutputOptions | void | null; + outputOptions?: (this: PluginContext, options: OutputOptions) => OutputOptions | void | null; outro?: AddonHook; renderChunk?: RenderChunkHook; renderError?: (this: PluginContext, err?: Error) => Promise | void; From 592a4ddd9e525525f56b91bdacfa897b589b2ce5 Mon Sep 17 00:00:00 2001 From: Comandeer Date: Thu, 7 Mar 2019 00:08:02 +0100 Subject: [PATCH 7/8] Add docs for outputOptions hook. --- docs/05-plugins.md | 6 ++++++ 1 file changed, 6 insertions(+) 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` From d00cb97f53240e6b1d9d5af459025b2f4c49db59 Mon Sep 17 00:00:00 2001 From: Comandeer Date: Thu, 7 Mar 2019 20:57:26 +0100 Subject: [PATCH 8/8] Simplify replace options. --- src/rollup/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rollup/index.ts b/src/rollup/index.ts index 665d7af05ff..20d82f14808 100644 --- a/src/rollup/index.ts +++ b/src/rollup/index.ts @@ -432,9 +432,7 @@ function normalizeOutputOptions( // now outputOptions is an array, but rollup.rollup API doesn't support arrays const mergedOutputOptions = mergedOptions.outputOptions[0]; const outputOptionsReducer = (outputOptions: OutputOptions, result: OutputOptions) => { - if (!result) return outputOptions; - - return {...outputOptions, ...result}; + return result || outputOptions; }; const outputOptions = pluginDriver.hookReduceArg0Sync( 'outputOptions',