Skip to content

Commit 6a050ee

Browse files
committed
Merge branch 'next' into chore
# Conflicts: # yarn.lock
2 parents fd45e3b + 6bb2436 commit 6a050ee

11 files changed

+325
-69
lines changed

lib/DependenciesBlock.js

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66

77
const DependenciesBlockVariable = require("./DependenciesBlockVariable");
88

9-
const disconnect = i => i.disconnect();
10-
11-
const unseal = i => i.unseal();
12-
139
class DependenciesBlock {
1410
constructor() {
1511
this.dependencies = [];
@@ -42,39 +38,54 @@ class DependenciesBlock {
4238
}
4339

4440
updateHash(hash) {
45-
const updateHash = i => i.updateHash(hash);
46-
47-
this.dependencies.forEach(updateHash);
48-
this.blocks.forEach(updateHash);
49-
this.variables.forEach(updateHash);
41+
for(const dep of this.dependencies)
42+
dep.updateHash(hash);
43+
for(const block of this.blocks)
44+
block.updateHash(hash);
45+
for(const variable of this.variables)
46+
variable.updateHash(hash);
5047
}
5148

5249
disconnect() {
53-
this.dependencies.forEach(disconnect);
54-
this.blocks.forEach(disconnect);
55-
this.variables.forEach(disconnect);
50+
for(const dep of this.dependencies)
51+
dep.disconnect();
52+
for(const block of this.blocks)
53+
block.disconnect();
54+
for(const variable of this.variables)
55+
variable.disconnect();
5656
}
5757

5858
unseal() {
59-
this.blocks.forEach(unseal);
59+
for(const block of this.blocks)
60+
block.unseal();
6061
}
6162

6263
hasDependencies(filter) {
6364
if(filter) {
64-
if(this.dependencies.some(filter)) {
65-
return true;
65+
for(const dep of this.dependencies) {
66+
if(filter(dep))
67+
return true;
6668
}
6769
} else {
6870
if(this.dependencies.length > 0) {
6971
return true;
7072
}
7173
}
7274

73-
return this.blocks.concat(this.variables).some(item => item.hasDependencies(filter));
75+
for(const block of this.blocks) {
76+
if(block.hasDependencies(filter))
77+
return true;
78+
}
79+
for(const variable of this.variables) {
80+
if(variable.hasDependencies(filter))
81+
return true;
82+
}
83+
return false;
7484
}
7585

7686
sortItems() {
77-
this.blocks.forEach(block => block.sortItems());
87+
for(const block of this.blocks)
88+
block.sortItems();
7889
}
7990
}
8091

lib/Module.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,14 @@ class Module extends DependenciesBlock {
8787
this.usedExports = null;
8888
this.providedExports = null;
8989
this.optimizationBailout = [];
90+
91+
// delayed operations
92+
this._rewriteChunkInReasons = undefined;
9093
}
9194

9295
disconnect() {
9396
this.reasons.length = 0;
97+
this._rewriteChunkInReasons = undefined;
9498
this._chunks.clear();
9599

96100
this.id = null;
@@ -196,6 +200,11 @@ class Module extends DependenciesBlock {
196200
}
197201

198202
hasReasonForChunk(chunk) {
203+
if(this._rewriteChunkInReasons) {
204+
for(const operation of this._rewriteChunkInReasons)
205+
this._doRewriteChunkInReasons(operation.oldChunk, operation.newChunks);
206+
this._rewriteChunkInReasons = undefined;
207+
}
199208
for(let i = 0; i < this.reasons.length; i++) {
200209
if(this.reasons[i].hasChunk(chunk))
201210
return true;
@@ -208,6 +217,16 @@ class Module extends DependenciesBlock {
208217
}
209218

210219
rewriteChunkInReasons(oldChunk, newChunks) {
220+
// This is expensive. Delay operation until we really need the data
221+
if(this._rewriteChunkInReasons === undefined)
222+
this._rewriteChunkInReasons = [];
223+
this._rewriteChunkInReasons.push({
224+
oldChunk,
225+
newChunks
226+
});
227+
}
228+
229+
_doRewriteChunkInReasons(oldChunk, newChunks) {
211230
for(let i = 0; i < this.reasons.length; i++) {
212231
this.reasons[i].rewriteChunks(oldChunk, newChunks);
213232
}

lib/RequestShortener.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,27 @@ class RequestShortener {
4242
this.buildinsAsModule = buildinsAsModule;
4343
this.buildinsRegExp = createRegExpForPath(buildins);
4444
}
45+
46+
this.cache = new Map();
4547
}
4648

4749
shorten(request) {
4850
if(!request) return request;
49-
request = normalizeBackSlashDirection(request);
51+
const cacheEntry = this.cache.get(request);
52+
if(cacheEntry !== undefined) return cacheEntry;
53+
let result = normalizeBackSlashDirection(request);
5054
if(this.buildinsAsModule && this.buildinsRegExp)
51-
request = request.replace(this.buildinsRegExp, "!(webpack)");
55+
result = result.replace(this.buildinsRegExp, "!(webpack)");
5256
if(this.currentDirectoryRegExp)
53-
request = request.replace(this.currentDirectoryRegExp, "!.");
57+
result = result.replace(this.currentDirectoryRegExp, "!.");
5458
if(this.parentDirectoryRegExp)
55-
request = request.replace(this.parentDirectoryRegExp, "!..");
59+
result = result.replace(this.parentDirectoryRegExp, "!..");
5660
if(!this.buildinsAsModule && this.buildinsRegExp)
57-
request = request.replace(this.buildinsRegExp, "!(webpack)");
58-
request = request.replace(INDEX_JS_REGEXP, "$1");
59-
return request.replace(FRONT_OR_BACK_BANG_REGEXP, "");
61+
result = result.replace(this.buildinsRegExp, "!(webpack)");
62+
result = result.replace(INDEX_JS_REGEXP, "$1");
63+
result = result.replace(FRONT_OR_BACK_BANG_REGEXP, "");
64+
this.cache.set(request, result);
65+
return result;
6066
}
6167
}
6268

lib/optimize/ConcatenatedModule.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -321,12 +321,13 @@ class ConcatenatedModule extends Module {
321321
}
322322

323323
_createIdentifier() {
324-
const orderedConcatenationListIdentifiers = this._orderedConcatenationList.map(info => {
325-
switch(info.type) {
326-
case "concatenated":
327-
return info.module.identifier();
324+
let orderedConcatenationListIdentifiers = "";
325+
for(let i = 0; i < this._orderedConcatenationList.length; i++) {
326+
if(this._orderedConcatenationList[i].type === "concatenated") {
327+
orderedConcatenationListIdentifiers += this._orderedConcatenationList[i].module.identifier();
328+
orderedConcatenationListIdentifiers += " ";
328329
}
329-
}).filter(Boolean).join(" ");
330+
}
330331
const hash = createHash("md5");
331332
hash.update(orderedConcatenationListIdentifiers);
332333
return this.rootModule.identifier() + " " + hash.digest("hex");

lib/optimize/MergeDuplicateChunksPlugin.js

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,53 @@ class MergeDuplicateChunksPlugin {
99
apply(compiler) {
1010
compiler.plugin("compilation", (compilation) => {
1111
compilation.plugin("optimize-chunks-basic", (chunks) => {
12-
const map = Object.create(null);
13-
chunks.slice().forEach((chunk) => {
14-
if(chunk.hasEntryModule()) return;
15-
const ident = chunk.getModulesIdent();
16-
const otherChunk = map[ident];
17-
if(otherChunk) {
18-
if(otherChunk.integrate(chunk, "duplicate"))
19-
chunks.splice(chunks.indexOf(chunk), 1);
20-
return;
12+
// remember already tested chunks for performance
13+
const notDuplicates = new Set();
14+
15+
// for each chunk
16+
for(const chunk of chunks) {
17+
18+
// track a Set of all chunk that could be duplicates
19+
let possibleDuplicates;
20+
for(const module of chunk.modulesIterable) {
21+
if(possibleDuplicates === undefined) {
22+
// when possibleDuplicates is not yet set,
23+
// create a new Set from chunks of the current module
24+
// including only chunks with the same number of modules
25+
for(const dup of module.chunksIterable) {
26+
if(dup !== chunk && chunk.getNumberOfModules() === dup.getNumberOfModules() && !notDuplicates.has(dup)) {
27+
// delay allocating the new Set until here, reduce memory pressure
28+
if(possibleDuplicates === undefined)
29+
possibleDuplicates = new Set();
30+
possibleDuplicates.add(dup);
31+
}
32+
}
33+
// when no chunk is possible we can break here
34+
if(possibleDuplicates === undefined) break;
35+
} else {
36+
// validate existing possible duplicates
37+
for(const dup of possibleDuplicates) {
38+
// remove possible duplicate when module is not contained
39+
if(!dup.containsModule(module))
40+
possibleDuplicates.delete(dup);
41+
}
42+
// when all chunks has been removed we can break here
43+
if(possibleDuplicates.size === 0) break;
44+
}
2145
}
22-
map[ident] = chunk;
23-
});
46+
47+
// when we found duplicates
48+
if(possibleDuplicates !== undefined && possibleDuplicates.size > 0) {
49+
for(const otherChunk of possibleDuplicates) {
50+
// merge them
51+
if(chunk.integrate(otherChunk, "duplicate"))
52+
chunks.splice(chunks.indexOf(otherChunk), 1);
53+
}
54+
}
55+
56+
// don't check already processed chunks twice
57+
notDuplicates.add(chunk);
58+
}
2459
});
2560
});
2661
}

lib/optimize/RemoveParentModulesPlugin.js

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
"use strict";
66

7+
const Queue = require("../util/Queue");
8+
79
const hasModule = (chunk, module, checkedChunks) => {
810
if(chunk.containsModule(module)) return [chunk];
911
if(chunk.getNumberOfParents() === 0) return false;
@@ -33,30 +35,68 @@ class RemoveParentModulesPlugin {
3335
apply(compiler) {
3436
compiler.plugin("compilation", (compilation) => {
3537
compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], (chunks) => {
36-
for(var index = 0; index < chunks.length; index++) {
37-
var chunk = chunks[index];
38-
if(chunk.getNumberOfParents() === 0) continue;
38+
const queue = new Queue(chunks);
39+
const availableModulesMap = new Map();
3940

40-
// TODO consider Map when performance has improved https://gist.github.com/sokra/b36098368da7b8f6792fd7c85fca6311
41-
var cache = Object.create(null);
42-
var modules = chunk.getModules();
43-
for(var i = 0; i < modules.length; i++) {
44-
var module = modules[i];
41+
for(const chunk of chunks) {
42+
// initialize available modules for chunks without parents
43+
if(chunk.getNumberOfParents() === 0)
44+
availableModulesMap.set(chunk, new Set());
45+
}
4546

46-
var dId = module.getChunkIdsIdent();
47-
var parentChunksWithModule;
48-
if(dId === null) {
49-
parentChunksWithModule = allHaveModule(chunk.getParents(), module);
50-
} else if(dId in cache) {
51-
parentChunksWithModule = cache[dId];
52-
} else {
53-
parentChunksWithModule = cache[dId] = allHaveModule(chunk.getParents(), module);
54-
}
55-
if(parentChunksWithModule) {
56-
module.rewriteChunkInReasons(chunk, Array.from(parentChunksWithModule));
57-
chunk.removeModule(module);
47+
while(queue.length > 0) {
48+
const chunk = queue.dequeue();
49+
let availableModules = availableModulesMap.get(chunk);
50+
let changed = false;
51+
for(const parent of chunk.parentsIterable) {
52+
const availableModulesInParent = availableModulesMap.get(parent);
53+
if(availableModulesInParent !== undefined) {
54+
// If we know the available modules in parent: process these
55+
if(availableModules === undefined) {
56+
// if we have not own info yet: create new entry
57+
availableModules = new Set(availableModulesInParent);
58+
for(const m of parent.modulesIterable)
59+
availableModules.add(m);
60+
availableModulesMap.set(chunk, availableModules);
61+
changed = true;
62+
} else {
63+
for(const m of availableModules) {
64+
if(!parent.containsModule(m) && !availableModulesInParent.has(m)) {
65+
availableModules.delete(m);
66+
changed = true;
67+
}
68+
}
69+
}
5870
}
5971
}
72+
if(changed) {
73+
// if something changed: enqueue our children
74+
for(const child of chunk.chunksIterable)
75+
queue.enqueue(child);
76+
}
77+
}
78+
79+
// now we have available modules for every chunk
80+
81+
for(const chunk of chunks) {
82+
// remove modules from chunk if they are already available
83+
const availableModules = availableModulesMap.get(chunk);
84+
const modules = new Set(chunk.modulesIterable);
85+
const toRemove = new Set();
86+
if(modules.size < availableModules.size) {
87+
for(const m of modules)
88+
if(availableModules.has(m))
89+
toRemove.add(m);
90+
} else {
91+
for(const m of availableModules)
92+
if(modules.has(m))
93+
toRemove.add(m);
94+
}
95+
for(const module of toRemove) {
96+
const parentChunksWithModule = allHaveModule(chunk.getParents(), module);
97+
module.rewriteChunkInReasons(chunk, Array.from(parentChunksWithModule));
98+
chunk.removeModule(module);
99+
}
60100
}
61101
});
62102
});

lib/web/JsonpMainTemplatePlugin.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,8 @@ class JsonpMainTemplatePlugin {
241241
function hotDisposeChunk(chunkId) {
242242
delete installedChunks[chunkId];
243243
}
244-
var parentHotUpdateCallback = this[${JSON.stringify(hotUpdateFunction)}];
245-
this[${JSON.stringify(hotUpdateFunction)}] = ${runtimeSource}`;
244+
var parentHotUpdateCallback = window[${JSON.stringify(hotUpdateFunction)}];
245+
window[${JSON.stringify(hotUpdateFunction)}] = ${runtimeSource}`;
246246
});
247247
mainTemplate.plugin("hash", hash => {
248248
hash.update("jsonp");

lib/webworker/WebWorkerMainTemplatePlugin.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ class WebWorkerMainTemplatePlugin {
8181
});
8282

8383
return source + "\n" +
84-
`var parentHotUpdateCallback = this[${JSON.stringify(hotUpdateFunction)}];\n` +
85-
`this[${JSON.stringify(hotUpdateFunction)}] = ` +
84+
`var parentHotUpdateCallback = self[${JSON.stringify(hotUpdateFunction)}];\n` +
85+
`self[${JSON.stringify(hotUpdateFunction)}] = ` +
8686
Template.getFunctionContent(require("./WebWorkerMainTemplate.runtime.js"))
8787
.replace(/\/\/\$semicolon/g, ";")
8888
.replace(/\$require\$/g, mainTemplate.requireFn)

0 commit comments

Comments
 (0)