@@ -51,6 +51,8 @@ import {
5151import { readTsconfig } from '../angular-cli-files/utilities/read-tsconfig' ;
5252import { augmentAppWithServiceWorker } from '../angular-cli-files/utilities/service-worker' ;
5353import {
54+ generateBuildStats ,
55+ generateBundleStats ,
5456 statsErrorsToString ,
5557 statsToString ,
5658 statsWarningsToString ,
@@ -64,7 +66,12 @@ import {
6466 normalizeSourceMaps ,
6567} from '../utils' ;
6668import { manglingDisabled } from '../utils/mangle-options' ;
67- import { CacheKey , ProcessBundleOptions , ProcessBundleResult } from '../utils/process-bundle' ;
69+ import {
70+ CacheKey ,
71+ ProcessBundleFile ,
72+ ProcessBundleOptions ,
73+ ProcessBundleResult ,
74+ } from '../utils/process-bundle' ;
6875import { assertCompatibleAngularVersion } from '../utils/version' ;
6976import {
7077 generateBrowserWebpackConfigFromContext ,
@@ -202,9 +209,6 @@ export function buildWebpackBrowser(
202209 // Check Angular version.
203210 assertCompatibleAngularVersion ( context . workspaceRoot , context . logger ) ;
204211
205- const loggingFn =
206- transforms . logging || createBrowserLoggingCallback ( ! ! options . verbose , context . logger ) ;
207-
208212 return from ( initialize ( options , context , host , transforms . webpackConfiguration ) ) . pipe (
209213 // tslint:disable-next-line: no-big-function
210214 switchMap ( ( { config : configs , projectRoot } ) => {
@@ -222,14 +226,24 @@ export function buildWebpackBrowser(
222226 ` ) ;
223227 }
224228
229+ const useBundleDownleveling =
230+ isDifferentialLoadingNeeded && ! ( fullDifferential || options . watch ) ;
231+ const startTime = Date . now ( ) ;
232+
225233 return from ( configs ) . pipe (
226234 // the concurrency parameter (3rd parameter of mergeScan) is deliberately
227235 // set to 1 to make sure the build steps are executed in sequence.
228236 mergeScan (
229237 ( lastResult , config ) => {
230238 // Make sure to only run the 2nd build step, if 1st one succeeded
231239 if ( lastResult . success ) {
232- return runWebpack ( config , context , { logging : loggingFn } ) ;
240+ return runWebpack ( config , context , {
241+ logging :
242+ transforms . logging ||
243+ ( useBundleDownleveling
244+ ? ( ) => { }
245+ : createBrowserLoggingCallback ( ! ! options . verbose , context . logger ) ) ,
246+ } ) ;
233247 } else {
234248 return of ( ) ;
235249 }
@@ -242,7 +256,19 @@ export function buildWebpackBrowser(
242256 switchMap ( async buildEvents => {
243257 configs . length = 0 ;
244258 const success = buildEvents . every ( r => r . success ) ;
245- if ( success ) {
259+ if ( ! success && useBundleDownleveling ) {
260+ // If using bundle downleveling then there is only one build
261+ // If it fails show any diagnostic messages and bail
262+ const webpackStats = buildEvents [ 0 ] . webpackStats ;
263+ if ( webpackStats && webpackStats . warnings . length > 0 ) {
264+ context . logger . warn ( statsWarningsToString ( webpackStats , { colors : true } ) ) ;
265+ }
266+ if ( webpackStats && webpackStats . errors . length > 0 ) {
267+ context . logger . error ( statsErrorsToString ( webpackStats , { colors : true } ) ) ;
268+ }
269+
270+ return { success } ;
271+ } else if ( success ) {
246272 let noModuleFiles : EmittedFiles [ ] | undefined ;
247273 let moduleFiles : EmittedFiles [ ] | undefined ;
248274 let files : EmittedFiles [ ] | undefined ;
@@ -263,7 +289,7 @@ export function buildWebpackBrowser(
263289 noModuleFiles = secondBuild . emittedFiles ;
264290 }
265291 } else if ( isDifferentialLoadingNeeded && ! fullDifferential ) {
266- const { emittedFiles = [ ] } = firstBuild ;
292+ const { emittedFiles = [ ] , webpackStats } = firstBuild ;
267293 moduleFiles = [ ] ;
268294 noModuleFiles = [ ] ;
269295
@@ -342,7 +368,9 @@ export function buildWebpackBrowser(
342368 filename,
343369 code,
344370 map,
345- name : file . name ,
371+ // id is always present for non-assets
372+ // tslint:disable-next-line: no-non-null-assertion
373+ name : file . id ! ,
346374 optimizeOnly : true ,
347375 } ) ;
348376
@@ -356,7 +384,9 @@ export function buildWebpackBrowser(
356384 filename,
357385 code,
358386 map,
359- name : file . name ,
387+ // id is always present for non-assets
388+ // tslint:disable-next-line: no-non-null-assertion
389+ name : file . id ! ,
360390 runtime : file . file . startsWith ( 'runtime' ) ,
361391 ignoreOriginal : es5Polyfills ,
362392 } ) ;
@@ -600,6 +630,73 @@ export function buildWebpackBrowser(
600630 }
601631
602632 context . logger . info ( 'ES5 bundle generation complete.' ) ;
633+
634+ type ArrayElement < A > = A extends ReadonlyArray < infer T > ? T : never ;
635+ function generateBundleInfoStats (
636+ id : string | number ,
637+ bundle : ProcessBundleFile ,
638+ chunk : ArrayElement < webpack . Stats . ToJsonOutput [ 'chunks' ] > | undefined ,
639+ ) : string {
640+ return generateBundleStats (
641+ {
642+ id,
643+ size : bundle . size ,
644+ files : bundle . map ? [ bundle . filename , bundle . map . filename ] : [ bundle . filename ] ,
645+ names : chunk && chunk . names ,
646+ entry : ! ! chunk && chunk . names . includes ( 'runtime' ) ,
647+ initial : ! ! chunk && chunk . initial ,
648+ rendered : true ,
649+ } ,
650+ true ,
651+ ) ;
652+ }
653+
654+ let bundleInfoText = '' ;
655+ const processedNames = new Set < string > ( ) ;
656+ for ( const result of processResults ) {
657+ processedNames . add ( result . name ) ;
658+
659+ const chunk =
660+ webpackStats &&
661+ webpackStats . chunks &&
662+ webpackStats . chunks . find ( c => result . name === c . id . toString ( ) ) ;
663+ if ( result . original ) {
664+ bundleInfoText +=
665+ '\n' + generateBundleInfoStats ( result . name , result . original , chunk ) ;
666+ }
667+ if ( result . downlevel ) {
668+ bundleInfoText +=
669+ '\n' + generateBundleInfoStats ( result . name , result . downlevel , chunk ) ;
670+ }
671+ }
672+
673+ if ( webpackStats && webpackStats . chunks ) {
674+ for ( const chunk of webpackStats . chunks ) {
675+ if ( processedNames . has ( chunk . id . toString ( ) ) ) {
676+ continue ;
677+ }
678+
679+ const asset =
680+ webpackStats . assets && webpackStats . assets . find ( a => a . name === chunk . files [ 0 ] ) ;
681+ bundleInfoText +=
682+ '\n' + generateBundleStats ( { ...chunk , size : asset && asset . size } , true ) ;
683+ }
684+ }
685+
686+ bundleInfoText +=
687+ '\n' +
688+ generateBuildStats (
689+ ( webpackStats && webpackStats . hash ) || '<unknown>' ,
690+ Date . now ( ) - startTime ,
691+ true ,
692+ ) ;
693+ context . logger . info ( bundleInfoText ) ;
694+ if ( webpackStats && webpackStats . warnings . length > 0 ) {
695+ context . logger . warn ( statsWarningsToString ( webpackStats , { colors : true } ) ) ;
696+ }
697+ if ( webpackStats && webpackStats . errors . length > 0 ) {
698+ context . logger . error ( statsErrorsToString ( webpackStats , { colors : true } ) ) ;
699+ }
603700 } else {
604701 const { emittedFiles = [ ] } = firstBuild ;
605702 files = emittedFiles . filter ( x => x . name !== 'polyfills-es5' ) ;
0 commit comments