diff --git a/lib/compiler.js b/lib/compiler.js index fa099bb2..31878d46 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -45,11 +45,6 @@ class HtmlWebpackChildCompiler { * @type {string[]} */ this.fileDependencies = []; - /** - * Store if the cache was already verified for the given compilation - * @type {WeakMap}} - */ - this.cacheVerifiedForCompilation = new WeakMap(); } /** @@ -184,37 +179,6 @@ class HtmlWebpackChildCompiler { return this.compilationPromise; } - - /** - * Returns `false` if any template file depenendencies has changed - * for the given main compilation - * - * @param {WebpackCompilation} mainCompilation - * @returns {boolean} - */ - hasOutDatedTemplateCache (mainCompilation) { - // Check if cache validation was already computed - const isCacheValid = this.cacheVerifiedForCompilation.get(mainCompilation); - if (isCacheValid !== undefined) { - return isCacheValid; - } - // If the compilation was never run there is no invalid cache - if (!this.compilationStartedTimestamp) { - this.cacheVerifiedForCompilation.set(mainCompilation, false); - return false; - } - // Check if any dependent file was changed after the last compilation - const fileTimestamps = mainCompilation.fileTimestamps; - const isCacheOutOfDate = this.fileDependencies.some((fileDependency) => { - const timestamp = fileTimestamps.get(fileDependency); - // If the timestamp is not known the file is new - // If the timestamp is larger then the file has changed - // Otherwise the file is still the same - return !timestamp || timestamp > this.compilationStartedTimestamp; - }); - this.cacheVerifiedForCompilation.set(mainCompilation, isCacheOutOfDate); - return isCacheOutOfDate; - } } /** @@ -339,17 +303,6 @@ function compileTemplate (templatePath, outputFilename, mainCompilation) { }); } -/** - * Returns false if the cache is not valid anymore - * - * @param {WebpackCompilation} compilation - * @returns {boolean} - */ -function hasOutDatedTemplateCache (compilation) { - const childCompiler = childCompilerCache.get(compilation.compiler); - return childCompiler ? childCompiler.hasOutDatedTemplateCache(compilation) : false; -} - /** * Return all file dependencies of the last child compilation * @@ -361,6 +314,66 @@ function getFileDependencies (compiler) { return childCompiler.fileDependencies; } +/** + * @type {WeakMap>}} + */ +const hasOutdatedCompilationDependenciesMap = new WeakMap(); +/** + * Returns `true` if the file dependencies of the current childCompiler + * for the given mainCompilation are outdated. + * + * Uses the `hasOutdatedCompilationDependenciesMap` cache if possible. + * + * @param {WebpackCompilation} mainCompilation + * @returns {boolean} + */ +function hasOutDatedTemplateCache (mainCompilation) { + const childCompiler = getChildCompiler(mainCompilation.compiler); + /** + * @type {WeakMap|undefined} + */ + let hasOutdatedChildCompilerDependenciesMap = hasOutdatedCompilationDependenciesMap.get(mainCompilation); + // Create map for childCompiler if none exist + if (!hasOutdatedChildCompilerDependenciesMap) { + hasOutdatedChildCompilerDependenciesMap = new WeakMap(); + hasOutdatedCompilationDependenciesMap.set(mainCompilation, hasOutdatedChildCompilerDependenciesMap); + } + // Try to get the `checkChildCompilerCache` result from cache + let isOutdated = hasOutdatedChildCompilerDependenciesMap.get(childCompiler); + if (isOutdated !== undefined) { + return isOutdated; + } + // If `checkChildCompilerCache` has never been called for the given + // `mainCompilation` and `childCompiler` combination call it: + isOutdated = isChildCompilerCacheOutdated(mainCompilation, childCompiler); + hasOutdatedChildCompilerDependenciesMap.set(childCompiler, isOutdated); + return isOutdated; +} + +/** + * Returns `true` if the file dependencies of the given childCompiler are outdated. + * + * @param {WebpackCompilation} mainCompilation + * @param {HtmlWebpackChildCompiler} childCompiler + * @returns {boolean} + */ +function isChildCompilerCacheOutdated (mainCompilation, childCompiler) { + // If the compilation was never run there is no invalid cache + if (!childCompiler.compilationStartedTimestamp) { + return false; + } + // Check if any dependent file was changed after the last compilation + const fileTimestamps = mainCompilation.fileTimestamps; + const isCacheOutOfDate = childCompiler.fileDependencies.some((fileDependency) => { + const timestamp = fileTimestamps.get(fileDependency); + // If the timestamp is not known the file is new + // If the timestamp is larger then the file has changed + // Otherwise the file is still the same + return !timestamp || timestamp > childCompiler.compilationStartedTimestamp; + }); + return isCacheOutOfDate; +} + module.exports = { addTemplateToCompiler, compileTemplate, diff --git a/spec/caching.spec.js b/spec/caching.spec.js index ee584c30..e6016060 100644 --- a/spec/caching.spec.js +++ b/spec/caching.spec.js @@ -39,7 +39,7 @@ function setUpCompiler (htmlWebpackPlugin) { path: OUTPUT_DIR, filename: 'index_bundle.js' }, - plugins: [htmlWebpackPlugin] + plugins: Array.from(arguments) }; const compiler = new WebpackRecompilationSimulator(webpack(webpackConfig)); return compiler; @@ -202,6 +202,45 @@ describe('HtmlWebpackPluginCaching', () => { .then(done); }); + it('should not slow down linear (15 plugins should not take twice as much time as a 1 plugin)', done => { + const template = path.join(__dirname, 'fixtures/plain.html'); + const createHtmlWebpackPlugin = () => new HtmlWebpackPlugin({ + template: template + }); + let singlePluginCompileStart; + let singleCompileRunDuration; + let multiPluginComileStart; + let multiCompileRunDuration; + + let singleCompiler = setUpCompiler(createHtmlWebpackPlugin()); + let multiCompiler = setUpCompiler.apply(null, Array(15).fill(0).map(() => createHtmlWebpackPlugin())); + + Promise.resolve() + .then(function singleCompileRun () { + singlePluginCompileStart = process.hrtime(); + return singleCompiler.run() + // Change the template file and compile again + .then(() => { + singleCompileRunDuration = process.hrtime(singlePluginCompileStart); + }); + }) + .then(function multiCompileRun () { + multiPluginComileStart = process.hrtime(); + return multiCompiler.run() + // Change the template file and compile again + .then(() => { + multiCompileRunDuration = process.hrtime(multiPluginComileStart); + }); + }).then(function meassureTime () { + const singleCompileRunDurationInNs = singleCompileRunDuration[0] * 1e9 + singleCompileRunDuration[1]; + const multiCompileRunDurationInNs = multiCompileRunDuration[0] * 1e9 + multiCompileRunDuration[1]; + const speedComarision = multiCompileRunDurationInNs / singleCompileRunDurationInNs * 100; + + expect(speedComarision).toBeLessThan(200); + done(); + }); + }); + it('should keep watching the webpack html if only a js file was changed', done => { const template = path.join(__dirname, 'fixtures/plain.html'); const jsFile = path.join(__dirname, 'fixtures/index.js');