Skip to content

Commit

Permalink
feat(hooks): Provide static getHook method for access to all html-web…
Browse files Browse the repository at this point in the history
…pack-plugin hooks (#995)

BREAKING CHANGE: The html-webpack-plugin doesn't add its hooks to the compilation object anymore
  • Loading branch information
jantimon committed Jul 8, 2018
1 parent bbc07a3 commit 82b34a1
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 56 deletions.
3 changes: 1 addition & 2 deletions README.md
Expand Up @@ -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'
Expand Down
32 changes: 21 additions & 11 deletions index.js
Expand Up @@ -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<HtmlWebpackPluginOptions>} options
* @param {Partial<HtmlWebpackPluginOptions>} [options]
*/
constructor (options) {
// Default options
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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})));
})
Expand All @@ -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 => {
Expand All @@ -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
Expand Down Expand Up @@ -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;
1 change: 0 additions & 1 deletion lib/compiler.js
Expand Up @@ -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
Expand Down
59 changes: 19 additions & 40 deletions lib/hooks.js
Expand Up @@ -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 */
Expand Down Expand Up @@ -75,18 +63,23 @@ const AsyncSeriesWaterfallHook = require('tapable').AsyncSeriesWaterfallHook;
}} HtmlWebpackPluginHooks
*/

/**
* @type {WeakMap<WebpackCompilation, HtmlWebpackPluginHooks>}}
*/
const htmlWebpackPluginHooksMap = new WeakMap();

/**
* Returns all public hooks of the html webpack plugin for the given compilation
*
* @param {WebpackCompilation} compilation
* @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,
Expand All @@ -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 {
<T extends keyof HtmlWebpackPluginHooks>(compilation: WebpackCompilation, hookName: T) => HtmlWebpackPluginHooks[T]
}
*/
const getHtmlWebpackPluginHook = (compilation, hookName) => {
const hooks = getHtmlWebpackPluginHooks(compilation);
return /** @type {any} */hooks[hookName];
};

module.exports = {
getHtmlWebpackPluginHooks,
getHtmlWebpackPluginHook
getHtmlWebpackPluginHooks
};
2 changes: 1 addition & 1 deletion spec/BasicSpec.js
Expand Up @@ -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
);
Expand Down
2 changes: 1 addition & 1 deletion spec/ExampleSpec.js
Expand Up @@ -3,7 +3,7 @@
* and matches them against their dist folder
*/

/* eslint-env jasmine */
/* eslint-env jasmine */
'use strict';

var path = require('path');
Expand Down

0 comments on commit 82b34a1

Please sign in to comment.