@@ -23,17 +23,93 @@ const getRequests = chunk => {
23
23
24
24
module . exports = class AutomaticCommonsChunksPlugin {
25
25
constructor ( options ) {
26
- this . options = Object . assign ( { } , {
27
- initialChunks : false ,
28
- minSize : 30000 ,
29
- minChunks : 2 ,
30
- maxRequests : 4 ,
31
- name : undefined , // function(module, chunks) => string | undefined
32
- enforce : undefined // function(module, module) => true | false
33
- } , options ) ;
26
+ this . options = AutomaticCommonsChunksPlugin . normalizeOptions ( options ) ;
34
27
this . alreadyOptimized = new WeakSet ( ) ;
35
28
}
36
29
30
+ static normalizeOptions ( options ) {
31
+ return {
32
+ minSize : options . minSize || 0 ,
33
+ includeInitialChunks : options . includeInitialChunks || false ,
34
+ minChunks : options . minChunks || 2 ,
35
+ maxAsyncRequests : options . maxAsyncRequests || 1 ,
36
+ maxInitialRequests : options . maxInitialRequests || 1 ,
37
+ getName : AutomaticCommonsChunksPlugin . normalizeName ( options . name ) ,
38
+ getCacheGroup : AutomaticCommonsChunksPlugin . normalizeCacheGroups ( options . cacheGroups ) ,
39
+ } ;
40
+ }
41
+
42
+ static normalizeName ( option ) {
43
+ if ( option === true ) {
44
+ return ( module , chunks ) => {
45
+ const names = chunks . map ( c => c . name ) ;
46
+ if ( ! names . every ( Boolean ) ) return ;
47
+ names . sort ( ) ;
48
+ return names . join ( "~" ) ;
49
+ } ;
50
+ }
51
+ if ( typeof option === "string" ) {
52
+ return ( ) => {
53
+ return option ;
54
+ } ;
55
+ }
56
+ if ( typeof option === "function" )
57
+ return option ;
58
+ return ( ) => { } ;
59
+ }
60
+
61
+ static normalizeCacheGroups ( cacheGroups ) {
62
+ if ( typeof cacheGroups === "function" ) {
63
+ return cacheGroups ;
64
+ }
65
+ if ( typeof cacheGroups === "string" || cacheGroups instanceof RegExp ) {
66
+ cacheGroups = {
67
+ "vendors" : cacheGroups
68
+ } ;
69
+ }
70
+ if ( cacheGroups && typeof cacheGroups === "object" ) {
71
+ return ( module , chunks ) => {
72
+ for ( const key of Object . keys ( cacheGroups ) ) {
73
+ let option = cacheGroups [ key ] ;
74
+ if ( option instanceof RegExp || typeof option === "string" ) {
75
+ option = {
76
+ test : option
77
+ } ;
78
+ }
79
+ if ( typeof option === "function" ) {
80
+ const result = option ( module , chunks ) ;
81
+ if ( result ) {
82
+ return Object . assign ( {
83
+ key
84
+ } , result ) ;
85
+ }
86
+ }
87
+ if ( AutomaticCommonsChunksPlugin . checkTest ( option . test , module ) ) {
88
+ return {
89
+ key : key ,
90
+ name : AutomaticCommonsChunksPlugin . normalizeName ( option . name ) ( module , chunks ) ,
91
+ enforce : option . enforce
92
+ } ;
93
+ }
94
+ }
95
+ } ;
96
+ }
97
+ return ( ) => { } ;
98
+ }
99
+
100
+ static checkTest ( test , module ) {
101
+ if ( typeof test === "function" )
102
+ return test ( module ) ;
103
+ if ( ! module . nameForCondition )
104
+ return false ;
105
+ const name = module . nameForCondition ( ) ;
106
+ if ( typeof test === "string" )
107
+ return name . startsWith ( test ) ;
108
+ if ( test instanceof RegExp )
109
+ return test . test ( name ) ;
110
+ return ! ! test ;
111
+ }
112
+
37
113
apply ( compiler ) {
38
114
compiler . hooks . compilation . tap ( "AutomaticCommonsChunksPlugin" , compilation => {
39
115
compilation . hooks . unseal . tap ( "AutomaticCommonsChunksPlugin" , ( ) => {
@@ -46,42 +122,42 @@ module.exports = class AutomaticCommonsChunksPlugin {
46
122
const indexMap = new Map ( ) ;
47
123
let index = 1 ;
48
124
for ( const chunk of chunks ) {
49
- if ( chunk . isInitial ( ) === this . options . initialChunks )
125
+ if ( this . options . includeInitialChunks || ! chunk . isInitial ( ) )
50
126
indexMap . set ( chunk , index ++ ) ;
51
127
}
52
128
// Map a list of chunks to a list of modules
53
129
// For the key the chunk "index" is used, the value is a SortableSet of modules
54
130
const chunksInfoMap = new Map ( ) ;
55
131
// Walk through all modules
56
132
for ( const module of compilation . modules ) {
133
+ // Ignore entry modules
134
+ if ( module . isEntryModule ( ) ) continue ;
57
135
// Get indices of chunks in which this module occurs
58
136
const chunkIndices = Array . from ( module . chunksIterable , chunk => indexMap . get ( chunk ) ) . filter ( Boolean ) ;
59
137
// Get array of chunks
60
138
const chunks = Array . from ( module . chunksIterable ) . filter ( chunk => indexMap . get ( chunk ) !== undefined ) ;
61
- // Get enforce from "enforce" option
62
- let enforce = this . options . enforce ;
63
- if ( typeof enforce === "function" )
64
- enforce = enforce ( module , chunks ) ;
139
+ // Get cache group
140
+ const cacheGroup = this . options . getCacheGroup ( module , chunks ) ;
141
+ const groupKey = cacheGroup === undefined ? undefined : cacheGroup . key ;
142
+ const enforce = cacheGroup === undefined ? false : cacheGroup . enforce ;
143
+ const name = cacheGroup !== undefined ? cacheGroup . name : this . options . getName ( module , chunks ) ;
65
144
// Break if minimum number of chunks is not reached
66
145
if ( ! enforce && chunkIndices . length < this . options . minChunks )
67
146
continue ;
68
- // Get name from "name" option
69
- let name = typeof enforce === "string" ? enforce : this . options . name ;
70
- if ( typeof name === "function" )
71
- name = name ( module , chunks ) ;
72
147
// Create key for maps
73
148
// When it has a name we use the name as key
74
149
// Elsewise we create the key from chunks
75
150
// This automatically merges equal names
76
151
const chunksKey = chunkIndices . sort ( ) . join ( ) ;
77
- let key = name ? name : chunksKey ;
78
- if ( enforce ) key += ",enforced" ;
152
+ let key = name || groupKey || chunksKey ;
153
+ key += ! ! enforce ;
79
154
// Add module to maps
80
155
let info = chunksInfoMap . get ( key ) ;
81
156
if ( info === undefined ) {
82
157
chunksInfoMap . set ( key , info = {
83
158
modules : new SortableSet ( undefined , sortByIdentifier ) ,
84
159
enforce,
160
+ groupKey,
85
161
name,
86
162
chunks : new Map ( ) ,
87
163
reusedableChunks : new Set ( ) ,
@@ -166,8 +242,13 @@ module.exports = class AutomaticCommonsChunksPlugin {
166
242
for ( const chunk of item . chunks . keys ( ) ) {
167
243
// skip if we address ourself
168
244
if ( chunk . name === chunkName || chunk === newChunk ) continue ;
169
- // respect max requests when not a named chunk
170
- if ( ! enforced && getRequests ( chunk ) >= this . options . maxRequests ) continue ;
245
+ // respect max requests when not enforced
246
+ if ( ! enforced ) {
247
+ const maxRequests = chunk . isInitial ( ) ?
248
+ this . options . maxInitialRequests :
249
+ this . options . maxAsyncRequests ;
250
+ if ( getRequests ( chunk ) >= maxRequests ) continue ;
251
+ }
171
252
if ( newChunk === undefined ) {
172
253
// Create the new chunk
173
254
newChunk = compilation . addChunk ( chunkName ) ;
@@ -183,14 +264,14 @@ module.exports = class AutomaticCommonsChunksPlugin {
183
264
// If we successfully creates a new chunk
184
265
if ( isReused ) {
185
266
// Add a note to the chunk
186
- newChunk . chunkReason = " reused as commons chunk";
267
+ newChunk . chunkReason = item . groupKey + ": reused as commons chunk";
187
268
if ( chunkName ) {
188
269
newChunk . chunkReason += " " + chunkName ;
189
270
}
190
271
changed = true ;
191
272
} else if ( newChunk ) {
192
273
// Add a note to the chunk
193
- newChunk . chunkReason = enforced ? "vendors chunk " : "commons chunk" ;
274
+ newChunk . chunkReason = item . groupKey + ": " + ( enforced ? "vendors" : "commons" ) + " chunk";
194
275
// If the choosen name is already an entry point we remove the entry point
195
276
if ( chunkName ) {
196
277
const entrypoint = compilation . entrypoints . get ( chunkName ) ;
0 commit comments