@@ -53,6 +53,8 @@ import {
53
53
import { readTsconfig } from '../angular-cli-files/utilities/read-tsconfig' ;
54
54
import { augmentAppWithServiceWorker } from '../angular-cli-files/utilities/service-worker' ;
55
55
import {
56
+ generateBuildStats ,
57
+ generateBundleStats ,
56
58
statsErrorsToString ,
57
59
statsToString ,
58
60
statsWarningsToString ,
@@ -66,7 +68,12 @@ import {
66
68
normalizeSourceMaps ,
67
69
} from '../utils' ;
68
70
import { 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' ;
70
77
import { assertCompatibleAngularVersion } from '../utils/version' ;
71
78
import {
72
79
generateBrowserWebpackConfigFromContext ,
@@ -200,9 +207,6 @@ export function buildWebpackBrowser(
200
207
// Check Angular version.
201
208
assertCompatibleAngularVersion ( context . workspaceRoot , context . logger ) ;
202
209
203
- const loggingFn =
204
- transforms . logging || createBrowserLoggingCallback ( ! ! options . verbose , context . logger ) ;
205
-
206
210
return from ( initialize ( options , context , host , transforms . webpackConfiguration ) ) . pipe (
207
211
// tslint:disable-next-line: no-big-function
208
212
switchMap ( ( { workspace, config : configs } ) => {
@@ -233,14 +237,24 @@ export function buildWebpackBrowser(
233
237
` ) ;
234
238
}
235
239
240
+ const useBundleDownleveling =
241
+ isDifferentialLoadingNeeded && ! ( fullDifferential || options . watch ) ;
242
+ const startTime = Date . now ( ) ;
243
+
236
244
return from ( configs ) . pipe (
237
245
// the concurrency parameter (3rd parameter of mergeScan) is deliberately
238
246
// set to 1 to make sure the build steps are executed in sequence.
239
247
mergeScan (
240
248
( lastResult , config ) => {
241
249
// Make sure to only run the 2nd build step, if 1st one succeeded
242
250
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
+ } ) ;
244
258
} else {
245
259
return of ( ) ;
246
260
}
@@ -253,7 +267,19 @@ export function buildWebpackBrowser(
253
267
switchMap ( async buildEvents => {
254
268
configs . length = 0 ;
255
269
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 ) {
257
283
let noModuleFiles : EmittedFiles [ ] | undefined ;
258
284
let moduleFiles : EmittedFiles [ ] | undefined ;
259
285
let files : EmittedFiles [ ] | undefined ;
@@ -274,7 +300,7 @@ export function buildWebpackBrowser(
274
300
noModuleFiles = secondBuild . emittedFiles ;
275
301
}
276
302
} else if ( isDifferentialLoadingNeeded && ! fullDifferential ) {
277
- const { emittedFiles = [ ] } = firstBuild ;
303
+ const { emittedFiles = [ ] , webpackStats } = firstBuild ;
278
304
moduleFiles = [ ] ;
279
305
noModuleFiles = [ ] ;
280
306
@@ -353,7 +379,9 @@ export function buildWebpackBrowser(
353
379
filename,
354
380
code,
355
381
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 ! ,
357
385
optimizeOnly : true ,
358
386
} ) ;
359
387
@@ -367,7 +395,9 @@ export function buildWebpackBrowser(
367
395
filename,
368
396
code,
369
397
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 ! ,
371
401
runtime : file . file . startsWith ( 'runtime' ) ,
372
402
ignoreOriginal : es5Polyfills ,
373
403
} ) ;
@@ -611,6 +641,73 @@ export function buildWebpackBrowser(
611
641
}
612
642
613
643
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
+ }
614
711
} else {
615
712
const { emittedFiles = [ ] } = firstBuild ;
616
713
files = emittedFiles . filter ( x => x . name !== 'polyfills-es5' ) ;
0 commit comments