aboutsummaryrefslogtreecommitdiff
path: root/node_modules/webpack/lib/Compilation.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/webpack/lib/Compilation.js')
-rw-r--r--node_modules/webpack/lib/Compilation.js191
1 files changed, 144 insertions, 47 deletions
diff --git a/node_modules/webpack/lib/Compilation.js b/node_modules/webpack/lib/Compilation.js
index b59c0eb53..72b195745 100644
--- a/node_modules/webpack/lib/Compilation.js
+++ b/node_modules/webpack/lib/Compilation.js
@@ -232,7 +232,10 @@ class Compilation extends Tapable {
callback();
};
- _this.semaphore.acquire(() => {
+ const semaphore = _this.semaphore;
+ semaphore.acquire(() => {
+ if(_this === null) return semaphore.release();
+
const factory = item[0];
factory.create({
contextInfo: {
@@ -242,6 +245,8 @@ class Compilation extends Tapable {
context: module.context,
dependencies: dependencies
}, function factoryCallback(err, dependentModule) {
+ if(_this === null) return semaphore.release();
+
let afterFactory;
function isOptional() {
@@ -265,11 +270,11 @@ class Compilation extends Tapable {
}
if(err) {
- _this.semaphore.release();
+ semaphore.release();
return errorOrWarningAndCallback(new ModuleNotFoundError(module, err, dependencies));
}
if(!dependentModule) {
- _this.semaphore.release();
+ semaphore.release();
return process.nextTick(callback);
}
if(_this.profile) {
@@ -302,7 +307,7 @@ class Compilation extends Tapable {
}
}
- _this.semaphore.release();
+ semaphore.release();
return process.nextTick(callback);
}
@@ -322,7 +327,7 @@ class Compilation extends Tapable {
module.profile.building = afterBuilding - afterFactory;
}
- _this.semaphore.release();
+ semaphore.release();
if(recursive) {
return process.nextTick(_this.processModuleDependencies.bind(_this, dependentModule, callback));
} else {
@@ -335,8 +340,10 @@ class Compilation extends Tapable {
iterationDependencies(dependencies);
_this.buildModule(dependentModule, isOptional(), module, dependencies, err => {
+ if(_this === null) return semaphore.release();
+
if(err) {
- _this.semaphore.release();
+ semaphore.release();
return errorOrWarningAndCallback(err);
}
@@ -345,7 +352,7 @@ class Compilation extends Tapable {
dependentModule.profile.building = afterBuilding - afterFactory;
}
- _this.semaphore.release();
+ semaphore.release();
if(recursive) {
_this.processModuleDependencies(dependentModule, callback);
} else {
@@ -578,8 +585,8 @@ class Compilation extends Tapable {
chunk.entryModule = module;
self.assignIndex(module);
self.assignDepth(module);
- self.processDependenciesBlockForChunk(module, chunk);
});
+ self.processDependenciesBlocksForChunks(self.chunks.slice());
self.sortModules(self.modules);
self.applyPlugins0("optimize");
@@ -846,62 +853,90 @@ class Compilation extends Tapable {
}
}
- processDependenciesBlockForChunk(module, chunk) {
- let block = module;
- const initialChunk = chunk;
+ // This method creates the Chunk graph from the Module graph
+ processDependenciesBlocksForChunks(inputChunks) {
+ // Process is splitting into two parts:
+ // Part one traverse the module graph and builds a very basic chunks graph
+ // in chunkDependencies.
+ // Part two traverse every possible way through the basic chunk graph and
+ // tracks the available modules. While traversing it connects chunks with
+ // eachother and Blocks with Chunks. It stops traversing when all modules
+ // for a chunk are already available. So it doesn't connect unneeded chunks.
+
const chunkDependencies = new Map(); // Map<Chunk, Array<{Module, Chunk}>>
+ const allCreatedChunks = new Set();
+
+ // PART ONE
+ const blockChunks = new Map();
+
+ // Start with the provided modules/chunks
+ const queue = inputChunks.map(chunk => ({
+ block: chunk.entryModule,
+ chunk: chunk
+ }));
+
+ let block, chunk;
+
+ // For each async Block in graph
const iteratorBlock = b => {
- let c;
- if(!b.chunks) {
+ // 1. We create a chunk for this Block
+ // but only once (blockChunks map)
+ let c = blockChunks.get(b);
+ if(c === undefined) {
c = this.addChunk(b.chunkName, b.module, b.loc);
- b.chunks = [c];
- c.addBlock(b);
- } else {
- c = b.chunks[0];
+ blockChunks.set(b, c);
+ allCreatedChunks.add(c);
+ // We initialize the chunks property
+ // this is later filled with the chunk when needed
+ b.chunks = [];
}
+
+ // 2. We store the Block+Chunk mapping as dependency for the chunk
let deps = chunkDependencies.get(chunk);
if(!deps) chunkDependencies.set(chunk, deps = []);
deps.push({
- chunk: c,
- module
+ block: b,
+ chunk: c
});
+
+ // 3. We enqueue the DependenciesBlock for traversal
queue.push({
block: b,
- module: null,
chunk: c
});
};
+ // For each Dependency in the graph
const iteratorDependency = d => {
+ // We skip Dependencies without Module pointer
if(!d.module) {
return;
}
+ // We skip weak Dependencies
if(d.weak) {
return;
}
+ // We connect Module and Chunk when not already done
if(chunk.addModule(d.module)) {
d.module.addChunk(chunk);
+
+ // And enqueue the Module for traversal
queue.push({
block: d.module,
- module: d.module,
chunk
});
}
};
- const queue = [{
- block,
- module,
- chunk
- }];
-
+ // Iterative traversal of the Module graph
+ // Recursive would be simpler to write but could result in Stack Overflows
while(queue.length) {
const queueItem = queue.pop();
block = queueItem.block;
- module = queueItem.module;
chunk = queueItem.chunk;
+ // Traverse all variables, Dependencies and Blocks
if(block.variables) {
iterationBlockVariable(block.variables, iteratorDependency);
}
@@ -915,46 +950,108 @@ class Compilation extends Tapable {
}
}
- chunk = initialChunk;
- let chunks = new Set();
- const queue2 = [{
+ // PART TWO
+
+ let availableModules;
+ let newAvailableModules;
+ const queue2 = inputChunks.map(chunk => ({
chunk,
- chunks
- }];
+ availableModules: new Set()
+ }));
- const filterFn = dep => {
- if(chunks.has(dep.chunk)) return false;
- for(const chunk of chunks) {
- if(chunk.containsModule(dep.module))
+ // Helper function to check if all modules of a chunk are available
+ const areModulesAvailable = (chunk, availableModules) => {
+ for(const module of chunk.modulesIterable) {
+ if(!availableModules.has(module))
return false;
}
return true;
};
+ // For each edge in the basic chunk graph
+ const filterFn = dep => {
+ // Filter egdes that are not needed because all modules are already available
+ // This also filters circular dependencies in the chunks graph
+ const depChunk = dep.chunk;
+ if(areModulesAvailable(depChunk, newAvailableModules))
+ return false; // break all modules are already available
+ return true;
+ };
+
+ const minAvailableModulesMap = new Map();
+
+ // Iterative traversing of the basic chunk graph
while(queue2.length) {
const queueItem = queue2.pop();
chunk = queueItem.chunk;
- chunks = queueItem.chunks;
+ availableModules = queueItem.availableModules;
+
+ // 1. Get minimal available modules
+ // It doesn't make sense to traverse a chunk again with more available modules.
+ // This step calculates the minimal available modules and skips traversal when
+ // the list didn't shrink.
+ let minAvailableModules = minAvailableModulesMap.get(chunk);
+ if(minAvailableModules === undefined) {
+ minAvailableModulesMap.set(chunk, new Set(availableModules));
+ } else {
+ let deletedModules = false;
+ for(const m of minAvailableModules) {
+ if(!availableModules.has(m)) {
+ minAvailableModules.delete(m);
+ deletedModules = true;
+ }
+ }
+ if(!deletedModules)
+ continue;
+ }
+ // 2. Get the edges at this point of the graph
const deps = chunkDependencies.get(chunk);
if(!deps) continue;
+ if(deps.length === 0) continue;
- const depsFiltered = deps.filter(filterFn);
+ // 3. Create a new Set of available modules at this points
+ newAvailableModules = new Set(availableModules);
+ for(const m of chunk.modulesIterable)
+ newAvailableModules.add(m);
- for(let i = 0; i < depsFiltered.length; i++) {
- const dep = depsFiltered[i];
+ // 4. Filter edges with available modules
+ const filteredDeps = deps.filter(filterFn);
+
+ // 5. Foreach remaining edge
+ const nextChunks = new Set();
+ for(let i = 0; i < filteredDeps.length; i++) {
+ const dep = filteredDeps[i];
const depChunk = dep.chunk;
- chunk.addChunk(depChunk);
- depChunk.addParent(chunk);
+ const depBlock = dep.block;
+
+ // 6. Connnect block with chunk
+ if(depChunk.addBlock(depBlock)) {
+ depBlock.chunks.push(depChunk);
+ }
+
+ // 7. Connect chunk with parent
+ if(chunk.addChunk(depChunk)) {
+ depChunk.addParent(chunk);
+ }
- const newChunks = depsFiltered.length > 1 ? new Set(chunks) : chunks;
- newChunks.add(chunk);
+ nextChunks.add(depChunk);
+ }
+
+ // 8. Enqueue further traversal
+ for(const nextChunk of nextChunks) {
queue2.push({
- chunk: depChunk,
- chunks: newChunks
+ chunk: nextChunk,
+ availableModules: newAvailableModules
});
}
}
+
+ // Remove all unconnected chunks
+ for(const chunk of allCreatedChunks) {
+ if(chunk.parents.length === 0)
+ chunk.remove("unconnected");
+ }
}
removeChunkFromDependencies(block, chunk) {