diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c2269c..cc72775 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
Change History
==============
+v2.21.0
+----
+* Add `html-webpack-plugin-alter-asset-tags` event to allow plugins to adjust the script/link tags
+
v2.20.0
----
* Exclude chunks works now even if combined with dependency sort
diff --git a/README.md b/README.md
index 9dfba89..e9ee5df 100644
--- a/README.md
+++ b/README.md
@@ -248,6 +248,7 @@ Async:
* `html-webpack-plugin-before-html-generation`
* `html-webpack-plugin-before-html-processing`
+ * `html-webpack-plugin-alter-asset-tags`
* `html-webpack-plugin-after-html-processing`
* `html-webpack-plugin-after-emit`
diff --git a/index.js b/index.js
index 40766e7..8d5b189 100644
--- a/index.js
+++ b/index.js
@@ -143,8 +143,15 @@ HtmlWebpackPlugin.prototype.apply = function (compiler) {
});
})
.then(function (html) {
- // Add the stylesheets, scripts and so on to the resulting html
- return self.postProcessHtml(html, assets);
+ // Prepare script and link tags
+ var assetTags = self.generateAssetTags(assets);
+ var pluginArgs = {head: assetTags.head, body: assetTags.body, plugin: self, chunks: chunks, outputName: self.childCompilationOutputName};
+ // Allow plugins to change the assetTag definitions
+ return applyPluginsAsyncWaterfall('html-webpack-plugin-alter-asset-tags', pluginArgs)
+ .then(function () {
+ // Add the stylesheets, scripts and so on to the resulting html
+ return self.postProcessHtml(html, assets, { body: pluginArgs.body, head: pluginArgs.head });
+ });
})
// Allow plugins to change the html after assets are injected
.then(function (html) {
@@ -259,7 +266,7 @@ HtmlWebpackPlugin.prototype.executeTemplate = function (templateFunction, chunks
*
* Returns a promise
*/
-HtmlWebpackPlugin.prototype.postProcessHtml = function (html, assets) {
+HtmlWebpackPlugin.prototype.postProcessHtml = function (html, assets, assetTags) {
var self = this;
if (typeof html !== 'string') {
return Promise.reject('Expected html to be a string but got ' + JSON.stringify(html));
@@ -268,7 +275,7 @@ HtmlWebpackPlugin.prototype.postProcessHtml = function (html, assets) {
// Inject
.then(function () {
if (self.options.inject) {
- return self.injectAssetsIntoHtml(html, assets);
+ return self.injectAssetsIntoHtml(html, assets, assetTags);
} else {
return html;
}
@@ -449,27 +456,45 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function (compilation, chu
/**
* Injects the assets into the given html string
*/
-HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function (html, assets) {
+HtmlWebpackPlugin.prototype.generateAssetTags = function (assets) {
// Turn script files into script tags
var scripts = assets.js.map(function (scriptPath) {
- return '';
+ return {
+ tagName: 'script',
+ closeTag: true,
+ attributes: {
+ type: 'text/javascript',
+ src: scriptPath
+ }
+ };
});
// Make tags self-closing in case of xhtml
- var xhtml = this.options.xhtml ? '/' : '';
+ var selfClosingTag = !!this.options.xhtml;
// Turn css files into link tags
var styles = assets.css.map(function (stylePath) {
- return '';
+ return {
+ tagName: 'link',
+ selfClosingTag: selfClosingTag,
+ attributes: {
+ href: stylePath,
+ rel: 'stylesheet'
+ }
+ };
});
- // Injections
- var htmlRegExp = /(]*>)/i;
+ // Injection targets
var head = [];
- var headRegExp = /(<\/head>)/i;
var body = [];
- var bodyRegExp = /(<\/body>)/i;
// If there is a favicon present, add it to the head
if (assets.favicon) {
- head.push('');
+ head.push({
+ tagName: 'link',
+ selfClosingTag: selfClosingTag,
+ attributes: {
+ rel: 'shortcut icon',
+ href: assets.favicon
+ }
+ });
}
// Add styles to the head
head = head.concat(styles);
@@ -479,6 +504,18 @@ HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function (html, assets) {
} else {
body = body.concat(scripts);
}
+ return {head: head, body: body};
+};
+
+/**
+ * Injects the assets into the given html string
+ */
+HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function (html, assets, assetTags) {
+ var htmlRegExp = /(]*>)/i;
+ var headRegExp = /(<\/head>)/i;
+ var bodyRegExp = /(<\/body>)/i;
+ var body = assetTags.body.map(this.createHtmlTag);
+ var head = assetTags.head.map(this.createHtmlTag);
if (body.length) {
if (bodyRegExp.test(html)) {
@@ -533,6 +570,18 @@ HtmlWebpackPlugin.prototype.appendHash = function (url, hash) {
return url + (url.indexOf('?') === -1 ? '?' : '&') + hash;
};
+/**
+ * Turn a tag definition into a html string
+ */
+HtmlWebpackPlugin.prototype.createHtmlTag = function (tagDefinition) {
+ var attributes = Object.keys(tagDefinition.attributes || {}).map(function (attributeName) {
+ return attributeName + '="' + tagDefinition.attributes[attributeName] + '"';
+ });
+ return '<' + [tagDefinition.tagName].concat(attributes).join(' ') + (tagDefinition.selfClosingTag ? '/' : '') + '>' +
+ (tagDefinition.innerHTML || '') +
+ (tagDefinition.closeTag ? '' + tagDefinition.tagName + '>' : '');
+};
+
/**
* Helper to return the absolute template path with a fallback loader
*/
diff --git a/package.json b/package.json
index e61e341..e099d15 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "html-webpack-plugin",
- "version": "2.20.0",
+ "version": "2.21.0",
"description": "Simplifies creation of HTML files to serve your webpack bundles",
"main": "index.js",
"files": [
diff --git a/spec/BasicSpec.js b/spec/BasicSpec.js
index 9034081..330bc1d 100644
--- a/spec/BasicSpec.js
+++ b/spec/BasicSpec.js
@@ -20,7 +20,7 @@ var HtmlWebpackPlugin = require('../index.js');
var OUTPUT_DIR = path.join(__dirname, '../dist');
-jasmine.getEnv().defaultTimeoutInterval = 10000;
+jasmine.getEnv().defaultTimeoutInterval = 30000;
function testHtmlPlugin (webpackConfig, expectedResults, outputFile, done, expectErrors, expectWarnings) {
outputFile = outputFile || 'index.html';
@@ -717,6 +717,38 @@ describe('HtmlWebpackPlugin', function () {
['Public path is https://cdn.com'], null, done);
});
+ it('fires the html-webpack-plugin-alter-asset-tags event', function (done) {
+ var eventFired = false;
+ var examplePlugin = {
+ apply: function (compiler) {
+ compiler.plugin('compilation', function (compilation) {
+ compilation.plugin('html-webpack-plugin-alter-asset-tags', function (object, callback) {
+ expect(typeof object.body).toBe('object');
+ expect(typeof object.head).toBe('object');
+ eventFired = true;
+ callback();
+ });
+ });
+ }
+ };
+ testHtmlPlugin({
+ entry: {
+ app: path.join(__dirname, 'fixtures/index.js')
+ },
+ output: {
+ path: OUTPUT_DIR,
+ filename: '[name]_bundle.js'
+ },
+ plugins: [
+ new HtmlWebpackPlugin(),
+ examplePlugin
+ ]
+ }, [], null, function () {
+ expect(eventFired).toBe(true);
+ done();
+ });
+ });
+
it('fires the html-webpack-plugin-before-html-processing event', function (done) {
var eventFired = false;
var examplePlugin = {
diff --git a/spec/CachingSpec.js b/spec/CachingSpec.js
index cc47bdf..7751a50 100644
--- a/spec/CachingSpec.js
+++ b/spec/CachingSpec.js
@@ -18,7 +18,7 @@ var HtmlWebpackPlugin = require('../index.js');
var OUTPUT_DIR = path.join(__dirname, '../dist');
-jasmine.getEnv().defaultTimeoutInterval = 10000;
+jasmine.getEnv().defaultTimeoutInterval = 30000;
function setUpCompiler (htmlWebpackPlugin) {
spyOn(htmlWebpackPlugin, 'evaluateCompilationResult').and.callThrough();
diff --git a/spec/ExampleSpec.js b/spec/ExampleSpec.js
index 98e79a7..29220a2 100644
--- a/spec/ExampleSpec.js
+++ b/spec/ExampleSpec.js
@@ -20,7 +20,7 @@ var webpackMajorVersion = require('webpack/package.json').version.split('.')[0];
var OUTPUT_DIR = path.join(__dirname, '../dist');
-jasmine.getEnv().defaultTimeoutInterval = 10000;
+jasmine.getEnv().defaultTimeoutInterval = 30000;
function runExample (exampleName, done) {
var examplePath = path.resolve(__dirname, '..', 'examples', exampleName);