Skip to content

Commit d1a5e5f

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 d1a5e5f

File tree

4 files changed

+462
-66
lines changed

4 files changed

+462
-66
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: 45 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,55 @@ 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+
// Ensure output subdirectories exist
92+
const basePath = path.dirname(file.path);
93+
await ensureDirectoryExists(basePath);
94+
95+
// Write file contents
96+
await fs.writeFile(path.join(outputPath, file.path), file.contents);
97+
});
9398

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

0 commit comments

Comments
 (0)