From 82b34a1dd2e96cbcd715fafe4e97073efd30cc9f Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Sun, 8 Jul 2018 16:52:05 +0200 Subject: [PATCH] feat(hooks): Provide static getHook method for access to all html-webpack-plugin hooks (#995) BREAKING CHANGE: The html-webpack-plugin doesn't add its hooks to the compilation object anymore --- README.md | 3 +-- index.js | 32 +++++++++++++++--------- lib/compiler.js | 1 - lib/hooks.js | 59 +++++++++++++++------------------------------ spec/BasicSpec.js | 2 +- spec/ExampleSpec.js | 2 +- 6 files changed, 43 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 640a8599..21c112e0 100644 --- a/README.md +++ b/README.md @@ -308,8 +308,7 @@ function MyPlugin(options) { MyPlugin.prototype.apply = function (compiler) { compiler.hooks.compilation.tap('MyPlugin', (compilation) => { console.log('The compiler is starting a new compilation...'); - - compilation.hooks.htmlWebpackPluginAfterHtmlProcessing.tapAsync( + HtmlWebpackPlugin.getHooks(compilation).htmlWebpackPluginAfterHtmlProcessing.tapAsync( 'MyPlugin', (data, cb) => { data.html += 'The Magic Footer' diff --git a/index.js b/index.js index 1d894e44..4a3e7fa8 100644 --- a/index.js +++ b/index.js @@ -21,14 +21,13 @@ const childCompiler = require('./lib/compiler.js'); const prettyError = require('./lib/errors.js'); const chunkSorter = require('./lib/chunksorter.js'); const getHtmlWebpackPluginHooks = require('./lib/hooks.js').getHtmlWebpackPluginHooks; -const getHtmlWebpackPluginHook = require('./lib/hooks.js').getHtmlWebpackPluginHook; const fsStatAsync = promisify(fs.stat); const fsReadFileAsync = promisify(fs.readFile); class HtmlWebpackPlugin { /** - * @param {Partial} options + * @param {Partial} [options] */ constructor (options) { // Default options @@ -60,10 +59,7 @@ class HtmlWebpackPlugin { this.childCompilationOutputName = undefined; this.assetJson = undefined; this.hash = undefined; - /** - * The major version number of this plugin - */ - this.version = 4; + this.version = HtmlWebpackPlugin.version; } /** @@ -173,7 +169,7 @@ class HtmlWebpackPlugin { }) // Allow plugins to make changes to the assets before invoking the template // This only makes sense to use if `inject` is `false` - .then(compilationResult => getHtmlWebpackPluginHook(compilation, 'htmlWebpackPluginBeforeHtmlGeneration').promise({ + .then(compilationResult => getHtmlWebpackPluginHooks(compilation).htmlWebpackPluginBeforeHtmlGeneration.promise({ assets: assets, outputName: self.childCompilationOutputName, plugin: self @@ -186,7 +182,7 @@ class HtmlWebpackPlugin { // Allow plugins to change the html before assets are injected .then(html => { const pluginArgs = {html: html, assets: assets, plugin: self, outputName: self.childCompilationOutputName}; - return getHtmlWebpackPluginHook(compilation, 'htmlWebpackPluginBeforeHtmlProcessing').promise(pluginArgs); + return getHtmlWebpackPluginHooks(compilation).htmlWebpackPluginBeforeHtmlProcessing.promise(pluginArgs); }) .then(result => { const html = result.html; @@ -195,7 +191,7 @@ class HtmlWebpackPlugin { const assetTags = self.generateHtmlTagObjects(assets); const pluginArgs = {head: assetTags.head, body: assetTags.body, plugin: self, outputName: self.childCompilationOutputName}; // Allow plugins to change the assetTag definitions - return getHtmlWebpackPluginHook(compilation, 'htmlWebpackPluginAlterAssetTags').promise(pluginArgs) + return getHtmlWebpackPluginHooks(compilation).htmlWebpackPluginAlterAssetTags.promise(pluginArgs) .then(result => self.postProcessHtml(html, assets, { body: result.body, head: result.head }) .then(html => _.extend(result, {html: html, assets: assets}))); }) @@ -204,7 +200,7 @@ class HtmlWebpackPlugin { const html = result.html; const assets = result.assets; const pluginArgs = {html: html, assets: assets, plugin: self, outputName: self.childCompilationOutputName}; - return getHtmlWebpackPluginHook(compilation, 'htmlWebpackPluginAfterHtmlProcessing').promise(pluginArgs) + return getHtmlWebpackPluginHooks(compilation).htmlWebpackPluginAfterHtmlProcessing.promise(pluginArgs) .then(result => result.html); }) .catch(err => { @@ -222,7 +218,7 @@ class HtmlWebpackPlugin { size: () => html.length }; }) - .then(() => getHtmlWebpackPluginHook(compilation, 'htmlWebpackPluginAfterEmit').promise({ + .then(() => getHtmlWebpackPluginHooks(compilation).htmlWebpackPluginAfterEmit.promise({ html: compilation.assets[self.childCompilationOutputName], outputName: self.childCompilationOutputName, plugin: self @@ -697,4 +693,18 @@ function templateParametersGenerator (compilation, assets, options) { } }; } + +// Statics: +/** + * The major version number of this plugin + */ +HtmlWebpackPlugin.version = 4; + +/** + * A static helper to get the hooks for this plugin + * + * Usage: HtmlWebpackPlugin.getHook(compilation, 'HookName').tap('YourPluginName', () => { ... }); + */ +HtmlWebpackPlugin.getHooks = getHtmlWebpackPluginHooks; + module.exports = HtmlWebpackPlugin; diff --git a/lib/compiler.js b/lib/compiler.js index 106a5ba8..d9a871ed 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -22,7 +22,6 @@ const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin'); * for multile HtmlWebpackPlugin instances to improve the compilation performance. */ class HtmlWebpackChildCompiler { - constructor () { /** * @type {string[]} templateIds diff --git a/lib/hooks.js b/lib/hooks.js index f9f5e4e9..49d6595b 100644 --- a/lib/hooks.js +++ b/lib/hooks.js @@ -5,18 +5,6 @@ 'use strict'; /** * This file provides access to all public htmlWebpackPlugin hooks - * - * Usage: - * ```js - * const getHtmlWebpackPluginHooks = require('html-webpack-plugin/lib/hooks').getHtmlWebpackPluginHooks; - * - * compiler.hooks.compilation.tap('YOUR_PLUGIN_NAME', (compilation) => { - * const htmlWebpackPluginHooks = getHtmlWebpackPluginHooks(compilation); - * htmlWebpackPluginHooks.htmlWebpackPluginAfterEmit.tap('YOUR_PLUGIN_NAME', (pluginArgs) => { - * // your code - * }); - * }); - * ``` */ /** @typedef {import("webpack/lib/Compilation.js")} WebpackCompilation */ @@ -75,6 +63,11 @@ const AsyncSeriesWaterfallHook = require('tapable').AsyncSeriesWaterfallHook; }} HtmlWebpackPluginHooks */ +/** + * @type {WeakMap}} + */ +const htmlWebpackPluginHooksMap = new WeakMap(); + /** * Returns all public hooks of the html webpack plugin for the given compilation * @@ -82,11 +75,11 @@ const AsyncSeriesWaterfallHook = require('tapable').AsyncSeriesWaterfallHook; * @returns {HtmlWebpackPluginHooks} */ function getHtmlWebpackPluginHooks (compilation) { - /** @type {HtmlWebpackPluginHooks} */ - const hooks = compilation.hooks; + let hooks = htmlWebpackPluginHooksMap.get(compilation); // Setup the hooks only once - if (!hooks.htmlWebpackPluginAfterEmit) { - attachHooksToCompilation(compilation); + if (hooks === undefined) { + hooks = createHtmlWebpackPluginHooks(); + htmlWebpackPluginHooksMap.set(compilation, hooks); } return { htmlWebpackPluginBeforeHtmlGeneration: hooks.htmlWebpackPluginBeforeHtmlGeneration, @@ -101,32 +94,18 @@ function getHtmlWebpackPluginHooks (compilation) { * Add hooks to the webpack compilation object to allow foreign plugins to * extend the HtmlWebpackPlugin * - * @param {WebpackCompilation} compilation + * @returns {HtmlWebpackPluginHooks} */ -function attachHooksToCompilation (compilation) { - /** @type {HtmlWebpackPluginHooks} */ - const hooks = compilation.hooks; - hooks.htmlWebpackPluginBeforeHtmlGeneration = new AsyncSeriesWaterfallHook(['pluginArgs']); - hooks.htmlWebpackPluginBeforeHtmlProcessing = new AsyncSeriesWaterfallHook(['pluginArgs']); - hooks.htmlWebpackPluginAlterAssetTags = new AsyncSeriesWaterfallHook(['pluginArgs']); - hooks.htmlWebpackPluginAfterHtmlProcessing = new AsyncSeriesWaterfallHook(['pluginArgs']); - hooks.htmlWebpackPluginAfterEmit = new AsyncSeriesWaterfallHook(['pluginArgs']); +function createHtmlWebpackPluginHooks () { + return { + htmlWebpackPluginBeforeHtmlGeneration: new AsyncSeriesWaterfallHook(['pluginArgs']), + htmlWebpackPluginBeforeHtmlProcessing: new AsyncSeriesWaterfallHook(['pluginArgs']), + htmlWebpackPluginAlterAssetTags: new AsyncSeriesWaterfallHook(['pluginArgs']), + htmlWebpackPluginAfterHtmlProcessing: new AsyncSeriesWaterfallHook(['pluginArgs']), + htmlWebpackPluginAfterEmit: new AsyncSeriesWaterfallHook(['pluginArgs']) + }; } -/** - * Small workaround helper to work around https://github.com/Microsoft/TypeScript/issues/1178 - * Returns the hook of the given name - * - * @type { - (compilation: WebpackCompilation, hookName: T) => HtmlWebpackPluginHooks[T] - } - */ -const getHtmlWebpackPluginHook = (compilation, hookName) => { - const hooks = getHtmlWebpackPluginHooks(compilation); - return /** @type {any} */hooks[hookName]; -}; - module.exports = { - getHtmlWebpackPluginHooks, - getHtmlWebpackPluginHook + getHtmlWebpackPluginHooks }; diff --git a/spec/BasicSpec.js b/spec/BasicSpec.js index 21fe0264..345f9243 100644 --- a/spec/BasicSpec.js +++ b/spec/BasicSpec.js @@ -136,7 +136,7 @@ function getChunksInfoFromStats (stats) { function tapCompilationEvent (compilation, eventName, handler) { // Webpack 4 has a new interface if (compilation.hooks) { - return compilation.hooks[trainCaseToCamelCase(eventName)].tapAsync( + return HtmlWebpackPlugin.getHooks(compilation)[trainCaseToCamelCase(eventName)].tapAsync( 'AsyncPlugin' + tapCompilationEvent.counter++, handler ); diff --git a/spec/ExampleSpec.js b/spec/ExampleSpec.js index a3780648..378c7fa5 100644 --- a/spec/ExampleSpec.js +++ b/spec/ExampleSpec.js @@ -3,7 +3,7 @@ * and matches them against their dist folder */ - /* eslint-env jasmine */ +/* eslint-env jasmine */ 'use strict'; var path = require('path');