diff --git a/index.js b/index.js
index f742a476..0f11874b 100644
--- a/index.js
+++ b/index.js
@@ -6,7 +6,7 @@ const promisify = require('util.promisify');
// Import types
/* eslint-disable */
-///
+///
/* eslint-enable */
/** @typedef {import("webpack/lib/Compiler.js")} WebpackCompiler */
/** @typedef {import("webpack/lib/Compilation.js")} WebpackCompilation */
@@ -15,14 +15,14 @@ const vm = require('vm');
const fs = require('fs');
const _ = require('lodash');
const path = require('path');
-const SyncWaterfallHook = require('tapable').SyncWaterfallHook;
-const AsyncSeriesWaterfallHook = require('tapable').AsyncSeriesWaterfallHook;
const htmlTagObjectToString = require('./lib/html-tags').htmlTagObjectToString;
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);
@@ -86,18 +86,7 @@ class HtmlWebpackPlugin {
}
// setup hooks for third party plugins
- compiler.hooks.compilation.tap('HtmlWebpackPluginHooks', compilation => {
- // Setup the hooks only once
- if (compilation.hooks.htmlWebpackPluginAlterChunks) {
- return;
- }
- compilation.hooks.htmlWebpackPluginAlterChunks = new SyncWaterfallHook(['chunks', 'objectWithPluginRef']);
- compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration = new AsyncSeriesWaterfallHook(['pluginArgs']);
- compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing = new AsyncSeriesWaterfallHook(['pluginArgs']);
- compilation.hooks.htmlWebpackPluginAlterAssetTags = new AsyncSeriesWaterfallHook(['pluginArgs']);
- compilation.hooks.htmlWebpackPluginAfterHtmlProcessing = new AsyncSeriesWaterfallHook(['pluginArgs']);
- compilation.hooks.htmlWebpackPluginAfterEmit = new AsyncSeriesWaterfallHook(['pluginArgs']);
- });
+ compiler.hooks.compilation.tap('HtmlWebpackPluginHooks', getHtmlWebpackPluginHooks);
compiler.hooks.make.tapAsync('HtmlWebpackPlugin', (compilation, callback) => {
// Compile the template (queued)
@@ -126,7 +115,6 @@ class HtmlWebpackPlugin {
* @param {() => void} callback
*/
(compilation, callback) => {
- const applyPluginsAsyncWaterfall = self.applyPluginsAsyncWaterfall(compilation);
// Get all entry point names for this html file
const entryNames = Array.from(compilation.entrypoints.keys());
const filteredEntryNames = self.filterChunks(entryNames, self.options.chunks, self.options.excludeChunks);
@@ -176,7 +164,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 => applyPluginsAsyncWaterfall('htmlWebpackPluginBeforeHtmlGeneration', false, {
+ .then(compilationResult => getHtmlWebpackPluginHook(compilation, 'htmlWebpackPluginBeforeHtmlGeneration').promise({
assets: assets,
outputName: self.childCompilationOutputName,
plugin: self
@@ -189,7 +177,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 applyPluginsAsyncWaterfall('htmlWebpackPluginBeforeHtmlProcessing', true, pluginArgs);
+ return getHtmlWebpackPluginHook(compilation, 'htmlWebpackPluginBeforeHtmlProcessing').promise(pluginArgs);
})
.then(result => {
const html = result.html;
@@ -198,7 +186,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 applyPluginsAsyncWaterfall('htmlWebpackPluginAlterAssetTags', true, pluginArgs)
+ return getHtmlWebpackPluginHook(compilation, 'htmlWebpackPluginAlterAssetTags').promise(pluginArgs)
.then(result => self.postProcessHtml(html, assets, { body: result.body, head: result.head })
.then(html => _.extend(result, {html: html, assets: assets})));
})
@@ -207,7 +195,7 @@ class HtmlWebpackPlugin {
const html = result.html;
const assets = result.assets;
const pluginArgs = {html: html, assets: assets, plugin: self, outputName: self.childCompilationOutputName};
- return applyPluginsAsyncWaterfall('htmlWebpackPluginAfterHtmlProcessing', true, pluginArgs)
+ return getHtmlWebpackPluginHook(compilation, 'htmlWebpackPluginAfterHtmlProcessing').promise(pluginArgs)
.then(result => result.html);
})
.catch(err => {
@@ -225,7 +213,7 @@ class HtmlWebpackPlugin {
size: () => html.length
};
})
- .then(() => applyPluginsAsyncWaterfall('htmlWebpackPluginAfterEmit', false, {
+ .then(() => getHtmlWebpackPluginHook(compilation, 'htmlWebpackPluginAfterEmit').promise({
html: compilation.assets[self.childCompilationOutputName],
outputName: self.childCompilationOutputName,
plugin: self
@@ -684,30 +672,6 @@ class HtmlWebpackPlugin {
files.sort();
return files;
}
-
- /**
- * Helper to promisify compilation.applyPluginsAsyncWaterfall that returns
- * a function that helps to merge given plugin arguments with processed ones
- *
- * @param {WebpackCompilation} compilation
- *
- */
- applyPluginsAsyncWaterfall (compilation) {
- return (eventName, requiresResult, pluginArgs) => {
- if (!compilation.hooks[eventName]) {
- compilation.errors.push(
- new Error('No hook found for ' + eventName)
- );
- }
- return compilation.hooks[eventName].promise(pluginArgs)
- .then(result => {
- if (requiresResult && !result) {
- throw new Error('Using ' + eventName + ' did not return a result.');
- }
- return result;
- });
- };
- }
}
/**
diff --git a/lib/hooks.js b/lib/hooks.js
new file mode 100644
index 00000000..20a18f2a
--- /dev/null
+++ b/lib/hooks.js
@@ -0,0 +1,135 @@
+// @ts-check
+/* eslint-disable */
+///
+/* eslint-enable */
+'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 */
+/** @typedef {import("../index.js")} HtmlWebpackPlugin */
+
+const AsyncSeriesWaterfallHook = require('tapable').AsyncSeriesWaterfallHook;
+
+// The following typedef holds the API definition for all available hooks
+// to allow easier access when using ts-check or typescript inside plugins
+/** @typedef {{
+ htmlWebpackPluginBeforeHtmlGeneration:
+ AsyncSeriesWaterfallHook<{
+ assets: {
+ publicPath: string,
+ js: Array<{entryName: string, path: string}>,
+ css: Array<{entryName: string, path: string}>,
+ manifest: string,
+ },
+ outputName: string,
+ plugin: HtmlWebpackPlugin
+ }>,
+ htmlWebpackPluginBeforeHtmlProcessing:
+ AsyncSeriesWaterfallHook<{
+ html: string,
+ assets: {
+ publicPath: string,
+ js: Array<{entryName: string, path: string}>,
+ css: Array<{entryName: string, path: string}>,
+ manifest: string,
+ },
+ outputName: string,
+ plugin: HtmlWebpackPlugin,
+ }>,
+ htmlWebpackPluginAfterHtmlProcessing:
+ AsyncSeriesWaterfallHook<{
+ html: string,
+ assets: {
+ publicPath: string,
+ js: Array<{entryName: string, path: string}>,
+ css: Array<{entryName: string, path: string}>,
+ manifest: string,
+ },
+ outputName: string,
+ plugin: HtmlWebpackPlugin,
+ }>,
+ htmlWebpackPluginAlterAssetTags:
+ AsyncSeriesWaterfallHook<{
+ head: Array,
+ body: Array,
+ outputName: string,
+ plugin: HtmlWebpackPlugin
+ }>,
+ htmlWebpackPluginAfterEmit:
+ AsyncSeriesWaterfallHook<{
+ html: string,
+ outputName: string,
+ plugin: HtmlWebpackPlugin
+ }>,
+ }} HtmlWebpackPluginHooks
+ */
+
+/**
+ * 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;
+ // Setup the hooks only once
+ if (!hooks.htmlWebpackPluginAfterEmit) {
+ attachHooksToCompilation(compilation);
+ }
+ return {
+ htmlWebpackPluginBeforeHtmlGeneration: hooks.htmlWebpackPluginBeforeHtmlGeneration,
+ htmlWebpackPluginBeforeHtmlProcessing: hooks.htmlWebpackPluginBeforeHtmlProcessing,
+ htmlWebpackPluginAlterAssetTags: hooks.htmlWebpackPluginAlterAssetTags,
+ htmlWebpackPluginAfterHtmlProcessing: hooks.htmlWebpackPluginAfterHtmlProcessing,
+ htmlWebpackPluginAfterEmit: hooks.htmlWebpackPluginAfterEmit
+ };
+}
+
+/**
+ * Add hooks to the webpack compilation object to allow foreign plugins to
+ * extend the HtmlWebpackPlugin
+ *
+ * @param {WebpackCompilation} compilation
+ */
+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']);
+}
+
+/**
+ * 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
+};
diff --git a/package.json b/package.json
index e089a534..820d6c4e 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
"webpack-recompilation-simulator": "^1.3.0"
},
"dependencies": {
+ "@types/tapable": "1.0.2",
"html-minifier": "^3.2.3",
"loader-utils": "^1.1.0",
"lodash": "^4.17.10",
diff --git a/index.d.ts b/typings.d.ts
similarity index 91%
rename from index.d.ts
rename to typings.d.ts
index 39bc3626..3d9d31a9 100644
--- a/index.d.ts
+++ b/typings.d.ts
@@ -2,7 +2,7 @@
/**
* The plugin options
*/
-type HtmlWebpackPluginOptions = {
+interface HtmlWebpackPluginOptions {
/**
* The title to use for the generated HTML document
*/
@@ -22,7 +22,7 @@ type HtmlWebpackPluginOptions = {
templateParameters:
false // Pass an empty object to the template function
| ((compilation: any, assets, options: HtmlWebpackPluginOptions) => {})
- | Object
+ | {[option: string]: any}
/**
* The file to write the HTML to.
* Defaults to `index.html`.
@@ -76,6 +76,12 @@ type HtmlWebpackPluginOptions = {
* Enforce self closing tags e.g.
*/
xhtml: boolean
+
+ /**
+ * In addition to the options actually used by this plugin, you can use this hash to pass arbitrary data through
+ * to your template.
+ */
+ [option: string]: any;
}
/**