Skip to content

Commit fa25bd7

Browse files
authored
Merge pull request jantimon#345 from ampedandwired/feature/alter-tags
Add 'html-webpack-plugin-alter-asset-tags'
2 parents 6c516a8 + 86badc3 commit fa25bd7

File tree

7 files changed

+103
-17
lines changed

7 files changed

+103
-17
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
Change History
22
==============
33

4+
v2.21.0
5+
----
6+
* Add `html-webpack-plugin-alter-asset-tags` event to allow plugins to adjust the script/link tags
7+
48
v2.20.0
59
----
610
* Exclude chunks works now even if combined with dependency sort

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ Async:
248248

249249
* `html-webpack-plugin-before-html-generation`
250250
* `html-webpack-plugin-before-html-processing`
251+
* `html-webpack-plugin-alter-asset-tags`
251252
* `html-webpack-plugin-after-html-processing`
252253
* `html-webpack-plugin-after-emit`
253254

index.js

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,15 @@ HtmlWebpackPlugin.prototype.apply = function (compiler) {
143143
});
144144
})
145145
.then(function (html) {
146-
// Add the stylesheets, scripts and so on to the resulting html
147-
return self.postProcessHtml(html, assets);
146+
// Prepare script and link tags
147+
var assetTags = self.generateAssetTags(assets);
148+
var pluginArgs = {head: assetTags.head, body: assetTags.body, plugin: self, chunks: chunks, outputName: self.childCompilationOutputName};
149+
// Allow plugins to change the assetTag definitions
150+
return applyPluginsAsyncWaterfall('html-webpack-plugin-alter-asset-tags', pluginArgs)
151+
.then(function () {
152+
// Add the stylesheets, scripts and so on to the resulting html
153+
return self.postProcessHtml(html, assets, { body: pluginArgs.body, head: pluginArgs.head });
154+
});
148155
})
149156
// Allow plugins to change the html after assets are injected
150157
.then(function (html) {
@@ -259,7 +266,7 @@ HtmlWebpackPlugin.prototype.executeTemplate = function (templateFunction, chunks
259266
*
260267
* Returns a promise
261268
*/
262-
HtmlWebpackPlugin.prototype.postProcessHtml = function (html, assets) {
269+
HtmlWebpackPlugin.prototype.postProcessHtml = function (html, assets, assetTags) {
263270
var self = this;
264271
if (typeof html !== 'string') {
265272
return Promise.reject('Expected html to be a string but got ' + JSON.stringify(html));
@@ -268,7 +275,7 @@ HtmlWebpackPlugin.prototype.postProcessHtml = function (html, assets) {
268275
// Inject
269276
.then(function () {
270277
if (self.options.inject) {
271-
return self.injectAssetsIntoHtml(html, assets);
278+
return self.injectAssetsIntoHtml(html, assets, assetTags);
272279
} else {
273280
return html;
274281
}
@@ -449,27 +456,45 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function (compilation, chu
449456
/**
450457
* Injects the assets into the given html string
451458
*/
452-
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function (html, assets) {
459+
HtmlWebpackPlugin.prototype.generateAssetTags = function (assets) {
453460
// Turn script files into script tags
454461
var scripts = assets.js.map(function (scriptPath) {
455-
return '<script type="text/javascript" src="' + scriptPath + '"></script>';
462+
return {
463+
tagName: 'script',
464+
closeTag: true,
465+
attributes: {
466+
type: 'text/javascript',
467+
src: scriptPath
468+
}
469+
};
456470
});
457471
// Make tags self-closing in case of xhtml
458-
var xhtml = this.options.xhtml ? '/' : '';
472+
var selfClosingTag = !!this.options.xhtml;
459473
// Turn css files into link tags
460474
var styles = assets.css.map(function (stylePath) {
461-
return '<link href="' + stylePath + '" rel="stylesheet"' + xhtml + '>';
475+
return {
476+
tagName: 'link',
477+
selfClosingTag: selfClosingTag,
478+
attributes: {
479+
href: stylePath,
480+
rel: 'stylesheet'
481+
}
482+
};
462483
});
463-
// Injections
464-
var htmlRegExp = /(<html[^>]*>)/i;
484+
// Injection targets
465485
var head = [];
466-
var headRegExp = /(<\/head>)/i;
467486
var body = [];
468-
var bodyRegExp = /(<\/body>)/i;
469487

470488
// If there is a favicon present, add it to the head
471489
if (assets.favicon) {
472-
head.push('<link rel="shortcut icon" href="' + assets.favicon + '"' + xhtml + '>');
490+
head.push({
491+
tagName: 'link',
492+
selfClosingTag: selfClosingTag,
493+
attributes: {
494+
rel: 'shortcut icon',
495+
href: assets.favicon
496+
}
497+
});
473498
}
474499
// Add styles to the head
475500
head = head.concat(styles);
@@ -479,6 +504,18 @@ HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function (html, assets) {
479504
} else {
480505
body = body.concat(scripts);
481506
}
507+
return {head: head, body: body};
508+
};
509+
510+
/**
511+
* Injects the assets into the given html string
512+
*/
513+
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function (html, assets, assetTags) {
514+
var htmlRegExp = /(<html[^>]*>)/i;
515+
var headRegExp = /(<\/head>)/i;
516+
var bodyRegExp = /(<\/body>)/i;
517+
var body = assetTags.body.map(this.createHtmlTag);
518+
var head = assetTags.head.map(this.createHtmlTag);
482519

483520
if (body.length) {
484521
if (bodyRegExp.test(html)) {
@@ -533,6 +570,18 @@ HtmlWebpackPlugin.prototype.appendHash = function (url, hash) {
533570
return url + (url.indexOf('?') === -1 ? '?' : '&') + hash;
534571
};
535572

573+
/**
574+
* Turn a tag definition into a html string
575+
*/
576+
HtmlWebpackPlugin.prototype.createHtmlTag = function (tagDefinition) {
577+
var attributes = Object.keys(tagDefinition.attributes || {}).map(function (attributeName) {
578+
return attributeName + '="' + tagDefinition.attributes[attributeName] + '"';
579+
});
580+
return '<' + [tagDefinition.tagName].concat(attributes).join(' ') + (tagDefinition.selfClosingTag ? '/' : '') + '>' +
581+
(tagDefinition.innerHTML || '') +
582+
(tagDefinition.closeTag ? '</' + tagDefinition.tagName + '>' : '');
583+
};
584+
536585
/**
537586
* Helper to return the absolute template path with a fallback loader
538587
*/

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "html-webpack-plugin",
3-
"version": "2.20.0",
3+
"version": "2.21.0",
44
"description": "Simplifies creation of HTML files to serve your webpack bundles",
55
"main": "index.js",
66
"files": [

spec/BasicSpec.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var HtmlWebpackPlugin = require('../index.js');
2020

2121
var OUTPUT_DIR = path.join(__dirname, '../dist');
2222

23-
jasmine.getEnv().defaultTimeoutInterval = 10000;
23+
jasmine.getEnv().defaultTimeoutInterval = 30000;
2424

2525
function testHtmlPlugin (webpackConfig, expectedResults, outputFile, done, expectErrors, expectWarnings) {
2626
outputFile = outputFile || 'index.html';
@@ -717,6 +717,38 @@ describe('HtmlWebpackPlugin', function () {
717717
['Public path is https://cdn.com'], null, done);
718718
});
719719

720+
it('fires the html-webpack-plugin-alter-asset-tags event', function (done) {
721+
var eventFired = false;
722+
var examplePlugin = {
723+
apply: function (compiler) {
724+
compiler.plugin('compilation', function (compilation) {
725+
compilation.plugin('html-webpack-plugin-alter-asset-tags', function (object, callback) {
726+
expect(typeof object.body).toBe('object');
727+
expect(typeof object.head).toBe('object');
728+
eventFired = true;
729+
callback();
730+
});
731+
});
732+
}
733+
};
734+
testHtmlPlugin({
735+
entry: {
736+
app: path.join(__dirname, 'fixtures/index.js')
737+
},
738+
output: {
739+
path: OUTPUT_DIR,
740+
filename: '[name]_bundle.js'
741+
},
742+
plugins: [
743+
new HtmlWebpackPlugin(),
744+
examplePlugin
745+
]
746+
}, [], null, function () {
747+
expect(eventFired).toBe(true);
748+
done();
749+
});
750+
});
751+
720752
it('fires the html-webpack-plugin-before-html-processing event', function (done) {
721753
var eventFired = false;
722754
var examplePlugin = {

spec/CachingSpec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var HtmlWebpackPlugin = require('../index.js');
1818

1919
var OUTPUT_DIR = path.join(__dirname, '../dist');
2020

21-
jasmine.getEnv().defaultTimeoutInterval = 10000;
21+
jasmine.getEnv().defaultTimeoutInterval = 30000;
2222

2323
function setUpCompiler (htmlWebpackPlugin) {
2424
spyOn(htmlWebpackPlugin, 'evaluateCompilationResult').and.callThrough();

spec/ExampleSpec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var webpackMajorVersion = require('webpack/package.json').version.split('.')[0];
2020

2121
var OUTPUT_DIR = path.join(__dirname, '../dist');
2222

23-
jasmine.getEnv().defaultTimeoutInterval = 10000;
23+
jasmine.getEnv().defaultTimeoutInterval = 30000;
2424

2525
function runExample (exampleName, done) {
2626
var examplePath = path.resolve(__dirname, '..', 'examples', exampleName);

0 commit comments

Comments
 (0)