@@ -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,58 @@ 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+ // Ensure output subdirectories exist
103+ await ensureDirectoryExists ( asset . destination ) ;
104+
105+ // Copy file contents
106+ await fs . copyFile (
107+ asset . source ,
108+ path . join ( outputPath , asset . destination ) ,
109+ fsConstants . COPYFILE_FICLONE ,
110+ ) ;
111+ } ) ;
112+ }
113+ }
114+
115+ const MAX_CONCURRENT_WRITES = 64 ;
116+ async function emitFilesToDisk < T = BuildOutputAsset | BuildOutputFile > (
117+ files : T [ ] ,
118+ writeFileCallback : ( asset : T ) => Promise < void > ,
119+ ) : Promise < void > {
120+ // Write files in groups of MAX_CONCURRENT_WRITES to avoid too many open files
121+ for ( let fileIndex = 0 ; fileIndex < files . length ; ) {
122+ const groupMax = Math . min ( fileIndex + MAX_CONCURRENT_WRITES , files . length ) ;
123+
124+ const actions = [ ] ;
125+ while ( fileIndex < groupMax ) {
126+ actions . push ( writeFileCallback ( files [ fileIndex ++ ] ) ) ;
127+ }
128+
129+ await Promise . all ( actions ) ;
107130 }
108131}
109132
0 commit comments