5
5
"use strict" ;
6
6
7
7
const SortableSet = require ( "../util/SortableSet" ) ;
8
+ const GraphHelpers = require ( "../GraphHelpers" ) ;
8
9
9
10
const sortByIdentifier = ( a , b ) => {
10
11
if ( a . identifier ( ) > b . identifier ( ) ) return 1 ;
@@ -13,20 +14,23 @@ const sortByIdentifier = (a, b) => {
13
14
} ;
14
15
15
16
const getRequests = chunk => {
16
- return Math . max (
17
- chunk . mapBlocks ( block => block . chunks . length ) . reduce ( Math . max , 0 ) ,
18
- chunk . mapEntrypoints ( ep => ep . chunks . length ) . reduce ( Math . max , 0 )
19
- ) ;
17
+ let requests = 0 ;
18
+ for ( const chunkGroup of chunk . groupsIterable ) {
19
+ requests = Math . max ( requests , chunkGroup . chunks . length ) ;
20
+ }
21
+ return requests ;
20
22
} ;
21
23
22
24
module . exports = class AutomaticCommonsChunksPlugin {
23
25
constructor ( options ) {
24
26
this . options = Object . assign ( { } , {
25
27
initialChunks : false ,
26
28
minSize : 30000 ,
29
+ minChunks : 2 ,
27
30
maxRequests : 4 ,
28
31
onlyNamed : false ,
29
- name : undefined
32
+ name : undefined , // function(module, chunks) => string | undefined
33
+ enforce : undefined // function(module, module) => true | false
30
34
} , options ) ;
31
35
}
32
36
@@ -42,58 +46,67 @@ module.exports = class AutomaticCommonsChunksPlugin {
42
46
}
43
47
// Map a list of chunks to a list of modules
44
48
// For the key the chunk "index" is used, the value is a SortableSet of modules
45
- const chunksModulesMap = new Map ( ) ;
46
- // Map a list of chunks to a name (not every list of chunks is mapped, only when "name" option is used)
47
- const chunksNameMap = new Map ( ) ;
49
+ const chunksInfoMap = new Map ( ) ;
48
50
// Walk through all modules
49
51
for ( const module of compilation . modules ) {
50
52
// Get indices of chunks in which this module occurs
51
53
const chunkIndices = Array . from ( module . chunksIterable , chunk => indexMap . get ( chunk ) ) . filter ( Boolean ) ;
54
+ // Get array of chunks
55
+ const chunks = Array . from ( module . chunksIterable ) . filter ( chunk => indexMap . get ( chunk ) !== undefined ) ;
56
+ // Get enforce from "enforce" option
57
+ let enforce = this . options . enforce ;
58
+ if ( typeof enforce === "function" )
59
+ enforce = enforce ( module , chunks ) ;
60
+ // Break if minimum number of chunks is not reached
61
+ if ( ! enforce && chunkIndices . length < this . options . minChunks )
62
+ continue ;
52
63
// Get name from "name" option
53
64
let name = this . options . name ;
54
65
if ( typeof name === "function" )
55
- name = name ( module ) ;
56
- if ( name ) {
57
- chunkIndices . push ( `[${ name } ]` ) ;
58
- } else if ( this . options . onlyNamed ) {
59
- // May skip unnamed chunks if "onlyNamed" is used
60
- continue ;
61
- }
62
- // skip for modules which are only in one chunk or don't get a name
63
- if ( chunkIndices . length <= 1 ) continue ;
66
+ name = name ( module , chunks ) ;
64
67
// Create key for maps
65
- const key = chunkIndices . sort ( ) . join ( ) ;
68
+ // When it has a name we use the name as key
69
+ // Elsewise we create the key from chunks
70
+ // This automatically merges equal names
71
+ const chunksKey = chunkIndices . sort ( ) . join ( ) ;
72
+ const key = name ? name : chunksKey ;
66
73
// Add module to maps
67
- let modules = chunksModulesMap . get ( key ) ;
68
- if ( modules === undefined ) {
69
- chunksModulesMap . set ( key , modules = new SortableSet ( undefined , sortByIdentifier ) ) ;
70
- if ( name ) {
71
- // Note name when used
72
- chunksNameMap . set ( key , name ) ;
74
+ let info = chunksInfoMap . get ( key ) ;
75
+ if ( info === undefined ) {
76
+ chunksInfoMap . set ( key , info = {
77
+ modules : new SortableSet ( undefined , sortByIdentifier ) ,
78
+ enforce,
79
+ name,
80
+ chunks : new Set ( ) ,
81
+ chunksKeys : new Set ( )
82
+ } ) ;
83
+ }
84
+ info . modules . add ( module ) ;
85
+ if ( ! info . chunksKeys . has ( chunksKey ) ) {
86
+ info . chunksKeys . add ( chunksKey ) ;
87
+ for ( const chunk of chunks ) {
88
+ info . chunks . add ( chunk ) ;
73
89
}
74
90
}
75
- modules . add ( module ) ;
76
91
}
77
92
// Get size of module lists and sort them by name and size
78
- const entries = Array . from ( chunksModulesMap . entries ( ) , pair => {
79
- const modules = pair [ 1 ] ;
80
- const size = Array . from ( modules , m => m . size ( ) ) . reduce ( ( a , b ) => a + b , 0 ) ;
81
- return {
82
- key : pair [ 0 ] ,
83
- modules,
84
- size
85
- } ;
93
+ const entries = Array . from ( chunksInfoMap . entries ( ) , pair => {
94
+ const info = pair [ 1 ] ;
95
+ info . key = pair [ 0 ] ;
96
+ info . size = Array . from ( info . modules , m => m . size ( ) ) . reduce ( ( a , b ) => a + b , 0 ) ;
97
+ return info ;
98
+ } ) . filter ( item => {
99
+ if ( item . enforce ) return true ;
100
+ // Filter by size limit
101
+ if ( item . size < this . options . minSize ) return false ;
102
+ return true ;
86
103
} ) . sort ( ( a , b ) => {
87
104
// Sort
88
- // 1. by chunk name
89
- const chunkNameA = chunksNameMap . get ( a . key ) ;
90
- const chunkNameB = chunksNameMap . get ( b . key ) ;
91
- if ( chunkNameA && ! chunkNameB ) return - 1 ;
92
- if ( ! chunkNameA && chunkNameB ) return 1 ;
93
- if ( chunkNameA && chunkNameB ) {
94
- if ( chunkNameA < chunkNameB ) return - 1 ;
95
- if ( chunkNameA > chunkNameB ) return 1 ;
96
- }
105
+ // 1. by enforced (enforce first)
106
+ const enforcedA = a . enforce ;
107
+ const enforcedB = b . enforce ;
108
+ if ( enforcedA && ! enforcedB ) return - 1 ;
109
+ if ( ! enforcedA && enforcedB ) return 1 ;
97
110
// 2. by total modules size
98
111
const diffSize = b . size - a . size ;
99
112
if ( diffSize ) return diffSize ;
@@ -120,24 +133,22 @@ module.exports = class AutomaticCommonsChunksPlugin {
120
133
let changed = false ;
121
134
// Walk though all entries
122
135
for ( const item of entries ) {
123
- const chunkName = chunksNameMap . get ( item . key ) ;
124
- // Skip if size is smaller than minimum size
125
- if ( ! chunkName && item . size < this . options . minSize ) continue ;
136
+ const chunkName = item . name ;
137
+ const enforced = item . enforce ;
126
138
// Variable for the new chunk (lazy created)
127
139
let newChunk ;
128
140
// Walk through all chunks
129
- // All modules have the same chunks so we can use the first module
130
- const firstModule = item . modules . values ( ) . next ( ) . value ;
131
- for ( const chunk of firstModule . chunksIterable ) {
141
+ for ( const chunk of item . chunks ) {
132
142
// skip if we address ourself
133
143
if ( chunk . name === chunkName ) continue ;
134
- // only use selected chunks
135
- if ( ! indexMap . get ( chunk ) ) continue ;
136
144
// respect max requests when not a named chunk
137
- if ( ! chunkName && getRequests ( chunk ) >= this . options . maxRequests ) continue ;
145
+ if ( ! enforced && getRequests ( chunk ) >= this . options . maxRequests ) continue ;
138
146
if ( newChunk === undefined ) {
139
147
// Create the new chunk
140
- newChunk = compilation . addChunk ( chunkName ) ;
148
+ newChunk = compilation . addChunk ( ) ;
149
+ if ( chunkName ) {
150
+ newChunk . name = chunkName ;
151
+ }
141
152
}
142
153
// Add graph connections for splitted chunk
143
154
chunk . split ( newChunk ) ;
@@ -149,20 +160,20 @@ module.exports = class AutomaticCommonsChunksPlugin {
149
160
}
150
161
// If we successfully creates a new chunk
151
162
if ( newChunk ) {
163
+ // Add a note to the chunk
164
+ newChunk . chunkReason = enforced ? "vendors chunk" : "commons chunk" ;
152
165
// If the choosen name is already an entry point we remove the entry point
153
166
if ( chunkName ) {
154
- const entrypoint = compilation . entrypoints [ chunkName ] ;
167
+ const entrypoint = compilation . entrypoints . get ( chunkName ) ;
155
168
if ( entrypoint ) {
156
- delete compilation . entrypoints [ chunkName ] ;
169
+ compilation . entrypoints . delete ( chunkName ) ;
157
170
entrypoint . remove ( ) ;
158
171
}
172
+ newChunk . chunkReason += " " + chunkName ;
159
173
}
160
- // Add a note to the chunk
161
- newChunk . chunkReason = chunkName ? "vendors chunk" : "commons chunk" ;
162
174
// Add all modules to the new chunk
163
175
for ( const module of item . modules ) {
164
- newChunk . addModule ( module ) ;
165
- module . addChunk ( newChunk ) ;
176
+ GraphHelpers . connectChunkAndModule ( newChunk , module ) ;
166
177
}
167
178
changed = true ;
168
179
}
0 commit comments