refactor(core): NgZone.onError reports to application error handler (#60946)

This commit ensures that errors from the `NgZone.onError` subscription
in bootstrap are reported to the internal application error handler.
Practically speaking, this should not affect anything today, as the
application handler goes directly to the `ErrorHandler` from the root.
While TestBed installs an application error handler that rethrows,
`TestBed.createComponent` does not create components through this
bootstrap function.

PR Close #60946
This commit is contained in:
Andrew Scott
2025-04-21 09:46:11 -07:00
committed by Miles Malerba
parent e8147df0e1
commit 3261d14391
+7 -18
View File
@@ -9,7 +9,7 @@ import {Subscription} from 'rxjs';
import {PROVIDED_NG_ZONE} from '../change_detection/scheduling/ng_zone_scheduling';
import {R3Injector} from '../di/r3_injector';
import {ErrorHandler} from '../error_handler';
import {INTERNAL_APPLICATION_ERROR_HANDLER} from '../error_handler';
import {RuntimeError, RuntimeErrorCode} from '../errors';
import {DEFAULT_LOCALE_ID} from '../i18n/localization';
import {LOCALE_ID} from '../i18n/tokens';
@@ -90,17 +90,8 @@ export function bootstrap<M>(
} else {
config.moduleRef.resolveInjectorInitializers();
}
const exceptionHandler = envInjector.get(ErrorHandler, null);
const exceptionHandler = envInjector.get(INTERNAL_APPLICATION_ERROR_HANDLER);
if (typeof ngDevMode === 'undefined' || ngDevMode) {
if (exceptionHandler === null) {
const errorMessage = isApplicationBootstrapConfig(config)
? 'No `ErrorHandler` found in the Dependency Injection tree.'
: 'No ErrorHandler. Is platform module (BrowserModule) included';
throw new RuntimeError(
RuntimeErrorCode.MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP,
errorMessage,
);
}
if (envInjector.get(PROVIDED_ZONELESS) && envInjector.get(PROVIDED_NG_ZONE)) {
throw new RuntimeError(
RuntimeErrorCode.PROVIDED_BOTH_ZONE_AND_ZONELESS,
@@ -113,9 +104,7 @@ export function bootstrap<M>(
let onErrorSubscription: Subscription;
ngZone.runOutsideAngular(() => {
onErrorSubscription = ngZone.onError.subscribe({
next: (error: any) => {
exceptionHandler!.handleError(error);
},
next: exceptionHandler,
});
});
@@ -142,7 +131,7 @@ export function bootstrap<M>(
});
}
return _callAndReportToErrorHandler(exceptionHandler!, ngZone, () => {
return _callAndReportToErrorHandler(exceptionHandler, ngZone, () => {
const initStatus = envInjector.get(ApplicationInitStatus);
initStatus.runInitializers();
@@ -203,7 +192,7 @@ function moduleDoBootstrap(
}
function _callAndReportToErrorHandler(
errorHandler: ErrorHandler,
errorHandler: (e: unknown) => void,
ngZone: NgZone,
callback: () => any,
): any {
@@ -211,7 +200,7 @@ function _callAndReportToErrorHandler(
const result = callback();
if (isPromise(result)) {
return result.catch((e: any) => {
ngZone.runOutsideAngular(() => errorHandler.handleError(e));
ngZone.runOutsideAngular(() => errorHandler(e));
// rethrow as the exception handler might not do it
throw e;
});
@@ -219,7 +208,7 @@ function _callAndReportToErrorHandler(
return result;
} catch (e) {
ngZone.runOutsideAngular(() => errorHandler.handleError(e));
ngZone.runOutsideAngular(() => errorHandler(e));
// rethrow as the exception handler might not do it
throw e;
}