From 8c9238e1d75be67f1adce5f6c46f0aa5007ebbb0 Mon Sep 17 00:00:00 2001 From: Maruthan G Date: Sat, 28 Mar 2026 02:01:39 +0530 Subject: [PATCH] fix(@angular/build): correct misleading error message for top-level await When using top-level await in a Zone.js application, esbuild would show an error mentioning "target environment" with browser versions, which is misleading. The actual reason is that async/await is downleveled for Zone.js compatibility and top-level await cannot be downleveled. This change augments the esbuild error with a note explaining that top-level await is not supported in applications that use Zone.js, and provides a link to the zoneless Angular documentation. Fixes #28904 --- .../src/builders/application/execute-build.ts | 28 +++++++++ .../behavior/top-level-await-error_spec.ts | 58 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 packages/angular/build/src/builders/application/tests/behavior/top-level-await-error_spec.ts diff --git a/packages/angular/build/src/builders/application/execute-build.ts b/packages/angular/build/src/builders/application/execute-build.ts index aaddc5b6ef7e..a4c4c4df44ff 100644 --- a/packages/angular/build/src/builders/application/execute-build.ts +++ b/packages/angular/build/src/builders/application/execute-build.ts @@ -21,6 +21,7 @@ import { extractLicenses } from '../../tools/esbuild/license-extractor'; import { profileAsync } from '../../tools/esbuild/profiling'; import { calculateEstimatedTransferSizes, + isZonelessApp, logBuildStats, transformSupportedBrowsersToTargets, } from '../../tools/esbuild/utils'; @@ -156,6 +157,33 @@ export async function executeBuild( // Return if the bundling has errors if (bundlingResult.errors) { + // If Zone.js is used, augment top-level await errors with a more helpful message. + // esbuild's default error mentions "target environment" with browser versions, but + // the actual reason is that async/await is downleveled for Zone.js compatibility. + if (!isZonelessApp(options.polyfills)) { + for (const error of bundlingResult.errors) { + if ( + error.text?.startsWith( + 'Top-level await is not available in the configured target environment', + ) + ) { + error.notes = [ + { + text: + 'Top-level await is not supported in applications that use Zone.js. ' + + 'Consider removing Zone.js or moving this code into an async function.', + location: null, + }, + { + text: 'For more information about zoneless Angular applications, visit: https://angular.dev/guide/zoneless', + location: null, + }, + ...(error.notes ?? []), + ]; + } + } + } + executionResult.addErrors(bundlingResult.errors); return executionResult; diff --git a/packages/angular/build/src/builders/application/tests/behavior/top-level-await-error_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/top-level-await-error_spec.ts new file mode 100644 index 000000000000..bccd8019ce62 --- /dev/null +++ b/packages/angular/build/src/builders/application/tests/behavior/top-level-await-error_spec.ts @@ -0,0 +1,58 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { buildApplication } from '../../index'; +import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setup'; + +describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { + describe('Behavior: "Top-level await error message"', () => { + it('should show a Zone.js-specific error when top-level await is used with Zone.js', async () => { + await harness.writeFile( + 'src/main.ts', + ` + const value = await Promise.resolve('test'); + console.log(value); + `, + ); + + harness.useTarget('build', { + ...BASE_OPTIONS, + polyfills: ['zone.js'], + }); + + const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false }); + expect(result?.success).toBeFalse(); + expect(logs).toContain( + jasmine.objectContaining({ + message: jasmine.stringMatching( + 'Top-level await is not supported in applications that use Zone.js', + ), + }), + ); + }); + + it('should not show a Zone.js-specific error when top-level await is used without Zone.js', async () => { + await harness.writeFile( + 'src/main.ts', + ` + const value = await Promise.resolve('test'); + console.log(value); + `, + ); + + harness.useTarget('build', { + ...BASE_OPTIONS, + polyfills: [], + }); + + const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false }); + // Without Zone.js, top-level await should be supported and the build should succeed + expect(result?.success).toBeTrue(); + }); + }); +});