diff --git a/CHANGELOG.md b/CHANGELOG.md index bd709da..4fe3ae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Change History ============== +v2.16.0 +---- +* Add support for dynamic filenames like index[hash].html + v2.15.0 ---- * Add full unit test coverage for the webpack 2 beta version diff --git a/index.js b/index.js index 762d096..7753670 100644 --- a/index.js +++ b/index.js @@ -48,13 +48,15 @@ HtmlWebpackPlugin.prototype.apply = function (compiler) { .catch(function (err) { compilation.errors.push(prettyError(err, compiler.context).toString()); return { - content: self.options.showErrors ? prettyError(err, compiler.context).toJsonHtml() : 'ERROR' + content: self.options.showErrors ? prettyError(err, compiler.context).toJsonHtml() : 'ERROR', + outputName: self.options.filename }; }) .then(function (compilationResult) { // If the compilation change didnt change the cache is valid isCompilationCached = compilationResult.hash && self.childCompilerHash === compilationResult.hash; self.childCompilerHash = compilationResult.hash; + self.childCompilationOutputName = compilationResult.outputName; callback(); return compilationResult.content; }); @@ -107,10 +109,14 @@ HtmlWebpackPlugin.prototype.apply = function (compiler) { // Allow plugins to make changes to the assets before invoking the template // This only makes sense to use if `inject` is `false` .then(function (compilationResult) { - return applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-generation', {assets: assets, plugin: self}) - .then(function () { - return compilationResult; - }); + return applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-generation', { + assets: assets, + outputName: self.childCompilationOutputName, + plugin: self + }) + .then(function () { + return compilationResult; + }); }) // Execute the template .then(function (compilationResult) { @@ -122,7 +128,7 @@ HtmlWebpackPlugin.prototype.apply = function (compiler) { }) // Allow plugins to change the html before assets are injected .then(function (html) { - var pluginArgs = {html: html, assets: assets, plugin: self}; + var pluginArgs = {html: html, assets: assets, plugin: self, outputName: self.childCompilationOutputName}; return applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-processing', pluginArgs) .then(function () { return pluginArgs.html; @@ -134,7 +140,7 @@ HtmlWebpackPlugin.prototype.apply = function (compiler) { }) // Allow plugins to change the html after assets are injected .then(function (html) { - var pluginArgs = {html: html, assets: assets, plugin: self}; + var pluginArgs = {html: html, assets: assets, plugin: self, outputName: self.childCompilationOutputName}; return applyPluginsAsyncWaterfall('html-webpack-plugin-after-html-processing', pluginArgs) .then(function () { return pluginArgs.html; @@ -150,7 +156,7 @@ HtmlWebpackPlugin.prototype.apply = function (compiler) { }) .then(function (html) { // Replace the compilation result with the evaluated html code - compilation.assets[self.options.filename] = { + compilation.assets[self.childCompilationOutputName] = { source: function () { return html; }, @@ -162,8 +168,14 @@ HtmlWebpackPlugin.prototype.apply = function (compiler) { .then(function () { // Let other plugins know that we are done: return applyPluginsAsyncWaterfall('html-webpack-plugin-after-emit', { - html: compilation.assets[self.options.filename], + html: compilation.assets[self.childCompilationOutputName], + outputName: self.childCompilationOutputName, plugin: self + }).catch(function (err) { + console.error(err); + return null; + }).then(function () { + return null; }); }) // Let webpack continue with it @@ -349,7 +361,7 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function (compilation, chu // If a hard coded public path exists use it ? compilation.mainTemplate.getPublicPath({hash: webpackStatsJson.hash}) // If no public path was set get a relative url path - : path.relative(path.resolve(compilation.options.output.path, path.dirname(self.options.filename)), compilation.options.output.path) + : path.relative(path.resolve(compilation.options.output.path, path.dirname(self.childCompilationOutputName)), compilation.options.output.path) .split(path.sep).join('/'); if (publicPath.length && publicPath.substr(-1, 1) !== '/') { diff --git a/lib/compiler.js b/lib/compiler.js index 1577830..326384e 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -6,6 +6,7 @@ */ 'use strict'; var Promise = require('bluebird'); +var _ = require('lodash'); var path = require('path'); var NodeTemplatePlugin = require('webpack/lib/node/NodeTemplatePlugin'); var NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin'); @@ -36,7 +37,8 @@ module.exports.compileTemplate = function compileTemplate (template, context, ou filename: outputFilename, publicPath: compilation.outputOptions.publicPath }; - var cachedAsset = compilation.assets[outputOptions.filename]; + // Store the result of the parent compilation before we start the child compilation + var assetsBeforeCompilation = _.assign({}, compilation.assets[outputOptions.filename]); // Create an additional child compiler which takes the template // and turns it into an Node.JS html factory. // This allows us to use loaders during the compilation @@ -66,9 +68,17 @@ module.exports.compileTemplate = function compileTemplate (template, context, ou // Compile and return a promise return new Promise(function (resolve, reject) { childCompiler.runAsChild(function (err, entries, childCompilation) { - compilation.assets[outputOptions.filename] = cachedAsset; - if (cachedAsset === undefined) { - delete compilation.assets[outputOptions.filename]; + // Replace [hash] placeholders in filename + var outputName = compilation.mainTemplate.applyPluginsWaterfall('asset-path', outputOptions.filename, { + hash: childCompilation.hash, + chunk: entries[0] + }); + // Restore the parent compilation to the state like it + // was before the child compilation + compilation.assets[outputName] = assetsBeforeCompilation[outputName]; + if (assetsBeforeCompilation[outputName] === undefined) { + // If it wasn't there - delete it + delete compilation.assets[outputName]; } // Resolve / reject the promise if (childCompilation && childCompilation.errors && childCompilation.errors.length) { @@ -82,8 +92,10 @@ module.exports.compileTemplate = function compileTemplate (template, context, ou resolve({ // Hash of the template entry point hash: entries[0].hash, + // Output name + outputName: outputName, // Compiled code - content: childCompilation.assets[outputOptions.filename].source() + content: childCompilation.assets[outputName].source() }); } }); diff --git a/package.json b/package.json index c67c5c0..b8854bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "html-webpack-plugin", - "version": "2.15.0", + "version": "2.16.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 fc70fd0..bf8de01 100644 --- a/spec/BasicSpec.js +++ b/spec/BasicSpec.js @@ -38,6 +38,14 @@ function testHtmlPlugin (webpackConfig, expectedResults, outputFile, done, expec } else { expect(compilationWarnings).toBe(''); } + if (outputFile instanceof RegExp) { + var matches = Object.keys(stats.compilation.assets).filter(function (item) { + return outputFile.test(item); + }); + expect(matches.length).toBe(1); + outputFile = matches[0]; + } + expect(outputFile.indexOf('[hash]') === -1).toBe(true); var outputFileExists = fs.existsSync(path.join(OUTPUT_DIR, outputFile)); expect(outputFileExists).toBe(true); if (!outputFileExists) { @@ -573,6 +581,19 @@ describe('HtmlWebpackPlugin', function () { }, ['