Skip to content

Commit d5aae9d

Browse files
committed
WIP9
1 parent 5e18c1c commit d5aae9d

File tree

26 files changed

+523
-602
lines changed

26 files changed

+523
-602
lines changed

lib/Chunk.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
const util = require("util");
88
const SortableSet = require("./util/SortableSet");
9+
const GraphHelpers = require("./GraphHelpers");
910
let debugId = 1000;
1011

1112
const sortById = (a, b) => {
@@ -201,9 +202,8 @@ class Chunk {
201202
}
202203

203204
moveModule(module, otherChunk) {
204-
module.removeChunk(this);
205-
module.addChunk(otherChunk);
206-
otherChunk.addModule(module);
205+
GraphHelpers.disconnectChunkAndModule(this, module);
206+
GraphHelpers.connectChunkAndModule(otherChunk, module);
207207
module.rewriteChunkInReasons(this, [otherChunk]);
208208
}
209209

@@ -253,8 +253,25 @@ class Chunk {
253253
}
254254

255255
canBeIntegrated(otherChunk) {
256-
if(this.isInitial() !== otherChunk.isInitial())
257-
return false;
256+
const isAvailable = (a, b) => {
257+
const queue = new Set(b.groupsIterable);
258+
for(const chunkGroup of queue) {
259+
if(a.isInGroup(chunkGroup)) continue;
260+
if(chunkGroup.isInitial()) return false;
261+
for(const parent of chunkGroup.parentsIterable)
262+
queue.add(parent);
263+
}
264+
return true;
265+
}
266+
if(this.isInitial() !== otherChunk.isInitial()) {
267+
if(this.isInitial()) {
268+
return isAvailable(this, otherChunk);
269+
} else if(otherChunk.isInitial()) {
270+
return isAvailable(otherChunk, this);
271+
} else {
272+
return false;
273+
}
274+
}
258275
if(this.hasEntryModule() || otherChunk.hasEntryModule())
259276
return false;
260277
return true;

lib/GraphHelpers.js

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,45 @@
1-
const Chunk = require("./Chunk");
2-
const ChunkGroup = require("./ChunkGroup");
3-
const Module = require("./Module");
4-
const DependenciesBlock = require("./DependenciesBlock");
5-
61
// TODO remove this function
7-
function assert(value, Type, name) {
2+
function assert(value, typeName, name) {
3+
const Type = require(`./${typeName}`);
84
if(value instanceof Type) return;
95
throw new Error(`${name} is not a ${Type.name}`);
106
}
117

128
exports.connectChunkGroupAndChunk = (chunkGroup, chunk) => {
13-
assert(chunkGroup, ChunkGroup, "chunkGroup");
14-
assert(chunk, Chunk, "chunk");
9+
assert(chunkGroup, "ChunkGroup", "chunkGroup");
10+
assert(chunk, "Chunk", "chunk");
1511
if(chunkGroup.pushChunk(chunk)) {
1612
chunk.addGroup(chunkGroup);
1713
}
1814
};
1915

2016
exports.connectChunkGroupParentAndChild = (parent, child) => {
21-
assert(parent, ChunkGroup, "parent");
22-
assert(child, ChunkGroup, "child");
17+
assert(parent, "ChunkGroup", "parent");
18+
assert(child, "ChunkGroup", "child");
2319
if(parent.addChild(child)) {
2420
child.addParent(parent);
2521
}
2622
};
2723

2824
exports.connectChunkAndModule = (chunk, module) => {
29-
assert(chunk, Chunk, "chunk");
30-
assert(module, Module, "module");
25+
assert(chunk, "Chunk", "chunk");
26+
assert(module, "Module", "module");
3127
if(module.addChunk(chunk)) {
3228
chunk.addModule(module);
3329
}
3430
};
3531

3632
exports.disconnectChunkAndModule = (chunk, module) => {
37-
assert(chunk, Chunk, "chunk");
38-
assert(module, Module, "module");
33+
assert(chunk, "Chunk", "chunk");
34+
assert(module, "Module", "module");
3935
chunk.removeModule(module);
4036
module.removeChunk(chunk);
4137
};
4238

4339
exports.connectDependenciesBlockAndChunkGroup = (depBlock, chunkGroup) => {
44-
assert(depBlock, DependenciesBlock, "depBlock");
45-
assert(chunkGroup, ChunkGroup, "chunkGroup");
40+
assert(depBlock, "DependenciesBlock", "depBlock");
41+
assert(chunkGroup, "ChunkGroup", "chunkGroup");
4642
if(chunkGroup.addBlock(depBlock)) {
4743
depBlock.chunkGroup = chunkGroup;
4844
}
49-
}
45+
};

lib/Module.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,14 @@ class Module extends DependenciesBlock {
157157
return this._chunks.getFromUnorderedCache(getDebugIdent);
158158
}
159159

160+
isEntryModule() {
161+
for(const chunk of this._chunks) {
162+
if(chunk.entryModule === this)
163+
return true;
164+
}
165+
return false;
166+
}
167+
160168
get optional() {
161169
return this.reasons.length > 0 && this.reasons.every(r => r.dependency && r.dependency.optional);
162170
}

lib/WebpackOptionsApply.js

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -287,47 +287,58 @@ class WebpackOptionsApply extends OptionsApply {
287287
new FlagDependencyUsagePlugin().apply(compiler);
288288
if(options.optimization.concatenateModules)
289289
new ModuleConcatenationPlugin().apply(compiler);
290-
const normalizeCommonsChunksNameOption = value => {
290+
const nameOptionToName = value => {
291291
if(value === true) {
292292
return (module, chunks) => {
293293
const names = chunks.map(c => c.name);
294294
if(!names.every(Boolean)) return;
295295
names.sort();
296296
return names.join("~");
297297
};
298-
} else if(typeof value === "string") {
299-
return (module, chunks) => {
300-
return value;
301-
};
302298
} else {
303299
return value;
304300
}
305-
}
306-
if(options.optimization.asyncCommonsChunks) {
307-
const accpOptions = Object.assign({}, options.optimization.asyncCommonsChunks, {
308-
initialChunks: false,
309-
name: normalizeCommonsChunksNameOption(typeof options.optimization.asyncCommonsChunks === "object" ? options.optimization.asyncCommonsChunks.name : undefined)
310-
});
311-
new AutomaticCommonsChunksPlugin(accpOptions).apply(compiler);
312-
}
313-
if(options.optimization.initialCommonsChunks || options.optimization.initialVendorsChunk) {
314-
let nameOption = options.optimization.initialVendorsChunk;
315-
if(nameOption === true) {
316-
nameOption = {
317-
vendors: /[\\/]node_modules[\\/]/
301+
};
302+
const initialVendorsOptionToEnforce = value => {
303+
if(value === true) {
304+
value = /[\\/]node_modules[\\/]/;
305+
}
306+
if(typeof value === "string") {
307+
value = {
308+
[value]: /[\\/]node_modules[\\/]/
309+
};
310+
}
311+
return vendorsOptionToEnforce(value);
312+
};
313+
const vendorsOptionToEnforce = value => {
314+
if(value === true) {
315+
value = /[\\/]node_modules[\\/]/;
316+
}
317+
if(typeof value === "string") {
318+
value = {
319+
[value]: /[\\/]node_modules[\\/]/
320+
};
321+
}
322+
if(value instanceof RegExp) {
323+
return (module, chunks) => {
324+
if(!module.nameForCondition) return;
325+
const name = module.nameForCondition();
326+
return value.test(name);
318327
};
319328
}
320-
if(typeof nameOption === "string") {
321-
nameOption = {
322-
[nameOption]: /[\\/]node_modules[\\/]/
329+
if(typeof value === "string") {
330+
return (module, chunks) => {
331+
if(!module.nameForCondition) return;
332+
const name = module.nameForCondition();
333+
return name.startsWith(value);
323334
};
324335
}
325-
if(nameOption && typeof nameOption === "object") {
326-
nameOption = ((nameOption) => ((module, chunks) => {
336+
if(value && typeof value === "object") {
337+
return (module, chunks) => {
327338
if(!module.nameForCondition) return;
328339
const name = module.nameForCondition();
329-
for(const chunkName of Object.keys(nameOption)) {
330-
const regExp = nameOption[chunkName];
340+
for(const chunkName of Object.keys(value)) {
341+
const regExp = value[chunkName];
331342
if(typeof regExp === "string") {
332343
if(name.startsWith(regExp))
333344
return chunkName;
@@ -342,26 +353,30 @@ class WebpackOptionsApply extends OptionsApply {
342353
return chunkName;
343354
}
344355
}
345-
}))(nameOption);
346-
}
347-
let commonsNameOption = normalizeCommonsChunksNameOption(typeof options.optimization.initialCommonsChunks === "object" ? options.optimization.initialCommonsChunks.name : undefined);
348-
let name;
349-
if(commonsNameOption && !nameOption) {
350-
name = commonsNameOption;
351-
} else if(commonsNameOption) {
352-
name = (module, chunks) => {
353-
const r = nameOption(module, chunks);
354-
if(r !== undefined) return r;
355-
return commonsNameOption(module, chunks);
356356
};
357-
} else {
358-
name = nameOption;
359357
}
358+
return value;
359+
};
360+
if(options.optimization.asyncCommonsChunks || options.optimization.asyncVendorsChunks) {
361+
const accpOptions = Object.assign({}, options.optimization.asyncCommonsChunks, {
362+
initialChunks: false,
363+
name: nameOptionToName(typeof options.optimization.asyncCommonsChunks === "object" ? options.optimization.asyncCommonsChunks.name : undefined),
364+
enforce: vendorsOptionToEnforce(options.optimization.asyncVendorsChunks)
365+
});
366+
if(!options.optimization.asyncCommonsChunks) {
367+
accpOptions.minChunks = Infinity;
368+
}
369+
new AutomaticCommonsChunksPlugin(accpOptions).apply(compiler);
370+
}
371+
if(options.optimization.initialCommonsChunks || options.optimization.initialVendorsChunks) {
360372
const accpOptions = Object.assign({}, options.optimization.initialCommonsChunks, {
361373
initialChunks: true,
362-
onlyNamed: !options.optimization.initialCommonsChunks,
363-
name
374+
name: nameOptionToName(typeof options.optimization.initialCommonsChunks === "object" ? options.optimization.initialCommonsChunks.name : undefined),
375+
enforce: initialVendorsOptionToEnforce(options.optimization.initialVendorsChunks)
364376
});
377+
if(!options.optimization.initialCommonsChunks) {
378+
accpOptions.minChunks = Infinity;
379+
}
365380
new AutomaticCommonsChunksPlugin(accpOptions).apply(compiler);
366381
}
367382
if(options.optimization.noEmitOnErrors)

lib/WebpackOptionsDefaulter.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,9 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
165165
this.set("optimization.usedExports", "make", options => isProductionLikeMode(options));
166166
this.set("optimization.concatenateModules", "make", options => isProductionLikeMode(options));
167167
this.set("optimization.asyncCommonsChunks", true);
168+
this.set("optimization.asyncVendorsChunks", true);
168169
this.set("optimization.initialCommonsChunks", false);
169-
this.set("optimization.initialVendorsChunk", false);
170+
this.set("optimization.initialVendorsChunks", false);
170171
this.set("optimization.noEmitOnErrors", "make", options => isProductionLikeMode(options));
171172
this.set("optimization.namedModules", "make", options => options.mode === "development");
172173
this.set("optimization.namedChunks", "make", options => options.mode === "development");

lib/optimize/AutomaticCommonsChunksPlugin.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ module.exports = class AutomaticCommonsChunksPlugin {
2828
minSize: 30000,
2929
minChunks: 2,
3030
maxRequests: 4,
31-
onlyNamed: false,
3231
name: undefined, // function(module, chunks) => string | undefined
3332
enforce: undefined // function(module, module) => true | false
3433
}, options);
@@ -64,15 +63,16 @@ module.exports = class AutomaticCommonsChunksPlugin {
6463
if(!enforce && chunkIndices.length < this.options.minChunks)
6564
continue;
6665
// Get name from "name" option
67-
let name = this.options.name;
66+
let name = typeof enforce === "string" ? enforce : this.options.name;
6867
if(typeof name === "function")
6968
name = name(module, chunks);
7069
// Create key for maps
7170
// When it has a name we use the name as key
7271
// Elsewise we create the key from chunks
7372
// This automatically merges equal names
7473
const chunksKey = chunkIndices.sort().join();
75-
const key = name ? name : chunksKey;
74+
let key = name ? name : chunksKey;
75+
if(enforce) key += ",enforced";
7676
// Add module to maps
7777
let info = chunksInfoMap.get(key);
7878
if(info === undefined) {
@@ -133,6 +133,14 @@ module.exports = class AutomaticCommonsChunksPlugin {
133133
if(aModuleIdentifier < bModuleIdentifier) return 1;
134134
}
135135
});
136+
console.log(entries.map(e => {
137+
return {
138+
key: e.key,
139+
size: e.size,
140+
enforce: e.enforce,
141+
chunks: Array.from(e.chunks.keys(), c => c.name || c.debugId)
142+
};
143+
}));
136144

137145
let changed = false;
138146
// Walk though all entries

lib/optimize/LimitChunkCountPlugin.js

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,37 +21,35 @@ class LimitChunkCountPlugin {
2121
if(maxChunks < 1) return;
2222
if(chunks.length <= maxChunks) return;
2323

24-
if(chunks.length > maxChunks) {
25-
const sortedExtendedPairCombinations = chunks.reduce((combinations, a, idx) => {
26-
// create combination pairs
27-
for(let i = 0; i < idx; i++) {
28-
const b = chunks[i];
29-
combinations.push([b, a]);
30-
}
31-
return combinations;
32-
}, []).map((pair) => {
33-
// extend combination pairs with size and integrated size
34-
const a = pair[0].size(options);
35-
const b = pair[1].size(options);
36-
const ab = pair[0].integratedSize(pair[1], options);
37-
return [a + b - ab, ab, pair[0], pair[1], a, b];
38-
}).filter((extendedPair) => {
39-
// filter pairs that do not have an integratedSize
40-
// meaning they can NOT be integrated!
41-
return extendedPair[1] !== false;
42-
}).sort((a, b) => { // sadly javascript does an inplace sort here
43-
// sort them by size
44-
const diff = b[0] - a[0];
45-
if(diff !== 0) return diff;
46-
return a[1] - b[1];
47-
});
24+
const sortedExtendedPairCombinations = chunks.reduce((combinations, a, idx) => {
25+
// create combination pairs
26+
for(let i = 0; i < idx; i++) {
27+
const b = chunks[i];
28+
combinations.push([b, a]);
29+
}
30+
return combinations;
31+
}, []).map((pair) => {
32+
// extend combination pairs with size and integrated size
33+
const a = pair[0].size(options);
34+
const b = pair[1].size(options);
35+
const ab = pair[0].integratedSize(pair[1], options);
36+
return [a + b - ab, ab, pair[0], pair[1], a, b];
37+
}).filter((extendedPair) => {
38+
// filter pairs that do not have an integratedSize
39+
// meaning they can NOT be integrated!
40+
return extendedPair[1] !== false;
41+
}).sort((a, b) => { // sadly javascript does an inplace sort here
42+
// sort them by size
43+
const diff = b[0] - a[0];
44+
if(diff !== 0) return diff;
45+
return a[1] - b[1];
46+
});
4847

49-
const pair = sortedExtendedPairCombinations[0];
48+
const pair = sortedExtendedPairCombinations[0];
5049

51-
if(pair && pair[2].integrate(pair[3], "limit")) {
52-
chunks.splice(chunks.indexOf(pair[3]), 1);
53-
return true;
54-
}
50+
if(pair && pair[2].integrate(pair[3], "limit")) {
51+
chunks.splice(chunks.indexOf(pair[3]), 1);
52+
return true;
5553
}
5654
});
5755
});

lib/optimize/ModuleConcatenationPlugin.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,14 @@ class ModuleConcatenationPlugin {
8787

8888
relevantModules.push(module);
8989

90-
const chunks = module.getChunks();
91-
9290
// Module must not be the entry points
93-
if(chunks.some(chunk => chunk.entryModule === module)) {
91+
if(module.isEntryModule()) {
9492
setBailoutReason(module, "Module is an entry point");
9593
continue;
9694
}
9795

9896
// Module must be in any chunk (we don't want to do useless work)
99-
if(chunks.length === 0) {
97+
if(module.getNumberOfChunks() === 0) {
10098
setBailoutReason(module, "Module is not in any chunk");
10199
continue;
102100
}

0 commit comments

Comments
 (0)