Skip to content

Commit 417044f

Browse files
committed
add AutomaticCommonsChunksPlugin
enable it by default for async chunks add options for initial chunks
1 parent 069a3e6 commit 417044f

File tree

23 files changed

+493
-272
lines changed

23 files changed

+493
-272
lines changed

lib/WebpackOptionsApply.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const SideEffectsFlagPlugin = require("./optimize/SideEffectsFlagPlugin");
5151
const FlagDependencyUsagePlugin = require("./FlagDependencyUsagePlugin");
5252
const FlagDependencyExportsPlugin = require("./FlagDependencyExportsPlugin");
5353
const ModuleConcatenationPlugin = require("./optimize/ModuleConcatenationPlugin");
54+
const AutomaticCommonsChunksPlugin = require("./optimize/AutomaticCommonsChunksPlugin");
5455
const NoEmitOnErrorsPlugin = require("./NoEmitOnErrorsPlugin");
5556
const NamedModulesPlugin = require("./NamedModulesPlugin");
5657
const NamedChunksPlugin = require("./NamedChunksPlugin");
@@ -286,6 +287,40 @@ class WebpackOptionsApply extends OptionsApply {
286287
new FlagDependencyUsagePlugin().apply(compiler);
287288
if(options.optimization.concatenateModules)
288289
new ModuleConcatenationPlugin().apply(compiler);
290+
if(options.optimization.asyncCommonsChunks) {
291+
new AutomaticCommonsChunksPlugin({
292+
initialChunks: false
293+
}).apply(compiler);
294+
}
295+
if(options.optimization.initialCommonsChunks || options.optimization.initialVendorsChunk) {
296+
let nameOption = options.optimization.initialVendorsChunk;
297+
if(nameOption === true) {
298+
nameOption = {
299+
vendors: /[\\/]node_modules[\\/]/
300+
};
301+
}
302+
if(typeof options.optimization.initialVendorChunk === "string") {
303+
nameOption = {
304+
[nameOption]: /[\\/]node_modules[\\/]/
305+
};
306+
}
307+
if(nameOption && typeof nameOption === "object") {
308+
nameOption = module => {
309+
if(!module.nameForCondition) return;
310+
const name = module.nameForCondition();
311+
for(const chunkName of Object.keys(nameOption)) {
312+
const regExp = nameOption[chunkName];
313+
if(regExp.test(name))
314+
return chunkName;
315+
}
316+
};
317+
}
318+
new AutomaticCommonsChunksPlugin({
319+
initialChunks: true,
320+
onlyNamed: !options.optimization.initialCommonsChunks,
321+
name: nameOption
322+
}).apply(compiler);
323+
}
289324
if(options.optimization.noEmitOnErrors)
290325
new NoEmitOnErrorsPlugin().apply(compiler);
291326
if(options.optimization.namedModules)

lib/WebpackOptionsDefaulter.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
164164
this.set("optimization.providedExports", true);
165165
this.set("optimization.usedExports", "make", options => isProductionLikeMode(options));
166166
this.set("optimization.concatenateModules", "make", options => isProductionLikeMode(options));
167+
this.set("optimization.asyncCommonsChunks", true);
168+
this.set("optimization.initialCommonsChunks", false);
169+
this.set("optimization.initialVendorsChunk", false);
167170
this.set("optimization.noEmitOnErrors", "make", options => isProductionLikeMode(options));
168171
this.set("optimization.namedModules", "make", options => options.mode === "development");
169172
this.set("optimization.namedChunks", "make", options => options.mode === "development");
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
"use strict";
6+
7+
module.exports = class AutomaticCommonsChunksPlugin {
8+
constructor(options) {
9+
this.options = Object.assign({}, {
10+
initialChunks: false,
11+
onlyNamed: false,
12+
name: undefined
13+
}, options);
14+
}
15+
16+
apply(compiler) {
17+
compiler.hooks.compilation.tap("AutomaticCommonsChunksPlugin", compilation => {
18+
compilation.hooks.optimizeChunks.tap("AutomaticCommonsChunksPlugin", chunks => {
19+
const indexMap = new Map();
20+
let index = 1;
21+
for(const chunk of chunks) {
22+
if(chunk.isInitial() === this.options.initialChunks)
23+
indexMap.set(chunk, index++);
24+
}
25+
const chunksModulesMap = new Map();
26+
const chunksNameMap = new Map();
27+
for(const module of compilation.modules) {
28+
const chunkIndices = Array.from(module.chunksIterable, chunk => indexMap.get(chunk)).filter(Boolean);
29+
let name = this.options.name;
30+
if(typeof name === "function")
31+
name = name(module);
32+
if(name) {
33+
chunkIndices.push(name);
34+
} else if(this.options.onlyNamed) {
35+
continue;
36+
}
37+
if(chunkIndices.length <= 1) continue;
38+
const key = chunkIndices.sort().join();
39+
let modules = chunksModulesMap.get(key);
40+
if(modules === undefined) {
41+
chunksModulesMap.set(key, modules = []);
42+
if(name) {
43+
chunksNameMap.set(key, name);
44+
}
45+
}
46+
modules.push(module);
47+
}
48+
for(const [key, modules] of chunksModulesMap.entries()) {
49+
let chunkName = chunksNameMap.get(key);
50+
const newChunk = compilation.addChunk(chunkName);
51+
for(const chunk of modules[0].chunksIterable) {
52+
chunk.split(newChunk);
53+
for(const module of modules) {
54+
chunk.removeModule(module);
55+
module.rewriteChunkInReasons(chunk, [newChunk]);
56+
}
57+
}
58+
for(const module of modules) {
59+
newChunk.addModule(module);
60+
module.addChunk(newChunk);
61+
}
62+
}
63+
});
64+
});
65+
}
66+
};

lib/webpack.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ exportPlugins(exports, {
106106
exportPlugins(exports.optimize = {}, {
107107
"AggressiveMergingPlugin": () => require("./optimize/AggressiveMergingPlugin"),
108108
"AggressiveSplittingPlugin": () => require("./optimize/AggressiveSplittingPlugin"),
109+
"AutomaticCommonsChunksPlugin": () => require("./optimize/AutomaticCommonsChunksPlugin"),
109110
"CommonsChunkPlugin": () => require("./optimize/CommonsChunkPlugin"),
110111
"ChunkModuleIdRangePlugin": () => require("./optimize/ChunkModuleIdRangePlugin"),
111112
"DedupePlugin": () => require("./optimize/DedupePlugin"),

schemas/WebpackOptions.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,35 @@
13231323
"description": "Concatenate modules when possible to generate less modules, more efficient code and enable more optimizations by the minimizer",
13241324
"type": "boolean"
13251325
},
1326+
"asyncCommonsChunks": {
1327+
"description": "Automatically deduplicate modules in on-demand chunks by creating additional async-loaded shared chunks",
1328+
"type": "boolean"
1329+
},
1330+
"initialCommonsChunks": {
1331+
"description": "Automatically deduplicate modules in initial chunks by creating additional initial-loaded shared chunks (These chunks need to be added to the HTML)",
1332+
"type": "boolean"
1333+
},
1334+
"initialVendorsChunk": {
1335+
"description": "Automatically extract vendor modules (inside of node_modules) into separate chunk assuming they change less often ",
1336+
"oneOf": [
1337+
{
1338+
"type": "boolean"
1339+
},
1340+
{
1341+
"type": "object",
1342+
"additionalProperties": {
1343+
"instanceof": "RegExp"
1344+
}
1345+
},
1346+
{
1347+
"type": "string",
1348+
"minLength": 1
1349+
},
1350+
{
1351+
"instanceof": "Function"
1352+
}
1353+
]
1354+
},
13261355
"noEmitOnErrors": {
13271356
"description": "Avoid emitting assets when errors occur",
13281357
"type": "boolean"
Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,34 @@
1-
Hash: 68735d6d492523fdd0c6
1+
Hash: 0f07aca3bcc352ccf4df
22
Time: Xms
33
Asset Size Chunks Chunk Names
44
8111929f6761a49abd03.js 1.96 KiB 0 [emitted]
55
eed136bb9b1f0de139a9.js 1.94 KiB 1 [emitted]
6-
127dcc911b6e4c95204d.js 1 KiB 2 [emitted]
7-
2bfb7afd1f901f4f309f.js 1.94 KiB 3 [emitted]
6+
a787132a2376949dd13f.js 1.94 KiB 2 [emitted]
7+
c04e6c1042793fb803ed.js 1 KiB 3 [emitted]
88
e013686b7b1ce4bd99ba.js 1.94 KiB 4 [emitted]
9-
5ac554478945d88843c9.js 1 KiB 5 [emitted]
9+
af4d0eb806c83010aa9c.js 1 KiB 5 [emitted]
1010
2eb796a695b728235608.js 1.01 KiB 6 [emitted]
11-
8d11ec270922149a5452.js 8.37 KiB 7 [emitted] main
12-
Entrypoint main = 8d11ec270922149a5452.js
13-
chunk {0} 8111929f6761a49abd03.js 1.76 KiB {7} [recorded]
14-
> aggressive-splitted duplicate [11] ./index.js 4:0-51
15-
> aggressive-splitted duplicate [11] ./index.js 5:0-44
16-
> aggressive-splitted duplicate [11] ./index.js 6:0-72
11+
810442a28510d745748d.js 8.31 KiB 7 [emitted] main
12+
Entrypoint main = 810442a28510d745748d.js
13+
chunk {0} 8111929f6761a49abd03.js 1.76 KiB {7}
1714
[3] ./f.js 899 bytes {0} [built]
1815
[4] ./g.js 901 bytes {0} [built]
19-
chunk {1} eed136bb9b1f0de139a9.js 1.76 KiB {7} [recorded]
20-
> aggressive-splitted duplicate [11] ./index.js 3:0-30
21-
> aggressive-splitted duplicate [11] ./index.js 5:0-44
22-
> aggressive-splitted duplicate [11] ./index.js 6:0-72
16+
chunk {1} eed136bb9b1f0de139a9.js 1.76 KiB {7}
2317
[1] ./d.js 899 bytes {1} [built]
2418
[2] ./e.js 899 bytes {1} [built]
25-
chunk {2} 127dcc911b6e4c95204d.js 899 bytes {7}
26-
> aggressive-splitted duplicate [11] ./index.js 2:0-23
27-
> aggressive-splitted duplicate [11] ./index.js 3:0-30
28-
[5] ./c.js 899 bytes {2} [built]
29-
chunk {3} 2bfb7afd1f901f4f309f.js 1.76 KiB {7} [recorded]
30-
> aggressive-splitted duplicate [11] ./index.js 4:0-51
31-
> aggressive-splitted duplicate [11] ./index.js 6:0-72
32-
[8] ./j.js 901 bytes {3} [built]
33-
[9] ./k.js 899 bytes {3} [built]
19+
chunk {2} a787132a2376949dd13f.js 1.76 KiB {7} [recorded]
20+
[8] ./j.js 901 bytes {2} [built]
21+
[9] ./k.js 899 bytes {2} [built]
22+
chunk {3} c04e6c1042793fb803ed.js 899 bytes {7}
23+
[0] ./b.js 899 bytes {3} [built]
3424
chunk {4} e013686b7b1ce4bd99ba.js 1.76 KiB {7} [recorded]
35-
> aggressive-splitted duplicate [11] ./index.js 4:0-51
36-
> aggressive-splitted duplicate [11] ./index.js 6:0-72
3725
[6] ./h.js 899 bytes {4} [built]
3826
[7] ./i.js 899 bytes {4} [built]
39-
chunk {5} 5ac554478945d88843c9.js 899 bytes {7}
40-
> aggressive-splitted duplicate [11] ./index.js 2:0-23
41-
> aggressive-splitted duplicate [11] ./index.js 5:0-44
42-
> aggressive-splitted duplicate [11] ./index.js 6:0-72
43-
[0] ./b.js 899 bytes {5} [built]
27+
chunk {5} af4d0eb806c83010aa9c.js 899 bytes {7}
28+
[5] ./c.js 899 bytes {5} [built]
4429
chunk {6} 2eb796a695b728235608.js 899 bytes {7}
4530
> [11] ./index.js 1:0-16
4631
[10] ./a.js 899 bytes {6} [built]
47-
chunk {7} 8d11ec270922149a5452.js (main) 248 bytes [entry]
32+
chunk {7} 810442a28510d745748d.js (main) 248 bytes [entry]
4833
> main [11] ./index.js
4934
[11] ./index.js 248 bytes {7} [built]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import "./d";
2+
import "./e";
3+
import "x";
4+
import "y";
5+
export default "a";
6+
import("./g");
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import "./d";
2+
import "./f";
3+
import "x";
4+
import "y";
5+
export default "b";
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import "./d";
2+
import "./f";
3+
import "x";
4+
import "z";
5+
export default "c";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default "d";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default "e";
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
Entrypoint main = main.js
2+
Entrypoint a = 2.js 3.js a.js
3+
Entrypoint b = 2.js 3.js 0.js b.js
4+
Entrypoint c = 2.js 0.js c.js
5+
chunk {0} 0.js 20 bytes {2} {3} {4} {7} {8} [initial] [rendered]
6+
[2] ./f.js 20 bytes {0} [built]
7+
chunk {1} 1.chunk.js 34 bytes {2} {3} {4} {8} [rendered]
8+
> [] 6:0-13
9+
[8] ./g.js 34 bytes {1} [built]
10+
chunk {2} 2.js 40 bytes {7} [entry] [rendered]
11+
[0] ./d.js 20 bytes {2} [built]
12+
[1] ./node_modules/x.js 20 bytes {2} [built]
13+
chunk {3} 3.js 20 bytes {7} [initial] [rendered]
14+
[3] ./node_modules/y.js 20 bytes {3} [built]
15+
chunk {4} 4.chunk.js 122 bytes {7} [rendered]
16+
> [7] ./index.js 1:0-13
17+
[5] ./a.js + 1 modules 122 bytes {4} {8} [built]
18+
| ./a.js 87 bytes [built]
19+
| ./e.js 20 bytes [built]
20+
chunk {5} 5.chunk.js 72 bytes {7} [rendered]
21+
> [7] ./index.js 2:0-13
22+
[4] ./b.js 72 bytes {5} {9} [built]
23+
chunk {6} 6.chunk.js 107 bytes {7} [rendered]
24+
> [7] ./index.js 3:0-13
25+
[6] ./c.js + 1 modules 107 bytes {6} {10} [built]
26+
| ./c.js 72 bytes [built]
27+
| ./node_modules/z.js 20 bytes [built]
28+
chunk {7} main.js (main) 45 bytes [entry] [rendered]
29+
> main [7] ./index.js
30+
[7] ./index.js 45 bytes {7} [built]
31+
chunk {8} a.js (a) 122 bytes [initial] [rendered]
32+
> a []
33+
[5] ./a.js + 1 modules 122 bytes {4} {8} [built]
34+
| ./a.js 87 bytes [built]
35+
| ./e.js 20 bytes [built]
36+
chunk {9} b.js (b) 72 bytes [initial] [rendered]
37+
> b [4] ./b.js
38+
[4] ./b.js 72 bytes {5} {9} [built]
39+
chunk {10} c.js (c) 107 bytes [initial] [rendered]
40+
> c []
41+
[6] ./c.js + 1 modules 107 bytes {6} {10} [built]
42+
| ./c.js 72 bytes [built]
43+
| ./node_modules/z.js 20 bytes [built]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default "f";
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import "./f";
2+
export default "g";
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import("./a");
2+
import("./b");
3+
import("./c");

test/statsCases/async-commons-chunk-auto/node_modules/x.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/statsCases/async-commons-chunk-auto/node_modules/y.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/statsCases/async-commons-chunk-auto/node_modules/z.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module.exports = {
2+
mode: "production",
3+
entry: {
4+
main: "./",
5+
a: "./a",
6+
b: "./b",
7+
c: "./c"
8+
},
9+
output: {
10+
chunkFilename: "[name].chunk.js"
11+
},
12+
optimization: {
13+
initialVendorChunk: "libs"
14+
},
15+
stats: {
16+
hash: false,
17+
timings: false,
18+
assets: false,
19+
chunks: true,
20+
chunkOrigins: true,
21+
entrypoints: true,
22+
modules: false
23+
}
24+
};

0 commit comments

Comments
 (0)