@@ -66,7 +66,7 @@ import {
6666 normalizeSourceMaps ,
6767} from '../utils' ;
6868import { manglingDisabled } from '../utils/mangle-options' ;
69- import { CacheKey , ProcessBundleOptions } from '../utils/process-bundle' ;
69+ import { CacheKey , ProcessBundleOptions , ProcessBundleResult } from '../utils/process-bundle' ;
7070import { assertCompatibleAngularVersion } from '../utils/version' ;
7171import {
7272 generateBrowserWebpackConfigFromContext ,
@@ -280,11 +280,12 @@ export function buildWebpackBrowser(
280280
281281 // Common options for all bundle process actions
282282 const sourceMapOptions = normalizeSourceMaps ( options . sourceMap || false ) ;
283- const actionOptions = {
283+ const actionOptions : Partial < ProcessBundleOptions > = {
284284 optimize : normalizeOptimization ( options . optimization ) . scripts ,
285285 sourceMaps : sourceMapOptions . scripts ,
286286 hiddenSourceMaps : sourceMapOptions . hidden ,
287287 vendorSourceMaps : sourceMapOptions . vendor ,
288+ integrityAlgorithm : options . subresourceIntegrity ? 'sha384' : undefined ,
288289 } ;
289290
290291 const actions : ProcessBundleOptions [ ] = [ ] ;
@@ -314,8 +315,10 @@ export function buildWebpackBrowser(
314315 seen . add ( file . file ) ;
315316
316317 // 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 ) {
319322 moduleFiles . push ( file ) ;
320323 }
321324 // If not optimizing then ES2015 polyfills do not need processing
@@ -350,6 +353,7 @@ export function buildWebpackBrowser(
350353 filename,
351354 code,
352355 map,
356+ name : file . name ,
353357 optimizeOnly : true ,
354358 } ) ;
355359
@@ -363,6 +367,7 @@ export function buildWebpackBrowser(
363367 filename,
364368 code,
365369 map,
370+ name : file . name ,
366371 runtime : file . file . startsWith ( 'runtime' ) ,
367372 ignoreOriginal : es5Polyfills ,
368373 } ) ;
@@ -378,15 +383,18 @@ export function buildWebpackBrowser(
378383 context . logger . info ( 'Generating ES5 bundles for differential loading...' ) ;
379384
380385 const processActions : typeof actions = [ ] ;
386+ let processRuntimeAction : ProcessBundleOptions | undefined ;
381387 const cacheActions : { src : string ; dest : string } [ ] = [ ] ;
388+ const processResults : ProcessBundleResult [ ] = [ ] ;
382389 for ( const action of actions ) {
383390 // Create base cache key with elements:
384391 // * package version - different build-angular versions cause different final outputs
385392 // * 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 )
387395 . 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 } ` ;
390398 if ( manglingDisabled ) {
391399 baseCacheKey += '|MD' ;
392400 }
@@ -441,31 +449,86 @@ export function buildWebpackBrowser(
441449
442450 // If all required cached entries are present, use the cached entries
443451 // 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 } ` ;
450460 }
451- if ( cacheEntries [ CacheKey . OriginalMap ] ) {
461+
462+ let cacheEntry = cacheEntries [ CacheKey . OriginalCode ] ;
463+ if ( cacheEntry ) {
452464 cacheActions . push ( {
453- src : cacheEntries [ CacheKey . OriginalMap ] . path ,
454- dest : action . filename + '.map' ,
465+ src : cacheEntry . path ,
466+ dest : action . filename ,
455467 } ) ;
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+ } ;
456498 }
457- if ( cacheEntries [ CacheKey . DownlevelCode ] ) {
499+
500+ cacheEntry = cacheEntries [ CacheKey . DownlevelCode ] ;
501+ if ( cacheEntry ) {
458502 cacheActions . push ( {
459- src : cacheEntries [ CacheKey . DownlevelCode ] . path ,
503+ src : cacheEntry . path ,
460504 dest : action . filename . replace ( 'es2015' , 'es5' ) ,
461505 } ) ;
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+ }
462523 }
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+ } ;
469532 } else {
470533 processActions . push ( {
471534 ...action ,
@@ -517,11 +580,16 @@ export function buildWebpackBrowser(
517580 [ 'process' ] ,
518581 ) ;
519582 let completed = 0 ;
520- const workCallback = ( error : Error | null ) => {
583+ const workCallback = ( error : Error | null , result : ProcessBundleResult ) => {
521584 if ( error ) {
522585 workerFarm . end ( workers ) ;
523586 reject ( error ) ;
524- } else if ( ++ completed === processActions . length ) {
587+
588+ return ;
589+ }
590+
591+ processResults . push ( result ) ;
592+ if ( ++ completed === processActions . length ) {
525593 workerFarm . end ( workers ) ;
526594 resolve ( ) ;
527595 }
@@ -531,6 +599,17 @@ export function buildWebpackBrowser(
531599 } ) ;
532600 }
533601
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+
534613 context . logger . info ( 'ES5 bundle generation complete.' ) ;
535614 } else {
536615 const { emittedFiles = [ ] } = firstBuild ;
0 commit comments