From 0bac0088477cd97ea0b84b6a6dcc87bbf5db85ef Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 7 Apr 2025 14:08:34 -0700 Subject: [PATCH] feat(@schematics/angular): Add global error listeners to new app generation This commit adds the provider for global error listener in the browser. This is particularly useful for zoneless apps but also useful for Zone-based applications when errors happen outside the Angular zone. --- .../src/app/app-module.ts.template | 7 +++++-- .../src/app/app.config.ts.template | 8 ++++++-- .../angular/service-worker/index_spec.ts | 2 +- .../angular/utility/standalone/rules_spec.ts | 19 +++++++++++++++---- pnpm-lock.yaml | 19 +++++++++---------- 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/packages/schematics/angular/application/files/module-files/src/app/app-module.ts.template b/packages/schematics/angular/application/files/module-files/src/app/app-module.ts.template index e210bd4f6d68..a9a5a07900cb 100644 --- a/packages/schematics/angular/application/files/module-files/src/app/app-module.ts.template +++ b/packages/schematics/angular/application/files/module-files/src/app/app-module.ts.template @@ -1,4 +1,4 @@ -import { NgModule<% if(experimentalZoneless) { %>, provideExperimentalZonelessChangeDetection<% } %> } from '@angular/core'; +import { NgModule, provideBrowserGlobalErrorListeners<% if(experimentalZoneless) { %>, provideExperimentalZonelessChangeDetection<% } %> } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; <% if (routing) { %> import { AppRoutingModule } from './app-routing-module';<% } %> @@ -12,7 +12,10 @@ import { App } from './app'; BrowserModule<% if (routing) { %>, AppRoutingModule<% } %> ], - providers: [<% if (experimentalZoneless) { %>provideExperimentalZonelessChangeDetection()<% } %>], + providers: [ + provideBrowserGlobalErrorListeners()<% if (experimentalZoneless) { %>, + provideExperimentalZonelessChangeDetection()<% } %> + ], bootstrap: [App] }) export class AppModule { } diff --git a/packages/schematics/angular/application/files/standalone-files/src/app/app.config.ts.template b/packages/schematics/angular/application/files/standalone-files/src/app/app.config.ts.template index 98551b4f24d0..d45afde0b35e 100644 --- a/packages/schematics/angular/application/files/standalone-files/src/app/app.config.ts.template +++ b/packages/schematics/angular/application/files/standalone-files/src/app/app.config.ts.template @@ -1,8 +1,12 @@ -import { ApplicationConfig, <% if(!experimentalZoneless) { %>provideZoneChangeDetection<% } else { %>provideExperimentalZonelessChangeDetection<% } %> } from '@angular/core';<% if (routing) { %> +import { ApplicationConfig, provideBrowserGlobalErrorListeners, <% if(!experimentalZoneless) { %>provideZoneChangeDetection<% } else { %>provideExperimentalZonelessChangeDetection<% } %> } from '@angular/core';<% if (routing) { %> import { provideRouter } from '@angular/router'; import { routes } from './app.routes';<% } %> export const appConfig: ApplicationConfig = { - providers: [<% if(experimentalZoneless) { %>provideExperimentalZonelessChangeDetection()<% } else { %>provideZoneChangeDetection({ eventCoalescing: true })<% } %><% if (routing) {%>, provideRouter(routes)<% } %>] + providers: [ + provideBrowserGlobalErrorListeners(), + <% if(experimentalZoneless) { %>provideExperimentalZonelessChangeDetection()<% } else { %>provideZoneChangeDetection({ eventCoalescing: true })<% } %>, + <% if (routing) {%>provideRouter(routes)<% } %> + ] }; diff --git a/packages/schematics/angular/service-worker/index_spec.ts b/packages/schematics/angular/service-worker/index_spec.ts index e67a91f6d754..e9ec677f44ba 100644 --- a/packages/schematics/angular/service-worker/index_spec.ts +++ b/packages/schematics/angular/service-worker/index_spec.ts @@ -139,7 +139,7 @@ describe('Service Worker Schematic', () => { const tree = await schematicRunner.runSchematic('service-worker', defaultOptions, appTree); const content = tree.readContent('/projects/bar/src/app/app.config.ts'); expect(content).toContain( - `import { ApplicationConfig, provideZoneChangeDetection, isDevMode } from '@angular/core';`, + `import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection, isDevMode } from '@angular/core';`, ); }); diff --git a/packages/schematics/angular/utility/standalone/rules_spec.ts b/packages/schematics/angular/utility/standalone/rules_spec.ts index c6e3836344f0..2be4d89c61d3 100644 --- a/packages/schematics/angular/utility/standalone/rules_spec.ts +++ b/packages/schematics/angular/utility/standalone/rules_spec.ts @@ -423,7 +423,10 @@ describe('standalone utilities', () => { const content = readFile('app/app-module.ts'); assertContains(content, `import { SOME_TOKEN } from '@my/module';`); - assertContains(content, `providers: [{ provide: SOME_TOKEN, useValue: 123 }]`); + assertContains( + content, + `providers: [provideBrowserGlobalErrorListeners(),{ provide: SOME_TOKEN, useValue: 123 }]`, + ); }); it('should add a root provider to a standalone app', async () => { @@ -442,7 +445,11 @@ describe('standalone utilities', () => { assertContains(content, `import { provideModule } from '@my/module';`); assertContains( content, - `providers: [provideZoneChangeDetection({ eventCoalescing:true }),provideModule([])]`, + `providers: [ + provideBrowserGlobalErrorListeners(), + provideZoneChangeDetection({ eventCoalescing:true }), + provideModule([]), + ]`, ); }); @@ -453,11 +460,12 @@ describe('standalone utilities', () => { host.overwrite( getPathWithinProject(configPath), ` - import { ApplicationConfig } from '@angular/core'; + import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core'; import { provideRouter } from '@angular/router'; export const appConfig: ApplicationConfig = { providers: [ + provideBrowserGlobalErrorListeners(), provideRouter([]), ] }; @@ -474,7 +482,10 @@ describe('standalone utilities', () => { const content = readFile('app/app.config.ts'); assertContains(content, `import { provideModule } from '@my/module';`); - assertContains(content, `providers: [provideRouter([]),provideModule([]),]`); + assertContains( + content, + `providers: [provideBrowserGlobalErrorListeners(), provideRouter([]),provideModule([]),]`, + ); }); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d44b1d6c29c8..9262ddeaa4cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2001,8 +2001,8 @@ packages: cpu: [x64] os: [win32] - '@mdn/browser-compat-data@6.0.6': - resolution: {integrity: sha512-awlDnCGbtdkyLieMpIKJQEgK7mxnL3he4UHm5AGn+asofiemlx4LVHM0FsYmp6O1irAClPNS135zZLhD2SZi+A==} + '@mdn/browser-compat-data@6.0.8': + resolution: {integrity: sha512-l21VIVT7ozAQJFT16HHmCndeNMhMuYMlhRA8+W59cCsorW6oXZDm3v+lQKxIZwTLHnKfAt0i6f2ih6h9GS0OaQ==} '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} @@ -5972,7 +5972,6 @@ packages: node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} - deprecated: Use your platform's native DOMException instead node-fetch-native@1.6.6: resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} @@ -7715,8 +7714,8 @@ packages: weak-lru-cache@1.2.2: resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} - web-features@2.33.0: - resolution: {integrity: sha512-oLzTO29Ax9TyQGNoNxpC+2Hj9if7lm2tuuAiEAb01BxcBt7yH40LAmIDg5PtuJ39lnwqm4wELATIKhj6WlJJpQ==} + web-features@2.34.0: + resolution: {integrity: sha512-jv8rxEhmj0UcQ3v5EyH9cNN6ztyNjUZJfImgUdGtxYYYRi3BKWw00LPQ2HQn7Y61kxEbah5G1SQaXz+yskKLUA==} web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} @@ -9243,7 +9242,7 @@ snapshots: '@lmdb/lmdb-win32-x64@3.2.6': optional: true - '@mdn/browser-compat-data@6.0.6': {} + '@mdn/browser-compat-data@6.0.8': {} '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': optional: true @@ -10938,8 +10937,8 @@ snapshots: baseline-browser-mapping@2.2.1: dependencies: - '@mdn/browser-compat-data': 6.0.6 - web-features: 2.33.0 + '@mdn/browser-compat-data': 6.0.8 + web-features: 2.34.0 basic-ftp@5.0.5: {} @@ -12200,7 +12199,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.0(supports-color@10.0.0) + debug: 4.3.4 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -15869,7 +15868,7 @@ snapshots: weak-lru-cache@1.2.2: optional: true - web-features@2.33.0: {} + web-features@2.34.0: {} web-streams-polyfill@3.3.3: {}