@@ -167,6 +167,7 @@ class MiniCssExtractPlugin {
167167 result . push ( {
168168 render : ( ) =>
169169 this . renderContentAsset (
170+ compilation ,
170171 chunk ,
171172 renderedModules ,
172173 compilation . runtimeTemplate . requestShortener
@@ -192,6 +193,7 @@ class MiniCssExtractPlugin {
192193 result . push ( {
193194 render : ( ) =>
194195 this . renderContentAsset (
196+ compilation ,
195197 chunk ,
196198 renderedModules ,
197199 compilation . runtimeTemplate . requestShortener
@@ -381,27 +383,103 @@ class MiniCssExtractPlugin {
381383 return obj ;
382384 }
383385
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+
390389 const [ chunkGroup ] = chunk . groupsIterable ;
391390 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+ }
395472 } else {
396473 // fallback for older webpack versions
397474 // (to avoid a breaking change)
398475 // TODO remove this in next mayor version
399476 // and increase minimum webpack version to 4.12.0
400477 modules . sort ( ( a , b ) => a . index2 - b . index2 ) ;
478+ usedModules = modules ;
401479 }
402480 const source = new ConcatSource ( ) ;
403481 const externalsSource = new ConcatSource ( ) ;
404- for ( const m of modules ) {
482+ for ( const m of usedModules ) {
405483 if ( / ^ @ i m p o r t u r l / . test ( m . content ) ) {
406484 // HACK for IE
407485 // http://stackoverflow.com/a/14676665/1458162
0 commit comments