@angular/build:unit-test (vitest): zoneless project fails to compile with "Top-level await is not available" when zone.js is a transitive dep

Offen
#33,324 3 Kommentare 2 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Anfängerfreundlichkeit
70/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Aktiv
Tech-Stack
angular, typescript

Rechercherichtung

Lies @angular/build/src/builders/unit-test/runners/vitest/build-options.js und konzentriere dich auf getZoneTestingStrategy() und das virtuelle Modul angular:test-bed-init. Reproduziere den Fehler mit ng test in einem zoneless-Projekt und überprüfe anschließend, dass die Strategie keinen zone-init-Code ausgibt und das Unit-Test-Bundle ohne den top-level-await-Fehler kompiliert.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

area: @angular/build gemini-triaged
Command

test

Is this a regression?
  • Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was

21.2.13

Description

Description

A zoneless Angular project (provideZonelessChangeDetection() in main.ts, no zone.js in the polyfills array) fails to compile its unit-test bundle with:
X [ERROR] Top-level await is not available in the configured target environment ("chrome145.0", "ios17.0", "safari17.0" + 2 overrides)

Root cause

In @angular/build/src/builders/unit-test/runners/vitest/build-options.js, getZoneTestingStrategy() decides what to emit into the virtual angular:test-bed-init module:

function getZoneTestingStrategy(buildOptions, projectSourceRoot) {
  if (buildOptions.polyfills?.includes('zone.js/testing')) return 'none';
  if (buildOptions.polyfills?.includes('zone.js'))         return 'static';
  try {
    projectRequire.resolve('zone.js');
    return 'dynamic';                       // ← falls through to here for zoneless projects
  } catch { return 'none'; }
}

For a zoneless app, polyfills correctly contains neither 'zone.js' nor 'zone.js/testing' — but zone.js is still resolvable on disk because it's an optional/peer dep of @angular/core (and several Angular test utilities like fakeAsync / tick still reference it). The strategy therefore resolves to 'dynamic', which emits this into the virtual module:

if (typeof Zone !== 'undefined') {
  // 'zone.js/testing' is used to initialize the ZoneJS testing environment.
  // It must be imported dynamically to avoid a static dependency on 'zone.js'.
  await import('zone.js/testing');
}

The runtime guard typeof Zone !== 'undefined' would correctly skip the import on a zoneless app, but esbuild evaluates the top-level await at compile time and rejects the syntax — regardless of whether the branch can actually run. The build fails before the guard ever executes.

Expected

A zoneless project should resolve the strategy to 'none' and emit no zone-init code. The unit-test bundle should compile cleanly.

Actual

Strategy resolves to 'dynamic' because zone.js is transitively installed. The virtual angular:test-bed-init module contains a top-level await, which esbuild rejects against the configured browser targets — even though those targets (Chrome 145, iOS 17, Safari 17) actually do support TLA. The compile-time rejection appears to be tied to the output format guarantees rather than the targets themselves, so tightening browserslist does not help.

Minimal Reproduction
  1. Generate a zoneless Angular 22 app (or upgrade an existing zoneless app).
  2. Confirm main.ts uses provideZonelessChangeDetection() and polyfills in angular.json does not include 'zone.js' or 'zone.js/testing'.
  3. Configure test to use @angular/build:unit-test with the vitest runner.
  4. Run ng test.
Exception or Error
Top-level await is not available
Your Environment
_                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI       : 22.0.0
Angular           : 22.0.0
Node.js           : 24.16.0
Package Manager   : npm 11.13.0
Operating System  : win32 x64

┌────────────────────────────────────┬───────────────────┬───────────────────┐
│ Package                            │ Installed Version │ Requested Version │
├────────────────────────────────────┼───────────────────┼───────────────────┤
│ @angular-devkit/build-angular      │ 22.0.0            │ 22.0.0            │
│ @angular/animations                │ 22.0.0            │ 22.0.0            │
│ @angular/build                     │ 22.0.0            │ 22.0.0            │
│ @angular/cdk                       │ 21.2.13           │ 21.2.13           │
│ @angular/cli                       │ 22.0.0            │ 22.0.0            │
│ @angular/common                    │ 22.0.0            │ 22.0.0            │
│ @angular/compiler                  │ 22.0.0            │ 22.0.0            │
│ @angular/compiler-cli              │ 22.0.0            │ 22.0.0            │
│ @angular/core                      │ 22.0.0            │ 22.0.0            │
│ @angular/forms                     │ 22.0.0            │ 22.0.0            │
│ @angular/google-maps               │ 21.2.13           │ 21.2.13           │
│ @angular/language-service          │ 22.0.0            │ 22.0.0            │
│ @angular/material                  │ 21.2.13           │ 21.2.13           │
│ @angular/material-date-fns-adapter │ 21.2.13           │ 21.2.13           │
│ @angular/platform-browser          │ 22.0.0            │ 22.0.0            │
│ @angular/platform-browser-dynamic  │ 22.0.0            │ 22.0.0            │
│ @angular/platform-server           │ 22.0.0            │ 22.0.0            │
│ @angular/router                    │ 22.0.0            │ 22.0.0            │
│ @angular/service-worker            │ 22.0.0            │ 22.0.0            │
│ @angular/ssr                       │ 22.0.0            │ 22.0.0            │
│ rxjs                               │ 7.8.2             │ 7.8.2             │
│ typescript                         │ 6.0.3             │ 6.0.3             │
│ vitest                             │ 4.1.7             │ 4.1.7             │
Anything else relevant?

No response

Vorherrschende Sprache
TypeScript
Sterne
27k
Forks
11.8k
Ø Merge
16 Std. 35 Min.
Gemergte PRs (30 T.)
176

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus angular/angular-cli

Alle Issues in angular/angular-cli

Ähnliche Issues

Weitere Issues zu TypeScript

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.