Skip to content

Commit 066f176

Browse files
committed
refactor(@angular-devkit/build-angular): always return outputWithFiles when using the application builder
This allows access to file when using the `buildApplication` API Closes: #27386
1 parent 2a5caf1 commit 066f176

File tree

7 files changed

+59
-54
lines changed

7 files changed

+59
-54
lines changed

goldens/public-api/angular_devkit/build_angular/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,20 @@
88

99
import { BuilderContext } from '@angular-devkit/architect';
1010
import { BuilderOutput } from '@angular-devkit/architect';
11+
import { BuildOptions } from 'esbuild';
1112
import type { ConfigOptions } from 'karma';
1213
import { Configuration } from 'webpack';
1314
import { DevServerBuildOutput } from '@angular-devkit/build-webpack';
1415
import type http from 'node:http';
1516
import { json } from '@angular-devkit/core';
17+
import { Message } from 'esbuild';
18+
import { Metafile } from 'esbuild';
1619
import { Observable } from 'rxjs';
20+
import type { OnLoadResult } from 'esbuild';
1721
import { OutputFile } from 'esbuild';
22+
import type { PartialMessage } from 'esbuild';
1823
import type { Plugin as Plugin_2 } from 'esbuild';
24+
import type ts from 'typescript';
1925
import webpack from 'webpack';
2026
import { WebpackLoggingCallback } from '@angular-devkit/build-webpack';
2127

packages/angular_devkit/build_angular/src/builders/application/build-action.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
9+
import { BuilderContext } from '@angular-devkit/architect';
1010
import { existsSync } from 'node:fs';
1111
import path from 'node:path';
1212
import { BuildOutputFile } from '../../tools/esbuild/bundler-context';
@@ -37,8 +37,7 @@ const packageWatchFiles = [
3737
'.pnp.data.json',
3838
];
3939

40-
type BuildActionOutput = (ExecutionResult['outputWithFiles'] | ExecutionResult['output']) &
41-
BuilderOutput;
40+
export type BuildActionOutput = ExecutionResult['outputWithFiles'];
4241

4342
export async function* runEsBuildBuildAction(
4443
action: (rebuildState?: RebuildState) => Promise<ExecutionResult>,
@@ -223,7 +222,7 @@ export async function* runEsBuildBuildAction(
223222

224223
async function writeAndEmitOutput(
225224
writeToFileSystem: boolean,
226-
{ outputFiles, output, outputWithFiles, assetFiles }: ExecutionResult,
225+
{ outputFiles, outputWithFiles, assetFiles }: ExecutionResult,
227226
outputOptions: NormalizedApplicationBuildOptions['outputOptions'],
228227
writeToFileSystemFilter: ((file: BuildOutputFile) => boolean) | undefined,
229228
): Promise<BuildActionOutput> {
@@ -234,11 +233,9 @@ async function writeAndEmitOutput(
234233
: outputFiles;
235234

236235
await writeResultFiles(outputFilesToWrite, assetFiles, outputOptions);
237-
238-
return output;
239-
} else {
240-
// Requires casting due to unneeded `JsonObject` requirement. Remove once fixed.
241-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
242-
return outputWithFiles as any;
243236
}
237+
238+
// Requires casting due to unneeded `JsonObject` requirement. Remove once fixed.
239+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
240+
return outputWithFiles as any;
244241
}

packages/angular_devkit/build_angular/src/builders/application/index.ts

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88

99
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
1010
import type { Plugin } from 'esbuild';
11-
import { BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-context';
11+
import { BuildOutputFileType } from '../../tools/esbuild/bundler-context';
1212
import { createJsonBuildManifest } from '../../tools/esbuild/utils';
1313
import { colors as ansiColors } from '../../utils/color';
1414
import { purgeStaleBuildCache } from '../../utils/purge-cache';
1515
import { assertCompatibleAngularVersion } from '../../utils/version';
16-
import { runEsBuildBuildAction } from './build-action';
16+
import { BuildActionOutput, runEsBuildBuildAction } from './build-action';
1717
import { executeBuild } from './execute-build';
1818
import {
1919
ApplicationBuilderExtensions,
@@ -24,6 +24,8 @@ import { Schema as ApplicationBuilderOptions } from './schema';
2424

2525
export { ApplicationBuilderOptions };
2626

27+
export type ApplicationBuilderOutput = BuildActionOutput;
28+
2729
export async function* buildApplicationInternal(
2830
options: ApplicationBuilderInternalOptions,
2931
// TODO: Integrate abort signal support into builder system
@@ -44,9 +46,7 @@ export async function* buildApplicationInternal(
4446
// Determine project name from builder context target
4547
const projectName = target?.project;
4648
if (!projectName) {
47-
yield { success: false, error: `The 'application' builder requires a target to be specified.` };
48-
49-
return;
49+
throw new Error(`The 'application' builder requires a target to be specified.`);
5050
}
5151

5252
const normalizedOptions = await normalizeOptions(context, projectName, options, extensions);
@@ -57,21 +57,15 @@ export async function* buildApplicationInternal(
5757
if (writeServerBundles) {
5858
const { browser, server } = normalizedOptions.outputOptions;
5959
if (browser === '') {
60-
yield {
61-
success: false,
62-
error: `'outputPath.browser' cannot be configured to an empty string when SSR is enabled.`,
63-
};
64-
65-
return;
60+
throw new Error(
61+
`'outputPath.browser' cannot be configured to an empty string when SSR is enabled.`,
62+
);
6663
}
6764

6865
if (browser === server) {
69-
yield {
70-
success: false,
71-
error: `'outputPath.browser' and 'outputPath.server' cannot be configured to the same value.`,
72-
};
73-
74-
return;
66+
throw new Error(
67+
`'outputPath.browser' and 'outputPath.server' cannot be configured to the same value.`,
68+
);
7569
}
7670
}
7771

@@ -140,11 +134,6 @@ export async function* buildApplicationInternal(
140134
);
141135
}
142136

143-
export interface ApplicationBuilderOutput extends BuilderOutput {
144-
outputFiles?: BuildOutputFile[];
145-
assetFiles?: { source: string; destination: string }[];
146-
}
147-
148137
/**
149138
* Builds an application using the `application` builder with the provided
150139
* options.
@@ -202,4 +191,19 @@ export function buildApplication(
202191
return buildApplicationInternal(options, context, undefined, extensions);
203192
}
204193

205-
export default createBuilder(buildApplication);
194+
export default createBuilder(async function* (options, context) {
195+
for await (const result of buildApplication(options, context)) {
196+
// The builder system (architect) currently attempts to treat all results as JSON and
197+
// attempts to validate the object with a JSON schema validator. This can lead to slow
198+
// build completion (even after the actual build is fully complete) or crashes if the
199+
// size and/or quantity of output files is large. Architect only requires a `success`
200+
// property so that is all that will be passed here if the infrastructure settings have
201+
// not been explicitly set to avoid writes. Writing is only disabled when used directly
202+
// by the dev server which bypasses the architect behavior.
203+
const { success } = result;
204+
205+
yield {
206+
success,
207+
};
208+
}
209+
});

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

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
9+
import { BuilderContext, createBuilder } from '@angular-devkit/architect';
1010
import type { Plugin } from 'esbuild';
1111
import { constants as fsConstants } from 'node:fs';
1212
import fs from 'node:fs/promises';
@@ -15,7 +15,7 @@ import { BuildOutputFile } from '../../tools/esbuild/bundler-context';
1515
import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result';
1616
import { emitFilesToDisk } from '../../tools/esbuild/utils';
1717
import { deleteOutputDir } from '../../utils';
18-
import { buildApplicationInternal } from '../application';
18+
import { ApplicationBuilderOutput, buildApplicationInternal } from '../application';
1919
import { Schema as ApplicationBuilderOptions, OutputPathClass } from '../application/schema';
2020
import { logBuilderStatusWarnings } from './builder-status-warnings';
2121
import { Schema as BrowserBuilderOptions } from './schema';
@@ -34,12 +34,7 @@ export async function* buildEsbuildBrowser(
3434
write?: boolean;
3535
},
3636
plugins?: Plugin[],
37-
): AsyncIterable<
38-
BuilderOutput & {
39-
outputFiles?: BuildOutputFile[];
40-
assetFiles?: { source: string; destination: string }[];
41-
}
42-
> {
37+
): AsyncIterable<ApplicationBuilderOutput> {
4338
// Inform user of status of builder and options
4439
logBuilderStatusWarnings(userOptions, context);
4540
const normalizedOptions = normalizeOptions(userOptions);
@@ -103,8 +98,8 @@ function normalizeOptions(
10398
// We write the file directly from this builder to maintain webpack output compatibility
10499
// and not output browser files into '/browser'.
105100
async function writeResultFiles(
106-
outputFiles: BuildOutputFile[],
107-
assetFiles: BuildOutputAsset[] | undefined,
101+
outputFiles: Readonly<BuildOutputFile[]>,
102+
assetFiles: Readonly<BuildOutputAsset[]> | undefined,
108103
outputPath: string,
109104
) {
110105
const directoryExists = new Set<string>();

packages/angular_devkit/build_angular/src/builders/dev-server/vite-server.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,16 @@ export async function* serveWithVite(
177177
if (!result.success) {
178178
// If server is active, send an error notification
179179
if (result.errors?.length && server) {
180+
const { text = '', location } = result.errors[0];
180181
hadError = true;
182+
181183
server.ws.send({
182184
type: 'error',
183185
err: {
184-
message: result.errors[0].text,
186+
message: text,
185187
stack: '',
186-
loc: result.errors[0].location,
188+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
189+
loc: location as any,
187190
},
188191
});
189192
}
@@ -375,7 +378,7 @@ function handleUpdate(
375378
function analyzeResultFiles(
376379
normalizePath: (id: string) => string,
377380
htmlIndexPath: string,
378-
resultFiles: BuildOutputFile[],
381+
resultFiles: Readonly<BuildOutputFile[]>,
379382
generatedFiles: Map<string, OutputFileRecord>,
380383
) {
381384
const seen = new Set<string>(['/index.html']);

packages/angular_devkit/build_angular/src/tools/esbuild/bundler-execution-result.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,13 @@ export class ExecutionResult {
109109
this.externalMetadata = { implicitBrowser, implicitServer, explicit: explicit ?? [] };
110110
}
111111

112-
get output() {
113-
return {
114-
success: this.errors.length === 0,
115-
};
116-
}
117-
118-
get outputWithFiles() {
112+
get outputWithFiles(): Readonly<{
113+
success: boolean;
114+
outputFiles: Readonly<BuildOutputFile[]>;
115+
assetFiles: Readonly<BuildOutputAsset[]>;
116+
errors: Readonly<(Message | PartialMessage)[]>;
117+
externalMetadata: Readonly<ExternalResultMetadata> | undefined;
118+
}> {
119119
return {
120120
success: this.errors.length === 0,
121121
outputFiles: this.outputFiles,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ export async function writeResultFiles(
272272

273273
const MAX_CONCURRENT_WRITES = 64;
274274
export async function emitFilesToDisk<T = BuildOutputAsset | BuildOutputFile>(
275-
files: T[],
275+
files: Readonly<T[]>,
276276
writeFileCallback: (file: T) => Promise<void>,
277277
): Promise<void> {
278278
// Write files in groups of MAX_CONCURRENT_WRITES to avoid too many open files

0 commit comments

Comments
 (0)