11/* eslint-disable class-methods-use-this */
22
3- import webpack from 'webpack' ;
3+ import webpack , { version as webpackVersion } from 'webpack' ;
4+
45import validateOptions from 'schema-utils' ;
56
67import CssDependency from './CssDependency' ;
@@ -16,6 +17,8 @@ const {
1617 util : { createHash } ,
1718} = webpack ;
1819
20+ const isWebpack4 = webpackVersion [ 0 ] === '4' ;
21+
1922const MODULE_TYPE = 'css/mini-extract' ;
2023
2124const pluginName = 'mini-css-extract-plugin' ;
@@ -26,6 +29,19 @@ const REGEXP_NAME = /\[name\]/i;
2629const REGEXP_PLACEHOLDERS = / \[ ( n a m e | i d | c h u n k h a s h ) \] / g;
2730const DEFAULT_FILENAME = '[name].css' ;
2831
32+ const compareIds = ( a , b ) => {
33+ if ( typeof a !== typeof b ) {
34+ return typeof a < typeof b ? - 1 : 1 ;
35+ }
36+ if ( a < b ) return - 1 ;
37+ if ( a > b ) return 1 ;
38+ return 0 ;
39+ } ;
40+
41+ const compareModulesByIdentifier = ( a , b ) => {
42+ return compareIds ( a . identifier ( ) , b . identifier ( ) ) ;
43+ } ;
44+
2945class CssDependencyTemplate {
3046 apply ( ) { }
3147}
@@ -85,8 +101,8 @@ class CssModule extends webpack.Module {
85101 callback ( ) ;
86102 }
87103
88- updateHash ( hash ) {
89- super . updateHash ( hash ) ;
104+ updateHash ( hash , context ) {
105+ super . updateHash ( hash , context ) ;
90106
91107 hash . update ( this . content ) ;
92108 hash . update ( this . media || '' ) ;
@@ -141,94 +157,151 @@ class MiniCssExtractPlugin {
141157 new CssDependencyTemplate ( )
142158 ) ;
143159
144- compilation . mainTemplate . hooks . renderManifest . tap (
145- pluginName ,
146- ( result , { chunk } ) => {
147- const renderedModules = Array . from ( chunk . modulesIterable ) . filter (
148- ( module ) => module . type === MODULE_TYPE
149- ) ;
150-
151- if ( renderedModules . length > 0 ) {
152- result . push ( {
153- render : ( ) =>
154- this . renderContentAsset (
155- compilation ,
160+ if ( isWebpack4 ) {
161+ compilation . mainTemplate . hooks . renderManifest . tap (
162+ pluginName ,
163+ ( result , { chunk } ) => {
164+ const { chunkGraph } = compilation ;
165+
166+ const renderedModules = Array . from (
167+ this . getChunkModules ( chunk , chunkGraph )
168+ ) . filter ( ( module ) => module . type === MODULE_TYPE ) ;
169+
170+ const filenameTemplate =
171+ chunk . filenameTemplate ||
172+ ( ( { chunk : chunkData } ) =>
173+ this . options . moduleFilename ( chunkData ) ) ;
174+
175+ if ( renderedModules . length > 0 ) {
176+ result . push ( {
177+ render : ( ) =>
178+ this . renderContentAsset (
179+ compilation ,
180+ chunk ,
181+ renderedModules ,
182+ compilation . runtimeTemplate . requestShortener
183+ ) ,
184+ filenameTemplate,
185+ pathOptions : {
156186 chunk,
157- renderedModules ,
158- compilation . runtimeTemplate . requestShortener
159- ) ,
160- filenameTemplate : ( { chunk : chunkData } ) =>
161- this . options . moduleFilename ( chunkData ) ,
162- pathOptions : {
163- chunk,
164- contentHashType : MODULE_TYPE ,
165- } ,
166- identifier : `${ pluginName } .${ chunk . id } ` ,
167- hash : chunk . contentHash [ MODULE_TYPE ] ,
168- } ) ;
187+ contentHashType : MODULE_TYPE ,
188+ } ,
189+ identifier : `${ pluginName } .${ chunk . id } ` ,
190+ hash : chunk . contentHash [ MODULE_TYPE ] ,
191+ } ) ;
192+ }
169193 }
170- }
171- ) ;
172-
173- compilation . chunkTemplate . hooks . renderManifest . tap (
174- pluginName ,
175- ( result , { chunk } ) => {
176- const renderedModules = Array . from ( chunk . modulesIterable ) . filter (
177- ( module ) => module . type === MODULE_TYPE
178- ) ;
194+ ) ;
179195
180- if ( renderedModules . length > 0 ) {
181- result . push ( {
182- render : ( ) =>
183- this . renderContentAsset (
184- compilation ,
196+ compilation . chunkTemplate . hooks . renderManifest . tap (
197+ pluginName ,
198+ ( result , { chunk } ) => {
199+ const { chunkGraph } = compilation ;
200+
201+ const renderedModules = Array . from (
202+ this . getChunkModules ( chunk , chunkGraph )
203+ ) . filter ( ( module ) => module . type === MODULE_TYPE ) ;
204+
205+ const filenameTemplate =
206+ chunk . filenameTemplate || this . options . chunkFilename ;
207+
208+ if ( renderedModules . length > 0 ) {
209+ result . push ( {
210+ render : ( ) =>
211+ this . renderContentAsset (
212+ compilation ,
213+ chunk ,
214+ renderedModules ,
215+ compilation . runtimeTemplate . requestShortener
216+ ) ,
217+ filenameTemplate,
218+ pathOptions : {
185219 chunk,
186- renderedModules ,
187- compilation . runtimeTemplate . requestShortener
188- ) ,
189- filenameTemplate : this . options . chunkFilename ,
190- pathOptions : {
191- chunk,
192- contentHashType : MODULE_TYPE ,
193- } ,
194- identifier : `${ pluginName } .${ chunk . id } ` ,
195- hash : chunk . contentHash [ MODULE_TYPE ] ,
196- } ) ;
220+ contentHashType : MODULE_TYPE ,
221+ } ,
222+ identifier : `${ pluginName } .${ chunk . id } ` ,
223+ hash : chunk . contentHash [ MODULE_TYPE ] ,
224+ } ) ;
225+ }
197226 }
198- }
199- ) ;
200-
201- compilation . mainTemplate . hooks . hashForChunk . tap (
202- pluginName ,
203- ( hash , chunk ) => {
204- const { chunkFilename } = this . options ;
205-
206- if ( REGEXP_CHUNKHASH . test ( chunkFilename ) ) {
207- hash . update ( JSON . stringify ( chunk . getChunkMaps ( true ) . hash ) ) ;
227+ ) ;
228+ } else {
229+ compilation . hooks . renderManifest . tap (
230+ pluginName ,
231+ ( result , { chunk } ) => {
232+ const { chunkGraph } = compilation ;
233+
234+ const renderedModules = Array . from (
235+ this . getChunkModules ( chunk , chunkGraph )
236+ ) . filter ( ( module ) => module . type === MODULE_TYPE ) ;
237+
238+ const filenameTemplate =
239+ chunk . filenameTemplate ||
240+ chunk . hasRuntime ( ) ||
241+ chunk . isOnlyInitial ( )
242+ ? ( { chunk : chunkData } ) =>
243+ this . options . moduleFilename ( chunkData )
244+ : this . options . chunkFilename ;
245+
246+ if ( renderedModules . length > 0 ) {
247+ result . push ( {
248+ render : ( ) =>
249+ this . renderContentAsset (
250+ compilation ,
251+ chunk ,
252+ renderedModules ,
253+ compilation . runtimeTemplate . requestShortener
254+ ) ,
255+ filenameTemplate,
256+ pathOptions : {
257+ chunk,
258+ contentHashType : MODULE_TYPE ,
259+ } ,
260+ identifier : `${ pluginName } .${ chunk . id } ` ,
261+ hash : chunk . contentHash [ MODULE_TYPE ] ,
262+ } ) ;
263+ }
208264 }
265+ ) ;
266+ }
209267
210- if ( REGEXP_CONTENTHASH . test ( chunkFilename ) ) {
211- hash . update (
212- JSON . stringify (
213- chunk . getChunkMaps ( true ) . contentHash [ MODULE_TYPE ] || { }
214- )
215- ) ;
216- }
268+ /*
269+ * For webpack 5 this will be unneeded once the logic uses a RuntimeModule
270+ * as the content of runtime modules is hashed and added to the chunk hash automatically
271+ * */
272+ if ( isWebpack4 ) {
273+ compilation . mainTemplate . hooks . hashForChunk . tap (
274+ pluginName ,
275+ ( hash , chunk ) => {
276+ const { chunkFilename } = this . options ;
277+
278+ if ( REGEXP_CHUNKHASH . test ( chunkFilename ) ) {
279+ hash . update ( JSON . stringify ( chunk . getChunkMaps ( true ) . hash ) ) ;
280+ }
281+
282+ if ( REGEXP_CONTENTHASH . test ( chunkFilename ) ) {
283+ hash . update (
284+ JSON . stringify (
285+ chunk . getChunkMaps ( true ) . contentHash [ MODULE_TYPE ] || { }
286+ )
287+ ) ;
288+ }
217289
218- if ( REGEXP_NAME . test ( chunkFilename ) ) {
219- hash . update ( JSON . stringify ( chunk . getChunkMaps ( true ) . name ) ) ;
290+ if ( REGEXP_NAME . test ( chunkFilename ) ) {
291+ hash . update ( JSON . stringify ( chunk . getChunkMaps ( true ) . name ) ) ;
292+ }
220293 }
221- }
222- ) ;
294+ ) ;
295+ }
223296
224297 compilation . hooks . contentHash . tap ( pluginName , ( chunk ) => {
225- const { outputOptions } = compilation ;
298+ const { outputOptions, chunkGraph } = compilation ;
226299 const { hashFunction, hashDigest, hashDigestLength } = outputOptions ;
227300 const hash = createHash ( hashFunction ) ;
228301
229- for ( const m of chunk . modulesIterable ) {
302+ for ( const m of this . getChunkModules ( chunk , chunkGraph ) ) {
230303 if ( m . type === MODULE_TYPE ) {
231- m . updateHash ( hash ) ;
304+ m . updateHash ( hash , { chunkGraph } ) ;
232305 }
233306 }
234307
@@ -242,7 +315,7 @@ class MiniCssExtractPlugin {
242315 const { mainTemplate } = compilation ;
243316
244317 mainTemplate . hooks . localVars . tap ( pluginName , ( source , chunk ) => {
245- const chunkMap = this . getCssChunkObject ( chunk ) ;
318+ const chunkMap = this . getCssChunkObject ( chunk , compilation ) ;
246319
247320 if ( Object . keys ( chunkMap ) . length > 0 ) {
248321 return Template . asString ( [
@@ -263,17 +336,25 @@ class MiniCssExtractPlugin {
263336 mainTemplate . hooks . requireEnsure . tap (
264337 pluginName ,
265338 ( source , chunk , hash ) => {
266- const chunkMap = this . getCssChunkObject ( chunk ) ;
339+ const chunkMap = this . getCssChunkObject ( chunk , compilation ) ;
267340
268341 if ( Object . keys ( chunkMap ) . length > 0 ) {
342+ const maintemplateObject = isWebpack4 ? mainTemplate : compilation ;
269343 const chunkMaps = chunk . getChunkMaps ( ) ;
270- const { crossOriginLoading } = mainTemplate . outputOptions ;
271- const linkHrefPath = mainTemplate . getAssetPath (
344+ const { crossOriginLoading } = maintemplateObject . outputOptions ;
345+ const linkHrefPath = maintemplateObject . getAssetPath (
272346 JSON . stringify ( this . options . chunkFilename ) ,
273347 {
274- hash : `" + ${ mainTemplate . renderCurrentHashCode ( hash ) } + "` ,
348+ hash : isWebpack4
349+ ? `" + ${ mainTemplate . renderCurrentHashCode ( hash ) } + "`
350+ : `" + ${ webpack . RuntimeGlobals . getFullHash } + "` ,
275351 hashWithLength : ( length ) =>
276- `" + ${ mainTemplate . renderCurrentHashCode ( hash , length ) } + "` ,
352+ isWebpack4
353+ ? `" + ${ mainTemplate . renderCurrentHashCode (
354+ hash ,
355+ length
356+ ) } + "`
357+ : `" + ${ webpack . RuntimeGlobals . getFullHash } + "` ,
277358 chunk : {
278359 id : '" + chunkId + "' ,
279360 hash : `" + ${ JSON . stringify ( chunkMaps . hash ) } [chunkId] + "` ,
@@ -334,7 +415,11 @@ class MiniCssExtractPlugin {
334415 'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {' ,
335416 Template . indent ( [
336417 `var href = ${ linkHrefPath } ;` ,
337- `var fullhref = ${ mainTemplate . requireFn } .p + href;` ,
418+ `var fullhref = ${
419+ isWebpack4
420+ ? mainTemplate . requireFn
421+ : webpack . RuntimeGlobals . require
422+ } .p + href;`,
338423 'var existingLinkTags = document.getElementsByTagName("link");' ,
339424 'for(var i = 0; i < existingLinkTags.length; i++) {' ,
340425 Template . indent ( [
@@ -395,11 +480,21 @@ class MiniCssExtractPlugin {
395480 } ) ;
396481 }
397482
398- getCssChunkObject ( mainChunk ) {
483+ getChunkModules ( chunk , chunkGraph ) {
484+ return typeof chunkGraph !== 'undefined'
485+ ? chunkGraph . getOrderedChunkModulesIterable (
486+ chunk ,
487+ compareModulesByIdentifier
488+ )
489+ : chunk . modulesIterable ;
490+ }
491+
492+ getCssChunkObject ( mainChunk , compilation ) {
399493 const obj = { } ;
494+ const { chunkGraph } = compilation ;
400495
401496 for ( const chunk of mainChunk . getAllAsyncChunks ( ) ) {
402- for ( const module of chunk . modulesIterable ) {
497+ for ( const module of this . getChunkModules ( chunk , chunkGraph ) ) {
403498 if ( module . type === MODULE_TYPE ) {
404499 obj [ chunk . id ] = 1 ;
405500 break ;
@@ -414,8 +509,12 @@ class MiniCssExtractPlugin {
414509 let usedModules ;
415510
416511 const [ chunkGroup ] = chunk . groupsIterable ;
512+ const moduleIndexFunctionName =
513+ typeof compilation . chunkGraph !== 'undefined'
514+ ? 'getModulePostOrderIndex'
515+ : 'getModuleIndex2' ;
417516
418- if ( typeof chunkGroup . getModuleIndex2 === 'function' ) {
517+ if ( typeof chunkGroup [ moduleIndexFunctionName ] === 'function' ) {
419518 // Store dependencies for modules
420519 const moduleDependencies = new Map ( modules . map ( ( m ) => [ m , new Set ( ) ] ) ) ;
421520 const moduleDependenciesReasons = new Map (
@@ -430,7 +529,7 @@ class MiniCssExtractPlugin {
430529 . map ( ( m ) => {
431530 return {
432531 module : m ,
433- index : cg . getModuleIndex2 ( m ) ,
532+ index : cg [ moduleIndexFunctionName ] ( m ) ,
434533 } ;
435534 } )
436535 // eslint-disable-next-line no-undefined
0 commit comments