Skip to content

Commit fdcec79

Browse files
committed
fix(@angular-devkit/build-angular): several fixes to assets and files writes in browser-esbuild builder
This commit ports #26016 to the esbuilder and also fixes an issue where assets were being outputted in the wrong directory. Closes #26021
1 parent 55422d5 commit fdcec79

File tree

4 files changed

+463
-61
lines changed

4 files changed

+463
-61
lines changed

goldens/circular-deps/packages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
"packages/angular_devkit/build_angular/src/tools/esbuild/bundler-context.ts",
44
"packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts"
55
],
6+
[
7+
"packages/angular_devkit/build_angular/src/tools/esbuild/bundler-execution-result.ts",
8+
"packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts"
9+
],
610
[
711
"packages/angular_devkit/build_angular/src/tools/webpack/utils/stats.ts",
812
"packages/angular_devkit/build_angular/src/utils/bundle-calculator.ts"

packages/angular_devkit/build_angular/src/builders/browser-esbuild/index.ts

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { constants as fsConstants } from 'node:fs';
1212
import fs from 'node:fs/promises';
1313
import path from 'node:path';
1414
import { BuildOutputFile } from '../../tools/esbuild/bundler-context';
15+
import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result';
1516
import { buildApplicationInternal } from '../application';
1617
import { Schema as ApplicationBuilderOptions } from '../application/schema';
1718
import { logBuilderStatusWarnings } from './builder-status-warnings';
@@ -74,36 +75,58 @@ function normalizeOptions(options: BrowserBuilderOptions): ApplicationBuilderOpt
7475
// and not output browser files into '/browser'.
7576
async 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

Comments
 (0)