Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): several fixes to assets and files…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
alan-agius4 committed Oct 12, 2023
1 parent 63f85cc commit a1287c4
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { constants as fsConstants } from 'node:fs';
import fs from 'node:fs/promises';
import path from 'node:path';
import { BuildOutputFile } from '../../tools/esbuild/bundler-context';
import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result';
import { buildApplicationInternal } from '../application';
import { Schema as ApplicationBuilderOptions } from '../application/schema';
import { logBuilderStatusWarnings } from './builder-status-warnings';
Expand Down Expand Up @@ -74,36 +75,58 @@ function normalizeOptions(options: BrowserBuilderOptions): ApplicationBuilderOpt
// and not output browser files into '/browser'.
async function writeResultFiles(
outputFiles: BuildOutputFile[],
assetFiles: { source: string; destination: string }[] | undefined,
assetFiles: BuildOutputAsset[] | undefined,
outputPath: string,
) {
const directoryExists = new Set<string>();
await Promise.all(
outputFiles.map(async (file) => {
// Ensure output subdirectories exist
const basePath = path.dirname(file.path);
if (basePath && !directoryExists.has(basePath)) {
await fs.mkdir(path.join(outputPath, basePath), { recursive: true });
directoryExists.add(basePath);
}
// Write file contents
await fs.writeFile(path.join(outputPath, file.path), file.contents);
}),
);
const ensureDirectoryExists = async (basePath: string) => {
if (basePath && !directoryExists.has(basePath)) {
await fs.mkdir(path.join(outputPath, basePath), { recursive: true });
directoryExists.add(basePath);
}
};

// Writes the output file to disk and ensures the containing directories are present
await emitFilesToDisk(outputFiles, async (file: BuildOutputFile) => {
const fullOutputPath = file.fullOutputPath;
// Ensure output subdirectories exist
const basePath = path.dirname(fullOutputPath);
await ensureDirectoryExists(basePath);

// Write file contents
await fs.writeFile(path.join(outputPath, fullOutputPath), file.contents);
});

if (assetFiles?.length) {
await Promise.all(
assetFiles.map(async ({ source, destination }) => {
// Ensure output subdirectories exist
const basePath = path.dirname(destination);
if (basePath && !directoryExists.has(basePath)) {
await fs.mkdir(path.join(outputPath, basePath), { recursive: true });
directoryExists.add(basePath);
}
// Copy file contents
await fs.copyFile(source, path.join(outputPath), fsConstants.COPYFILE_FICLONE);
}),
);
await emitFilesToDisk(assetFiles, async (asset: BuildOutputAsset) => {
// Ensure output subdirectories exist
await ensureDirectoryExists(asset.destination);

// Copy file contents
await fs.copyFile(
asset.source,
path.join(outputPath, asset.destination),
fsConstants.COPYFILE_FICLONE,
);
});
}
}

const MAX_CONCURRENT_WRITES = 64;
async function emitFilesToDisk<T = BuildOutputAsset | BuildOutputFile>(
files: T[],
writeFileCallback: (asset: T) => Promise<void>,
): Promise<void> {
// Write files in groups of MAX_CONCURRENT_WRITES to avoid too many open files
for (let fileIndex = 0; fileIndex < files.length; ) {
const groupMax = Math.min(fileIndex + MAX_CONCURRENT_WRITES, files.length);

const actions = [];
while (fileIndex < groupMax) {
actions.push(writeFileCallback(files[fileIndex++]));
}

await Promise.all(actions);
}
}

Expand Down
64 changes: 30 additions & 34 deletions packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { BudgetCalculatorResult } from '../../utils/bundle-calculator';
import { Spinner } from '../../utils/spinner';
import { BundleStats, generateBuildStatsTable } from '../webpack/utils/stats';
import { BuildOutputFile, BuildOutputFileType, InitialFileRecord } from './bundler-context';
import { BuildOutputAsset } from './bundler-execution-result';

const compressAsync = promisify(brotliCompress);

Expand Down Expand Up @@ -179,63 +180,58 @@ const MAX_CONCURRENT_WRITES = 64;

export async function writeResultFiles(
outputFiles: BuildOutputFile[],
assetFiles: { source: string; destination: string }[] | undefined,
assetFiles: BuildOutputAsset[] | undefined,
outputPath: string,
) {
const directoryExists = new Set<string>();

// Writes the output file to disk and ensures the containing directories are present
const writeOutputFile = async (file: BuildOutputFile) => {
const fullOutputPath = file.fullOutputPath;
// Ensure output subdirectories exist
const basePath = path.dirname(fullOutputPath);
const ensureDirectoryExists = async (basePath: string) => {
if (basePath && !directoryExists.has(basePath)) {
await fs.mkdir(path.join(outputPath, basePath), { recursive: true });
directoryExists.add(basePath);
}
// Write file contents
await fs.writeFile(path.join(outputPath, fullOutputPath), file.contents);
};

// Write files in groups of MAX_CONCURRENT_WRITES to avoid too many open files
for (let fileIndex = 0; fileIndex < outputFiles.length; ) {
const groupMax = Math.min(fileIndex + MAX_CONCURRENT_WRITES, outputFiles.length);

const actions = [];
while (fileIndex < groupMax) {
actions.push(writeOutputFile(outputFiles[fileIndex++]));
}
// Writes the output file to disk and ensures the containing directories are present
await emitFilesToDisk(outputFiles, async (file: BuildOutputFile) => {
const fullOutputPath = file.fullOutputPath;
// Ensure output subdirectories exist
const basePath = path.dirname(fullOutputPath);
await ensureDirectoryExists(basePath);

await Promise.all(actions);
}
// Write file contents
await fs.writeFile(path.join(outputPath, fullOutputPath), file.contents);
});

if (assetFiles?.length) {
const copyAssetFile = async (asset: { source: string; destination: string }) => {
await emitFilesToDisk(assetFiles, async (asset: BuildOutputAsset) => {
// Ensure output subdirectories exist
const destPath = join('browser', asset.destination);
const basePath = path.dirname(destPath);
if (basePath && !directoryExists.has(basePath)) {
await fs.mkdir(path.join(outputPath, basePath), { recursive: true });
directoryExists.add(basePath);
}
await ensureDirectoryExists(destPath);

// Copy file contents
await fs.copyFile(
asset.source,
path.join(outputPath, destPath),
fsConstants.COPYFILE_FICLONE,
);
};

for (let fileIndex = 0; fileIndex < assetFiles.length; ) {
const groupMax = Math.min(fileIndex + MAX_CONCURRENT_WRITES, assetFiles.length);
});
}
}

const actions = [];
while (fileIndex < groupMax) {
actions.push(copyAssetFile(assetFiles[fileIndex++]));
}
async function emitFilesToDisk<T = BuildOutputAsset | BuildOutputFile>(
files: T[],
writeFileCallback: (asset: T) => Promise<void>,
): Promise<void> {
// Write files in groups of MAX_CONCURRENT_WRITES to avoid too many open files
for (let fileIndex = 0; fileIndex < files.length; ) {
const groupMax = Math.min(fileIndex + MAX_CONCURRENT_WRITES, files.length);

await Promise.all(actions);
const actions = [];
while (fileIndex < groupMax) {
actions.push(writeFileCallback(files[fileIndex++]));
}

await Promise.all(actions);
}
}

Expand Down

0 comments on commit a1287c4

Please sign in to comment.