@@ -167,6 +167,7 @@ class MiniCssExtractPlugin {
167
167
result . push ( {
168
168
render : ( ) =>
169
169
this . renderContentAsset (
170
+ compilation ,
170
171
chunk ,
171
172
renderedModules ,
172
173
compilation . runtimeTemplate . requestShortener
@@ -192,6 +193,7 @@ class MiniCssExtractPlugin {
192
193
result . push ( {
193
194
render : ( ) =>
194
195
this . renderContentAsset (
196
+ compilation ,
195
197
chunk ,
196
198
renderedModules ,
197
199
compilation . runtimeTemplate . requestShortener
@@ -381,27 +383,103 @@ class MiniCssExtractPlugin {
381
383
return obj ;
382
384
}
383
385
384
- renderContentAsset ( chunk , modules , requestShortener ) {
385
- // get first chunk group and take ordr from this one
386
- // When a chunk is shared between multiple chunk groups
387
- // with different order this can lead to wrong order
388
- // but it's not possible to create a correct order in
389
- // this case. Don't share chunks if you don't like it.
386
+ renderContentAsset ( compilation , chunk , modules , requestShortener ) {
387
+ let usedModules ;
388
+
390
389
const [ chunkGroup ] = chunk . groupsIterable ;
391
390
if ( typeof chunkGroup . getModuleIndex2 === 'function' ) {
392
- modules . sort (
393
- ( a , b ) => chunkGroup . getModuleIndex2 ( a ) - chunkGroup . getModuleIndex2 ( b )
394
- ) ;
391
+ // Store dependencies for modules
392
+ const moduleDependencies = new Map ( modules . map ( ( m ) => [ m , new Set ( ) ] ) ) ;
393
+
394
+ // Get ordered list of modules per chunk group
395
+ // This loop also gathers dependencies from the ordered lists
396
+ // Lists are in reverse order to allow to use Array.pop()
397
+ const modulesByChunkGroup = Array . from ( chunk . groupsIterable , ( cg ) => {
398
+ const sortedModules = modules
399
+ . map ( ( m ) => {
400
+ return {
401
+ module : m ,
402
+ index : cg . getModuleIndex2 ( m ) ,
403
+ } ;
404
+ } )
405
+ . filter ( ( item ) => item . index !== undefined )
406
+ . sort ( ( a , b ) => b . index - a . index )
407
+ . map ( ( item ) => item . module ) ;
408
+ for ( let i = 0 ; i < sortedModules . length ; i ++ ) {
409
+ const set = moduleDependencies . get ( sortedModules [ i ] ) ;
410
+ for ( let j = i + 1 ; j < sortedModules . length ; j ++ ) {
411
+ set . add ( sortedModules [ j ] ) ;
412
+ }
413
+ }
414
+
415
+ return sortedModules ;
416
+ } ) ;
417
+
418
+ // set with already included modules in correct order
419
+ usedModules = new Set ( ) ;
420
+
421
+ const unusedModulesFilter = ( m ) => ! usedModules . has ( m ) ;
422
+
423
+ while ( usedModules . size < modules . length ) {
424
+ let success = false ;
425
+ let bestMatch ;
426
+ let bestMatchDeps ;
427
+ // get first module where dependencies are fulfilled
428
+ for ( const list of modulesByChunkGroup ) {
429
+ // skip and remove already added modules
430
+ while ( list . length > 0 && usedModules . has ( list [ list . length - 1 ] ) )
431
+ list . pop ( ) ;
432
+
433
+ // skip empty lists
434
+ if ( list . length !== 0 ) {
435
+ const module = list [ list . length - 1 ] ;
436
+ const deps = moduleDependencies . get ( module ) ;
437
+ // determine dependencies that are not yet included
438
+ const failedDeps = Array . from ( deps ) . filter ( unusedModulesFilter ) ;
439
+
440
+ // store best match for fallback behavior
441
+ if ( ! bestMatchDeps || bestMatchDeps . length > failedDeps . length ) {
442
+ bestMatch = list ;
443
+ bestMatchDeps = failedDeps ;
444
+ }
445
+ if ( failedDeps . length === 0 ) {
446
+ // use this module and remove it from list
447
+ usedModules . add ( list . pop ( ) ) ;
448
+ success = true ;
449
+ break ;
450
+ }
451
+ }
452
+ }
453
+
454
+ if ( ! success ) {
455
+ // no module found => there is a conflict
456
+ // use list with fewest failed deps
457
+ // and emit a warning
458
+ const fallbackModule = bestMatch . pop ( ) ;
459
+ compilation . warnings . push (
460
+ new Error (
461
+ `chunk ${ chunk . name || chunk . id } [mini-css-extract-plugin]\n` +
462
+ 'Conflicting order between:\n' +
463
+ ` * ${ fallbackModule . readableIdentifier ( requestShortener ) } \n` +
464
+ `${ bestMatchDeps
465
+ . map ( ( m ) => ` * ${ m . readableIdentifier ( requestShortener ) } ` )
466
+ . join ( '\n' ) } `
467
+ )
468
+ ) ;
469
+ usedModules . add ( fallbackModule ) ;
470
+ }
471
+ }
395
472
} else {
396
473
// fallback for older webpack versions
397
474
// (to avoid a breaking change)
398
475
// TODO remove this in next mayor version
399
476
// and increase minimum webpack version to 4.12.0
400
477
modules . sort ( ( a , b ) => a . index2 - b . index2 ) ;
478
+ usedModules = modules ;
401
479
}
402
480
const source = new ConcatSource ( ) ;
403
481
const externalsSource = new ConcatSource ( ) ;
404
- for ( const m of modules ) {
482
+ for ( const m of usedModules ) {
405
483
if ( / ^ @ i m p o r t u r l / . test ( m . content ) ) {
406
484
// HACK for IE
407
485
// http://stackoverflow.com/a/14676665/1458162
0 commit comments