@@ -53,6 +53,8 @@ import {
5353import { readTsconfig } from '../angular-cli-files/utilities/read-tsconfig' ;
5454import { augmentAppWithServiceWorker } from '../angular-cli-files/utilities/service-worker' ;
5555import {
56+ generateBuildStats ,
57+ generateBundleStats ,
5658 statsErrorsToString ,
5759 statsToString ,
5860 statsWarningsToString ,
@@ -66,7 +68,12 @@ import {
6668 normalizeSourceMaps ,
6769} from '../utils' ;
6870import { manglingDisabled } from '../utils/mangle-options' ;
69- import { CacheKey , ProcessBundleOptions , ProcessBundleResult } from '../utils/process-bundle' ;
71+ import {
72+ CacheKey ,
73+ ProcessBundleFile ,
74+ ProcessBundleOptions ,
75+ ProcessBundleResult ,
76+ } from '../utils/process-bundle' ;
7077import { assertCompatibleAngularVersion } from '../utils/version' ;
7178import {
7279 generateBrowserWebpackConfigFromContext ,
@@ -200,9 +207,6 @@ export function buildWebpackBrowser(
200207 // Check Angular version.
201208 assertCompatibleAngularVersion ( context . workspaceRoot , context . logger ) ;
202209
203- const loggingFn =
204- transforms . logging || createBrowserLoggingCallback ( ! ! options . verbose , context . logger ) ;
205-
206210 return from ( initialize ( options , context , host , transforms . webpackConfiguration ) ) . pipe (
207211 // tslint:disable-next-line: no-big-function
208212 switchMap ( ( { workspace, config : configs } ) => {
@@ -233,14 +237,24 @@ export function buildWebpackBrowser(
233237 ` ) ;
234238 }
235239
240+ const useBundleDownleveling =
241+ isDifferentialLoadingNeeded && ! ( fullDifferential || options . watch ) ;
242+ const startTime = Date . now ( ) ;
243+
236244 return from ( configs ) . pipe (
237245 // the concurrency parameter (3rd parameter of mergeScan) is deliberately
238246 // set to 1 to make sure the build steps are executed in sequence.
239247 mergeScan (
240248 ( lastResult , config ) => {
241249 // Make sure to only run the 2nd build step, if 1st one succeeded
242250 if ( lastResult . success ) {
243- return runWebpack ( config , context , { logging : loggingFn } ) ;
251+ return runWebpack ( config , context , {
252+ logging :
253+ transforms . logging ||
254+ ( useBundleDownleveling
255+ ? ( ) => { }
256+ : createBrowserLoggingCallback ( ! ! options . verbose , context . logger ) ) ,
257+ } ) ;
244258 } else {
245259 return of ( ) ;
246260 }
@@ -253,7 +267,19 @@ export function buildWebpackBrowser(
253267 switchMap ( async buildEvents => {
254268 configs . length = 0 ;
255269 const success = buildEvents . every ( r => r . success ) ;
256- if ( success ) {
270+ if ( ! success && useBundleDownleveling ) {
271+ // If using bundle downleveling then there is only one build
272+ // If it fails show any diagnostic messages and bail
273+ const webpackStats = buildEvents [ 0 ] . webpackStats ;
274+ if ( webpackStats && webpackStats . warnings . length > 0 ) {
275+ context . logger . warn ( statsWarningsToString ( webpackStats , { colors : true } ) ) ;
276+ }
277+ if ( webpackStats && webpackStats . errors . length > 0 ) {
278+ context . logger . error ( statsErrorsToString ( webpackStats , { colors : true } ) ) ;
279+ }
280+
281+ return { success } ;
282+ } else if ( success ) {
257283 let noModuleFiles : EmittedFiles [ ] | undefined ;
258284 let moduleFiles : EmittedFiles [ ] | undefined ;
259285 let files : EmittedFiles [ ] | undefined ;
@@ -274,7 +300,7 @@ export function buildWebpackBrowser(
274300 noModuleFiles = secondBuild . emittedFiles ;
275301 }
276302 } else if ( isDifferentialLoadingNeeded && ! fullDifferential ) {
277- const { emittedFiles = [ ] } = firstBuild ;
303+ const { emittedFiles = [ ] , webpackStats } = firstBuild ;
278304 moduleFiles = [ ] ;
279305 noModuleFiles = [ ] ;
280306
@@ -353,7 +379,9 @@ export function buildWebpackBrowser(
353379 filename,
354380 code,
355381 map,
356- name : file . name ,
382+ // id is always present for non-assets
383+ // tslint:disable-next-line: no-non-null-assertion
384+ name : file . id ! ,
357385 optimizeOnly : true ,
358386 } ) ;
359387
@@ -367,7 +395,9 @@ export function buildWebpackBrowser(
367395 filename,
368396 code,
369397 map,
370- name : file . name ,
398+ // id is always present for non-assets
399+ // tslint:disable-next-line: no-non-null-assertion
400+ name : file . id ! ,
371401 runtime : file . file . startsWith ( 'runtime' ) ,
372402 ignoreOriginal : es5Polyfills ,
373403 } ) ;
@@ -611,6 +641,73 @@ export function buildWebpackBrowser(
611641 }
612642
613643 context . logger . info ( 'ES5 bundle generation complete.' ) ;
644+
645+ type ArrayElement < A > = A extends ReadonlyArray < infer T > ? T : never ;
646+ function generateBundleInfoStats (
647+ id : string | number ,
648+ bundle : ProcessBundleFile ,
649+ chunk : ArrayElement < webpack . Stats . ToJsonOutput [ 'chunks' ] > | undefined ,
650+ ) : string {
651+ return generateBundleStats (
652+ {
653+ id,
654+ size : bundle . size ,
655+ files : bundle . map ? [ bundle . filename , bundle . map . filename ] : [ bundle . filename ] ,
656+ names : chunk && chunk . names ,
657+ entry : ! ! chunk && chunk . names . includes ( 'runtime' ) ,
658+ initial : ! ! chunk && chunk . initial ,
659+ rendered : true ,
660+ } ,
661+ true ,
662+ ) ;
663+ }
664+
665+ let bundleInfoText = '' ;
666+ const processedNames = new Set < string > ( ) ;
667+ for ( const result of processResults ) {
668+ processedNames . add ( result . name ) ;
669+
670+ const chunk =
671+ webpackStats &&
672+ webpackStats . chunks &&
673+ webpackStats . chunks . find ( c => result . name === c . id . toString ( ) ) ;
674+ if ( result . original ) {
675+ bundleInfoText +=
676+ '\n' + generateBundleInfoStats ( result . name , result . original , chunk ) ;
677+ }
678+ if ( result . downlevel ) {
679+ bundleInfoText +=
680+ '\n' + generateBundleInfoStats ( result . name , result . downlevel , chunk ) ;
681+ }
682+ }
683+
684+ if ( webpackStats && webpackStats . chunks ) {
685+ for ( const chunk of webpackStats . chunks ) {
686+ if ( processedNames . has ( chunk . id . toString ( ) ) ) {
687+ continue ;
688+ }
689+
690+ const asset =
691+ webpackStats . assets && webpackStats . assets . find ( a => a . name === chunk . files [ 0 ] ) ;
692+ bundleInfoText +=
693+ '\n' + generateBundleStats ( { ...chunk , size : asset && asset . size } , true ) ;
694+ }
695+ }
696+
697+ bundleInfoText +=
698+ '\n' +
699+ generateBuildStats (
700+ ( webpackStats && webpackStats . hash ) || '<unknown>' ,
701+ Date . now ( ) - startTime ,
702+ true ,
703+ ) ;
704+ context . logger . info ( bundleInfoText ) ;
705+ if ( webpackStats && webpackStats . warnings . length > 0 ) {
706+ context . logger . warn ( statsWarningsToString ( webpackStats , { colors : true } ) ) ;
707+ }
708+ if ( webpackStats && webpackStats . errors . length > 0 ) {
709+ context . logger . error ( statsErrorsToString ( webpackStats , { colors : true } ) ) ;
710+ }
614711 } else {
615712 const { emittedFiles = [ ] } = firstBuild ;
616713 files = emittedFiles . filter ( x => x . name !== 'polyfills-es5' ) ;
0 commit comments