@@ -64,7 +64,7 @@ import {
6464 normalizeSourceMaps ,
6565} from '../utils' ;
6666import { manglingDisabled } from '../utils/mangle-options' ;
67- import { CacheKey , ProcessBundleOptions } from '../utils/process-bundle' ;
67+ import { CacheKey , ProcessBundleOptions , ProcessBundleResult } from '../utils/process-bundle' ;
6868import { assertCompatibleAngularVersion } from '../utils/version' ;
6969import {
7070 generateBrowserWebpackConfigFromContext ,
@@ -269,11 +269,12 @@ export function buildWebpackBrowser(
269269
270270 // Common options for all bundle process actions
271271 const sourceMapOptions = normalizeSourceMaps ( options . sourceMap || false ) ;
272- const actionOptions = {
272+ const actionOptions : Partial < ProcessBundleOptions > = {
273273 optimize : normalizeOptimization ( options . optimization ) . scripts ,
274274 sourceMaps : sourceMapOptions . scripts ,
275275 hiddenSourceMaps : sourceMapOptions . hidden ,
276276 vendorSourceMaps : sourceMapOptions . vendor ,
277+ integrityAlgorithm : options . subresourceIntegrity ? 'sha384' : undefined ,
277278 } ;
278279
279280 const actions : ProcessBundleOptions [ ] = [ ] ;
@@ -303,8 +304,10 @@ export function buildWebpackBrowser(
303304 seen . add ( file . file ) ;
304305
305306 // 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 ) {
308311 moduleFiles . push ( file ) ;
309312 }
310313 // If not optimizing then ES2015 polyfills do not need processing
@@ -339,6 +342,7 @@ export function buildWebpackBrowser(
339342 filename,
340343 code,
341344 map,
345+ name : file . name ,
342346 optimizeOnly : true ,
343347 } ) ;
344348
@@ -352,6 +356,7 @@ export function buildWebpackBrowser(
352356 filename,
353357 code,
354358 map,
359+ name : file . name ,
355360 runtime : file . file . startsWith ( 'runtime' ) ,
356361 ignoreOriginal : es5Polyfills ,
357362 } ) ;
@@ -367,15 +372,18 @@ export function buildWebpackBrowser(
367372 context . logger . info ( 'Generating ES5 bundles for differential loading...' ) ;
368373
369374 const processActions : typeof actions = [ ] ;
375+ let processRuntimeAction : ProcessBundleOptions | undefined ;
370376 const cacheActions : { src : string ; dest : string } [ ] = [ ] ;
377+ const processResults : ProcessBundleResult [ ] = [ ] ;
371378 for ( const action of actions ) {
372379 // Create base cache key with elements:
373380 // * package version - different build-angular versions cause different final outputs
374381 // * 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 )
376384 . 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 } ` ;
379387 if ( manglingDisabled ) {
380388 baseCacheKey += '|MD' ;
381389 }
@@ -430,31 +438,86 @@ export function buildWebpackBrowser(
430438
431439 // If all required cached entries are present, use the cached entries
432440 // 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 } ` ;
439449 }
440- if ( cacheEntries [ CacheKey . OriginalMap ] ) {
450+
451+ let cacheEntry = cacheEntries [ CacheKey . OriginalCode ] ;
452+ if ( cacheEntry ) {
441453 cacheActions . push ( {
442- src : cacheEntries [ CacheKey . OriginalMap ] . path ,
443- dest : action . filename + '.map' ,
454+ src : cacheEntry . path ,
455+ dest : action . filename ,
444456 } ) ;
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+ } ;
445487 }
446- if ( cacheEntries [ CacheKey . DownlevelCode ] ) {
488+
489+ cacheEntry = cacheEntries [ CacheKey . DownlevelCode ] ;
490+ if ( cacheEntry ) {
447491 cacheActions . push ( {
448- src : cacheEntries [ CacheKey . DownlevelCode ] . path ,
492+ src : cacheEntry . path ,
449493 dest : action . filename . replace ( 'es2015' , 'es5' ) ,
450494 } ) ;
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+ }
451512 }
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+ } ;
458521 } else {
459522 processActions . push ( {
460523 ...action ,
@@ -506,11 +569,16 @@ export function buildWebpackBrowser(
506569 [ 'process' ] ,
507570 ) ;
508571 let completed = 0 ;
509- const workCallback = ( error : Error | null ) => {
572+ const workCallback = ( error : Error | null , result : ProcessBundleResult ) => {
510573 if ( error ) {
511574 workerFarm . end ( workers ) ;
512575 reject ( error ) ;
513- } else if ( ++ completed === processActions . length ) {
576+
577+ return ;
578+ }
579+
580+ processResults . push ( result ) ;
581+ if ( ++ completed === processActions . length ) {
514582 workerFarm . end ( workers ) ;
515583 resolve ( ) ;
516584 }
@@ -520,6 +588,17 @@ export function buildWebpackBrowser(
520588 } ) ;
521589 }
522590
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+
523602 context . logger . info ( 'ES5 bundle generation complete.' ) ;
524603 } else {
525604 const { emittedFiles = [ ] } = firstBuild ;
0 commit comments