@@ -12,6 +12,7 @@ import * as path from 'path';
1212import textTable from 'text-table' ;
1313import { Configuration , StatsCompilation } from 'webpack' ;
1414import { Schema as BrowserBuilderOptions } from '../../builders/browser/schema' ;
15+ import { BudgetCalculatorResult } from '../../utils/bundle-calculator' ;
1516import { colors as ansiColors , removeColor } from '../../utils/color' ;
1617import { markAsyncChunksNonInitial } from './async-chunks' ;
1718import { getStatsOptions , normalizeExtraEntryPoints } from './helpers' ;
@@ -71,36 +72,67 @@ function generateBuildStatsTable(
7172 colors : boolean ,
7273 showTotalSize : boolean ,
7374 showEstimatedTransferSize : boolean ,
75+ budgetFailures ?: BudgetCalculatorResult [ ] ,
7476) : string {
7577 const g = ( x : string ) => ( colors ? ansiColors . greenBright ( x ) : x ) ;
7678 const c = ( x : string ) => ( colors ? ansiColors . cyanBright ( x ) : x ) ;
79+ const r = ( x : string ) => ( colors ? ansiColors . redBright ( x ) : x ) ;
80+ const y = ( x : string ) => ( colors ? ansiColors . yellowBright ( x ) : x ) ;
7781 const bold = ( x : string ) => ( colors ? ansiColors . bold ( x ) : x ) ;
7882 const dim = ( x : string ) => ( colors ? ansiColors . dim ( x ) : x ) ;
7983
84+ const getSizeColor = ( name : string , file ?: string , defaultColor = c ) => {
85+ const severity = budgets . get ( name ) || ( file && budgets . get ( file ) ) ;
86+ switch ( severity ) {
87+ case 'warning' :
88+ return y ;
89+ case 'error' :
90+ return r ;
91+ default :
92+ return defaultColor ;
93+ }
94+ } ;
95+
8096 const changedEntryChunksStats : BundleStatsData [ ] = [ ] ;
8197 const changedLazyChunksStats : BundleStatsData [ ] = [ ] ;
8298
8399 let initialTotalRawSize = 0 ;
84100 let initialTotalEstimatedTransferSize ;
85101
102+ const budgets = new Map < string , string > ( ) ;
103+ if ( budgetFailures ) {
104+ for ( const { label, severity } of budgetFailures ) {
105+ // In some cases a file can have multiple budget failures.
106+ // Favor error.
107+ if ( label && ( ! budgets . has ( label ) || budgets . get ( label ) === 'warning' ) ) {
108+ budgets . set ( label , severity ) ;
109+ }
110+ }
111+ }
112+
86113 for ( const { initial, stats } of data ) {
87114 const [ files , names , rawSize , estimatedTransferSize ] = stats ;
88-
115+ const getRawSizeColor = getSizeColor ( names , files ) ;
89116 let data : BundleStatsData ;
90117
91118 if ( showEstimatedTransferSize ) {
92119 data = [
93120 g ( files ) ,
94121 names ,
95- c ( typeof rawSize === 'number' ? formatSize ( rawSize ) : rawSize ) ,
122+ getRawSizeColor ( typeof rawSize === 'number' ? formatSize ( rawSize ) : rawSize ) ,
96123 c (
97124 typeof estimatedTransferSize === 'number'
98125 ? formatSize ( estimatedTransferSize )
99126 : estimatedTransferSize ,
100127 ) ,
101128 ] ;
102129 } else {
103- data = [ g ( files ) , names , c ( typeof rawSize === 'number' ? formatSize ( rawSize ) : rawSize ) , '' ] ;
130+ data = [
131+ g ( files ) ,
132+ names ,
133+ getRawSizeColor ( typeof rawSize === 'number' ? formatSize ( rawSize ) : rawSize ) ,
134+ '' ,
135+ ] ;
104136 }
105137
106138 if ( initial ) {
@@ -135,7 +167,12 @@ function generateBuildStatsTable(
135167 if ( showTotalSize ) {
136168 bundleInfo . push ( [ ] ) ;
137169
138- const totalSizeElements = [ ' ' , 'Initial Total' , formatSize ( initialTotalRawSize ) ] ;
170+ const initialSizeTotalColor = getSizeColor ( 'bundle initial' , undefined , ( x ) => x ) ;
171+ const totalSizeElements = [
172+ ' ' ,
173+ 'Initial Total' ,
174+ initialSizeTotalColor ( formatSize ( initialTotalRawSize ) ) ,
175+ ] ;
139176 if ( showEstimatedTransferSize ) {
140177 totalSizeElements . push (
141178 typeof initialTotalEstimatedTransferSize === 'number'
@@ -180,7 +217,7 @@ function statsToString(
180217 json : StatsCompilation ,
181218 // eslint-disable-next-line @typescript-eslint/no-explicit-any
182219 statsConfig : any ,
183- bundleState ?: BundleStats [ ] ,
220+ budgetFailures ?: BudgetCalculatorResult [ ] ,
184221) : string {
185222 if ( ! json . chunks ?. length ) {
186223 return '' ;
@@ -189,45 +226,44 @@ function statsToString(
189226 const colors = statsConfig . colors ;
190227 const rs = ( x : string ) => ( colors ? ansiColors . reset ( x ) : x ) ;
191228
192- const changedChunksStats : BundleStats [ ] = bundleState ?? [ ] ;
229+ const changedChunksStats : BundleStats [ ] = [ ] ;
193230 let unchangedChunkNumber = 0 ;
194231 let hasEstimatedTransferSizes = false ;
195- if ( ! bundleState ?. length ) {
196- const isFirstRun = ! runsCache . has ( json . outputPath || '' ) ;
197-
198- for ( const chunk of json . chunks ) {
199- // During first build we want to display unchanged chunks
200- // but unchanged cached chunks are always marked as not rendered.
201- if ( ! isFirstRun && ! chunk . rendered ) {
202- continue ;
203- }
204232
205- const assets = json . assets ?. filter ( ( asset ) => chunk . files ?. includes ( asset . name ) ) ;
206- let rawSize = 0 ;
207- let estimatedTransferSize ;
208- if ( assets ) {
209- for ( const asset of assets ) {
210- if ( asset . name . endsWith ( '.map' ) ) {
211- continue ;
212- }
233+ const isFirstRun = ! runsCache . has ( json . outputPath || '' ) ;
234+
235+ for ( const chunk of json . chunks ) {
236+ // During first build we want to display unchanged chunks
237+ // but unchanged cached chunks are always marked as not rendered.
238+ if ( ! isFirstRun && ! chunk . rendered ) {
239+ continue ;
240+ }
241+
242+ const assets = json . assets ?. filter ( ( asset ) => chunk . files ?. includes ( asset . name ) ) ;
243+ let rawSize = 0 ;
244+ let estimatedTransferSize ;
245+ if ( assets ) {
246+ for ( const asset of assets ) {
247+ if ( asset . name . endsWith ( '.map' ) ) {
248+ continue ;
249+ }
213250
214- rawSize += asset . size ;
251+ rawSize += asset . size ;
215252
216- if ( typeof asset . info . estimatedTransferSize === 'number' ) {
217- if ( estimatedTransferSize === undefined ) {
218- estimatedTransferSize = 0 ;
219- hasEstimatedTransferSizes = true ;
220- }
221- estimatedTransferSize += asset . info . estimatedTransferSize ;
253+ if ( typeof asset . info . estimatedTransferSize === 'number' ) {
254+ if ( estimatedTransferSize === undefined ) {
255+ estimatedTransferSize = 0 ;
256+ hasEstimatedTransferSizes = true ;
222257 }
258+ estimatedTransferSize += asset . info . estimatedTransferSize ;
223259 }
224260 }
225- changedChunksStats . push ( generateBundleStats ( { ...chunk , rawSize, estimatedTransferSize } ) ) ;
226261 }
227- unchangedChunkNumber = json . chunks . length - changedChunksStats . length ;
228-
229- runsCache . add ( json . outputPath || '' ) ;
262+ changedChunksStats . push ( generateBundleStats ( { ...chunk , rawSize, estimatedTransferSize } ) ) ;
230263 }
264+ unchangedChunkNumber = json . chunks . length - changedChunksStats . length ;
265+
266+ runsCache . add ( json . outputPath || '' ) ;
231267
232268 // Sort chunks by size in descending order
233269 changedChunksStats . sort ( ( a , b ) => {
@@ -247,6 +283,7 @@ function statsToString(
247283 colors ,
248284 unchangedChunkNumber === 0 ,
249285 hasEstimatedTransferSizes ,
286+ budgetFailures ,
250287 ) ;
251288
252289 // In some cases we do things outside of webpack context
@@ -387,9 +424,9 @@ export function webpackStatsLogger(
387424 logger : logging . LoggerApi ,
388425 json : StatsCompilation ,
389426 config : Configuration ,
390- bundleStats ?: BundleStats [ ] ,
427+ budgetFailures ?: BudgetCalculatorResult [ ] ,
391428) : void {
392- logger . info ( statsToString ( json , config . stats , bundleStats ) ) ;
429+ logger . info ( statsToString ( json , config . stats , budgetFailures ) ) ;
393430
394431 if ( statsHasWarnings ( json ) ) {
395432 logger . warn ( statsWarningsToString ( json , config . stats ) ) ;
0 commit comments