@@ -36,7 +36,7 @@ class HtmlWebpackPlugin {
36
36
/**
37
37
* @type {HtmlWebpackPluginOptions }
38
38
*/
39
- this . options = _ . extend ( {
39
+ this . options = Object . assign ( {
40
40
template : path . join ( __dirname , 'default_index.ejs' ) ,
41
41
templateContent : undefined ,
42
42
templateParameters : templateParametersGenerator ,
@@ -125,138 +125,119 @@ class HtmlWebpackPlugin {
125
125
* @param {WebpackCompilation } compilation
126
126
* @param {() => void } callback
127
127
*/
128
- ( compilation , callback ) => {
129
- const applyPluginsAsyncWaterfall = self . applyPluginsAsyncWaterfall ( compilation ) ;
130
- // Get chunks info as json
131
- // Note: we're excluding stuff that we don't need to improve toJson serialization speed.
132
- const chunkOnlyConfig = {
133
- assets : false ,
134
- cached : false ,
135
- children : false ,
136
- chunks : true ,
137
- chunkModules : false ,
138
- chunkOrigins : false ,
139
- errorDetails : false ,
140
- hash : false ,
141
- modules : false ,
142
- reasons : false ,
143
- source : false ,
144
- timings : false ,
145
- version : false
146
- } ;
147
- const allChunks = compilation . getStats ( ) . toJson ( chunkOnlyConfig ) . chunks ;
148
- // Filter chunks (options.chunks and options.excludeCHunks)
149
- let chunks = self . filterChunks ( allChunks , self . options . chunks , self . options . excludeChunks ) ;
150
- // Sort chunks
151
- chunks = self . sortChunks ( chunks , self . options . chunksSortMode , compilation ) ;
152
- // Let plugins alter the chunks and the chunk sorting
153
- chunks = compilation . hooks . htmlWebpackPluginAlterChunks . call ( chunks , { plugin : self } ) ;
154
- // Get assets
155
- const assets = self . htmlWebpackPluginAssets ( compilation , chunks ) ;
156
- // If this is a hot update compilation, move on!
157
- // This solves a problem where an `index.html` file is generated for hot-update js files
158
- // It only happens in Webpack 2, where hot updates are emitted separately before the full bundle
159
- if ( self . isHotUpdateCompilation ( assets ) ) {
160
- return callback ( ) ;
161
- }
128
+ ( compilation , callback ) => {
129
+ const applyPluginsAsyncWaterfall = self . applyPluginsAsyncWaterfall ( compilation ) ;
130
+ // Get all entry point names for this html file
131
+ const entryNames = Array . from ( compilation . entrypoints . keys ( ) ) ;
132
+ const filteredEntryNames = self . filterChunks ( entryNames , self . options . chunks , self . options . excludeChunks ) ;
133
+ const sortedEntryNames = self . sortEntryChunks ( filteredEntryNames , this . options . chunksSortMode , compilation ) ;
134
+ // Turn the entry point names into file paths
135
+ const assets = self . htmlWebpackPluginAssets ( compilation , sortedEntryNames ) ;
136
+
137
+ // If this is a hot update compilation, move on!
138
+ // This solves a problem where an `index.html` file is generated for hot-update js files
139
+ // It only happens in Webpack 2, where hot updates are emitted separately before the full bundle
140
+ if ( self . isHotUpdateCompilation ( assets ) ) {
141
+ return callback ( ) ;
142
+ }
162
143
163
- // If the template and the assets did not change we don't have to emit the html
164
- const assetJson = JSON . stringify ( self . getAssetFiles ( assets ) ) ;
165
- if ( isCompilationCached && self . options . cache && assetJson === self . assetJson ) {
166
- return callback ( ) ;
167
- } else {
168
- self . assetJson = assetJson ;
169
- }
144
+ // If the template and the assets did not change we don't have to emit the html
145
+ const assetJson = JSON . stringify ( self . getAssetFiles ( assets ) ) ;
146
+ if ( isCompilationCached && self . options . cache && assetJson === self . assetJson ) {
147
+ return callback ( ) ;
148
+ } else {
149
+ self . assetJson = assetJson ;
150
+ }
170
151
171
- Promise . resolve ( )
152
+ Promise . resolve ( )
172
153
// Favicon
173
- . then ( ( ) => {
174
- if ( self . options . favicon ) {
175
- return self . addFileToAssets ( self . options . favicon , compilation )
176
- . then ( faviconBasename => {
177
- let publicPath = compilation . mainTemplate . getPublicPath ( { hash : compilation . hash } ) || '' ;
178
- if ( publicPath && publicPath . substr ( - 1 ) !== '/' ) {
179
- publicPath += '/' ;
180
- }
181
- assets . favicon = publicPath + faviconBasename ;
182
- } ) ;
183
- }
184
- } )
154
+ . then ( ( ) => {
155
+ if ( self . options . favicon ) {
156
+ return self . addFileToAssets ( self . options . favicon , compilation )
157
+ . then ( faviconBasename => {
158
+ let publicPath = compilation . mainTemplate . getPublicPath ( { hash : compilation . hash } ) || '' ;
159
+ if ( publicPath && publicPath . substr ( - 1 ) !== '/' ) {
160
+ publicPath += '/' ;
161
+ }
162
+ assets . favicon = publicPath + faviconBasename ;
163
+ } ) ;
164
+ }
165
+ } )
185
166
// Wait for the compilation to finish
186
- . then ( ( ) => compilationPromise )
187
- . then ( compiledTemplate => {
167
+ . then ( ( ) => compilationPromise )
168
+ . then ( compiledTemplate => {
188
169
// Allow to use a custom function / string instead
189
- if ( self . options . templateContent !== undefined ) {
190
- return self . options . templateContent ;
191
- }
192
- // Once everything is compiled evaluate the html factory
193
- // and replace it with its content
194
- return self . evaluateCompilationResult ( compilation , compiledTemplate ) ;
195
- } )
170
+ if ( self . options . templateContent !== undefined ) {
171
+ return self . options . templateContent ;
172
+ }
173
+ // Once everything is compiled evaluate the html factory
174
+ // and replace it with its content
175
+ return self . evaluateCompilationResult ( compilation , compiledTemplate ) ;
176
+ } )
196
177
// Allow plugins to make changes to the assets before invoking the template
197
178
// This only makes sense to use if `inject` is `false`
198
- . then ( compilationResult => applyPluginsAsyncWaterfall ( 'htmlWebpackPluginBeforeHtmlGeneration' , false , {
199
- assets : assets ,
200
- outputName : self . childCompilationOutputName ,
201
- plugin : self
202
- } )
203
- . then ( ( ) => compilationResult ) )
179
+ . then ( compilationResult => applyPluginsAsyncWaterfall ( 'htmlWebpackPluginBeforeHtmlGeneration' , false , {
180
+ assets : assets ,
181
+ outputName : self . childCompilationOutputName ,
182
+ plugin : self
183
+ } )
184
+ . then ( ( ) => compilationResult ) )
204
185
// Execute the template
205
- . then ( compilationResult => typeof compilationResult !== 'function'
206
- ? compilationResult
207
- : self . executeTemplate ( compilationResult , chunks , assets , compilation ) )
186
+ . then ( compilationResult => typeof compilationResult !== 'function'
187
+ ? compilationResult
188
+ : self . executeTemplate ( compilationResult , assets , compilation ) )
208
189
// Allow plugins to change the html before assets are injected
209
- . then ( html => {
210
- const pluginArgs = { html : html , assets : assets , plugin : self , outputName : self . childCompilationOutputName } ;
211
- return applyPluginsAsyncWaterfall ( 'htmlWebpackPluginBeforeHtmlProcessing' , true , pluginArgs ) ;
212
- } )
213
- . then ( result => {
214
- const html = result . html ;
215
- const assets = result . assets ;
216
- // Prepare script and link tags
217
- const assetTags = self . generateHtmlTagObjects ( assets ) ;
218
- const pluginArgs = { head : assetTags . head , body : assetTags . body , plugin : self , chunks : chunks , outputName : self . childCompilationOutputName } ;
219
- // Allow plugins to change the assetTag definitions
220
- return applyPluginsAsyncWaterfall ( 'htmlWebpackPluginAlterAssetTags' , true , pluginArgs )
221
- . then ( result => self . postProcessHtml ( html , assets , { body : result . body , head : result . head } )
222
- . then ( html => _ . extend ( result , { html : html , assets : assets } ) ) ) ;
223
- } )
190
+ . then ( html => {
191
+ const pluginArgs = { html : html , assets : assets , plugin : self , outputName : self . childCompilationOutputName } ;
192
+ return applyPluginsAsyncWaterfall ( 'htmlWebpackPluginBeforeHtmlProcessing' , true , pluginArgs ) ;
193
+ } )
194
+ . then ( result => {
195
+ const html = result . html ;
196
+ const assets = result . assets ;
197
+ // Prepare script and link tags
198
+ const assetTags = self . generateHtmlTagObjects ( assets ) ;
199
+ const pluginArgs = { head : assetTags . head , body : assetTags . body , plugin : self , outputName : self . childCompilationOutputName } ;
200
+ // Allow plugins to change the assetTag definitions
201
+ return applyPluginsAsyncWaterfall ( 'htmlWebpackPluginAlterAssetTags' , true , pluginArgs )
202
+ . then ( result => self . postProcessHtml ( html , assets , { body : result . body , head : result . head } )
203
+ . then ( html => _ . extend ( result , { html : html , assets : assets } ) ) ) ;
204
+ } )
224
205
// Allow plugins to change the html after assets are injected
225
- . then ( result => {
226
- const html = result . html ;
227
- const assets = result . assets ;
228
- const pluginArgs = { html : html , assets : assets , plugin : self , outputName : self . childCompilationOutputName } ;
229
- return applyPluginsAsyncWaterfall ( 'htmlWebpackPluginAfterHtmlProcessing' , true , pluginArgs )
230
- . then ( result => result . html ) ;
231
- } )
232
- . catch ( err => {
206
+ . then ( result => {
207
+ const html = result . html ;
208
+ const assets = result . assets ;
209
+ const pluginArgs = { html : html , assets : assets , plugin : self , outputName : self . childCompilationOutputName } ;
210
+ return applyPluginsAsyncWaterfall ( 'htmlWebpackPluginAfterHtmlProcessing' , true , pluginArgs )
211
+ . then ( result => result . html ) ;
212
+ } )
213
+ . catch ( err => {
233
214
// In case anything went wrong the promise is resolved
234
215
// with the error message and an error is logged
235
- compilation . errors . push ( prettyError ( err , compiler . context ) . toString ( ) ) ;
236
- // Prevent caching
237
- self . hash = null ;
238
- return self . options . showErrors ? prettyError ( err , compiler . context ) . toHtml ( ) : 'ERROR' ;
239
- } )
240
- . then ( html => {
216
+ compilation . errors . push ( prettyError ( err , compiler . context ) . toString ( ) ) ;
217
+ // Prevent caching
218
+ self . hash = null ;
219
+ return self . options . showErrors ? prettyError ( err , compiler . context ) . toHtml ( ) : 'ERROR' ;
220
+ } )
221
+ . then ( html => {
241
222
// Replace the compilation result with the evaluated html code
242
- compilation . assets [ self . childCompilationOutputName ] = {
243
- source : ( ) => html ,
244
- size : ( ) => html . length
245
- } ;
246
- } )
247
- . then ( ( ) => applyPluginsAsyncWaterfall ( 'htmlWebpackPluginAfterEmit' , false , {
248
- html : compilation . assets [ self . childCompilationOutputName ] ,
249
- outputName : self . childCompilationOutputName ,
250
- plugin : self
251
- } ) . catch ( err => {
252
- console . error ( err ) ;
253
- return null ;
254
- } ) . then ( ( ) => null ) )
223
+ compilation . assets [ self . childCompilationOutputName ] = {
224
+ source : ( ) => html ,
225
+ size : ( ) => html . length
226
+ } ;
227
+ } )
228
+ . then ( ( ) => applyPluginsAsyncWaterfall ( 'htmlWebpackPluginAfterEmit' , false , {
229
+ html : compilation . assets [ self . childCompilationOutputName ] ,
230
+ outputName : self . childCompilationOutputName ,
231
+ plugin : self
232
+ } ) . catch ( err => {
233
+ console . error ( err ) ;
234
+ return null ;
235
+ } ) . then ( ( ) => null ) )
255
236
// Let webpack continue with it
256
- . then ( ( ) => {
257
- callback ( ) ;
258
- } ) ;
259
- } ) ;
237
+ . then ( ( ) => {
238
+ callback ( ) ;
239
+ } ) ;
240
+ } ) ;
260
241
}
261
242
262
243
/**
@@ -309,7 +290,7 @@ class HtmlWebpackPlugin {
309
290
*
310
291
* @returns Promise<string>
311
292
*/
312
- executeTemplate ( templateFunction , chunks , assets , compilation ) {
293
+ executeTemplate ( templateFunction , assets , compilation ) {
313
294
// Template processing
314
295
const templateParams = this . getTemplateParameters ( compilation , assets ) ;
315
296
let html = '' ;
@@ -363,35 +344,38 @@ class HtmlWebpackPlugin {
363
344
fsStatAsync ( filename ) ,
364
345
fsReadFileAsync ( filename )
365
346
] )
366
- . then ( ( [ size , source ] ) => {
367
- return {
368
- size,
369
- source
370
- } ;
371
- } )
372
- . catch ( ( ) => Promise . reject ( new Error ( 'HtmlWebpackPlugin: could not load file ' + filename ) ) )
373
- . then ( results => {
374
- const basename = path . basename ( filename ) ;
375
- compilation . fileDependencies . add ( filename ) ;
376
- compilation . assets [ basename ] = {
377
- source : ( ) => results . source ,
378
- size : ( ) => results . size . size
379
- } ;
380
- return basename ;
381
- } ) ;
347
+ . then ( ( [ size , source ] ) => {
348
+ return {
349
+ size,
350
+ source
351
+ } ;
352
+ } )
353
+ . catch ( ( ) => Promise . reject ( new Error ( 'HtmlWebpackPlugin: could not load file ' + filename ) ) )
354
+ . then ( results => {
355
+ const basename = path . basename ( filename ) ;
356
+ compilation . fileDependencies . add ( filename ) ;
357
+ compilation . assets [ basename ] = {
358
+ source : ( ) => results . source ,
359
+ size : ( ) => results . size . size
360
+ } ;
361
+ return basename ;
362
+ } ) ;
382
363
}
383
364
384
365
/**
385
366
* Helper to sort chunks
367
+ * @param {string[] } entryNames
368
+ * @param {string|((entryNameA: string, entryNameB: string) => number) } sortMode
369
+ * @param {WebpackCompilation } compilation
386
370
*/
387
- sortChunks ( chunks , sortMode , compilation ) {
371
+ sortEntryChunks ( entryNames , sortMode , compilation ) {
388
372
// Custom function
389
373
if ( typeof sortMode === 'function' ) {
390
- return chunks . sort ( sortMode ) ;
374
+ return entryNames . sort ( sortMode ) ;
391
375
}
392
376
// Check if the given sort mode is a valid chunkSorter sort mode
393
377
if ( typeof chunkSorter [ sortMode ] !== 'undefined' ) {
394
- return chunkSorter [ sortMode ] ( chunks , this . options , compilation ) ;
378
+ return chunkSorter [ sortMode ] ( entryNames , compilation , this . options ) ;
395
379
}
396
380
throw new Error ( '"' + sortMode + '" is not a valid chunk sort mode' ) ;
397
381
}
@@ -403,20 +387,7 @@ class HtmlWebpackPlugin {
403
387
* @param {string[] } excludedChunks
404
388
*/
405
389
filterChunks ( chunks , includedChunks , excludedChunks ) {
406
- return chunks . filter ( chunk => {
407
- const chunkName = chunk . names [ 0 ] ;
408
- // This chunk doesn't have a name. This script can't handled it.
409
- if ( chunkName === undefined ) {
410
- return false ;
411
- }
412
- // Skip if the chunk should be lazy loaded
413
- if ( typeof chunk . isInitial === 'function' ) {
414
- if ( ! chunk . isInitial ( ) ) {
415
- return false ;
416
- }
417
- } else if ( ! chunk . initial ) {
418
- return false ;
419
- }
390
+ return chunks . filter ( chunkName => {
420
391
// Skip if the chunks should be filtered and the given chunk was not added explicity
421
392
if ( Array . isArray ( includedChunks ) && includedChunks . indexOf ( chunkName ) === - 1 ) {
422
393
return false ;
@@ -435,9 +406,10 @@ class HtmlWebpackPlugin {
435
406
}
436
407
437
408
/**
438
- *
409
+ * The htmlWebpackPluginAssets extracts the asset information of a webpack compilation
410
+ * for all given entry names
439
411
* @param {WebpackCompilation } compilation
440
- * @param {any } chunks
412
+ * @param {string[] } entryNames
441
413
* @returns {{
442
414
publicPath: string,
443
415
js: Array<{entryName: string, path: string}>,
@@ -446,10 +418,14 @@ class HtmlWebpackPlugin {
446
418
favicon?: string
447
419
}}
448
420
*/
449
- htmlWebpackPluginAssets ( compilation , chunks ) {
421
+ htmlWebpackPluginAssets ( compilation , entryNames ) {
450
422
const compilationHash = compilation . hash ;
451
423
452
- // Use the configured public path or build a relative path
424
+ /**
425
+ * @type {string } the configured public path to the asset root
426
+ * if a publicPath is set in the current webpack config use it otherwise
427
+ * fallback to a realtive path
428
+ */
453
429
let publicPath = typeof compilation . options . output . publicPath !== 'undefined'
454
430
// If a hard coded public path exists use it
455
431
? compilation . mainTemplate . getPublicPath ( { hash : compilationHash } )
@@ -478,50 +454,46 @@ class HtmlWebpackPlugin {
478
454
// Will contain all css files
479
455
css : [ ] ,
480
456
// Will contain the html5 appcache manifest files if it exists
481
- manifest : Object . keys ( compilation . assets ) . find ( assetFile => path . extname ( assetFile ) === '.appcache' )
457
+ manifest : Object . keys ( compilation . assets ) . find ( assetFile => path . extname ( assetFile ) === '.appcache' ) ,
458
+ // Favicon
459
+ favicon : undefined
482
460
} ;
483
461
484
462
// Append a hash for cache busting
485
463
if ( this . options . hash ) {
486
464
assets . manifest = this . appendHash ( assets . manifest , compilationHash ) ;
487
465
}
488
466
489
- for ( let i = 0 ; i < chunks . length ; i ++ ) {
490
- const chunk = chunks [ i ] ;
491
- const chunkName = chunk . names [ 0 ] ;
492
-
493
- // Prepend the public path to all chunk files
494
- let chunkFiles = [ ] . concat ( chunk . files ) . map ( chunkFile => publicPath + chunkFile ) ;
495
-
496
- // Append a hash for cache busting
497
- if ( this . options . hash ) {
498
- chunkFiles = chunkFiles . map ( chunkFile => this . appendHash ( chunkFile , compilationHash ) ) ;
499
- }
500
-
501
- // Webpack outputs an array for each chunk when using sourcemaps
502
- // or when one chunk hosts js and css simultaneously
503
- const js = chunkFiles . find ( chunkFile => / .j s ( $ | \? ) / . test ( chunkFile ) ) ;
504
- if ( js ) {
505
- assets . js . push ( {
506
- entryName : chunkName ,
507
- path : js
467
+ // Extract paths to .js and .css files from the current compilation
468
+ const extensionRegexp = / \. ( c s s | j s ) ( \? | $ ) / ;
469
+ for ( let i = 0 ; i < entryNames . length ; i ++ ) {
470
+ const entryName = entryNames [ i ] ;
471
+ const entryPointFiles = compilation . entrypoints . get ( entryName ) . getFiles ( ) ;
472
+ // Prepend the publicPath and append the hash depending on the
473
+ // webpack.output.publicPath and hashOptions
474
+ // E.g. bundle.js -> /bundle.js?hash
475
+ const entryPointPublicPaths = entryPointFiles
476
+ . map ( chunkFile => {
477
+ const entryPointPublicPath = publicPath + chunkFile ;
478
+ return this . options . hash
479
+ ? this . appendHash ( entryPointPublicPath , compilationHash )
480
+ : entryPointPublicPath ;
508
481
} ) ;
509
- }
510
482
511
- // Gather all css files
512
- const css = chunkFiles . filter ( chunkFile => / .c s s ( $ | \? ) / . test ( chunkFile ) ) ;
513
- css . forEach ( ( cssPath ) => {
514
- assets . css . push ( {
515
- entryName : chunkName ,
516
- path : cssPath
483
+ entryPointPublicPaths . forEach ( ( entryPointPublicPaths ) => {
484
+ const extMatch = extensionRegexp . exec ( entryPointPublicPaths ) ;
485
+ // Skip if the public path is not a .css or .js file
486
+ if ( ! extMatch ) {
487
+ return ;
488
+ }
489
+ // ext will contain .js or .css
490
+ const ext = extMatch [ 1 ] ;
491
+ assets [ ext ] . push ( {
492
+ entryName : entryName ,
493
+ path : entryPointPublicPaths
517
494
} ) ;
518
495
} ) ;
519
496
}
520
-
521
- // Duplicate css assets can occur on occasion if more than one chunk
522
- // requires the same css.
523
- assets . css = _ . uniq ( assets . css ) ;
524
-
525
497
return assets ;
526
498
}
527
499
@@ -728,12 +700,12 @@ class HtmlWebpackPlugin {
728
700
) ;
729
701
}
730
702
return compilation . hooks [ eventName ] . promise ( pluginArgs )
731
- . then ( result => {
732
- if ( requiresResult && ! result ) {
733
- throw new Error ( 'Using ' + eventName + ' did not return a result.' ) ;
734
- }
735
- return result ;
736
- } ) ;
703
+ . then ( result => {
704
+ if ( requiresResult && ! result ) {
705
+ throw new Error ( 'Using ' + eventName + ' did not return a result.' ) ;
706
+ }
707
+ return result ;
708
+ } ) ;
737
709
} ;
738
710
}
739
711
}
0 commit comments