Skip to content

Commit

Permalink
refactor(childcompiler): Move fileTimeStampCache out of childCompiler
Browse files Browse the repository at this point in the history
  • Loading branch information
jantimon committed Jul 16, 2018
1 parent c1c50b2 commit cef1502
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 48 deletions.
107 changes: 60 additions & 47 deletions lib/compiler.js
Expand Up @@ -45,11 +45,6 @@ class HtmlWebpackChildCompiler {
* @type {string[]}
*/
this.fileDependencies = [];
/**
* Store if the cache was already verified for the given compilation
* @type {WeakMap<WebpackCompilation, boolean>}}
*/
this.cacheVerifiedForCompilation = new WeakMap();
}

/**
Expand Down Expand Up @@ -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;
}
}

/**
Expand Down Expand Up @@ -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
*
Expand All @@ -361,6 +314,66 @@ function getFileDependencies (compiler) {
return childCompiler.fileDependencies;
}

/**
* @type {WeakMap<WebpackCompilation, WeakMap<HtmlWebpackChildCompiler, boolean>>}}
*/
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<HtmlWebpackChildCompiler, boolean>|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,
Expand Down
41 changes: 40 additions & 1 deletion spec/caching.spec.js
Expand Up @@ -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;
Expand Down Expand Up @@ -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');
Expand Down

0 comments on commit cef1502

Please sign in to comment.