@@ -12,6 +12,7 @@ import { constants as fsConstants } from 'node:fs';
1212import fs from 'node:fs/promises' ;
1313import path from 'node:path' ;
1414import { BuildOutputFile } from '../../tools/esbuild/bundler-context' ;
15+ import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result' ;
1516import { buildApplicationInternal } from '../application' ;
1617import { Schema as ApplicationBuilderOptions } from '../application/schema' ;
1718import { logBuilderStatusWarnings } from './builder-status-warnings' ;
@@ -74,36 +75,61 @@ function normalizeOptions(options: BrowserBuilderOptions): ApplicationBuilderOpt
7475// and not output browser files into '/browser'.
7576async function writeResultFiles (
7677 outputFiles : BuildOutputFile [ ] ,
77- assetFiles : { source : string ; destination : string } [ ] | undefined ,
78+ assetFiles : BuildOutputAsset [ ] | undefined ,
7879 outputPath : string ,
7980) {
8081 const directoryExists = new Set < string > ( ) ;
81- await Promise . all (
82- outputFiles . map ( async ( file ) => {
83- // Ensure output subdirectories exist
84- const basePath = path . dirname ( file . path ) ;
85- if ( basePath && ! directoryExists . has ( basePath ) ) {
86- await fs . mkdir ( path . join ( outputPath , basePath ) , { recursive : true } ) ;
87- directoryExists . add ( basePath ) ;
88- }
89- // Write file contents
90- await fs . writeFile ( path . join ( outputPath , file . path ) , file . contents ) ;
91- } ) ,
92- ) ;
82+ const ensureDirectoryExists = async ( basePath : string ) => {
83+ if ( basePath && ! directoryExists . has ( basePath ) ) {
84+ await fs . mkdir ( path . join ( outputPath , basePath ) , { recursive : true } ) ;
85+ directoryExists . add ( basePath ) ;
86+ }
87+ } ;
88+
89+ // Writes the output file to disk and ensures the containing directories are present
90+ await emitFilesToDisk ( outputFiles , async ( file : BuildOutputFile ) => {
91+ const fullOutputPath = file . fullOutputPath ;
92+ // Ensure output subdirectories exist
93+ const basePath = path . dirname ( fullOutputPath ) ;
94+ await ensureDirectoryExists ( basePath ) ;
95+
96+ // Write file contents
97+ await fs . writeFile ( path . join ( outputPath , fullOutputPath ) , file . contents ) ;
98+ } ) ;
9399
94100 if ( assetFiles ?. length ) {
95- await Promise . all (
96- assetFiles . map ( async ( { source, destination } ) => {
97- // Ensure output subdirectories exist
98- const basePath = path . dirname ( destination ) ;
99- if ( basePath && ! directoryExists . has ( basePath ) ) {
100- await fs . mkdir ( path . join ( outputPath , basePath ) , { recursive : true } ) ;
101- directoryExists . add ( basePath ) ;
102- }
103- // Copy file contents
104- await fs . copyFile ( source , path . join ( outputPath ) , fsConstants . COPYFILE_FICLONE ) ;
105- } ) ,
106- ) ;
101+ await emitFilesToDisk ( assetFiles , async ( asset : BuildOutputAsset ) => {
102+ const destination = asset . destination ;
103+ const basePath = path . dirname ( destination ) ;
104+
105+ // Ensure output subdirectories exist
106+ await ensureDirectoryExists ( basePath ) ;
107+
108+ // Copy file contents
109+ await fs . copyFile (
110+ asset . source ,
111+ path . join ( outputPath , destination ) ,
112+ fsConstants . COPYFILE_FICLONE ,
113+ ) ;
114+ } ) ;
115+ }
116+ }
117+
118+ const MAX_CONCURRENT_WRITES = 64 ;
119+ async function emitFilesToDisk < T = BuildOutputAsset | BuildOutputFile > (
120+ files : T [ ] ,
121+ writeFileCallback : ( asset : T ) => Promise < void > ,
122+ ) : Promise < void > {
123+ // Write files in groups of MAX_CONCURRENT_WRITES to avoid too many open files
124+ for ( let fileIndex = 0 ; fileIndex < files . length ; ) {
125+ const groupMax = Math . min ( fileIndex + MAX_CONCURRENT_WRITES , files . length ) ;
126+
127+ const actions = [ ] ;
128+ while ( fileIndex < groupMax ) {
129+ actions . push ( writeFileCallback ( files [ fileIndex ++ ] ) ) ;
130+ }
131+
132+ await Promise . all ( actions ) ;
107133 }
108134}
109135
0 commit comments