@@ -66,7 +66,7 @@ import {
66
66
normalizeSourceMaps ,
67
67
} from '../utils' ;
68
68
import { manglingDisabled } from '../utils/mangle-options' ;
69
- import { CacheKey , ProcessBundleOptions } from '../utils/process-bundle' ;
69
+ import { CacheKey , ProcessBundleOptions , ProcessBundleResult } from '../utils/process-bundle' ;
70
70
import { assertCompatibleAngularVersion } from '../utils/version' ;
71
71
import {
72
72
generateBrowserWebpackConfigFromContext ,
@@ -280,11 +280,12 @@ export function buildWebpackBrowser(
280
280
281
281
// Common options for all bundle process actions
282
282
const sourceMapOptions = normalizeSourceMaps ( options . sourceMap || false ) ;
283
- const actionOptions = {
283
+ const actionOptions : Partial < ProcessBundleOptions > = {
284
284
optimize : normalizeOptimization ( options . optimization ) . scripts ,
285
285
sourceMaps : sourceMapOptions . scripts ,
286
286
hiddenSourceMaps : sourceMapOptions . hidden ,
287
287
vendorSourceMaps : sourceMapOptions . vendor ,
288
+ integrityAlgorithm : options . subresourceIntegrity ? 'sha384' : undefined ,
288
289
} ;
289
290
290
291
const actions : ProcessBundleOptions [ ] = [ ] ;
@@ -314,8 +315,10 @@ export function buildWebpackBrowser(
314
315
seen . add ( file . file ) ;
315
316
316
317
// All files at this point except ES5 polyfills are module scripts
317
- const es5Polyfills = file . file . startsWith ( 'polyfills-es5' ) ;
318
- if ( ! es5Polyfills && ! file . file . startsWith ( 'polyfills-nomodule-es5' ) ) {
318
+ const es5Polyfills =
319
+ file . file . startsWith ( 'polyfills-es5' ) ||
320
+ file . file . startsWith ( 'polyfills-nomodule-es5' ) ;
321
+ if ( ! es5Polyfills ) {
319
322
moduleFiles . push ( file ) ;
320
323
}
321
324
// If not optimizing then ES2015 polyfills do not need processing
@@ -350,6 +353,7 @@ export function buildWebpackBrowser(
350
353
filename,
351
354
code,
352
355
map,
356
+ name : file . name ,
353
357
optimizeOnly : true ,
354
358
} ) ;
355
359
@@ -363,6 +367,7 @@ export function buildWebpackBrowser(
363
367
filename,
364
368
code,
365
369
map,
370
+ name : file . name ,
366
371
runtime : file . file . startsWith ( 'runtime' ) ,
367
372
ignoreOriginal : es5Polyfills ,
368
373
} ) ;
@@ -378,15 +383,18 @@ export function buildWebpackBrowser(
378
383
context . logger . info ( 'Generating ES5 bundles for differential loading...' ) ;
379
384
380
385
const processActions : typeof actions = [ ] ;
386
+ let processRuntimeAction : ProcessBundleOptions | undefined ;
381
387
const cacheActions : { src : string ; dest : string } [ ] = [ ] ;
388
+ const processResults : ProcessBundleResult [ ] = [ ] ;
382
389
for ( const action of actions ) {
383
390
// Create base cache key with elements:
384
391
// * package version - different build-angular versions cause different final outputs
385
392
// * code length/hash - ensure cached version matches the same input code
386
- const codeHash = createHash ( 'sha1' )
393
+ const algorithm = action . integrityAlgorithm || 'sha1' ;
394
+ const codeHash = createHash ( algorithm )
387
395
. update ( action . code )
388
- . digest ( 'hex ' ) ;
389
- let baseCacheKey = `${ packageVersion } |${ action . code . length } |${ codeHash } ` ;
396
+ . digest ( 'base64 ' ) ;
397
+ let baseCacheKey = `${ packageVersion } |${ action . code . length } |${ algorithm } - ${ codeHash } ` ;
390
398
if ( manglingDisabled ) {
391
399
baseCacheKey += '|MD' ;
392
400
}
@@ -441,31 +449,86 @@ export function buildWebpackBrowser(
441
449
442
450
// If all required cached entries are present, use the cached entries
443
451
// Otherwise process the files
444
- if ( cached ) {
445
- if ( cacheEntries [ CacheKey . OriginalCode ] ) {
446
- cacheActions . push ( {
447
- src : cacheEntries [ CacheKey . OriginalCode ] . path ,
448
- dest : action . filename ,
449
- } ) ;
452
+ // If SRI is enabled always process the runtime bundle
453
+ // Lazy route integrity values are stored in the runtime bundle
454
+ if ( action . integrityAlgorithm && action . runtime ) {
455
+ processRuntimeAction = action ;
456
+ } else if ( cached ) {
457
+ const result : ProcessBundleResult = { name : action . name } ;
458
+ if ( action . integrityAlgorithm ) {
459
+ result . integrity = `${ action . integrityAlgorithm } -${ codeHash } ` ;
450
460
}
451
- if ( cacheEntries [ CacheKey . OriginalMap ] ) {
461
+
462
+ let cacheEntry = cacheEntries [ CacheKey . OriginalCode ] ;
463
+ if ( cacheEntry ) {
452
464
cacheActions . push ( {
453
- src : cacheEntries [ CacheKey . OriginalMap ] . path ,
454
- dest : action . filename + '.map' ,
465
+ src : cacheEntry . path ,
466
+ dest : action . filename ,
455
467
} ) ;
468
+ result . original = {
469
+ filename : action . filename ,
470
+ size : cacheEntry . size ,
471
+ integrity : cacheEntry . metadata && cacheEntry . metadata . integrity ,
472
+ } ;
473
+
474
+ cacheEntry = cacheEntries [ CacheKey . OriginalMap ] ;
475
+ if ( cacheEntry ) {
476
+ cacheActions . push ( {
477
+ src : cacheEntry . path ,
478
+ dest : action . filename + '.map' ,
479
+ } ) ;
480
+ result . original . map = {
481
+ filename : action . filename + '.map' ,
482
+ size : cacheEntry . size ,
483
+ } ;
484
+ }
485
+ } else if ( ! action . ignoreOriginal ) {
486
+ // If the original wasn't processed (and therefore not cached), add info
487
+ result . original = {
488
+ filename : action . filename ,
489
+ size : Buffer . byteLength ( action . code , 'utf8' ) ,
490
+ map :
491
+ action . map === undefined
492
+ ? undefined
493
+ : {
494
+ filename : action . filename + '.map' ,
495
+ size : Buffer . byteLength ( action . map , 'utf8' ) ,
496
+ } ,
497
+ } ;
456
498
}
457
- if ( cacheEntries [ CacheKey . DownlevelCode ] ) {
499
+
500
+ cacheEntry = cacheEntries [ CacheKey . DownlevelCode ] ;
501
+ if ( cacheEntry ) {
458
502
cacheActions . push ( {
459
- src : cacheEntries [ CacheKey . DownlevelCode ] . path ,
503
+ src : cacheEntry . path ,
460
504
dest : action . filename . replace ( 'es2015' , 'es5' ) ,
461
505
} ) ;
506
+ result . downlevel = {
507
+ filename : action . filename . replace ( 'es2015' , 'es5' ) ,
508
+ size : cacheEntry . size ,
509
+ integrity : cacheEntry . metadata && cacheEntry . metadata . integrity ,
510
+ } ;
511
+
512
+ cacheEntry = cacheEntries [ CacheKey . DownlevelMap ] ;
513
+ if ( cacheEntry ) {
514
+ cacheActions . push ( {
515
+ src : cacheEntry . path ,
516
+ dest : action . filename . replace ( 'es2015' , 'es5' ) + '.map' ,
517
+ } ) ;
518
+ result . downlevel . map = {
519
+ filename : action . filename . replace ( 'es2015' , 'es5' ) + '.map' ,
520
+ size : cacheEntry . size ,
521
+ } ;
522
+ }
462
523
}
463
- if ( cacheEntries [ CacheKey . DownlevelMap ] ) {
464
- cacheActions . push ( {
465
- src : cacheEntries [ CacheKey . DownlevelMap ] . path ,
466
- dest : action . filename . replace ( 'es2015' , 'es5' ) + '.map' ,
467
- } ) ;
468
- }
524
+
525
+ processResults . push ( result ) ;
526
+ } else if ( action . runtime ) {
527
+ processRuntimeAction = {
528
+ ...action ,
529
+ cacheKeys,
530
+ cachePath : cacheDownlevelPath || undefined ,
531
+ } ;
469
532
} else {
470
533
processActions . push ( {
471
534
...action ,
@@ -517,11 +580,16 @@ export function buildWebpackBrowser(
517
580
[ 'process' ] ,
518
581
) ;
519
582
let completed = 0 ;
520
- const workCallback = ( error : Error | null ) => {
583
+ const workCallback = ( error : Error | null , result : ProcessBundleResult ) => {
521
584
if ( error ) {
522
585
workerFarm . end ( workers ) ;
523
586
reject ( error ) ;
524
- } else if ( ++ completed === processActions . length ) {
587
+
588
+ return ;
589
+ }
590
+
591
+ processResults . push ( result ) ;
592
+ if ( ++ completed === processActions . length ) {
525
593
workerFarm . end ( workers ) ;
526
594
resolve ( ) ;
527
595
}
@@ -531,6 +599,17 @@ export function buildWebpackBrowser(
531
599
} ) ;
532
600
}
533
601
602
+ // Runtime must be processed after all other files
603
+ if ( processRuntimeAction ) {
604
+ const runtimeOptions = {
605
+ ...processRuntimeAction ,
606
+ runtimeData : processResults ,
607
+ } ;
608
+ processResults . push (
609
+ await import ( '../utils/process-bundle' ) . then ( m => m . processAsync ( runtimeOptions ) ) ,
610
+ ) ;
611
+ }
612
+
534
613
context . logger . info ( 'ES5 bundle generation complete.' ) ;
535
614
} else {
536
615
const { emittedFiles = [ ] } = firstBuild ;
0 commit comments