Skip to content

Commit a8f701a

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

File tree

2 files changed

+78
-59
lines changed
  • packages/angular_devkit/build_angular/src

2 files changed

+78
-59
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: 30 additions & 34 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

@@ -179,63 +180,58 @@ const MAX_CONCURRENT_WRITES = 64;
179180

180181
export async function writeResultFiles(
181182
outputFiles: BuildOutputFile[],
182-
assetFiles: { source: string; destination: string }[] | undefined,
183+
assetFiles: BuildOutputAsset[] | undefined,
183184
outputPath: string,
184185
) {
185186
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);
187+
const ensureDirectoryExists = async (basePath: string) => {
192188
if (basePath && !directoryExists.has(basePath)) {
193189
await fs.mkdir(path.join(outputPath, basePath), { recursive: true });
194190
directoryExists.add(basePath);
195191
}
196-
// Write file contents
197-
await fs.writeFile(path.join(outputPath, fullOutputPath), file.contents);
198192
};
199193

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-
}
194+
// Writes the output file to disk and ensures the containing directories are present
195+
await emitFilesToDisk(outputFiles, async (file: BuildOutputFile) => {
196+
const fullOutputPath = file.fullOutputPath;
197+
// Ensure output subdirectories exist
198+
const basePath = path.dirname(fullOutputPath);
199+
await ensureDirectoryExists(basePath);
208200

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

212205
if (assetFiles?.length) {
213-
const copyAssetFile = async (asset: { source: string; destination: string }) => {
206+
await emitFilesToDisk(assetFiles, async (asset: BuildOutputAsset) => {
214207
// Ensure output subdirectories exist
215208
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-
}
209+
await ensureDirectoryExists(destPath);
210+
221211
// Copy file contents
222212
await fs.copyFile(
223213
asset.source,
224214
path.join(outputPath, destPath),
225215
fsConstants.COPYFILE_FICLONE,
226216
);
227-
};
228-
229-
for (let fileIndex = 0; fileIndex < assetFiles.length; ) {
230-
const groupMax = Math.min(fileIndex + MAX_CONCURRENT_WRITES, assetFiles.length);
217+
});
218+
}
219+
}
231220

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

237-
await Promise.all(actions);
229+
const actions = [];
230+
while (fileIndex < groupMax) {
231+
actions.push(writeFileCallback(files[fileIndex++]));
238232
}
233+
234+
await Promise.all(actions);
239235
}
240236
}
241237

0 commit comments

Comments
 (0)