@@ -64,7 +64,7 @@ import {
64
64
normalizeSourceMaps ,
65
65
} from '../utils' ;
66
66
import { manglingDisabled } from '../utils/mangle-options' ;
67
- import { CacheKey , ProcessBundleOptions } from '../utils/process-bundle' ;
67
+ import { CacheKey , ProcessBundleOptions , ProcessBundleResult } from '../utils/process-bundle' ;
68
68
import { assertCompatibleAngularVersion } from '../utils/version' ;
69
69
import {
70
70
generateBrowserWebpackConfigFromContext ,
@@ -269,11 +269,12 @@ export function buildWebpackBrowser(
269
269
270
270
// Common options for all bundle process actions
271
271
const sourceMapOptions = normalizeSourceMaps ( options . sourceMap || false ) ;
272
- const actionOptions = {
272
+ const actionOptions : Partial < ProcessBundleOptions > = {
273
273
optimize : normalizeOptimization ( options . optimization ) . scripts ,
274
274
sourceMaps : sourceMapOptions . scripts ,
275
275
hiddenSourceMaps : sourceMapOptions . hidden ,
276
276
vendorSourceMaps : sourceMapOptions . vendor ,
277
+ integrityAlgorithm : options . subresourceIntegrity ? 'sha384' : undefined ,
277
278
} ;
278
279
279
280
const actions : ProcessBundleOptions [ ] = [ ] ;
@@ -303,8 +304,10 @@ export function buildWebpackBrowser(
303
304
seen . add ( file . file ) ;
304
305
305
306
// All files at this point except ES5 polyfills are module scripts
306
- const es5Polyfills = file . file . startsWith ( 'polyfills-es5' ) ;
307
- if ( ! es5Polyfills && ! file . file . startsWith ( 'polyfills-nomodule-es5' ) ) {
307
+ const es5Polyfills =
308
+ file . file . startsWith ( 'polyfills-es5' ) ||
309
+ file . file . startsWith ( 'polyfills-nomodule-es5' ) ;
310
+ if ( ! es5Polyfills ) {
308
311
moduleFiles . push ( file ) ;
309
312
}
310
313
// If not optimizing then ES2015 polyfills do not need processing
@@ -339,6 +342,7 @@ export function buildWebpackBrowser(
339
342
filename,
340
343
code,
341
344
map,
345
+ name : file . name ,
342
346
optimizeOnly : true ,
343
347
} ) ;
344
348
@@ -352,6 +356,7 @@ export function buildWebpackBrowser(
352
356
filename,
353
357
code,
354
358
map,
359
+ name : file . name ,
355
360
runtime : file . file . startsWith ( 'runtime' ) ,
356
361
ignoreOriginal : es5Polyfills ,
357
362
} ) ;
@@ -367,15 +372,18 @@ export function buildWebpackBrowser(
367
372
context . logger . info ( 'Generating ES5 bundles for differential loading...' ) ;
368
373
369
374
const processActions : typeof actions = [ ] ;
375
+ let processRuntimeAction : ProcessBundleOptions | undefined ;
370
376
const cacheActions : { src : string ; dest : string } [ ] = [ ] ;
377
+ const processResults : ProcessBundleResult [ ] = [ ] ;
371
378
for ( const action of actions ) {
372
379
// Create base cache key with elements:
373
380
// * package version - different build-angular versions cause different final outputs
374
381
// * code length/hash - ensure cached version matches the same input code
375
- const codeHash = createHash ( 'sha1' )
382
+ const algorithm = action . integrityAlgorithm || 'sha1' ;
383
+ const codeHash = createHash ( algorithm )
376
384
. update ( action . code )
377
- . digest ( 'hex ' ) ;
378
- let baseCacheKey = `${ packageVersion } |${ action . code . length } |${ codeHash } ` ;
385
+ . digest ( 'base64 ' ) ;
386
+ let baseCacheKey = `${ packageVersion } |${ action . code . length } |${ algorithm } - ${ codeHash } ` ;
379
387
if ( manglingDisabled ) {
380
388
baseCacheKey += '|MD' ;
381
389
}
@@ -430,31 +438,86 @@ export function buildWebpackBrowser(
430
438
431
439
// If all required cached entries are present, use the cached entries
432
440
// Otherwise process the files
433
- if ( cached ) {
434
- if ( cacheEntries [ CacheKey . OriginalCode ] ) {
435
- cacheActions . push ( {
436
- src : cacheEntries [ CacheKey . OriginalCode ] . path ,
437
- dest : action . filename ,
438
- } ) ;
441
+ // If SRI is enabled always process the runtime bundle
442
+ // Lazy route integrity values are stored in the runtime bundle
443
+ if ( action . integrityAlgorithm && action . runtime ) {
444
+ processRuntimeAction = action ;
445
+ } else if ( cached ) {
446
+ const result : ProcessBundleResult = { name : action . name } ;
447
+ if ( action . integrityAlgorithm ) {
448
+ result . integrity = `${ action . integrityAlgorithm } -${ codeHash } ` ;
439
449
}
440
- if ( cacheEntries [ CacheKey . OriginalMap ] ) {
450
+
451
+ let cacheEntry = cacheEntries [ CacheKey . OriginalCode ] ;
452
+ if ( cacheEntry ) {
441
453
cacheActions . push ( {
442
- src : cacheEntries [ CacheKey . OriginalMap ] . path ,
443
- dest : action . filename + '.map' ,
454
+ src : cacheEntry . path ,
455
+ dest : action . filename ,
444
456
} ) ;
457
+ result . original = {
458
+ filename : action . filename ,
459
+ size : cacheEntry . size ,
460
+ integrity : cacheEntry . metadata && cacheEntry . metadata . integrity ,
461
+ } ;
462
+
463
+ cacheEntry = cacheEntries [ CacheKey . OriginalMap ] ;
464
+ if ( cacheEntry ) {
465
+ cacheActions . push ( {
466
+ src : cacheEntry . path ,
467
+ dest : action . filename + '.map' ,
468
+ } ) ;
469
+ result . original . map = {
470
+ filename : action . filename + '.map' ,
471
+ size : cacheEntry . size ,
472
+ } ;
473
+ }
474
+ } else if ( ! action . ignoreOriginal ) {
475
+ // If the original wasn't processed (and therefore not cached), add info
476
+ result . original = {
477
+ filename : action . filename ,
478
+ size : Buffer . byteLength ( action . code , 'utf8' ) ,
479
+ map :
480
+ action . map === undefined
481
+ ? undefined
482
+ : {
483
+ filename : action . filename + '.map' ,
484
+ size : Buffer . byteLength ( action . map , 'utf8' ) ,
485
+ } ,
486
+ } ;
445
487
}
446
- if ( cacheEntries [ CacheKey . DownlevelCode ] ) {
488
+
489
+ cacheEntry = cacheEntries [ CacheKey . DownlevelCode ] ;
490
+ if ( cacheEntry ) {
447
491
cacheActions . push ( {
448
- src : cacheEntries [ CacheKey . DownlevelCode ] . path ,
492
+ src : cacheEntry . path ,
449
493
dest : action . filename . replace ( 'es2015' , 'es5' ) ,
450
494
} ) ;
495
+ result . downlevel = {
496
+ filename : action . filename . replace ( 'es2015' , 'es5' ) ,
497
+ size : cacheEntry . size ,
498
+ integrity : cacheEntry . metadata && cacheEntry . metadata . integrity ,
499
+ } ;
500
+
501
+ cacheEntry = cacheEntries [ CacheKey . DownlevelMap ] ;
502
+ if ( cacheEntry ) {
503
+ cacheActions . push ( {
504
+ src : cacheEntry . path ,
505
+ dest : action . filename . replace ( 'es2015' , 'es5' ) + '.map' ,
506
+ } ) ;
507
+ result . downlevel . map = {
508
+ filename : action . filename . replace ( 'es2015' , 'es5' ) + '.map' ,
509
+ size : cacheEntry . size ,
510
+ } ;
511
+ }
451
512
}
452
- if ( cacheEntries [ CacheKey . DownlevelMap ] ) {
453
- cacheActions . push ( {
454
- src : cacheEntries [ CacheKey . DownlevelMap ] . path ,
455
- dest : action . filename . replace ( 'es2015' , 'es5' ) + '.map' ,
456
- } ) ;
457
- }
513
+
514
+ processResults . push ( result ) ;
515
+ } else if ( action . runtime ) {
516
+ processRuntimeAction = {
517
+ ...action ,
518
+ cacheKeys,
519
+ cachePath : cacheDownlevelPath || undefined ,
520
+ } ;
458
521
} else {
459
522
processActions . push ( {
460
523
...action ,
@@ -506,11 +569,16 @@ export function buildWebpackBrowser(
506
569
[ 'process' ] ,
507
570
) ;
508
571
let completed = 0 ;
509
- const workCallback = ( error : Error | null ) => {
572
+ const workCallback = ( error : Error | null , result : ProcessBundleResult ) => {
510
573
if ( error ) {
511
574
workerFarm . end ( workers ) ;
512
575
reject ( error ) ;
513
- } else if ( ++ completed === processActions . length ) {
576
+
577
+ return ;
578
+ }
579
+
580
+ processResults . push ( result ) ;
581
+ if ( ++ completed === processActions . length ) {
514
582
workerFarm . end ( workers ) ;
515
583
resolve ( ) ;
516
584
}
@@ -520,6 +588,17 @@ export function buildWebpackBrowser(
520
588
} ) ;
521
589
}
522
590
591
+ // Runtime must be processed after all other files
592
+ if ( processRuntimeAction ) {
593
+ const runtimeOptions = {
594
+ ...processRuntimeAction ,
595
+ runtimeData : processResults ,
596
+ } ;
597
+ processResults . push (
598
+ await import ( '../utils/process-bundle' ) . then ( m => m . processAsync ( runtimeOptions ) ) ,
599
+ ) ;
600
+ }
601
+
523
602
context . logger . info ( 'ES5 bundle generation complete.' ) ;
524
603
} else {
525
604
const { emittedFiles = [ ] } = firstBuild ;
0 commit comments