mirror of https://github.com/webpack/webpack.git
add caching to FlagDependencyExportsPlugin
This commit is contained in:
parent
4eecad3a83
commit
b5db48291b
|
@ -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;
|
||||
|
|
|
@ -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 callback(err);
|
||||
return this.hooks.finishRebuildingModule.callAsync(module, err2 => {
|
||||
if (err2) return callback(err2);
|
||||
return callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
this.processModuleDependencies(module, err => {
|
||||
|
@ -1125,41 +1127,44 @@ 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) {
|
||||
this.reportDependencyErrorsAndWarnings(module, [module]);
|
||||
if (module.isOptional(this.moduleGraph)) {
|
||||
for (const error of module.errors) {
|
||||
if (!error.module) {
|
||||
error.module = module;
|
||||
// extract warnings and errors from modules
|
||||
for (const module of modules) {
|
||||
this.reportDependencyErrorsAndWarnings(module, [module]);
|
||||
if (module.isOptional(this.moduleGraph)) {
|
||||
for (const error of module.errors) {
|
||||
if (!error.module) {
|
||||
error.module = module;
|
||||
}
|
||||
this.warnings.push(error);
|
||||
}
|
||||
} else {
|
||||
for (const error of module.errors) {
|
||||
if (!error.module) {
|
||||
error.module = module;
|
||||
}
|
||||
this.errors.push(error);
|
||||
}
|
||||
this.warnings.push(error);
|
||||
}
|
||||
} else {
|
||||
for (const error of module.errors) {
|
||||
if (!error.module) {
|
||||
error.module = module;
|
||||
for (const warning of module.warnings) {
|
||||
if (!warning.module) {
|
||||
warning.module = module;
|
||||
}
|
||||
this.errors.push(error);
|
||||
this.warnings.push(warning);
|
||||
}
|
||||
}
|
||||
for (const warning of module.warnings) {
|
||||
if (!warning.module) {
|
||||
warning.module = module;
|
||||
}
|
||||
this.warnings.push(warning);
|
||||
}
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
unseal() {
|
||||
|
|
|
@ -569,15 +569,17 @@ class Compiler {
|
|||
this.hooks.make.callAsync(compilation, err => {
|
||||
if (err) return callback(err);
|
||||
|
||||
compilation.finish();
|
||||
|
||||
compilation.seal(err => {
|
||||
compilation.finish(err => {
|
||||
if (err) return callback(err);
|
||||
|
||||
this.hooks.afterCompile.callAsync(compilation, err => {
|
||||
compilation.seal(err => {
|
||||
if (err) return callback(err);
|
||||
|
||||
return callback(null, compilation);
|
||||
this.hooks.afterCompile.callAsync(compilation, err => {
|
||||
if (err) return callback(err);
|
||||
|
||||
return callback(null, compilation);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,132 +40,181 @@ 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();
|
||||
|
||||
let module;
|
||||
let moduleWithExports;
|
||||
let moduleProvidedExports;
|
||||
let providedExportsAreTemporary = false;
|
||||
// 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);
|
||||
|
||||
/**
|
||||
* @param {DependenciesBlock} depBlock the dependencies block
|
||||
* @returns {boolean} true if the traversal must be continued
|
||||
*/
|
||||
const processDependenciesBlock = depBlock => {
|
||||
for (const dep of depBlock.dependencies) {
|
||||
if (processDependency(dep)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (const block of depBlock.blocks) {
|
||||
if (processDependenciesBlock(block)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
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);
|
||||
|
||||
/**
|
||||
* @param {Dependency} dep the dependency
|
||||
* @returns {boolean} true if the traversal must be continued
|
||||
*/
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
// break if it should move to the worst state
|
||||
if (exports === true) {
|
||||
moduleGraph.setProvidedExports(module, true);
|
||||
notifyDependencies();
|
||||
return true;
|
||||
}
|
||||
// merge in new exports
|
||||
if (Array.isArray(exports)) {
|
||||
if (addToSet(moduleProvidedExports, exports)) {
|
||||
notifyDependencies();
|
||||
}
|
||||
}
|
||||
// store dependencies
|
||||
const exportDeps = exportDesc.dependencies;
|
||||
if (exportDeps) {
|
||||
providedExportsAreTemporary = true;
|
||||
for (const exportDependency of exportDeps) {
|
||||
// add dependency for this module
|
||||
const set = dependencies.get(exportDependency);
|
||||
if (set === undefined) {
|
||||
dependencies.set(exportDependency, new Set([module]));
|
||||
} else {
|
||||
set.add(module);
|
||||
/** @type {Set<Module>} */
|
||||
const modulesToStore = new Set();
|
||||
|
||||
/** @type {Map<Module, Set<Module>>} */
|
||||
const dependencies = new Map();
|
||||
|
||||
/** @type {Module} */
|
||||
let module;
|
||||
/** @type {Set<string> | true} */
|
||||
let moduleProvidedExports;
|
||||
let providedExportsAreCacheable = true;
|
||||
|
||||
/**
|
||||
* @param {DependenciesBlock} depBlock the dependencies block
|
||||
* @returns {boolean} true if the traversal must be continued
|
||||
*/
|
||||
const processDependenciesBlock = depBlock => {
|
||||
for (const dep of depBlock.dependencies) {
|
||||
if (processDependency(dep)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (const block of depBlock.blocks) {
|
||||
if (processDependenciesBlock(block)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Dependency} dep the dependency
|
||||
* @returns {boolean} true if the traversal must be continued
|
||||
*/
|
||||
const processDependency = dep => {
|
||||
const exportDesc = dep.getExports(moduleGraph);
|
||||
if (!exportDesc) return;
|
||||
const exports = exportDesc.exports;
|
||||
// break early if it's only in the worst state
|
||||
if (moduleProvidedExports === true) {
|
||||
return true;
|
||||
}
|
||||
// break if it should move to the worst state
|
||||
if (exports === true) {
|
||||
moduleProvidedExports = true;
|
||||
notifyDependencies();
|
||||
return true;
|
||||
}
|
||||
// merge in new exports
|
||||
if (Array.isArray(exports)) {
|
||||
if (addToSet(moduleProvidedExports, exports)) {
|
||||
notifyDependencies();
|
||||
}
|
||||
}
|
||||
// store dependencies
|
||||
const exportDeps = exportDesc.dependencies;
|
||||
if (exportDeps) {
|
||||
providedExportsAreCacheable = false;
|
||||
for (const exportDependency of exportDeps) {
|
||||
// add dependency for this module
|
||||
const set = dependencies.get(exportDependency);
|
||||
if (set === undefined) {
|
||||
dependencies.set(exportDependency, new Set([module]));
|
||||
} else {
|
||||
set.add(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const notifyDependencies = () => {
|
||||
const deps = dependencies.get(module);
|
||||
if (deps !== undefined) {
|
||||
for (const dep of deps) {
|
||||
queue.enqueue(dep);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
while (queue.length > 0) {
|
||||
module = queue.dequeue();
|
||||
|
||||
const providedExports = moduleGraph.getProvidedExports(
|
||||
module
|
||||
);
|
||||
if (providedExports !== true) {
|
||||
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());
|
||||
providedExportsAreCacheable = true;
|
||||
|
||||
processDependenciesBlock(module);
|
||||
|
||||
if (providedExportsAreCacheable) {
|
||||
modulesToStore.add(module);
|
||||
}
|
||||
|
||||
moduleGraph.setProvidedExports(
|
||||
module,
|
||||
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
|
||||
);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const notifyDependencies = () => {
|
||||
const deps = dependencies.get(module);
|
||||
if (deps !== undefined) {
|
||||
for (const dep of deps) {
|
||||
queue.enqueue(dep);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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);
|
||||
if (providedExports !== true) {
|
||||
moduleWithExports =
|
||||
module.buildMeta && module.buildMeta.exportsType;
|
||||
moduleProvidedExports = Array.isArray(providedExports)
|
||||
? new Set(providedExports)
|
||||
: new Set();
|
||||
providedExportsAreTemporary = false;
|
||||
|
||||
processDependenciesBlock(module);
|
||||
|
||||
if (providedExportsAreTemporary) {
|
||||
temporaryProvidedExports.add(module);
|
||||
} else {
|
||||
temporaryProvidedExports.delete(module);
|
||||
}
|
||||
|
||||
if (!moduleWithExports) {
|
||||
moduleGraph.setProvidedExports(module, true);
|
||||
notifyDependencies();
|
||||
} else if (moduleGraph.getProvidedExports(module) !== true) {
|
||||
moduleGraph.setProvidedExports(
|
||||
module,
|
||||
Array.from(moduleProvidedExports)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -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}`
|
||||
)
|
||||
|
|
|
@ -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'/]
|
||||
]
|
|
@ -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'/]
|
||||
]
|
Loading…
Reference in New Issue