Skip to content

Commit 9c6907f

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 9c6907f

File tree

2 files changed

+79
-61
lines changed
  • packages/angular_devkit/build_angular/src

2 files changed

+79
-61
lines changed

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

packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { BudgetCalculatorResult } from '../../utils/bundle-calculator';
1919
import { Spinner } from '../../utils/spinner';
2020
import { BundleStats, generateBuildStatsTable } from '../webpack/utils/stats';
2121
import { BuildOutputFile, BuildOutputFileType, InitialFileRecord } from './bundler-context';
22+
import { BuildOutputAsset } from './bundler-execution-result';
2223

2324
const compressAsync = promisify(brotliCompress);
2425

@@ -175,67 +176,61 @@ export function getFeatureSupport(target: string[]): BuildOptions['supported'] {
175176
return supported;
176177
}
177178

178-
const MAX_CONCURRENT_WRITES = 64;
179-
180179
export async function writeResultFiles(
181180
outputFiles: BuildOutputFile[],
182-
assetFiles: { source: string; destination: string }[] | undefined,
181+
assetFiles: BuildOutputAsset[] | undefined,
183182
outputPath: string,
184183
) {
185184
const directoryExists = new Set<string>();
186-
187-
// Writes the output file to disk and ensures the containing directories are present
188-
const writeOutputFile = async (file: BuildOutputFile) => {
189-
const fullOutputPath = file.fullOutputPath;
190-
// Ensure output subdirectories exist
191-
const basePath = path.dirname(fullOutputPath);
185+
const ensureDirectoryExists = async (basePath: string) => {
192186
if (basePath && !directoryExists.has(basePath)) {
193187
await fs.mkdir(path.join(outputPath, basePath), { recursive: true });
194188
directoryExists.add(basePath);
195189
}
196-
// Write file contents
197-
await fs.writeFile(path.join(outputPath, fullOutputPath), file.contents);
198190
};
199191

200-
// Write files in groups of MAX_CONCURRENT_WRITES to avoid too many open files
201-
for (let fileIndex = 0; fileIndex < outputFiles.length; ) {
202-
const groupMax = Math.min(fileIndex + MAX_CONCURRENT_WRITES, outputFiles.length);
203-
204-
const actions = [];
205-
while (fileIndex < groupMax) {
206-
actions.push(writeOutputFile(outputFiles[fileIndex++]));
207-
}
192+
// Writes the output file to disk and ensures the containing directories are present
193+
await emitFilesToDisk(outputFiles, async (file: BuildOutputFile) => {
194+
const fullOutputPath = file.fullOutputPath;
195+
// Ensure output subdirectories exist
196+
const basePath = path.dirname(fullOutputPath);
197+
await ensureDirectoryExists(basePath);
208198

209-
await Promise.all(actions);
210-
}
199+
// Write file contents
200+
await fs.writeFile(path.join(outputPath, fullOutputPath), file.contents);
201+
});
211202

212203
if (assetFiles?.length) {
213-
const copyAssetFile = async (asset: { source: string; destination: string }) => {
204+
await emitFilesToDisk(assetFiles, async (asset: BuildOutputAsset) => {
214205
// Ensure output subdirectories exist
215206
const destPath = join('browser', asset.destination);
216-
const basePath = path.dirname(destPath);
217-
if (basePath && !directoryExists.has(basePath)) {
218-
await fs.mkdir(path.join(outputPath, basePath), { recursive: true });
219-
directoryExists.add(basePath);
220-
}
207+
await ensureDirectoryExists(destPath);
208+
221209
// Copy file contents
222210
await fs.copyFile(
223211
asset.source,
224212
path.join(outputPath, destPath),
225213
fsConstants.COPYFILE_FICLONE,
226214
);
227-
};
228-
229-
for (let fileIndex = 0; fileIndex < assetFiles.length; ) {
230-
const groupMax = Math.min(fileIndex + MAX_CONCURRENT_WRITES, assetFiles.length);
215+
});
216+
}
217+
}
231218

232-
const actions = [];
233-
while (fileIndex < groupMax) {
234-
actions.push(copyAssetFile(assetFiles[fileIndex++]));
235-
}
219+
const MAX_CONCURRENT_WRITES = 64;
220+
async function emitFilesToDisk<T = BuildOutputAsset | BuildOutputFile>(
221+
files: T[],
222+
writeFileCallback: (asset: T) => Promise<void>,
223+
): Promise<void> {
224+
// Write files in groups of MAX_CONCURRENT_WRITES to avoid too many open files
225+
for (let fileIndex = 0; fileIndex < files.length; ) {
226+
const groupMax = Math.min(fileIndex + MAX_CONCURRENT_WRITES, files.length);
236227

237-
await Promise.all(actions);
228+
const actions = [];
229+
while (fileIndex < groupMax) {
230+
actions.push(writeFileCallback(files[fileIndex++]));
238231
}
232+
233+
await Promise.all(actions);
239234
}
240235
}
241236

0 commit comments

Comments
 (0)