2023-12-18 13:12:25 +08:00
"use strict" ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports . default = void 0 ;
var _webpack = _interopRequireDefault ( require ( "webpack" ) ) ;
var _webpackSources = _interopRequireDefault ( require ( "webpack-sources" ) ) ;
2024-01-16 21:26:16 +08:00
var _CssDependency = _interopRequireDefault ( require ( "./CssDependency" ) ) ;
2023-12-18 13:12:25 +08:00
function _interopRequireDefault ( obj ) { return obj && obj . _ _esModule ? obj : { default : obj } ; }
/* eslint-disable class-methods-use-this */
const {
ConcatSource ,
SourceMapSource ,
OriginalSource
} = _webpackSources . default ;
const {
Template ,
util : {
createHash
}
} = _webpack . default ;
const MODULE _TYPE = 'css/mini-extract' ;
const pluginName = 'mini-css-extract-plugin' ;
const REGEXP _CHUNKHASH = /\[chunkhash(?::(\d+))?\]/i ;
const REGEXP _CONTENTHASH = /\[contenthash(?::(\d+))?\]/i ;
const REGEXP _NAME = /\[name\]/i ;
2024-01-16 21:26:16 +08:00
const REGEXP _PLACEHOLDERS = /\[(name|id|chunkhash)\]/g ;
const DEFAULT _FILENAME = '[name].css' ;
2023-12-18 13:12:25 +08:00
class CssDependencyTemplate {
apply ( ) { }
}
class CssModule extends _webpack . default . Module {
constructor ( dependency ) {
super ( MODULE _TYPE , dependency . context ) ;
this . id = '' ;
this . _identifier = dependency . identifier ;
this . _identifierIndex = dependency . identifierIndex ;
this . content = dependency . content ;
this . media = dependency . media ;
this . sourceMap = dependency . sourceMap ;
} // no source() so webpack doesn't do add stuff to the bundle
size ( ) {
return this . content . length ;
}
identifier ( ) {
return ` css ${ this . _identifier } ${ this . _identifierIndex } ` ;
}
readableIdentifier ( requestShortener ) {
return ` css ${ requestShortener . shorten ( this . _identifier ) } ${ this . _identifierIndex ? ` ( ${ this . _identifierIndex } ) ` : '' } ` ;
}
nameForCondition ( ) {
const resource = this . _identifier . split ( '!' ) . pop ( ) ;
const idx = resource . indexOf ( '?' ) ;
if ( idx >= 0 ) {
return resource . substring ( 0 , idx ) ;
}
return resource ;
}
updateCacheModule ( module ) {
this . content = module . content ;
this . media = module . media ;
this . sourceMap = module . sourceMap ;
}
needRebuild ( ) {
return true ;
}
build ( options , compilation , resolver , fileSystem , callback ) {
this . buildInfo = { } ;
this . buildMeta = { } ;
callback ( ) ;
}
updateHash ( hash ) {
super . updateHash ( hash ) ;
hash . update ( this . content ) ;
hash . update ( this . media || '' ) ;
hash . update ( this . sourceMap ? JSON . stringify ( this . sourceMap ) : '' ) ;
}
}
class CssModuleFactory {
create ( {
dependencies : [ dependency ]
} , callback ) {
callback ( null , new CssModule ( dependency ) ) ;
}
}
class MiniCssExtractPlugin {
2024-01-16 21:26:16 +08:00
constructor ( options = { } ) {
2023-12-18 13:12:25 +08:00
this . options = Object . assign ( {
2024-01-16 21:26:16 +08:00
filename : DEFAULT _FILENAME ,
moduleFilename : ( ) => this . options . filename || DEFAULT _FILENAME ,
ignoreOrder : false
2023-12-18 13:12:25 +08:00
} , options ) ;
if ( ! this . options . chunkFilename ) {
const {
filename
2024-01-16 21:26:16 +08:00
} = this . options ; // Anything changing depending on chunk is fine
2023-12-18 13:12:25 +08:00
2024-01-16 21:26:16 +08:00
if ( filename . match ( REGEXP _PLACEHOLDERS ) ) {
2023-12-18 13:12:25 +08:00
this . options . chunkFilename = filename ;
} else {
// Elsewise prefix '[id].' in front of the basename to make it changing
this . options . chunkFilename = filename . replace ( /(^|\/)([^/]*(?:\?|$))/ , '$1[id].$2' ) ;
}
}
}
apply ( compiler ) {
compiler . hooks . thisCompilation . tap ( pluginName , compilation => {
2024-01-16 21:26:16 +08:00
compilation . dependencyFactories . set ( _CssDependency . default , new CssModuleFactory ( ) ) ;
compilation . dependencyTemplates . set ( _CssDependency . default , new CssDependencyTemplate ( ) ) ;
2023-12-18 13:12:25 +08:00
compilation . mainTemplate . hooks . renderManifest . tap ( pluginName , ( result , {
chunk
} ) => {
const renderedModules = Array . from ( chunk . modulesIterable ) . filter ( module => module . type === MODULE _TYPE ) ;
if ( renderedModules . length > 0 ) {
result . push ( {
render : ( ) => this . renderContentAsset ( compilation , chunk , renderedModules , compilation . runtimeTemplate . requestShortener ) ,
2024-01-16 21:26:16 +08:00
filenameTemplate : ( {
chunk : chunkData
} ) => this . options . moduleFilename ( chunkData ) ,
2023-12-18 13:12:25 +08:00
pathOptions : {
chunk ,
contentHashType : MODULE _TYPE
} ,
identifier : ` ${ pluginName } . ${ chunk . id } ` ,
hash : chunk . contentHash [ MODULE _TYPE ]
} ) ;
}
} ) ;
compilation . chunkTemplate . hooks . renderManifest . tap ( pluginName , ( result , {
chunk
} ) => {
const renderedModules = Array . from ( chunk . modulesIterable ) . filter ( module => module . type === MODULE _TYPE ) ;
if ( renderedModules . length > 0 ) {
result . push ( {
render : ( ) => this . renderContentAsset ( compilation , chunk , renderedModules , compilation . runtimeTemplate . requestShortener ) ,
filenameTemplate : this . options . chunkFilename ,
pathOptions : {
chunk ,
contentHashType : MODULE _TYPE
} ,
identifier : ` ${ pluginName } . ${ chunk . id } ` ,
hash : chunk . contentHash [ MODULE _TYPE ]
} ) ;
}
} ) ;
compilation . mainTemplate . hooks . hashForChunk . tap ( pluginName , ( hash , chunk ) => {
const {
chunkFilename
} = this . options ;
if ( REGEXP _CHUNKHASH . test ( chunkFilename ) ) {
hash . update ( JSON . stringify ( chunk . getChunkMaps ( true ) . hash ) ) ;
}
if ( REGEXP _CONTENTHASH . test ( chunkFilename ) ) {
hash . update ( JSON . stringify ( chunk . getChunkMaps ( true ) . contentHash [ MODULE _TYPE ] || { } ) ) ;
}
if ( REGEXP _NAME . test ( chunkFilename ) ) {
hash . update ( JSON . stringify ( chunk . getChunkMaps ( true ) . name ) ) ;
}
} ) ;
compilation . hooks . contentHash . tap ( pluginName , chunk => {
const {
outputOptions
} = compilation ;
const {
hashFunction ,
hashDigest ,
hashDigestLength
} = outputOptions ;
const hash = createHash ( hashFunction ) ;
for ( const m of chunk . modulesIterable ) {
if ( m . type === MODULE _TYPE ) {
m . updateHash ( hash ) ;
}
}
const {
contentHash
} = chunk ;
contentHash [ MODULE _TYPE ] = hash . digest ( hashDigest ) . substring ( 0 , hashDigestLength ) ;
} ) ;
const {
mainTemplate
} = compilation ;
mainTemplate . hooks . localVars . tap ( pluginName , ( source , chunk ) => {
const chunkMap = this . getCssChunkObject ( chunk ) ;
if ( Object . keys ( chunkMap ) . length > 0 ) {
return Template . asString ( [ source , '' , '// object to store loaded CSS chunks' , 'var installedCssChunks = {' , Template . indent ( chunk . ids . map ( id => ` ${ JSON . stringify ( id ) } : 0 ` ) . join ( ',\n' ) ) , '}' ] ) ;
}
return source ;
} ) ;
mainTemplate . hooks . requireEnsure . tap ( pluginName , ( source , chunk , hash ) => {
const chunkMap = this . getCssChunkObject ( chunk ) ;
if ( Object . keys ( chunkMap ) . length > 0 ) {
const chunkMaps = chunk . getChunkMaps ( ) ;
const {
crossOriginLoading
} = mainTemplate . outputOptions ;
const linkHrefPath = mainTemplate . getAssetPath ( JSON . stringify ( this . options . chunkFilename ) , {
hash : ` " + ${ mainTemplate . renderCurrentHashCode ( hash ) } + " ` ,
hashWithLength : length => ` " + ${ mainTemplate . renderCurrentHashCode ( hash , length ) } + " ` ,
chunk : {
id : '" + chunkId + "' ,
hash : ` " + ${ JSON . stringify ( chunkMaps . hash ) } [chunkId] + " ` ,
hashWithLength ( length ) {
const shortChunkHashMap = Object . create ( null ) ;
for ( const chunkId of Object . keys ( chunkMaps . hash ) ) {
if ( typeof chunkMaps . hash [ chunkId ] === 'string' ) {
shortChunkHashMap [ chunkId ] = chunkMaps . hash [ chunkId ] . substring ( 0 , length ) ;
}
}
return ` " + ${ JSON . stringify ( shortChunkHashMap ) } [chunkId] + " ` ;
} ,
contentHash : {
[ MODULE _TYPE ] : ` " + ${ JSON . stringify ( chunkMaps . contentHash [ MODULE _TYPE ] ) } [chunkId] + " `
} ,
contentHashWithLength : {
[ MODULE _TYPE ] : length => {
const shortContentHashMap = { } ;
const contentHash = chunkMaps . contentHash [ MODULE _TYPE ] ;
for ( const chunkId of Object . keys ( contentHash ) ) {
if ( typeof contentHash [ chunkId ] === 'string' ) {
shortContentHashMap [ chunkId ] = contentHash [ chunkId ] . substring ( 0 , length ) ;
}
}
return ` " + ${ JSON . stringify ( shortContentHashMap ) } [chunkId] + " ` ;
}
} ,
name : ` " + ( ${ JSON . stringify ( chunkMaps . name ) } [chunkId]||chunkId) + " `
} ,
contentHashType : MODULE _TYPE
} ) ;
return Template . asString ( [ source , '' , ` // ${ pluginName } CSS loading ` , ` var cssChunks = ${ JSON . stringify ( chunkMap ) } ; ` , 'if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);' , 'else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {' , Template . indent ( [ 'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {' , Template . indent ( [ ` var href = ${ linkHrefPath } ; ` , ` var fullhref = ${ mainTemplate . requireFn } .p + href; ` , 'var existingLinkTags = document.getElementsByTagName("link");' , 'for(var i = 0; i < existingLinkTags.length; i++) {' , Template . indent ( [ 'var tag = existingLinkTags[i];' , 'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");' , 'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();' ] ) , '}' , 'var existingStyleTags = document.getElementsByTagName("style");' , 'for(var i = 0; i < existingStyleTags.length; i++) {' , Template . indent ( [ 'var tag = existingStyleTags[i];' , 'var dataHref = tag.getAttribute("data-href");' , 'if(dataHref === href || dataHref === fullhref) return resolve();' ] ) , '}' , 'var linkTag = document.createElement("link");' , 'linkTag.rel = "stylesheet";' , 'linkTag.type = "text/css";' , 'linkTag.onload = resolve;' , 'linkTag.onerror = function(event) {' , Template . indent ( [ 'var request = event && event.target && event.target.src || fullhref;' , 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");' , 'err.code = "CSS_CHUNK_LOAD_FAILED";' , 'err.request = request;' , 'delete installedCssChunks[chunkId]' , 'linkTag.parentNode.removeChild(linkTag)' , 'reject(err);' ] ) , '};' , 'linkTag.href = fullhref;' , crossOriginLoading ? Template . asString ( [ ` if (linkTag.href.indexOf(window.location.origin + '/') !== 0) { ` , Template . indent ( ` linkTag.crossOrigin = ${ JSON . stringify ( crossOriginLoading ) } ; ` ) , '}' ] ) : '' , 'var head = document.getElementsByTagName("head")[0];' , 'head.appendChild(linkTag);' ] ) , '}).then(function() {' , Template . indent ( [ 'installedCssChunks[chunkId] = 0;' ] ) , '}));' ] ) , '}' ] ) ;
}
return source ;
} ) ;
} ) ;
}
getCssChunkObject ( mainChunk ) {
const obj = { } ;
for ( const chunk of mainChunk . getAllAsyncChunks ( ) ) {
for ( const module of chunk . modulesIterable ) {
if ( module . type === MODULE _TYPE ) {
obj [ chunk . id ] = 1 ;
break ;
}
}
}
return obj ;
}
renderContentAsset ( compilation , chunk , modules , requestShortener ) {
let usedModules ;
const [ chunkGroup ] = chunk . groupsIterable ;
if ( typeof chunkGroup . getModuleIndex2 === 'function' ) {
// Store dependencies for modules
2024-01-16 21:26:16 +08:00
const moduleDependencies = new Map ( modules . map ( m => [ m , new Set ( ) ] ) ) ;
const moduleDependenciesReasons = new Map ( modules . map ( m => [ m , new Map ( ) ] ) ) ; // Get ordered list of modules per chunk group
2023-12-18 13:12:25 +08:00
// This loop also gathers dependencies from the ordered lists
// Lists are in reverse order to allow to use Array.pop()
const modulesByChunkGroup = Array . from ( chunk . groupsIterable , cg => {
const sortedModules = modules . map ( m => {
return {
module : m ,
index : cg . getModuleIndex2 ( m )
} ;
} ) // eslint-disable-next-line no-undefined
. filter ( item => item . index !== undefined ) . sort ( ( a , b ) => b . index - a . index ) . map ( item => item . module ) ;
for ( let i = 0 ; i < sortedModules . length ; i ++ ) {
const set = moduleDependencies . get ( sortedModules [ i ] ) ;
2024-01-16 21:26:16 +08:00
const reasons = moduleDependenciesReasons . get ( sortedModules [ i ] ) ;
2023-12-18 13:12:25 +08:00
for ( let j = i + 1 ; j < sortedModules . length ; j ++ ) {
2024-01-16 21:26:16 +08:00
const module = sortedModules [ j ] ;
set . add ( module ) ;
const reason = reasons . get ( module ) || new Set ( ) ;
reason . add ( cg ) ;
reasons . set ( module , reason ) ;
2023-12-18 13:12:25 +08:00
}
}
return sortedModules ;
} ) ; // set with already included modules in correct order
usedModules = new Set ( ) ;
const unusedModulesFilter = m => ! usedModules . has ( m ) ;
while ( usedModules . size < modules . length ) {
let success = false ;
let bestMatch ;
let bestMatchDeps ; // get first module where dependencies are fulfilled
for ( const list of modulesByChunkGroup ) {
// skip and remove already added modules
while ( list . length > 0 && usedModules . has ( list [ list . length - 1 ] ) ) {
list . pop ( ) ;
} // skip empty lists
if ( list . length !== 0 ) {
const module = list [ list . length - 1 ] ;
const deps = moduleDependencies . get ( module ) ; // determine dependencies that are not yet included
const failedDeps = Array . from ( deps ) . filter ( unusedModulesFilter ) ; // store best match for fallback behavior
if ( ! bestMatchDeps || bestMatchDeps . length > failedDeps . length ) {
bestMatch = list ;
bestMatchDeps = failedDeps ;
}
if ( failedDeps . length === 0 ) {
// use this module and remove it from list
usedModules . add ( list . pop ( ) ) ;
success = true ;
break ;
}
}
}
if ( ! success ) {
// no module found => there is a conflict
// use list with fewest failed deps
// and emit a warning
const fallbackModule = bestMatch . pop ( ) ;
2024-01-16 21:26:16 +08:00
if ( ! this . options . ignoreOrder ) {
const reasons = moduleDependenciesReasons . get ( fallbackModule ) ;
compilation . warnings . push ( new Error ( [ ` chunk ${ chunk . name || chunk . id } [ ${ pluginName } ] ` , 'Conflicting order. Following module has been added:' , ` * ${ fallbackModule . readableIdentifier ( requestShortener ) } ` , 'despite it was not able to fulfill desired ordering with these modules:' , ... bestMatchDeps . map ( m => {
const goodReasonsMap = moduleDependenciesReasons . get ( m ) ;
const goodReasons = goodReasonsMap && goodReasonsMap . get ( fallbackModule ) ;
const failedChunkGroups = Array . from ( reasons . get ( m ) , cg => cg . name ) . join ( ', ' ) ;
const goodChunkGroups = goodReasons && Array . from ( goodReasons , cg => cg . name ) . join ( ', ' ) ;
return [ ` * ${ m . readableIdentifier ( requestShortener ) } ` , ` - couldn't fulfill desired order of chunk group(s) ${ failedChunkGroups } ` , goodChunkGroups && ` - while fulfilling desired order of chunk group(s) ${ goodChunkGroups } ` ] . filter ( Boolean ) . join ( '\n' ) ;
} ) ] . join ( '\n' ) ) ) ;
}
2023-12-18 13:12:25 +08:00
usedModules . add ( fallbackModule ) ;
}
}
} else {
// fallback for older webpack versions
// (to avoid a breaking change)
2024-01-16 21:26:16 +08:00
// TODO remove this in next major version
2023-12-18 13:12:25 +08:00
// and increase minimum webpack version to 4.12.0
modules . sort ( ( a , b ) => a . index2 - b . index2 ) ;
usedModules = modules ;
}
const source = new ConcatSource ( ) ;
const externalsSource = new ConcatSource ( ) ;
for ( const m of usedModules ) {
if ( /^@import url/ . test ( m . content ) ) {
// HACK for IE
// http://stackoverflow.com/a/14676665/1458162
let {
content
} = m ;
if ( m . media ) {
// insert media into the @import
// this is rar
// TODO improve this and parse the CSS to support multiple medias
content = content . replace ( /;|\s*$/ , m . media ) ;
}
externalsSource . add ( content ) ;
externalsSource . add ( '\n' ) ;
} else {
if ( m . media ) {
source . add ( ` @media ${ m . media } { \n ` ) ;
}
if ( m . sourceMap ) {
source . add ( new SourceMapSource ( m . content , m . readableIdentifier ( requestShortener ) , m . sourceMap ) ) ;
} else {
source . add ( new OriginalSource ( m . content , m . readableIdentifier ( requestShortener ) ) ) ;
}
source . add ( '\n' ) ;
if ( m . media ) {
source . add ( '}\n' ) ;
}
}
}
return new ConcatSource ( externalsSource , source ) ;
}
}
MiniCssExtractPlugin . loader = require . resolve ( './loader' ) ;
var _default = MiniCssExtractPlugin ;
exports . default = _default ;