Skip to content

Commit 9eb58cf

Browse files
committed
fix(@angular-devkit/build-angular): fail build on non bundling error when using the esbuild based builders
This change fixes an issue were non bundling related errors that happen during prerendering, index generation and bundle budget failures did not cause the build to terminate with an error. (cherry picked from commit a9da656)
1 parent bece843 commit 9eb58cf

File tree

4 files changed

+48
-46
lines changed

4 files changed

+48
-46
lines changed

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

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
logMessages,
2929
transformSupportedBrowsersToTargets,
3030
} from '../../tools/esbuild/utils';
31-
import { checkBudgets } from '../../utils/bundle-calculator';
31+
import { BudgetCalculatorResult, checkBudgets } from '../../utils/bundle-calculator';
3232
import { colors } from '../../utils/color';
3333
import { copyAssets } from '../../utils/copy-assets';
3434
import { getSupportedBrowsers } from '../../utils/supported-browsers';
@@ -180,6 +180,37 @@ export async function executeBuild(
180180

181181
executionResult.outputFiles.push(...outputFiles);
182182

183+
const changedFiles =
184+
rebuildState && executionResult.findChangedFiles(rebuildState.previousOutputHashes);
185+
186+
// Analyze files for bundle budget failures if present
187+
let budgetFailures: BudgetCalculatorResult[] | undefined;
188+
if (options.budgets) {
189+
const compatStats = generateBudgetStats(metafile, initialFiles);
190+
budgetFailures = [...checkBudgets(options.budgets, compatStats, true)];
191+
if (budgetFailures.length > 0) {
192+
const errors = budgetFailures
193+
.filter((failure) => failure.severity === 'error')
194+
.map(({ message }) => message);
195+
const warnings = budgetFailures
196+
.filter((failure) => failure.severity !== 'error')
197+
.map(({ message }) => message);
198+
199+
await printWarningsAndErrorsToConsoleAndAddToResult(
200+
context,
201+
executionResult,
202+
warnings,
203+
errors,
204+
);
205+
}
206+
}
207+
208+
// Calculate estimated transfer size if scripts are optimized
209+
let estimatedTransferSizes;
210+
if (optimizationOptions.scripts || optimizationOptions.styles.minify) {
211+
estimatedTransferSizes = await calculateEstimatedTransferSizes(executionResult.outputFiles);
212+
}
213+
183214
// Check metafile for CommonJS module usage if optimizing scripts
184215
if (optimizationOptions.scripts) {
185216
const messages = checkCommonJSModules(metafile, options.allowedCommonJsDependencies);
@@ -202,29 +233,6 @@ export async function executeBuild(
202233
);
203234
}
204235

205-
// Analyze files for bundle budget failures if present
206-
let budgetFailures;
207-
if (options.budgets) {
208-
const compatStats = generateBudgetStats(metafile, initialFiles);
209-
budgetFailures = [...checkBudgets(options.budgets, compatStats, true)];
210-
if (budgetFailures.length > 0) {
211-
await logMessages(context, {
212-
errors: budgetFailures
213-
.filter((failure) => failure.severity === 'error')
214-
.map((failure) => ({ text: failure.message, location: null })),
215-
warnings: budgetFailures
216-
.filter((failure) => failure.severity !== 'error')
217-
.map((failure) => ({ text: failure.message, location: null })),
218-
});
219-
}
220-
}
221-
222-
// Calculate estimated transfer size if scripts are optimized
223-
let estimatedTransferSizes;
224-
if (optimizationOptions.scripts || optimizationOptions.styles.minify) {
225-
estimatedTransferSizes = await calculateEstimatedTransferSizes(executionResult.outputFiles);
226-
}
227-
228236
// Perform i18n translation inlining if enabled
229237
let prerenderedRoutes: string[];
230238
let errors: string[];
@@ -251,7 +259,7 @@ export async function executeBuild(
251259
executionResult.assetFiles.push(...result.additionalAssets);
252260
}
253261

254-
await printWarningsAndErrorsToConsole(context, warnings, errors);
262+
await printWarningsAndErrorsToConsoleAndAddToResult(context, executionResult, warnings, errors);
255263

256264
if (prerenderOptions) {
257265
executionResult.addOutputFile(
@@ -270,8 +278,6 @@ export async function executeBuild(
270278
context.logger.info(colors.magenta(prerenderMsg) + '\n');
271279
}
272280

273-
const changedFiles =
274-
rebuildState && executionResult.findChangedFiles(rebuildState.previousOutputHashes);
275281
logBuildStats(
276282
context,
277283
metafile,
@@ -293,13 +299,19 @@ export async function executeBuild(
293299
return executionResult;
294300
}
295301

296-
async function printWarningsAndErrorsToConsole(
302+
async function printWarningsAndErrorsToConsoleAndAddToResult(
297303
context: BuilderContext,
304+
executionResult: ExecutionResult,
298305
warnings: string[],
299306
errors: string[],
300307
): Promise<void> {
308+
const errorMessages = errors.map((text) => ({ text, location: null }));
309+
if (errorMessages.length) {
310+
executionResult.addErrors(errorMessages);
311+
}
312+
301313
await logMessages(context, {
302-
errors: errors.map((text) => ({ text, location: null })),
314+
errors: errorMessages,
303315
warnings: warnings.map((text) => ({ text, location: null })),
304316
});
305317
}

packages/angular_devkit/build_angular/src/builders/application/tests/options/bundle-budgets_spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
4242
});
4343

4444
const { result, logs } = await harness.executeOnce();
45+
expect(result?.success).toBeFalse();
4546
expect(logs).toContain(
4647
jasmine.objectContaining<logging.LogEntry>({
4748
level: 'error',

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

Lines changed: 3 additions & 3 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 type { Message } from 'esbuild';
9+
import type { Message, PartialMessage } from 'esbuild';
1010
import type { ChangedFiles } from '../../tools/esbuild/watcher';
1111
import type { SourceFileCache } from './angular/source-file-cache';
1212
import type { BuildOutputFile, BuildOutputFileType, BundlerContext } from './bundler-context';
@@ -30,7 +30,7 @@ export interface RebuildState {
3030
export class ExecutionResult {
3131
outputFiles: BuildOutputFile[] = [];
3232
assetFiles: BuildOutputAsset[] = [];
33-
errors: Message[] = [];
33+
errors: (Message | PartialMessage)[] = [];
3434
externalMetadata?: { implicit: string[]; explicit?: string[] };
3535

3636
constructor(
@@ -46,7 +46,7 @@ export class ExecutionResult {
4646
this.assetFiles.push(...assets);
4747
}
4848

49-
addErrors(errors: Message[]): void {
49+
addErrors(errors: (Message | PartialMessage)[]): void {
5050
this.errors.push(...errors);
5151
}
5252

tests/legacy-cli/e2e/tests/build/bundle-budgets.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,21 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import { getGlobalVariable } from '../../utils/env';
98
import { ng } from '../../utils/process';
109
import { updateJsonFile } from '../../utils/project';
1110
import { expectToFail } from '../../utils/utils';
1211

1312
export default async function () {
14-
const usingWebpack = !getGlobalVariable('argv')['esbuild'];
15-
1613
// Error
1714
await updateJsonFile('angular.json', (json) => {
1815
json.projects['test-project'].architect.build.configurations.production.budgets = [
1916
{ type: 'all', maximumError: '100b' },
2017
];
2118
});
2219

23-
if (usingWebpack) {
24-
const { message: errorMessage } = await expectToFail(() => ng('build'));
25-
if (!/Error.+budget/i.test(errorMessage)) {
26-
throw new Error('Budget error: all, max error.');
27-
}
28-
} else {
29-
// Application builder does not generate an error exit code for budget failures
30-
const { stderr } = await ng('build');
31-
if (!/Error.+budget/i.test(stderr)) {
32-
throw new Error('Budget error: all, max error.');
33-
}
20+
const { message: errorMessage } = await expectToFail(() => ng('build'));
21+
if (!/Error.+budget/i.test(errorMessage)) {
22+
throw new Error('Budget error: all, max error.');
3423
}
3524

3625
// Warning

0 commit comments

Comments
 (0)