@@ -8,9 +8,16 @@ const crypto = require("crypto");
8
8
const SortableSet = require ( "../util/SortableSet" ) ;
9
9
const GraphHelpers = require ( "../GraphHelpers" ) ;
10
10
const { isSubset } = require ( "../util/SetHelpers" ) ;
11
+ const deterministicGrouping = require ( "../util/deterministicGrouping" ) ;
12
+ const contextify = require ( "../util/identifier" ) . contextify ;
11
13
14
+ /** @typedef {import("../Compiler") } Compiler */
12
15
/** @typedef {import("../Chunk") } Chunk */
13
16
/** @typedef {import("../Module") } Module */
17
+ /** @typedef {import("../util/deterministicGrouping").Options<Module> } DeterministicGroupingOptionsForModule */
18
+ /** @typedef {import("../util/deterministicGrouping").GroupedItems<Module> } DeterministicGroupingGroupedItemsForModule */
19
+
20
+ const deterministicGroupingForModules = /** @type {function(DeterministicGroupingOptionsForModule): DeterministicGroupingGroupedItemsForModule[] } */ ( deterministicGrouping ) ;
14
21
15
22
const hashFilename = name => {
16
23
return crypto
@@ -104,6 +111,7 @@ module.exports = class SplitChunksPlugin {
104
111
options . chunks || "all"
105
112
) ,
106
113
minSize : options . minSize || 0 ,
114
+ maxSize : options . maxSize || 0 ,
107
115
minChunks : options . minChunks || 1 ,
108
116
maxAsyncRequests : options . maxAsyncRequests || 1 ,
109
117
maxInitialRequests : options . maxInitialRequests || 1 ,
@@ -112,11 +120,16 @@ module.exports = class SplitChunksPlugin {
112
120
name : options . name ,
113
121
automaticNameDelimiter : options . automaticNameDelimiter
114
122
} ) || ( ( ) => { } ) ,
123
+ hidePathInfo : options . hidePathInfo || false ,
115
124
filename : options . filename || undefined ,
116
125
getCacheGroups : SplitChunksPlugin . normalizeCacheGroups ( {
117
126
cacheGroups : options . cacheGroups ,
118
127
automaticNameDelimiter : options . automaticNameDelimiter
119
- } )
128
+ } ) ,
129
+ fallbackCacheGroup : SplitChunksPlugin . normalizeFallbackCacheGroup (
130
+ options . fallbackCacheGroup || { } ,
131
+ options
132
+ )
120
133
} ;
121
134
}
122
135
@@ -177,6 +190,26 @@ module.exports = class SplitChunksPlugin {
177
190
if ( typeof chunks === "function" ) return chunks ;
178
191
}
179
192
193
+ static normalizeFallbackCacheGroup (
194
+ {
195
+ minSize = undefined ,
196
+ maxSize = undefined ,
197
+ automaticNameDelimiter = undefined
198
+ } ,
199
+ {
200
+ minSize : defaultMinSize = undefined ,
201
+ maxSize : defaultMaxSize = undefined ,
202
+ automaticNameDelimiter : defaultAutomaticNameDelimiter = undefined
203
+ }
204
+ ) {
205
+ return {
206
+ minSize : typeof minSize === "number" ? minSize : defaultMinSize || 0 ,
207
+ maxSize : typeof maxSize === "number" ? maxSize : defaultMaxSize || 0 ,
208
+ automaticNameDelimiter :
209
+ automaticNameDelimiter || defaultAutomaticNameDelimiter || "~"
210
+ } ;
211
+ }
212
+
180
213
static normalizeCacheGroups ( { cacheGroups, automaticNameDelimiter } ) {
181
214
if ( typeof cacheGroups === "function" ) {
182
215
// TODO webpack 5 remove this
@@ -225,6 +258,7 @@ module.exports = class SplitChunksPlugin {
225
258
) ,
226
259
enforce : option . enforce ,
227
260
minSize : option . minSize ,
261
+ maxSize : option . maxSize ,
228
262
minChunks : option . minChunks ,
229
263
maxAsyncRequests : option . maxAsyncRequests ,
230
264
maxInitialRequests : option . maxInitialRequests ,
@@ -278,6 +312,10 @@ module.exports = class SplitChunksPlugin {
278
312
return false ;
279
313
}
280
314
315
+ /**
316
+ * @param {Compiler } compiler webpack compiler
317
+ * @returns {void }
318
+ */
281
319
apply ( compiler ) {
282
320
compiler . hooks . thisCompilation . tap ( "SplitChunksPlugin" , compilation => {
283
321
let alreadyOptimized = false ;
@@ -486,6 +524,12 @@ module.exports = class SplitChunksPlugin {
486
524
: cacheGroupSource . enforce
487
525
? 0
488
526
: this . options . minSize ,
527
+ maxSize :
528
+ cacheGroupSource . maxSize !== undefined
529
+ ? cacheGroupSource . maxSize
530
+ : cacheGroupSource . enforce
531
+ ? 0
532
+ : this . options . maxSize ,
489
533
minChunks :
490
534
cacheGroupSource . minChunks !== undefined
491
535
? cacheGroupSource . minChunks
@@ -537,6 +581,9 @@ module.exports = class SplitChunksPlugin {
537
581
}
538
582
}
539
583
584
+ /** @type {Map<Chunk, {minSize: number, maxSize: number, automaticNameDelimiter: string}> } */
585
+ const maxSizeQueueMap = new Map ( ) ;
586
+
540
587
while ( chunksInfoMap . size > 0 ) {
541
588
// Find best matching entry
542
589
let bestEntryKey ;
@@ -563,6 +610,7 @@ module.exports = class SplitChunksPlugin {
563
610
564
611
let chunkName = item . name ;
565
612
// Variable for the new chunk (lazy created)
613
+ /** @type {Chunk } */
566
614
let newChunk ;
567
615
// When no chunk name, check if we can reuse a chunk instead of creating a new one
568
616
let isReused = false ;
@@ -689,6 +737,22 @@ module.exports = class SplitChunksPlugin {
689
737
}
690
738
}
691
739
}
740
+
741
+ if ( item . cacheGroup . maxSize > 0 ) {
742
+ const oldMaxSizeSettings = maxSizeQueueMap . get ( newChunk ) ;
743
+ maxSizeQueueMap . set ( newChunk , {
744
+ minSize : Math . max (
745
+ oldMaxSizeSettings ? oldMaxSizeSettings . minSize : 0 ,
746
+ item . cacheGroup . minSize
747
+ ) ,
748
+ maxSize : Math . min (
749
+ oldMaxSizeSettings ? oldMaxSizeSettings . maxSize : Infinity ,
750
+ item . cacheGroup . maxSize
751
+ ) ,
752
+ automaticNameDelimiter : item . cacheGroup . automaticNameDelimiter
753
+ } ) ;
754
+ }
755
+
692
756
// remove all modules from other entries and update size
693
757
for ( const [ key , info ] of chunksInfoMap ) {
694
758
if ( isOverlap ( info . chunks , item . chunks ) ) {
@@ -709,6 +773,76 @@ module.exports = class SplitChunksPlugin {
709
773
}
710
774
}
711
775
}
776
+
777
+ // Make sure that maxSize is fulfilled
778
+ for ( const chunk of compilation . chunks . slice ( ) ) {
779
+ const { minSize, maxSize, automaticNameDelimiter } =
780
+ maxSizeQueueMap . get ( chunk ) || this . options . fallbackCacheGroup ;
781
+ if ( ! maxSize ) continue ;
782
+ const results = deterministicGroupingForModules ( {
783
+ maxSize,
784
+ minSize,
785
+ items : chunk . modulesIterable ,
786
+ getKey ( module ) {
787
+ const ident = contextify (
788
+ compilation . options . context ,
789
+ module . identifier ( )
790
+ ) ;
791
+ const name = module . nameForCondition
792
+ ? contextify (
793
+ compilation . options . context ,
794
+ module . nameForCondition ( )
795
+ )
796
+ : ident . replace ( / ^ .* ! | \? [ ^ ? ! ] * $ / g, "" ) ;
797
+ const fullKey =
798
+ name + automaticNameDelimiter + hashFilename ( ident ) ;
799
+ return fullKey . replace ( / [ \\ / ? ] / g, "_" ) ;
800
+ } ,
801
+ getSize ( module ) {
802
+ return module . size ( ) ;
803
+ }
804
+ } ) ;
805
+ results . sort ( ( a , b ) => {
806
+ if ( a . key < b . key ) return - 1 ;
807
+ if ( a . key > b . key ) return 1 ;
808
+ return 0 ;
809
+ } ) ;
810
+ for ( let i = 0 ; i < results . length ; i ++ ) {
811
+ const group = results [ i ] ;
812
+ const key = this . options . hidePathInfo
813
+ ? hashFilename ( group . key )
814
+ : group . key ;
815
+ let name = chunk . name
816
+ ? chunk . name + automaticNameDelimiter + key
817
+ : null ;
818
+ if ( name && name . length > 100 ) {
819
+ name =
820
+ name . slice ( 0 , 100 ) +
821
+ automaticNameDelimiter +
822
+ hashFilename ( name ) ;
823
+ }
824
+ let newPart ;
825
+ if ( i !== results . length - 1 ) {
826
+ newPart = compilation . addChunk ( name ) ;
827
+ chunk . split ( newPart ) ;
828
+ // Add all modules to the new chunk
829
+ for ( const module of group . items ) {
830
+ if ( typeof module . chunkCondition === "function" ) {
831
+ if ( ! module . chunkCondition ( newPart ) ) continue ;
832
+ }
833
+ // Add module to new chunk
834
+ GraphHelpers . connectChunkAndModule ( newPart , module ) ;
835
+ // Remove module from used chunks
836
+ chunk . removeModule ( module ) ;
837
+ module . rewriteChunkInReasons ( chunk , [ newPart ] ) ;
838
+ }
839
+ } else {
840
+ // change the chunk to be a part
841
+ newPart = chunk ;
842
+ chunk . name = name ;
843
+ }
844
+ }
845
+ }
712
846
}
713
847
) ;
714
848
} ) ;
0 commit comments