1
+ 'use strict' ;
2
+
3
+ Object . defineProperty ( exports , "__esModule" , {
4
+ value : true
5
+ } ) ;
6
+
7
+ var _fs = require ( 'fs' ) ;
8
+
9
+ var _fs2 = _interopRequireDefault ( _fs ) ;
10
+
11
+ var _path = require ( 'path' ) ;
12
+
13
+ var _path2 = _interopRequireDefault ( _path ) ;
14
+
15
+ var _webpack = require ( 'webpack' ) ;
16
+
17
+ var _webpack2 = _interopRequireDefault ( _webpack ) ;
18
+
19
+ var _webpackSources = require ( 'webpack-sources' ) ;
20
+
21
+ var _webpackSources2 = _interopRequireDefault ( _webpackSources ) ;
22
+
23
+ function _interopRequireDefault ( obj ) { return obj && obj . __esModule ? obj : { default : obj } ; }
24
+
25
+ const { ConcatSource, SourceMapSource, OriginalSource } = _webpackSources2 . default ;
26
+ const { Template, util : { createHash } } = _webpack2 . default ;
27
+
28
+ const NS = _path2 . default . dirname ( _fs2 . default . realpathSync ( __filename ) ) ;
29
+
30
+ const pluginName = 'mini-css-extract-plugin' ;
31
+
32
+ const REGEXP_CHUNKHASH = / \[ c h u n k h a s h (?: : ( \d + ) ) ? \] / i;
33
+ const REGEXP_CONTENTHASH = / \[ c o n t e n t h a s h (?: : ( \d + ) ) ? \] / i;
34
+ const REGEXP_NAME = / \[ n a m e \] / i;
35
+
36
+ class CssDependency extends _webpack2 . default . Dependency {
37
+ constructor ( { identifier, content, media, sourceMap } , context , identifierIndex ) {
38
+ super ( ) ;
39
+ this . identifier = identifier ;
40
+ this . identifierIndex = identifierIndex ;
41
+ this . content = content ;
42
+ this . media = media ;
43
+ this . sourceMap = sourceMap ;
44
+ this . context = context ;
45
+ }
46
+
47
+ getResourceIdentifier ( ) {
48
+ return `css-module-${ this . identifier } -${ this . identifierIndex } ` ;
49
+ }
50
+ }
51
+
52
+ class CssDependencyTemplate {
53
+ apply ( ) { }
54
+ }
55
+
56
+ class CssModule extends _webpack2 . default . Module {
57
+ constructor ( dependency ) {
58
+ super ( NS , dependency . context ) ;
59
+ this . _identifier = dependency . identifier ;
60
+ this . _identifierIndex = dependency . identifierIndex ;
61
+ this . content = dependency . content ;
62
+ this . media = dependency . media ;
63
+ this . sourceMap = dependency . sourceMap ;
64
+ }
65
+
66
+ // no source() so webpack doesn't do add stuff to the bundle
67
+
68
+ size ( ) {
69
+ return this . content . length ;
70
+ }
71
+
72
+ identifier ( ) {
73
+ return `css ${ this . _identifier } ${ this . _identifierIndex } ` ;
74
+ }
75
+
76
+ readableIdentifier ( requestShortener ) {
77
+ return `css ${ requestShortener . shorten ( this . _identifier ) } ${ this . _identifierIndex ? ` (${ this . _identifierIndex } )` : '' } ` ;
78
+ }
79
+
80
+ nameForCondition ( ) {
81
+ const resource = this . _identifier . split ( '!' ) . pop ( ) ;
82
+ const idx = resource . indexOf ( '?' ) ;
83
+ if ( idx >= 0 ) return resource . substring ( 0 , idx ) ;
84
+ return resource ;
85
+ }
86
+
87
+ updateCacheModule ( module ) {
88
+ this . content = module . content ;
89
+ this . media = module . media ;
90
+ this . sourceMap = module . sourceMap ;
91
+ }
92
+
93
+ needRebuild ( ) {
94
+ return true ;
95
+ }
96
+
97
+ build ( options , compilation , resolver , fileSystem , callback ) {
98
+ this . buildInfo = { } ;
99
+ this . buildMeta = { } ;
100
+ callback ( ) ;
101
+ }
102
+
103
+ updateHash ( hash ) {
104
+ super . updateHash ( hash ) ;
105
+ hash . update ( this . content ) ;
106
+ hash . update ( this . media || '' ) ;
107
+ hash . update ( JSON . stringify ( this . sourceMap || '' ) ) ;
108
+ }
109
+ }
110
+
111
+ class CssModuleFactory {
112
+ create ( { dependencies : [ dependency ] } , callback ) {
113
+ callback ( null , new CssModule ( dependency ) ) ;
114
+ }
115
+ }
116
+
117
+ class MiniCssExtractPlugin {
118
+ constructor ( options ) {
119
+ this . options = Object . assign ( {
120
+ filename : '[name].css'
121
+ } , options ) ;
122
+ if ( ! this . options . chunkFilename ) {
123
+ const { filename } = this . options ;
124
+ const hasName = filename . includes ( '[name]' ) ;
125
+ const hasId = filename . includes ( '[id]' ) ;
126
+ const hasChunkHash = filename . includes ( '[chunkhash]' ) ;
127
+ // Anything changing depending on chunk is fine
128
+ if ( hasChunkHash || hasName || hasId ) {
129
+ this . options . chunkFilename = filename ;
130
+ } else {
131
+ // Elsewise prefix '[id].' in front of the basename to make it changing
132
+ this . options . chunkFilename = filename . replace ( / ( ^ | \/ ) ( [ ^ / ] * (?: \? | $ ) ) / , '$1[id].$2' ) ;
133
+ }
134
+ }
135
+ }
136
+
137
+ apply ( compiler ) {
138
+ compiler . hooks . thisCompilation . tap ( pluginName , compilation => {
139
+ compilation . hooks . normalModuleLoader . tap ( pluginName , ( lc , m ) => {
140
+ const loaderContext = lc ;
141
+ const module = m ;
142
+ loaderContext [ NS ] = content => {
143
+ if ( ! Array . isArray ( content ) && content != null ) {
144
+ throw new Error ( `Exported value was not extracted as an array: ${ JSON . stringify ( content ) } ` ) ;
145
+ }
146
+ const identifierCountMap = new Map ( ) ;
147
+ for ( const line of content ) {
148
+ const count = identifierCountMap . get ( line . identifier ) || 0 ;
149
+ module . addDependency ( new CssDependency ( line , m . context , count ) ) ;
150
+ identifierCountMap . set ( line . identifier , count + 1 ) ;
151
+ }
152
+ } ;
153
+ } ) ;
154
+ compilation . dependencyFactories . set ( CssDependency , new CssModuleFactory ( ) ) ;
155
+ compilation . dependencyTemplates . set ( CssDependency , new CssDependencyTemplate ( ) ) ;
156
+ compilation . mainTemplate . hooks . renderManifest . tap ( pluginName , ( result , { chunk } ) => {
157
+ const renderedModules = Array . from ( chunk . modulesIterable ) . filter ( module => module . type === NS ) ;
158
+ if ( renderedModules . length > 0 ) {
159
+ result . push ( {
160
+ render : ( ) => this . renderContentAsset ( renderedModules , compilation . runtimeTemplate . requestShortener ) ,
161
+ filenameTemplate : this . options . filename ,
162
+ pathOptions : {
163
+ chunk,
164
+ contentHashType : NS
165
+ } ,
166
+ identifier : `mini-css-extract-plugin.${ chunk . id } ` ,
167
+ hash : chunk . contentHash [ NS ]
168
+ } ) ;
169
+ }
170
+ } ) ;
171
+ compilation . chunkTemplate . hooks . renderManifest . tap ( pluginName , ( result , { chunk } ) => {
172
+ const renderedModules = Array . from ( chunk . modulesIterable ) . filter ( module => module . type === NS ) ;
173
+ if ( renderedModules . length > 0 ) {
174
+ result . push ( {
175
+ render : ( ) => this . renderContentAsset ( renderedModules , compilation . runtimeTemplate . requestShortener ) ,
176
+ filenameTemplate : this . options . chunkFilename ,
177
+ pathOptions : {
178
+ chunk,
179
+ contentHashType : NS
180
+ } ,
181
+ identifier : `mini-css-extract-plugin.${ chunk . id } ` ,
182
+ hash : chunk . contentHash [ NS ]
183
+ } ) ;
184
+ }
185
+ } ) ;
186
+ compilation . mainTemplate . hooks . hashForChunk . tap ( pluginName , ( hash , chunk ) => {
187
+ const { chunkFilename } = this . options ;
188
+ if ( REGEXP_CHUNKHASH . test ( chunkFilename ) ) {
189
+ hash . update ( JSON . stringify ( chunk . getChunkMaps ( true ) . hash ) ) ;
190
+ }
191
+ if ( REGEXP_CONTENTHASH . test ( chunkFilename ) ) {
192
+ hash . update ( JSON . stringify ( chunk . getChunkMaps ( true ) . contentHash [ NS ] || { } ) ) ;
193
+ }
194
+ if ( REGEXP_NAME . test ( chunkFilename ) ) {
195
+ hash . update ( JSON . stringify ( chunk . getChunkMaps ( true ) . name ) ) ;
196
+ }
197
+ } ) ;
198
+ compilation . hooks . contentHash . tap ( pluginName , chunk => {
199
+ const { outputOptions } = compilation ;
200
+ const { hashFunction, hashDigest, hashDigestLength } = outputOptions ;
201
+ const hash = createHash ( hashFunction ) ;
202
+ for ( const m of chunk . modulesIterable ) {
203
+ if ( m . type === NS ) {
204
+ m . updateHash ( hash ) ;
205
+ }
206
+ }
207
+ const { contentHash } = chunk ;
208
+ contentHash [ NS ] = hash . digest ( hashDigest ) . substring ( 0 , hashDigestLength ) ;
209
+ } ) ;
210
+ const { mainTemplate } = compilation ;
211
+ mainTemplate . hooks . localVars . tap ( pluginName , ( source , chunk ) => {
212
+ const chunkMap = this . getCssChunkObject ( chunk ) ;
213
+ if ( Object . keys ( chunkMap ) . length > 0 ) {
214
+ return Template . asString ( [ source , '' , '// object to store loaded CSS chunks' , 'var installedCssChunks = {' , Template . indent ( chunk . ids . map ( id => `${ JSON . stringify ( id ) } : 0` ) . join ( ',\n' ) ) , '}' ] ) ;
215
+ }
216
+ return source ;
217
+ } ) ;
218
+ mainTemplate . hooks . requireEnsure . tap ( pluginName , ( source , chunk , hash ) => {
219
+ const chunkMap = this . getCssChunkObject ( chunk ) ;
220
+ if ( Object . keys ( chunkMap ) . length > 0 ) {
221
+ const chunkMaps = chunk . getChunkMaps ( ) ;
222
+ const linkHrefPath = mainTemplate . getAssetPath ( JSON . stringify ( this . options . chunkFilename ) , {
223
+ hash : `" + ${ mainTemplate . renderCurrentHashCode ( hash ) } + "` ,
224
+ hashWithLength : length => `" + ${ mainTemplate . renderCurrentHashCode ( hash , length ) } + "` ,
225
+ chunk : {
226
+ id : '" + chunkId + "' ,
227
+ hash : `" + ${ JSON . stringify ( chunkMaps . hash ) } [chunkId] + "` ,
228
+ hashWithLength ( length ) {
229
+ const shortChunkHashMap = Object . create ( null ) ;
230
+ for ( const chunkId of Object . keys ( chunkMaps . hash ) ) {
231
+ if ( typeof chunkMaps . hash [ chunkId ] === 'string' ) {
232
+ shortChunkHashMap [ chunkId ] = chunkMaps . hash [ chunkId ] . substring ( 0 , length ) ;
233
+ }
234
+ }
235
+ return `" + ${ JSON . stringify ( shortChunkHashMap ) } [chunkId] + "` ;
236
+ } ,
237
+ contentHash : {
238
+ [ NS ] : `" + ${ JSON . stringify ( chunkMaps . contentHash [ NS ] ) } [chunkId] + "`
239
+ } ,
240
+ contentHashWithLength : {
241
+ [ NS ] : length => {
242
+ const shortContentHashMap = { } ;
243
+ const contentHash = chunkMaps . contentHash [ NS ] ;
244
+ for ( const chunkId of Object . keys ( contentHash ) ) {
245
+ if ( typeof contentHash [ chunkId ] === 'string' ) {
246
+ shortContentHashMap [ chunkId ] = contentHash [ chunkId ] . substring ( 0 , length ) ;
247
+ }
248
+ }
249
+ return `" + ${ JSON . stringify ( shortContentHashMap ) } [chunkId] + "` ;
250
+ }
251
+ } ,
252
+ name : `" + (${ JSON . stringify ( chunkMaps . name ) } [chunkId]||chunkId) + "`
253
+ } ,
254
+ contentHashType : NS
255
+ } ) ;
256
+ return Template . asString ( [ source , '' , '// mini-css-extract-plugin 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.request = request;' , 'reject(err);' ] ) , '};' , 'linkTag.href = fullhref;' , 'var head = document.getElementsByTagName("head")[0];' , 'head.appendChild(linkTag);' ] ) , '}).then(function() {' , Template . indent ( [ 'installedCssChunks[chunkId] = 0;' ] ) , '}));' ] ) , '}' ] ) ;
257
+ }
258
+ return source ;
259
+ } ) ;
260
+ } ) ;
261
+ }
262
+
263
+ getCssChunkObject ( mainChunk ) {
264
+ const obj = { } ;
265
+ for ( const chunk of mainChunk . getAllAsyncChunks ( ) ) {
266
+ for ( const module of chunk . modulesIterable ) {
267
+ if ( module . type === NS ) {
268
+ obj [ chunk . id ] = 1 ;
269
+ break ;
270
+ }
271
+ }
272
+ }
273
+ return obj ;
274
+ }
275
+
276
+ renderContentAsset ( modules , requestShortener ) {
277
+ modules . sort ( ( a , b ) => a . index2 - b . index2 ) ;
278
+ const source = new ConcatSource ( ) ;
279
+ const externalsSource = new ConcatSource ( ) ;
280
+ for ( const m of modules ) {
281
+ if ( / ^ @ i m p o r t u r l / . test ( m . content ) ) {
282
+ // HACK for IE
283
+ // http://stackoverflow.com/a/14676665/1458162
284
+ let { content } = m ;
285
+ if ( m . media ) {
286
+ // insert media into the @import
287
+ // this is rar
288
+ // TODO improve this and parse the CSS to support multiple medias
289
+ content = content . replace ( / ; | \s * $ / , m . media ) ;
290
+ }
291
+ externalsSource . add ( content ) ;
292
+ externalsSource . add ( '\n' ) ;
293
+ } else {
294
+ if ( m . media ) {
295
+ source . add ( `@media ${ m . media } {\n` ) ;
296
+ }
297
+ if ( m . sourceMap ) {
298
+ source . add ( new SourceMapSource ( m . content , m . readableIdentifier ( requestShortener ) , m . sourceMap ) ) ;
299
+ } else {
300
+ source . add ( new OriginalSource ( m . content , m . readableIdentifier ( requestShortener ) ) ) ;
301
+ }
302
+ source . add ( '\n' ) ;
303
+ if ( m . media ) {
304
+ source . add ( '}\n' ) ;
305
+ }
306
+ }
307
+ }
308
+ return new ConcatSource ( externalsSource , source ) ;
309
+ }
310
+ }
311
+
312
+ MiniCssExtractPlugin . loader = require . resolve ( './loader' ) ;
313
+
314
+ exports . default = MiniCssExtractPlugin ;
0 commit comments