Last active
July 12, 2017 21:10
-
-
Save MagicDuck/5061fbf17406526521dae3771a4803ad to your computer and use it in GitHub Desktop.
webpack Compilation.js patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
/** | |
* processDependenciesBlockForChunk() would go into insane recursion in the old code, this patch fixes that up | |
* by using a stack to process the work instead; it also checks module membership into chunk.modules using a Set | |
* to speed things up. | |
* | |
* Original code license: | |
* MIT License http://www.opensource.org/licenses/mit-license.php | |
* Author Tobias Koppers @sokra | |
*/ | |
var Compilation = require("webpack/lib/Compilation"); | |
Compilation.prototype.processDependenciesBlockForChunk = function processDependenciesBlockForChunk(block, chunk) { | |
for (let c of this.chunks) { | |
c.__modulesSet = new Set(c.modules); | |
} | |
var workStack = [{block: block, chunk: chunk}]; | |
while(workStack.length > 0) { | |
var workUnit = workStack.pop(); | |
this.__processBlock(workUnit.block, workUnit.chunk, workStack); | |
} | |
for (let c of this.chunks) { | |
delete c.__modulesSet; | |
} | |
}; | |
Compilation.prototype.__processBlock = function __processBlock(block, chunk, workStack) { | |
if(block.variables) { | |
for (let v of block.variables) { | |
for (let dep of v.dependencies) { | |
this.__processDependency(chunk, dep, workStack); | |
} | |
} | |
} | |
if(block.dependencies) { | |
for (let dep of block.dependencies) { | |
this.__processDependency(chunk, dep, workStack); | |
} | |
} | |
if(block.blocks) { | |
for (let b of block.blocks) { | |
var c; | |
if(!b.chunks) { | |
c = this.addChunk(b.chunkName, b.module, b.loc); | |
c.__modulesSet = new Set(c.modules); | |
b.chunks = [c]; | |
c.addBlock(b); | |
} else { | |
c = b.chunks[0]; | |
} | |
chunk.addChunk(c); | |
c.addParent(chunk); | |
workStack.push({block: b, chunk: c}); | |
} | |
} | |
}; | |
Compilation.prototype.__processDependency = function __processDependency(chunk, d, workStack) { | |
if(!d.module) { | |
return; | |
} | |
if(typeof d.module.index !== "number") { | |
d.module.index = this.nextFreeModuleIndex++; | |
} | |
if(d.weak) { | |
return; | |
} | |
if(d.module.error) { | |
d.module = null; | |
return; | |
} | |
// Note: we check memebership using a set here to improve performance | |
if(!chunk.__modulesSet.has(d.module)) { | |
chunk.__modulesSet.add(d.module); | |
chunk.modules.push(d.module); | |
d.module.addChunk(chunk); | |
workStack.push({chunk: chunk, block: d.module}); | |
} | |
if(typeof d.module.index2 !== "number") { | |
d.module.index2 = this.nextFreeModuleIndex2++; | |
} | |
}; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var LoadersList = require("webpack-core/lib/LoadersList"); | |
// empty array returned when there are no matches | |
// this improves performance due to not having to allocate memory for each empty array | |
var NO_MATCHES = []; | |
var _match = LoadersList.prototype.match; | |
/** | |
* determines the list of loaders matched by the given request (str) | |
* @param {string} str request string | |
* @return {[loader]} loaders | |
*/ | |
LoadersList.prototype.match = function match(str) { | |
if (this.list.length === 0) { | |
return NO_MATCHES; | |
} | |
this.list._matchCache = this.list._matchCache || new Map(); | |
var result = this.list._matchCache.get(str); | |
if (!result) { | |
result = _match.call(this, str); | |
this.list._matchCache.set(str, result); | |
} | |
return result; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var MappingsContext = require("source-list-map").MappingsContext; | |
/** | |
* Note: this code was copied over from the original implementation and | |
* modified to do lookups using a Map to get O(1) amortized performance instead of indexOf(), which is O(n) | |
*/ | |
MappingsContext.prototype.ensureSource = function(source, originalSource) { | |
this.__sourcesIdxMap = this.__sourcesIdxMap || new Map(); | |
var idx = this.__sourcesIdxMap.get(source); | |
if(idx !== undefined) { | |
return idx; | |
} | |
// old code follows: | |
// originally lookup was done using indexOf | |
// var idx = this.sources.indexOf(source); | |
idx = this.sources.length; | |
this.sources.push(source); | |
this.__sourcesIdxMap.set(source, idx); | |
this.sourcesContent.push(originalSource); | |
if(typeof originalSource === "string") | |
this.hasSourceContent = true; | |
return idx; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
/** | |
* enhancements to bad/slow implementations of members of Module | |
* | |
* Original code license: | |
* MIT License http://www.opensource.org/licenses/mit-license.php | |
* Author Tobias Koppers @sokra | |
*/ | |
var Module = require("webpack/lib/Module"); | |
/** | |
* a slightly faster version of Module.prototype.moduleRewriteChunkInReasons() | |
* (unfortunatelly, not a ton better, I wish we didn't need this) | |
* @param {webpack Module} mod | |
* @param {webpack Chunk} oldChunk | |
* @param {webpack Chunk array} newChunks | |
*/ | |
Module.prototype.rewriteChunkInReasons = function(oldChunk, newChunks) { | |
this.reasons.forEach(function(r) { | |
var oldChunkIdx = (r.chunks || r.module.chunks).indexOf(oldChunk); | |
if (oldChunkIdx < 0) { | |
return; | |
} | |
if (!r.chunks) { | |
r.chunks = r.module.chunks.slice(); | |
} | |
r.chunks.splice(oldChunkIdx, 1); | |
for (let c of newChunks) { | |
if (r.chunks.indexOf(c) < 0) { | |
r.chunks.push(c); | |
} | |
} | |
}); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
/** | |
* overrides original RemoveParentModulesPlugin with a more efficient algorithm | |
* The idea of this plugin is to simply remove a module from a chunk if it occurs in all its eventual parents, | |
* since it's assumed that at least one of the parents has been loaded before we load the child chunk | |
*/ | |
var RemoveParentModulesPlugin = require("webpack/lib/optimize/RemoveParentModulesPlugin"); | |
/** | |
* checks if all eventual parents of a given childChunk occur in chunkSet | |
* If computeEventualParents is true, the set of eventual parents is returned as an array | |
* | |
* @param {Chunk[]} allChunks - all the chunks in the graph | |
* @param {Chunk} childChunk | |
* @param {Set<Chunk>} chunkSet | |
* @param {boolean} computeEventualParents | |
* @return {boolean|array} | |
*/ | |
function allEventualChunkParentsInSet(allChunks, childChunk, chunkSet, computeEventualParents) { | |
// if childChunk is an entry chunk, we're done | |
if (childChunk.parents.length === 0) { | |
return false; | |
} | |
// set up visited flag | |
for (let chunk of allChunks) { | |
chunk._rpm_visited = chunk._rpm_mark = chunkSet.has(chunk); | |
} | |
var eventualParents; | |
if (computeEventualParents) { | |
eventualParents = new Set(childChunk.parents.filter(p => p._rpm_mark)); | |
} | |
var chunkStack = childChunk.parents.filter(p => !p._rpm_visited); | |
while (chunkStack.length > 0) { | |
var chunk = chunkStack.pop(); | |
if (chunk.parents.length === 0) { | |
// we reached an entry point (which is not in chunkSet), bail out | |
return false; | |
} | |
chunk._rpm_visited = true; | |
for (let parent of chunk.parents) { | |
if (!parent._rpm_visited) { | |
chunkStack.push(parent); | |
} | |
if (eventualParents && parent._rpm_mark) { | |
eventualParents.add(parent); | |
} | |
} | |
} | |
return eventualParents ? Array.from(eventualParents) : true; | |
} | |
RemoveParentModulesPlugin.prototype.apply = function(compiler) { | |
compiler.plugin("this-compilation", function(compilation) { | |
compilation.plugin(["optimize-chunks", "optimize-extracted-chunks"], function(chunks) { | |
if (chunks.length <= 1) { | |
return; | |
} | |
// assign each chunk a unique id | |
chunks.forEach(function(chunk, index) { | |
chunk._rpm_id = index; | |
}); | |
// get a set of the unique modules which occur in more than 1 chunk | |
var modulesSet = new Set(); | |
chunks.forEach(function(chunk) { | |
chunk.modules.forEach(function(module) { | |
if (module.chunks.length > 1) { | |
modulesSet.add(module); | |
} | |
}); | |
}); | |
// construct set of unique 'module removing problems' to solve | |
var problemsMap = new Map(); | |
for (let module of modulesSet) { | |
var key = module.chunks.map(function(chunk) { | |
return chunk._rpm_id; | |
}).sort().join(","); | |
var problem = problemsMap.get(key); | |
if (!problem) { | |
problem = { | |
chunksSet: new Set(module.chunks), | |
modulesSet: new Set() | |
}; | |
problemsMap.set(key, problem); | |
} | |
problem.modulesSet.add(module); | |
} | |
// solve each problem | |
for (let problem of problemsMap.values()) { | |
var removedChunksSet = new Set(); | |
for (let chunk of problem.chunksSet) { | |
if (allEventualChunkParentsInSet(chunks, chunk, problem.chunksSet, false)) { | |
// we can remove problem.modulesSet modules from this chunk | |
removedChunksSet.add(chunk); | |
problem.chunksSet.delete(chunk); | |
chunk._rpm_modulesToRemove = chunk._rpm_modulesToRemove || []; | |
chunk._rpm_modulesToRemove.push(problem.modulesSet); | |
} | |
} | |
if (removedChunksSet.size > 0) { | |
// compute eventual parents of removed chunks (so we can rewrite module reasons) | |
var newChunks = Array.from(problem.chunksSet); | |
for (let chunk of removedChunksSet) { | |
chunk._rpm_eventualParents = newChunks.length === 1 | |
? newChunks | |
: allEventualChunkParentsInSet(chunks, chunk, problem.chunksSet, true); | |
} | |
// update module.chunks for each modules in problem.modulesSet | |
for (let module of problem.modulesSet) { | |
// update chunks array, preserving exiting array | |
module.chunks.length = 0; // clear array | |
for (let chunk of problem.chunksSet) { | |
module.chunks.push(chunk); | |
} | |
for (let chunk of removedChunksSet) { | |
module.rewriteChunkInReasons(chunk, chunk._rpm_eventualParents); | |
} | |
} | |
} | |
} | |
// update chunks.modules for any chunks with chunk._rpm_modulesToRemove | |
for (let chunk of chunks) { | |
if (chunk._rpm_modulesToRemove) { | |
var chunkMods = chunk.modules.slice(); | |
chunk.modules.length = 0; // clear array | |
for (let mod of chunkMods) { | |
if (!chunk._rpm_modulesToRemove.some(modulesSet => modulesSet.has(mod))) { | |
chunk.modules.push(mod); | |
} | |
} | |
} | |
} | |
// cleanup | |
for (let chunk of chunks) { | |
delete chunk._rpm_id; | |
delete chunk._rpm_visited; | |
delete chunk._rpm_modulesToRemove; | |
delete chunk._rpm_mark; | |
delete chunk._rpm_eventualParents; | |
} | |
}); | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment