diff --git a/examples/angular/angular.json b/examples/angular/angular.json index 8c27962ac..20e51e545 100644 --- a/examples/angular/angular.json +++ b/examples/angular/angular.json @@ -11,7 +11,7 @@ "prefix": "app", "architect": { "build": { - "builder": "@angular-devkit/build-angular:application", + "builder": "@angular/build:application", "options": { "outputPath": "dist/angular", "index": "src/index.html", @@ -61,7 +61,7 @@ "defaultConfiguration": "production" }, "serve": { - "builder": "@angular-devkit/build-angular:dev-server", + "builder": "@angular/build:dev-server", "configurations": { "production": { "buildTarget": "angular:build:production" @@ -73,10 +73,10 @@ "defaultConfiguration": "development" }, "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n" + "builder": "@angular/build:extract-i18n" }, "test": { - "builder": "@angular-devkit/build-angular:karma", + "builder": "@angular/build:karma", "options": { "polyfills": [ "zone.js", @@ -100,5 +100,31 @@ }, "cli": { "analytics": false + }, + "schematics": { + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." + } } } diff --git a/examples/angular/package.json b/examples/angular/package.json index c5f83da34..08b49b8be 100644 --- a/examples/angular/package.json +++ b/examples/angular/package.json @@ -11,16 +11,16 @@ }, "private": true, "dependencies": { - "@angular/animations": "~19.2.0", - "@angular/common": "~19.2.0", - "@angular/compiler": "~19.2.0", - "@angular/core": "~19.2.0", - "@angular/forms": "~19.2.0", - "@angular/platform-browser": "~19.2.0", - "@angular/platform-browser-dynamic": "~19.2.0", - "@angular/platform-server": "~19.2.0", - "@angular/router": "~19.2.0", - "@angular/ssr": "~19.2.0", + "@angular/animations": "~21.2.4", + "@angular/common": "~21.2.4", + "@angular/compiler": "~21.2.4", + "@angular/core": "~21.2.4", + "@angular/forms": "~21.2.4", + "@angular/platform-browser": "~21.2.4", + "@angular/platform-browser-dynamic": "~21.2.4", + "@angular/platform-server": "~21.2.4", + "@angular/router": "~21.2.4", + "@angular/ssr": "^21.2.2", "express": "^4.18.2", "rollbar": "file:../rollbar.tgz", "rxjs": "~7.8.0", @@ -28,9 +28,9 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@angular-devkit/build-angular": "~19.2.0", - "@angular/cli": "~19.2.0", - "@angular/compiler-cli": "~19.2.0", + "@angular/build": "^21.2.2", + "@angular/cli": "~21.2.2", + "@angular/compiler-cli": "~21.2.4", "@types/express": "^4.17.17", "@types/jasmine": "~5.1.0", "@types/node": "^18.18.0", @@ -40,6 +40,6 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", - "typescript": "~5.6.2" + "typescript": "~5.9.3" } } diff --git a/examples/angular/src/app/app.config.server.ts b/examples/angular/src/app/app.config.server.ts index 3514d3ae1..a64c97036 100644 --- a/examples/angular/src/app/app.config.server.ts +++ b/examples/angular/src/app/app.config.server.ts @@ -1,5 +1,5 @@ +import { provideServerRendering } from '@angular/ssr'; import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; -import { provideServerRendering } from '@angular/platform-server'; import { appConfig } from './app.config'; const serverConfig: ApplicationConfig = { diff --git a/examples/angular/src/app/app.config.ts b/examples/angular/src/app/app.config.ts index 53ccdf9fb..ec941aa28 100644 --- a/examples/angular/src/app/app.config.ts +++ b/examples/angular/src/app/app.config.ts @@ -1,19 +1,11 @@ // src/app/app.config.ts -import { ApplicationConfig, importProvidersFrom } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; +import type { ApplicationConfig } from '@angular/core'; import { ErrorHandler } from '@angular/core'; -import { RollbarService, RollbarErrorHandler } from './rollbar.errorhandler'; -import { RollbarFactory } from './rollbar.config'; +import { RollbarErrorHandler } from './rollbar.errorhandler'; export const appConfig: ApplicationConfig = { providers: [ - // The usual providers for a browser app - importProvidersFrom(BrowserModule), - - // Provide the Rollbar injection token with the Rollbar factory - { provide: RollbarService, useFactory: RollbarFactory }, - // Override Angular’s default ErrorHandler { provide: ErrorHandler, useClass: RollbarErrorHandler }, ], diff --git a/examples/angular/src/app/rollbar.config.ts b/examples/angular/src/app/rollbar.config.ts index 44050b94c..c889b8468 100644 --- a/examples/angular/src/app/rollbar.config.ts +++ b/examples/angular/src/app/rollbar.config.ts @@ -16,6 +16,7 @@ const rollbarConfig = { }, }; -export function RollbarFactory() { +export function createRollbar() { return new Rollbar(rollbarConfig); } + diff --git a/examples/angular/src/app/rollbar.errorhandler.ts b/examples/angular/src/app/rollbar.errorhandler.ts index eb517f476..8ecbdd89a 100644 --- a/examples/angular/src/app/rollbar.errorhandler.ts +++ b/examples/angular/src/app/rollbar.errorhandler.ts @@ -1,19 +1,33 @@ // src/app/rollbar.errorhandler.ts -import { ErrorHandler, inject, Injectable, InjectionToken } from '@angular/core'; -import Rollbar from 'rollbar'; - -// InjectionToken for providing a Rollbar instance -export const RollbarService = new InjectionToken('rollbar'); +import { type ErrorHandler, inject, Injectable, NgZone } from '@angular/core'; +import { defer, map, shareReplay } from 'rxjs'; @Injectable() export class RollbarErrorHandler implements ErrorHandler { - // Option 1: Use `inject` (if you’re using Angular v14+) - private rollbar = inject(RollbarService); + private ngZone = inject(NgZone); + + // Lazily load Rollbar on first error to keep it out of the initial bundle. + // `defer` ensures the dynamic import is not triggered until the first + // subscription. `shareReplay` caches the result so subsequent errors reuse + // the same Rollbar instance without re-importing or re-initializing. + private rollbar$ = defer(() => import('./rollbar.config')).pipe( + map(({ createRollbar }) => createRollbar()), + shareReplay({ bufferSize: 1, refCount: false }) + ); handleError(error: any): void { - // Send error to Rollbar - this.rollbar.error(error); - // Optionally rethrow the error if you want default logging - throw error; + // Run outside Angular's zone so that the dynamic import and Rollbar's + // internal async work do not trigger unnecessary change detection cycles. + this.ngZone.runOutsideAngular(() => { + this.rollbar$.subscribe(rollbar => { + // Send error to Rollbar + rollbar.error(error); + // Rollbar load failures are intentionally ignored — error reporting + // is best-effort and should never affect the application's behaviour. + }); + }); + // Log to the console so errors remain visible during development even + // if Rollbar has not finished loading yet. + console.error(error); } } diff --git a/examples/angular/src/main.server.ts b/examples/angular/src/main.server.ts index d3bbfc9cf..35123e7e0 100644 --- a/examples/angular/src/main.server.ts +++ b/examples/angular/src/main.server.ts @@ -1,7 +1,19 @@ -import { bootstrapApplication, type BootstrapContext } from '@angular/platform-browser'; +import { provideZoneChangeDetection } from '@angular/core'; +import { + bootstrapApplication, + type BootstrapContext, +} from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { config } from './app/app.config.server'; -const bootstrap = (context?: BootstrapContext) => bootstrapApplication(AppComponent, config, context); +const bootstrap = (context?: BootstrapContext) => + bootstrapApplication( + AppComponent, + { + ...config, + providers: [provideZoneChangeDetection(), ...config.providers], + }, + context, + ); export default bootstrap; diff --git a/examples/angular/src/main.ts b/examples/angular/src/main.ts index 5702eca82..36ed3c763 100644 --- a/examples/angular/src/main.ts +++ b/examples/angular/src/main.ts @@ -1,7 +1,10 @@ +import { provideZoneChangeDetection } from '@angular/core'; // src/main.ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { appConfig } from './app/app.config'; -bootstrapApplication(AppComponent, appConfig) - .catch((err) => console.error(err)); +bootstrapApplication(AppComponent, { + ...appConfig, + providers: [provideZoneChangeDetection(), ...appConfig.providers], +}).catch((err) => console.error(err));