Skip to content

Commit 5c8a0d2

Browse files
committed
Merge branch 'master' into next
# Conflicts: # test/statsCases/aggressive-splitting-entry/expected.txt # test/statsCases/aggressive-splitting-on-demand/expected.txt # test/statsCases/chunk-module-id-range/expected.txt # test/statsCases/chunks-development/expected.txt # test/statsCases/chunks/expected.txt # test/statsCases/color-disabled/expected.txt # test/statsCases/color-enabled-custom/expected.txt # test/statsCases/color-enabled/expected.txt # test/statsCases/commons-chunk-min-size-0/expected.txt # test/statsCases/commons-chunk-min-size-Infinity/expected.txt # test/statsCases/commons-plugin-issue-4980/expected.txt # test/statsCases/define-plugin/expected.txt # test/statsCases/exclude-with-loader/expected.txt # test/statsCases/external/expected.txt # test/statsCases/filter-warnings/expected.txt # test/statsCases/import-context-filter/expected.txt # test/statsCases/import-weak/expected.txt # test/statsCases/limit-chunk-count-plugin/expected.txt # test/statsCases/max-modules-default/expected.txt # test/statsCases/max-modules/expected.txt # test/statsCases/module-assets/expected.txt # test/statsCases/named-chunks-plugin-async/expected.txt # test/statsCases/named-chunks-plugin/expected.txt # test/statsCases/no-emit-on-errors-plugin-with-child-error/expected.txt # test/statsCases/optimize-chunks/expected.txt # test/statsCases/performance-different-mode-and-target/expected.txt # test/statsCases/preset-detailed/expected.txt # test/statsCases/preset-normal/expected.txt # test/statsCases/preset-verbose/expected.txt # test/statsCases/resolve-plugin-context/expected.txt # test/statsCases/reverse-sort-modules/expected.txt # test/statsCases/scope-hoisting-bailouts/expected.txt # test/statsCases/scope-hoisting-multi/expected.txt # test/statsCases/simple-more-info/expected.txt # test/statsCases/simple/expected.txt # test/statsCases/tree-shaking/expected.txt # test/statsCases/warnings-uglifyjs/expected.txt
2 parents 52d5eb7 + 7543c45 commit 5c8a0d2

File tree

99 files changed

+3085
-2586
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+3085
-2586
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ script: npm run travis:$JOB_PART
4343

4444
after_success:
4545
- cat ./coverage/lcov.info | node_modules/.bin/coveralls --verbose
46-
- bash <(curl -s https://codecov.io/bash) -F $JOB_PART --disable=gcov
46+
- bash <(curl -s https://codecov.io/bash) -F $JOB_PART -X gcov
4747
- rm -rf ./coverage
4848

4949
notifications:

appveyor.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ matrix:
3535

3636
test_script:
3737
- node --version
38-
- npm --version
3938
- yarn --version
4039
- cmd: set JEST=%jest%
41-
- cmd: npm run appveyor:%job_part%
42-
- cmd: npm install -g codecov && codecov -F %job_part% --disable=gcov
40+
- cmd: yarn appveyor:%job_part%
41+
- cmd: yarn unlink webpack
42+
- cmd: yarn global add codecov && codecov -F %job_part% --disable=gcov

lib/Compilation.js

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,61 @@ class Compilation extends Tapable {
11681168
const chunkDependencies = new Map(); // Map<Chunk, Array<{Module, Chunk}>>
11691169
const allCreatedChunkGroups = new Set();
11701170

1171+
// PREPARE
1172+
const blockInfoMap = new Map();
1173+
1174+
const iteratorDependency = d => {
1175+
// We skip Dependencies without Reference
1176+
const ref = d.getReference();
1177+
if (!ref) {
1178+
return;
1179+
}
1180+
// We skip Dependencies without Module pointer
1181+
const refModule = ref.module;
1182+
if (!refModule) {
1183+
return;
1184+
}
1185+
// We skip weak Dependencies
1186+
if (ref.weak) {
1187+
return;
1188+
}
1189+
1190+
blockInfoModules.add(refModule);
1191+
};
1192+
1193+
const iteratorBlockPrepare = b => {
1194+
blockInfoBlocks.push(b);
1195+
blockQueue.push(b);
1196+
};
1197+
1198+
let block, blockQueue, blockInfoModules, blockInfoBlocks;
1199+
for (const module of this.modules) {
1200+
blockQueue = [module];
1201+
while (blockQueue.length > 0) {
1202+
block = blockQueue.pop();
1203+
blockInfoModules = new Set();
1204+
blockInfoBlocks = [];
1205+
1206+
if (block.variables) {
1207+
iterationBlockVariable(block.variables, iteratorDependency);
1208+
}
1209+
1210+
if (block.dependencies) {
1211+
iterationOfArrayCallback(block.dependencies, iteratorDependency);
1212+
}
1213+
1214+
if (block.blocks) {
1215+
iterationOfArrayCallback(block.blocks, iteratorBlockPrepare);
1216+
}
1217+
1218+
const blockInfo = {
1219+
modules: blockInfoModules,
1220+
blocks: blockInfoBlocks
1221+
};
1222+
blockInfoMap.set(block, blockInfo);
1223+
}
1224+
}
1225+
11711226
// PART ONE
11721227

11731228
const blockChunkGroups = new Map();
@@ -1180,7 +1235,7 @@ class Compilation extends Tapable {
11801235
chunkGroup
11811236
}));
11821237

1183-
let module, block, chunk, chunkGroup;
1238+
let module, chunk, chunkGroup;
11841239

11851240
// For each async Block in graph
11861241
const iteratorBlock = b => {
@@ -1227,36 +1282,6 @@ class Compilation extends Tapable {
12271282
});
12281283
};
12291284

1230-
// For each Dependency in the graph
1231-
const iteratorDependency = d => {
1232-
// We skip Dependencies without Reference
1233-
const ref = d.getReference();
1234-
if (!ref) {
1235-
return;
1236-
}
1237-
// We skip Dependencies without Module pointer
1238-
const refModule = ref.module;
1239-
if (!refModule) {
1240-
return;
1241-
}
1242-
// We skip weak Dependencies
1243-
if (ref.weak) {
1244-
return;
1245-
}
1246-
// We connect Module and Chunk when not already done
1247-
if (chunk.addModule(refModule)) {
1248-
refModule.addChunk(chunk);
1249-
1250-
// And enqueue the Module for traversal
1251-
queue.push({
1252-
block: refModule,
1253-
module: refModule,
1254-
chunk,
1255-
chunkGroup
1256-
});
1257-
}
1258-
};
1259-
12601285
// Iterative traversal of the Module graph
12611286
// Recursive would be simpler to write but could result in Stack Overflows
12621287
while (queue.length) {
@@ -1266,18 +1291,27 @@ class Compilation extends Tapable {
12661291
chunk = queueItem.chunk;
12671292
chunkGroup = queueItem.chunkGroup;
12681293

1269-
// Traverse all variables, Dependencies and Blocks
1270-
if (block.variables) {
1271-
iterationBlockVariable(block.variables, iteratorDependency);
1272-
}
1273-
1274-
if (block.dependencies) {
1275-
iterationOfArrayCallback(block.dependencies, iteratorDependency);
1294+
// get prepared block info
1295+
const blockInfo = blockInfoMap.get(block);
1296+
1297+
// Traverse all referenced modules
1298+
for (const refModule of blockInfo.modules) {
1299+
// We connect Module and Chunk when not already done
1300+
if (chunk.addModule(refModule)) {
1301+
refModule.addChunk(chunk);
1302+
1303+
// And enqueue the Module for traversal
1304+
queue.push({
1305+
block: refModule,
1306+
module: refModule,
1307+
chunk,
1308+
chunkGroup
1309+
});
1310+
}
12761311
}
12771312

1278-
if (block.blocks) {
1279-
iterationOfArrayCallback(block.blocks, iteratorBlock);
1280-
}
1313+
// Traverse all Blocks
1314+
iterationOfArrayCallback(blockInfo.blocks, iteratorBlock);
12811315
}
12821316

12831317
// PART TWO

lib/HotModuleReplacementPlugin.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ const ModuleHotDeclineDependency = require("./dependencies/ModuleHotDeclineDepen
1212
const ConstDependency = require("./dependencies/ConstDependency");
1313
const NullFactory = require("./NullFactory");
1414
const ParserHelpers = require("./ParserHelpers");
15-
const createHash = require("./util/createHash");
1615

1716
module.exports = class HotModuleReplacementPlugin {
1817
constructor(options) {
@@ -74,9 +73,7 @@ module.exports = class HotModuleReplacementPlugin {
7473
records.moduleHashs = {};
7574
for (const module of compilation.modules) {
7675
const identifier = module.identifier();
77-
const hash = createHash(compilation.outputOptions.hashFunction);
78-
module.updateHash(hash);
79-
records.moduleHashs[identifier] = hash.digest("hex");
76+
records.moduleHashs[identifier] = module.hash;
8077
}
8178
records.chunkHashs = {};
8279
for (const chunk of compilation.chunks) {
@@ -136,9 +133,7 @@ module.exports = class HotModuleReplacementPlugin {
136133
return;
137134
for (const module of compilation.modules) {
138135
const identifier = module.identifier();
139-
let hash = createHash(compilation.outputOptions.hashFunction);
140-
module.updateHash(hash);
141-
hash = hash.digest("hex");
136+
let hash = module.hash;
142137
module.hotUpdate = records.moduleHashs[identifier] !== hash;
143138
}
144139
const hotUpdateMainContent = {

lib/NamedModulesPlugin.js

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

7+
const createHash = require("./util/createHash");
8+
const RequestShortener = require("./RequestShortener");
9+
10+
const getHash = str => {
11+
const hash = createHash("md4");
12+
hash.update(str);
13+
return hash.digest("hex").substr(0, 4);
14+
};
15+
716
class NamedModulesPlugin {
817
constructor(options) {
918
this.options = options || {};
@@ -12,11 +21,32 @@ class NamedModulesPlugin {
1221
apply(compiler) {
1322
compiler.hooks.compilation.tap("NamedModulesPlugin", compilation => {
1423
compilation.hooks.beforeModuleIds.tap("NamedModulesPlugin", modules => {
24+
const namedModules = new Map();
25+
const context = this.options.context || compiler.options.context;
26+
1527
for (const module of modules) {
1628
if (module.id === null && module.libIdent) {
17-
module.id = module.libIdent({
18-
context: this.options.context || compiler.options.context
19-
});
29+
module.id = module.libIdent({ context });
30+
}
31+
32+
if (module.id !== null) {
33+
const namedModule = namedModules.get(module.id);
34+
if (namedModule !== undefined) {
35+
namedModule.push(module);
36+
} else {
37+
namedModules.set(module.id, [module]);
38+
}
39+
}
40+
}
41+
42+
for (const namedModule of namedModules.values()) {
43+
if (namedModule.length > 1) {
44+
for (const module of namedModule) {
45+
const requestShortener = new RequestShortener(context);
46+
module.id = `${module.id}?${getHash(
47+
requestShortener.shorten(module.identifier())
48+
)}`;
49+
}
2050
}
2151
}
2252
});

lib/WebpackOptionsDefaulter.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,17 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
215215
);
216216
this.set("optimization.splitChunks", {});
217217
this.set("optimization.splitChunks.chunks", "async");
218-
this.set("optimization.splitChunks.minSize", 30000);
218+
this.set("optimization.splitChunks.minSize", "make", options => {
219+
return isProductionLikeMode(options) ? 30000 : 10000;
220+
});
219221
this.set("optimization.splitChunks.minChunks", 1);
220-
this.set("optimization.splitChunks.maxAsyncRequests", 5);
222+
this.set("optimization.splitChunks.maxAsyncRequests", "make", options => {
223+
return isProductionLikeMode(options) ? 5 : Infinity;
224+
});
221225
this.set("optimization.splitChunks.automaticNameDelimiter", "~");
222-
this.set("optimization.splitChunks.maxInitialRequests", 3);
226+
this.set("optimization.splitChunks.maxInitialRequests", "make", options => {
227+
return isProductionLikeMode(options) ? 3 : Infinity;
228+
});
223229
this.set("optimization.splitChunks.name", true);
224230
this.set("optimization.splitChunks.cacheGroups", {});
225231
this.set("optimization.splitChunks.cacheGroups.default", {

lib/optimize/FlagIncludedChunksPlugin.js

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,85 @@ class FlagIncludedChunksPlugin {
1010
compilation.hooks.optimizeChunkIds.tap(
1111
"FlagIncludedChunksPlugin",
1212
chunks => {
13+
// prepare two bit integers for each module
14+
// 2^31 is the max number represented as SMI in v8
15+
// we want the bits distributed this way:
16+
// the bit 2^31 is pretty rar and only one module should get it
17+
// so it has a probability of 1 / modulesCount
18+
// the first bit (2^0) is the easiest and every module could get it
19+
// if it doesn't get a better bit
20+
// from bit 2^n to 2^(n+1) there is a probability of p
21+
// so 1 / modulesCount == p^31
22+
// <=> p = sqrt31(1 / modulesCount)
23+
// so we use a modulo of 1 / sqrt31(1 / modulesCount)
24+
const moduleBits = new WeakMap();
25+
const modulesCount = compilation.modules.length;
26+
27+
// precalculate the modulo values for each bit
28+
const modulo = 1 / Math.pow(1 / modulesCount, 1 / 31);
29+
const modulos = Array.from(
30+
{ length: 31 },
31+
(x, i) => Math.pow(modulo, i) | 0
32+
);
33+
34+
// iterate all modules to generate bit values
35+
let i = 0;
36+
for (const module of compilation.modules) {
37+
let bit = 30;
38+
while (i % modulos[bit] !== 0) {
39+
bit--;
40+
}
41+
moduleBits.set(module, 1 << bit);
42+
i++;
43+
}
44+
45+
// interate all chunks to generate bitmaps
46+
const chunkModulesHash = new WeakMap();
47+
for (const chunk of chunks) {
48+
let hash = 0;
49+
for (const module of chunk.modulesIterable) {
50+
hash |= moduleBits.get(module);
51+
}
52+
chunkModulesHash.set(chunk, hash);
53+
}
54+
1355
for (const chunkA of chunks) {
14-
loopB: for (const chunkB of chunks) {
56+
const chunkAHash = chunkModulesHash.get(chunkA);
57+
const chunkAModulesCount = chunkA.getNumberOfModules();
58+
if (chunkAModulesCount === 0) continue;
59+
let bestModule = undefined;
60+
for (const module of chunkA.modulesIterable) {
61+
if (
62+
bestModule === undefined ||
63+
bestModule.getNumberOfChunks() > module.getNumberOfChunks()
64+
)
65+
bestModule = module;
66+
}
67+
loopB: for (const chunkB of bestModule.chunksIterable) {
1568
// as we iterate the same iterables twice
1669
// skip if we find ourselves
17-
if (chunkA === chunkB) continue loopB;
70+
if (chunkA === chunkB) continue;
71+
72+
const chunkBModulesCount = chunkB.getNumberOfModules();
73+
74+
// ids for empty chunks are not included
75+
if (chunkBModulesCount === 0) continue;
1876

1977
// instead of swapping A and B just bail
2078
// as we loop twice the current A will be B and B then A
21-
if (chunkA.getNumberOfModules() < chunkB.getNumberOfModules())
22-
continue loopB;
79+
if (chunkAModulesCount > chunkBModulesCount) continue;
80+
81+
// is chunkA in chunkB?
2382

24-
if (chunkB.getNumberOfModules() === 0) continue loopB;
83+
// we do a cheap check for the hash value
84+
const chunkBHash = chunkModulesHash.get(chunkB);
85+
if ((chunkBHash & chunkAHash) !== chunkAHash) continue;
2586

26-
// is chunkB in chunkA?
27-
for (const m of chunkB.modulesIterable) {
28-
if (!chunkA.containsModule(m)) continue loopB;
87+
// compare all modules
88+
for (const m of chunkA.modulesIterable) {
89+
if (!chunkB.containsModule(m)) continue loopB;
2990
}
30-
chunkA.ids.push(chunkB.id);
91+
chunkB.ids.push(chunkA.id);
3192
}
3293
}
3394
}

lib/optimize/RemoveEmptyChunksPlugin.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,18 @@ class RemoveEmptyChunksPlugin {
2222
"RemoveEmptyChunksPlugin",
2323
handler
2424
);
25+
compilation.hooks.optimizeChunksAdvanced.tap(
26+
"RemoveEmptyChunksPlugin",
27+
handler
28+
);
2529
compilation.hooks.optimizeExtractedChunksBasic.tap(
2630
"RemoveEmptyChunksPlugin",
2731
handler
2832
);
33+
compilation.hooks.optimizeExtractedChunksAdvanced.tap(
34+
"RemoveEmptyChunksPlugin",
35+
handler
36+
);
2937
});
3038
}
3139
}

0 commit comments

Comments
 (0)