@@ -11,6 +11,8 @@ import type { BuildOptions, Metafile, OutputFile } from 'esbuild';
1111import { constants as fsConstants } from 'node:fs' ;
1212import fs from 'node:fs/promises' ;
1313import path from 'node:path' ;
14+ import { promisify } from 'node:util' ;
15+ import { brotliCompress } from 'node:zlib' ;
1416import { copyAssets } from '../../utils/copy-assets' ;
1517import { assertIsError } from '../../utils/error' ;
1618import { transformSupportedBrowsersToTargets } from '../../utils/esbuild-targets' ;
@@ -33,6 +35,8 @@ import { createSourcemapIngorelistPlugin } from './sourcemap-ignorelist-plugin';
3335import { shutdownSassWorkerPool } from './stylesheets/sass-plugin' ;
3436import type { ChangedFiles } from './watcher' ;
3537
38+ const compressAsync = promisify ( brotliCompress ) ;
39+
3640interface RebuildState {
3741 rebuildContexts : BundlerContext [ ] ;
3842 codeBundleCache ?: SourceFileCache ;
@@ -259,7 +263,12 @@ async function execute(
259263 }
260264 }
261265
262- logBuildStats ( context , metafile , initialFiles ) ;
266+ // Calculate estimated transfer size if scripts are optimized
267+ let estimatedTransferSizes ;
268+ if ( optimizationOptions . scripts || optimizationOptions . styles . minify ) {
269+ estimatedTransferSizes = await calculateEstimatedTransferSizes ( executionResult . outputFiles ) ;
270+ }
271+ logBuildStats ( context , metafile , initialFiles , estimatedTransferSizes ) ;
263272
264273 const buildTime = Number ( process . hrtime . bigint ( ) - startTime ) / 10 ** 9 ;
265274 context . logger . info ( `Application bundle generation complete. [${ buildTime . toFixed ( 3 ) } seconds]` ) ;
@@ -700,7 +709,12 @@ export async function* buildEsbuildBrowserInternal(
700709
701710export default createBuilder ( buildEsbuildBrowser ) ;
702711
703- function logBuildStats ( context : BuilderContext , metafile : Metafile , initialFiles : FileInfo [ ] ) {
712+ function logBuildStats (
713+ context : BuilderContext ,
714+ metafile : Metafile ,
715+ initialFiles : FileInfo [ ] ,
716+ estimatedTransferSizes ?: Map < string , number > ,
717+ ) {
704718 const initial = new Map ( initialFiles . map ( ( info ) => [ info . file , info . name ] ) ) ;
705719 const stats : BundleStats [ ] = [ ] ;
706720 for ( const [ file , output ] of Object . entries ( metafile . outputs ) ) {
@@ -716,11 +730,45 @@ function logBuildStats(context: BuilderContext, metafile: Metafile, initialFiles
716730
717731 stats . push ( {
718732 initial : initial . has ( file ) ,
719- stats : [ file , initial . get ( file ) ?? '-' , output . bytes , '' ] ,
733+ stats : [
734+ file ,
735+ initial . get ( file ) ?? '-' ,
736+ output . bytes ,
737+ estimatedTransferSizes ?. get ( file ) ?? '-' ,
738+ ] ,
720739 } ) ;
721740 }
722741
723- const tableText = generateBuildStatsTable ( stats , true , true , false , undefined ) ;
742+ const tableText = generateBuildStatsTable ( stats , true , true , ! ! estimatedTransferSizes , undefined ) ;
724743
725744 context . logger . info ( '\n' + tableText + '\n' ) ;
726745}
746+
747+ async function calculateEstimatedTransferSizes ( outputFiles : OutputFile [ ] ) {
748+ const sizes = new Map < string , number > ( ) ;
749+
750+ const pendingCompression = [ ] ;
751+ for ( const outputFile of outputFiles ) {
752+ // Only calculate JavaScript and CSS files
753+ if ( ! outputFile . path . endsWith ( '.js' ) && ! outputFile . path . endsWith ( '.css' ) ) {
754+ continue ;
755+ }
756+
757+ // Skip compressing small files which may end being larger once compressed and will most likely not be
758+ // compressed in actual transit.
759+ if ( outputFile . contents . byteLength < 1024 ) {
760+ sizes . set ( outputFile . path , outputFile . contents . byteLength ) ;
761+ continue ;
762+ }
763+
764+ pendingCompression . push (
765+ compressAsync ( outputFile . contents ) . then ( ( result ) =>
766+ sizes . set ( outputFile . path , result . byteLength ) ,
767+ ) ,
768+ ) ;
769+ }
770+
771+ await Promise . all ( pendingCompression ) ;
772+
773+ return sizes ;
774+ }
0 commit comments