Skip to content

Commit 8c9238e

Browse files
committed
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
1 parent f1ed025 commit 8c9238e

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

packages/angular/build/src/builders/application/execute-build.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { extractLicenses } from '../../tools/esbuild/license-extractor';
2121
import { profileAsync } from '../../tools/esbuild/profiling';
2222
import {
2323
calculateEstimatedTransferSizes,
24+
isZonelessApp,
2425
logBuildStats,
2526
transformSupportedBrowsersToTargets,
2627
} from '../../tools/esbuild/utils';
@@ -156,6 +157,33 @@ export async function executeBuild(
156157

157158
// Return if the bundling has errors
158159
if (bundlingResult.errors) {
160+
// If Zone.js is used, augment top-level await errors with a more helpful message.
161+
// esbuild's default error mentions "target environment" with browser versions, but
162+
// the actual reason is that async/await is downleveled for Zone.js compatibility.
163+
if (!isZonelessApp(options.polyfills)) {
164+
for (const error of bundlingResult.errors) {
165+
if (
166+
error.text?.startsWith(
167+
'Top-level await is not available in the configured target environment',
168+
)
169+
) {
170+
error.notes = [
171+
{
172+
text:
173+
'Top-level await is not supported in applications that use Zone.js. ' +
174+
'Consider removing Zone.js or moving this code into an async function.',
175+
location: null,
176+
},
177+
{
178+
text: 'For more information about zoneless Angular applications, visit: https://angular.dev/guide/zoneless',
179+
location: null,
180+
},
181+
...(error.notes ?? []),
182+
];
183+
}
184+
}
185+
}
186+
159187
executionResult.addErrors(bundlingResult.errors);
160188

161189
return executionResult;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { buildApplication } from '../../index';
10+
import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setup';
11+
12+
describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
13+
describe('Behavior: "Top-level await error message"', () => {
14+
it('should show a Zone.js-specific error when top-level await is used with Zone.js', async () => {
15+
await harness.writeFile(
16+
'src/main.ts',
17+
`
18+
const value = await Promise.resolve('test');
19+
console.log(value);
20+
`,
21+
);
22+
23+
harness.useTarget('build', {
24+
...BASE_OPTIONS,
25+
polyfills: ['zone.js'],
26+
});
27+
28+
const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });
29+
expect(result?.success).toBeFalse();
30+
expect(logs).toContain(
31+
jasmine.objectContaining({
32+
message: jasmine.stringMatching(
33+
'Top-level await is not supported in applications that use Zone.js',
34+
),
35+
}),
36+
);
37+
});
38+
39+
it('should not show a Zone.js-specific error when top-level await is used without Zone.js', async () => {
40+
await harness.writeFile(
41+
'src/main.ts',
42+
`
43+
const value = await Promise.resolve('test');
44+
console.log(value);
45+
`,
46+
);
47+
48+
harness.useTarget('build', {
49+
...BASE_OPTIONS,
50+
polyfills: [],
51+
});
52+
53+
const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });
54+
// Without Zone.js, top-level await should be supported and the build should succeed
55+
expect(result?.success).toBeTrue();
56+
});
57+
});
58+
});

0 commit comments

Comments
 (0)