diff --git a/README.md b/README.md
index 1ebc199..c7765f3 100644
--- a/README.md
+++ b/README.md
@@ -154,7 +154,7 @@ plugins: [
]
```
-The `templateContent` option can also be a function:
+The `templateContent` option can also be a function to use another template language like jade:
```javascript
plugins: [
new HtmlWebpackPlugin({
@@ -206,3 +206,28 @@ template is rendered. This variable has the following attributes:
- `webpackConfig`: the webpack configuration that was used for this compilation. This
can be used, for example, to get the `publicPath` (`webpackConfig.output.publicPath`).
+
+
+Filtering chunks
+----------------
+
+To include only certain chunks you can limit the chunks being used:
+
+```javascript
+plugins: [
+ new HtmlWebpackPlugin({
+ chunks: ['app']
+ })
+]
+```
+
+It is also possible to exclude certain chunks by setting the `excludeChunks` option:
+
+```javascript
+plugins: [
+ new HtmlWebpackPlugin({
+ excludeChunks: ['dev-helper']
+ })
+]
+```
+
diff --git a/default_inject_index.html b/default_inject_index.html
new file mode 100644
index 0000000..6967979
--- /dev/null
+++ b/default_inject_index.html
@@ -0,0 +1,9 @@
+
+
+
+
+ {%=o.htmlWebpackPlugin.options.title || 'Webpack App'%}
+
+
+
+
diff --git a/index.js b/index.js
index 6ec4754..e9245da 100644
--- a/index.js
+++ b/index.js
@@ -16,7 +16,7 @@ HtmlWebpackPlugin.prototype.apply = function(compiler) {
templateParams.webpack = webpackStatsJson;
templateParams.htmlWebpackPlugin = {};
templateParams.htmlWebpackPlugin.assets = self.htmlWebpackPluginLegacyAssets(compilation, webpackStatsJson);
- templateParams.htmlWebpackPlugin.files = self.htmlWebpackPluginAssets(compilation, webpackStatsJson, self.options.hash);
+ templateParams.htmlWebpackPlugin.files = self.htmlWebpackPluginAssets(compilation, webpackStatsJson, self.options.chunks, self.options.excludeChunks);
templateParams.htmlWebpackPlugin.options = self.options;
templateParams.webpackConfig = compilation.options;
@@ -32,7 +32,8 @@ HtmlWebpackPlugin.prototype.apply = function(compiler) {
} else {
var templateFile = self.options.template;
if (!templateFile) {
- templateFile = path.join(__dirname, 'default_index.html');
+ // Use a special index file to prevent double script / style injection if the `inject` option is truthy
+ templateFile = path.join(__dirname, self.options.inject ? 'default_inject_index.html' : 'default_index.html');
}
compilation.fileDependencies.push(templateFile);
@@ -55,6 +56,10 @@ HtmlWebpackPlugin.prototype.emitHtml = function(compilation, htmlTemplateContent
} catch(e) {
compilation.errors.push(new Error('HtmlWebpackPlugin: template error ' + e));
}
+ // Inject link and script elements into an existing html file
+ if (this.options.inject) {
+ html = this.injectAssetsIntoHtml(html, templateParams);
+ }
compilation.assets[outputFilename] = {
source: function() {
return html;
@@ -66,9 +71,9 @@ HtmlWebpackPlugin.prototype.emitHtml = function(compilation, htmlTemplateContent
};
-HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webpackStatsJson, appendHash) {
+HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webpackStatsJson, includedChunks, excludedChunks) {
+ var self = this;
var publicPath = compilation.options.output.publicPath || '';
- var queryString = appendHash ? '?' + webpackStatsJson.hash : '';
var assets = {
// Will contain all js & css files by chunk
@@ -80,11 +85,14 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webp
// Will contain the html5 appcache manifest files if it exists
manifest: Object.keys(compilation.assets).filter(function(assetFile){
return path.extname(assetFile) === '.appcache';
- }).map(function(assetFile) {
- return assetFile + queryString;
})[0]
};
+ // Append a hash for cache busting
+ if (this.options.hash) {
+ assets.manifest = self.appendHash(assets.manifest, webpackStatsJson.hash);
+ }
+
var chunks = webpackStatsJson.chunks.sort(function orderEntryLast(a, b) {
if (a.entry !== b.entry) {
return b.entry ? 1 : -1;
@@ -96,13 +104,30 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webp
for (var i = 0; i < chunks.length; i++) {
var chunk = chunks[i];
var chunkName = chunk.names[0];
+
+ // Skip if the chunks should be filtered and the given chunk was not added explicity
+ if (Array.isArray(includedChunks) && includedChunks.indexOf(chunkName) === -1) {
+ continue;
+ }
+ // Skip if the chunks should be filtered and the given chunk was excluded explicity
+ if (Array.isArray(excludedChunks) && excludedChunks.indexOf(chunkName) !== -1) {
+ continue;
+ }
+
assets.chunks[chunkName] = {};
// Prepend the public path to all chunk files
var chunkFiles = [].concat(chunk.files).map(function(chunkFile) {
- return publicPath + chunkFile + queryString;
+ return publicPath + chunkFile;
});
+ // Append a hash for cache busting
+ if (this.options.hash) {
+ chunkFiles = chunkFiles.map(function(chunkFile) {
+ return self.appendHash(chunkFile, webpackStatsJson.hash);
+ });
+ }
+
// Webpack outputs an array for each chunk when using sourcemaps
// But we need only the entry file
var entry = chunkFiles[0];
@@ -111,7 +136,9 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webp
// Gather all css files
var css = chunkFiles.filter(function(chunkFile){
- return path.extname(chunkFile) === '.css';
+ // Some chunks may contain content hash in their names, for ex. 'main.css?1e7cac4e4d8b52fd5ccd2541146ef03f'.
+ // We must proper handle such cases, so we use regexp testing here
+ return /^.css($|\?)/.test(path.extname(chunkFile));
});
assets.chunks[chunkName].css = css;
assets.css = assets.css.concat(css);
@@ -124,6 +151,49 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webp
return assets;
};
+/**
+ * Injects the assets into the given html string
+ */
+HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, templateParams) {
+ var assets = templateParams.htmlWebpackPlugin.files;
+ var chunks = Object.keys(assets.chunks);
+
+ // Gather all css and script files
+ var styles = [];
+ var scripts = [];
+ chunks.forEach(function(chunkName) {
+ styles = styles.concat(assets.chunks[chunkName].css);
+ scripts.push(assets.chunks[chunkName].entry);
+ });
+ // Turn script files into script tags
+ scripts = scripts.map(function(scriptPath) {
+ return '';
+ });
+ // Turn css files into link tags
+ styles = styles.map(function(stylePath) {
+ return '';
+ });
+ // Append scripts to body element
+ html = html.replace(/(<\/body>)/i, function (match) {
+ return scripts.join('') + match;
+ });
+ // Append styles to head element
+ html = html.replace(/(<\/head>)/i, function (match) {
+ return styles.join('') + match;
+ });
+ // Inject manifest into the opening html tag
+ if (assets.manifest) {
+ html = html.replace(/()/i, function (match, start, end) {
+ // Append the manifest only if no manifest was specified
+ if (match.test(/\smanifest\s*=/)) {
+ return match;
+ }
+ return start + ' manifest="' + assets.manifest + '"' + end;
+ });
+ }
+ return html;
+};
+
/**
* A helper to support the templates written for html-webpack-plugin <= 1.1.0
*/
@@ -136,6 +206,15 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginLegacyAssets = function(compilation
return legacyAssets;
};
+/**
+ * Appends a cache busting hash
+ */
+HtmlWebpackPlugin.prototype.appendHash = function (url, hash) {
+ if (!url) {
+ return url;
+ }
+ return url + (url.indexOf('?') === -1 ? '?' : '&') + hash;
+};
module.exports = HtmlWebpackPlugin;
diff --git a/package.json b/package.json
index 9398f4d..9205c6f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "html-webpack-plugin",
- "version": "1.2.0",
+ "version": "1.3.0",
"description": "Simplifies creation of HTML files to serve your webpack bundles",
"main": "index.js",
"scripts": {
diff --git a/spec/HtmlWebpackPluginSpec.js b/spec/HtmlWebpackPluginSpec.js
index 0bea99b..d1cb91e 100644
--- a/spec/HtmlWebpackPluginSpec.js
+++ b/spec/HtmlWebpackPluginSpec.js
@@ -85,6 +85,77 @@ describe('HtmlWebpackPlugin', function() {
['