add caching to FlagDependencyExportsPlugin

This commit is contained in:
Tobias Koppers 2018-12-30 14:54:47 +01:00
parent 4eecad3a83
commit b5db48291b
7 changed files with 228 additions and 155 deletions

2
declarations.d.ts vendored
View File

@ -11,7 +11,7 @@ declare module "neo-async" {
export interface Dictionary<T> {
[key: string]: T;
}
export type IterableCollection<T> = T[] | IterableIterator<T> | Dictionary<T>;
export type IterableCollection<T> = T[] | Iterable<T> | Dictionary<T>;
export interface ErrorCallback<T> {
(err?: T): void;

View File

@ -262,10 +262,10 @@ class Compilation {
"module"
]),
/** @type {SyncHook<Iterable<Module>>} */
finishModules: new SyncHook(["modules"]),
/** @type {SyncHook<Module>} */
finishRebuildingModule: new SyncHook(["module"]),
/** @type {AsyncSeriesHook<Iterable<Module>>} */
finishModules: new AsyncSeriesHook(["modules"]),
/** @type {AsyncSeriesHook<Module>} */
finishRebuildingModule: new AsyncSeriesHook(["module"]),
/** @type {SyncHook} */
unseal: new SyncHook([]),
/** @type {SyncHook} */
@ -1115,8 +1115,10 @@ class Compilation {
this.buildQueue.invalidate(module);
this.buildModule(module, err => {
if (err) {
this.hooks.finishRebuildingModule.call(module);
return this.hooks.finishRebuildingModule.callAsync(module, err2 => {
if (err2) return callback(err2);
return callback(err);
});
}
this.processModuleDependencies(module, err => {
@ -1125,15 +1127,15 @@ class Compilation {
dependencies: oldDependencies,
blocks: oldBlocks
});
this.hooks.finishRebuildingModule.call(module);
callback();
this.hooks.finishRebuildingModule.callAsync(module, callback);
});
});
}
finish() {
finish(callback) {
const modules = this.modules;
this.hooks.finishModules.call(modules);
this.hooks.finishModules.callAsync(modules, err => {
if (err) return callback(err);
// extract warnings and errors from modules
for (const module of modules) {
@ -1160,6 +1162,9 @@ class Compilation {
this.warnings.push(warning);
}
}
callback();
});
}
unseal() {

View File

@ -569,7 +569,8 @@ class Compiler {
this.hooks.make.callAsync(compilation, err => {
if (err) return callback(err);
compilation.finish();
compilation.finish(err => {
if (err) return callback(err);
compilation.seal(err => {
if (err) return callback(err);
@ -582,6 +583,7 @@ class Compiler {
});
});
});
});
}
/**

View File

@ -5,6 +5,7 @@
"use strict";
const asyncLib = require("neo-async");
const Queue = require("./util/Queue");
/** @typedef {import("./Compiler")} Compiler */
@ -23,6 +24,12 @@ const addToSet = (a, b) => {
return changed;
};
const getCacheIdentifier = (compilation, module) => {
return `${
compilation.compilerPath
}/FlagDependencyExportsPlugin/${module.identifier()}`;
};
class FlagDependencyExportsPlugin {
/**
* @param {Compiler} compiler the compiler instance
@ -33,20 +40,54 @@ class FlagDependencyExportsPlugin {
"FlagDependencyExportsPlugin",
compilation => {
const moduleGraph = compilation.moduleGraph;
compilation.hooks.finishModules.tap(
compilation.hooks.finishModules.tapAsync(
"FlagDependencyExportsPlugin",
modules => {
/** @type {Map<Module, Set<Module>>} */
const dependencies = new Map();
(modules, callback) => {
/** @type {Queue<Module>} */
const queue = new Queue();
/** @type {WeakSet<Module>} */
const temporaryProvidedExports = new WeakSet();
// Step 1: Try to restore cached provided export info from cache
asyncLib.each(
modules,
(module, callback) => {
if (
module.buildInfo.cacheable !== true ||
typeof module.buildInfo.hash !== "string"
) {
// Enqueue uncacheable module for determining the exports
queue.enqueue(module);
return callback();
}
compilation.cache.get(
getCacheIdentifier(compilation, module),
module.buildInfo.hash,
(err, result) => {
if (err) return callback(err);
if (result !== undefined) {
moduleGraph.setProvidedExports(module, result);
} else {
// Without cached info enqueue module for determining the exports
queue.enqueue(module);
}
callback();
}
);
},
err => {
if (err) return callback(err);
/** @type {Set<Module>} */
const modulesToStore = new Set();
/** @type {Map<Module, Set<Module>>} */
const dependencies = new Map();
/** @type {Module} */
let module;
let moduleWithExports;
/** @type {Set<string> | true} */
let moduleProvidedExports;
let providedExportsAreTemporary = false;
let providedExportsAreCacheable = true;
/**
* @param {DependenciesBlock} depBlock the dependencies block
@ -73,15 +114,14 @@ class FlagDependencyExportsPlugin {
const processDependency = dep => {
const exportDesc = dep.getExports(moduleGraph);
if (!exportDesc) return;
moduleWithExports = true;
const exports = exportDesc.exports;
// break early if it's only in the worst state
if (moduleGraph.getProvidedExports(module) === true) {
if (moduleProvidedExports === true) {
return true;
}
// break if it should move to the worst state
if (exports === true) {
moduleGraph.setProvidedExports(module, true);
moduleProvidedExports = true;
notifyDependencies();
return true;
}
@ -94,7 +134,7 @@ class FlagDependencyExportsPlugin {
// store dependencies
const exportDeps = exportDesc.dependencies;
if (exportDeps) {
providedExportsAreTemporary = true;
providedExportsAreCacheable = false;
for (const exportDependency of exportDeps) {
// add dependency for this module
const set = dependencies.get(exportDependency);
@ -117,48 +157,64 @@ class FlagDependencyExportsPlugin {
}
};
// Start with all modules without provided exports
for (const module of modules) {
if (temporaryProvidedExports.has(module)) {
// Clear exports when they are temporary and recreate them
moduleGraph.setProvidedExports(module, null);
queue.enqueue(module);
} else if (!moduleGraph.getProvidedExports(module)) {
queue.enqueue(module);
}
}
while (queue.length > 0) {
module = queue.dequeue();
const providedExports = moduleGraph.getProvidedExports(module);
const providedExports = moduleGraph.getProvidedExports(
module
);
if (providedExports !== true) {
moduleWithExports =
module.buildMeta && module.buildMeta.exportsType;
moduleProvidedExports = Array.isArray(providedExports)
if (!module.buildMeta || !module.buildMeta.exportsType) {
// It's a module without declared exports
moduleGraph.setProvidedExports(module, true);
modulesToStore.add(module);
notifyDependencies();
} else {
// It's a module with declared exports
moduleProvidedExports = /** @type {Set<string> | true} */ (Array.isArray(
providedExports
)
? new Set(providedExports)
: new Set();
providedExportsAreTemporary = false;
: new Set());
providedExportsAreCacheable = true;
processDependenciesBlock(module);
if (providedExportsAreTemporary) {
temporaryProvidedExports.add(module);
} else {
temporaryProvidedExports.delete(module);
if (providedExportsAreCacheable) {
modulesToStore.add(module);
}
if (!moduleWithExports) {
moduleGraph.setProvidedExports(module, true);
notifyDependencies();
} else if (moduleGraph.getProvidedExports(module) !== true) {
moduleGraph.setProvidedExports(
module,
Array.from(moduleProvidedExports)
moduleProvidedExports === true
? true
: Array.from(moduleProvidedExports)
);
}
}
}
asyncLib.each(
modulesToStore,
(module, callback) => {
if (
module.buildInfo.cacheable !== true ||
typeof module.buildInfo.hash !== "string"
) {
// not cacheable
return callback();
}
compilation.cache.store(
getCacheIdentifier(compilation, module),
module.buildInfo.hash,
moduleGraph.getProvidedExports(module),
callback
);
},
callback
);
}
);
}
);

View File

@ -56,7 +56,7 @@ module.exports = function checkArrayExpectation(
return (
done(
new Error(
`More ${kind}s while compiling than expected:\n\n${array.join(
`More ${kind}s while compiling than expected:\n\n${array.map(explain).join(
"\n\n"
)}. Check expected ${kind}s: ${expectedFilename}`
)
@ -67,7 +67,7 @@ module.exports = function checkArrayExpectation(
return (
done(
new Error(
`Less ${kind}s while compiling than expected:\n\n${array.join(
`Less ${kind}s while compiling than expected:\n\n${array.map(explain).join(
"\n\n"
)}. Check expected ${kind}s: ${expectedFilename}`
)

View File

@ -0,0 +1,5 @@
module.exports = [
[/export 'default' \(imported as 'a'\) was not found in '\.\/loader!\.\/a'/],
[/export 'default' \(imported as 'a'\) was not found in '\.\/loader!\.\/a'/],
[/export 'default' \(imported as 'a'\) was not found in '\.\/loader!\.\/a'/]
]

View File

@ -0,0 +1,5 @@
module.exports = [
[/export 'default' \(imported as 'a'\) was not found in '\.\/a'/],
[/export 'default' \(imported as 'a'\) was not found in '\.\/a'/],
[/export 'default' \(imported as 'a'\) was not found in '\.\/a'/]
]