diff --git a/goldens/public-api/core/errors.md b/goldens/public-api/core/errors.md index cf8af8f848b..6e4300d0867 100644 --- a/goldens/public-api/core/errors.md +++ b/goldens/public-api/core/errors.md @@ -27,8 +27,6 @@ export const enum RuntimeErrorCode { // (undocumented) DUPLICATE_DIRECTITVE = 309, // (undocumented) - ERROR_HANDLER_NOT_FOUND = 402, - // (undocumented) EXPORT_NOT_FOUND = -301, // (undocumented) EXPRESSION_CHANGED_AFTER_CHECKED = -100, @@ -69,6 +67,8 @@ export const enum RuntimeErrorCode { // (undocumented) MISSING_LOCALE_DATA = 701, // (undocumented) + MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP = 402, + // (undocumented) MISSING_ZONEJS = 908, // (undocumented) MULTIPLE_COMPONENTS_MATCH = -300, diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index 162a17c69d1..97bb4c89810 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -12,7 +12,7 @@ "aio-local": { "uncompressed": { "runtime": 4325, - "main": 468169, + "main": 469002, "polyfills": 33836, "styles": 74561, "light-theme": 92890, diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index 12f468cb1b7..1b792400e9a 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -2,7 +2,7 @@ "cli-hello-world": { "uncompressed": { "runtime": 908, - "main": 126848, + "main": 128010, "polyfills": 33792 } }, @@ -19,14 +19,14 @@ "cli-hello-world-ivy-i18n": { "uncompressed": { "runtime": 926, - "main": 125546, + "main": 126699, "polyfills": 34676 } }, "cli-hello-world-lazy": { "uncompressed": { "runtime": 2734, - "main": 230728, + "main": 231317, "polyfills": 33810, "src_app_lazy_lazy_routes_ts": 487 } @@ -34,21 +34,21 @@ "forms": { "uncompressed": { "runtime": 888, - "main": 159074, + "main": 160227, "polyfills": 33772 } }, "animations": { "uncompressed": { "runtime": 898, - "main": 158262, + "main": 159415, "polyfills": 33782 } }, "standalone-bootstrap": { "uncompressed": { "runtime": 918, - "main": 86351, + "main": 86975, "polyfills": 33802 } }, diff --git a/packages/core/src/application_ref.ts b/packages/core/src/application_ref.ts index 469bd43fc69..059253fecfe 100644 --- a/packages/core/src/application_ref.ts +++ b/packages/core/src/application_ref.ts @@ -14,7 +14,7 @@ import {ApplicationInitStatus} from './application_init'; import {PLATFORM_INITIALIZER} from './application_tokens'; import {getCompilerFacade, JitCompilerUsage} from './compiler/compiler_facade'; import {Console} from './console'; -import {inject} from './di'; +import {ENVIRONMENT_INITIALIZER, inject} from './di'; import {Injectable} from './di/injectable'; import {InjectionToken} from './di/injection_token'; import {Injector} from './di/injector'; @@ -38,12 +38,12 @@ import {isStandalone} from './render3/definition'; import {assertStandaloneComponentType} from './render3/errors'; import {setLocaleId} from './render3/i18n/i18n_locale_id'; import {setJitOptions} from './render3/jit/jit_options'; -import {createEnvironmentInjector, NgModuleFactory as R3NgModuleFactory} from './render3/ng_module_ref'; +import {createEnvironmentInjector, createNgModuleRefWithProviders, NgModuleFactory as R3NgModuleFactory} from './render3/ng_module_ref'; import {publishDefaultGlobalUtils as _publishDefaultGlobalUtils} from './render3/util/global_utils'; import {TESTABILITY} from './testability/testability'; import {isPromise} from './util/lang'; import {stringify} from './util/stringify'; -import {IS_STABLE, NgZone, NoopNgZone} from './zone/ng_zone'; +import {isStableFactory, NgZone, NoopNgZone, ZONE_IS_STABLE_OBSERVABLE} from './zone/ng_zone'; const NG_DEV_MODE = typeof ngDevMode === 'undefined' || ngDevMode; @@ -216,7 +216,7 @@ export function internalCreateApplication(config: { // Create root application injector based on a set of providers configured at the platform // bootstrap level as well as providers passed to the bootstrap call by a user. const allAppProviders = [ - {provide: NgZone, useValue: ngZone}, + provideNgZoneChangeDetection(ngZone), ...(appProviders || []), ]; @@ -226,7 +226,7 @@ export function internalCreateApplication(config: { const exceptionHandler: ErrorHandler|null = envInjector.get(ErrorHandler, null); if (NG_DEV_MODE && !exceptionHandler) { throw new RuntimeError( - RuntimeErrorCode.ERROR_HANDLER_NOT_FOUND, + RuntimeErrorCode.MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP, 'No `ErrorHandler` found in the Dependency Injection tree.'); } @@ -448,25 +448,23 @@ export class PlatformRef { // So we create a mini parent injector that just contains the new NgZone and // pass that as parent to the NgModuleFactory. const ngZone = getNgZone(options?.ngZone, getNgZoneOptions(options)); - const providers: StaticProvider[] = [{provide: NgZone, useValue: ngZone}]; // Note: Create ngZoneInjector within ngZone.run so that all of the instantiated services are // created within the Angular zone // Do not try to replace ngZone.run with ApplicationRef#run because ApplicationRef would then be // created outside of the Angular zone. return ngZone.run(() => { - const ngZoneInjector = Injector.create( - {providers: providers, parent: this.injector, name: moduleFactory.moduleType.name}); - const moduleRef = >moduleFactory.create(ngZoneInjector); - const exceptionHandler: ErrorHandler|null = moduleRef.injector.get(ErrorHandler, null); - if (!exceptionHandler) { + const moduleRef = createNgModuleRefWithProviders( + moduleFactory.moduleType, this.injector, provideNgZoneChangeDetection(ngZone)); + const exceptionHandler = moduleRef.injector.get(ErrorHandler, null); + if (NG_DEV_MODE && exceptionHandler === null) { throw new RuntimeError( - RuntimeErrorCode.ERROR_HANDLER_NOT_FOUND, - ngDevMode && 'No ErrorHandler. Is platform module (BrowserModule) included?'); + RuntimeErrorCode.MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP, + 'No ErrorHandler. Is platform module (BrowserModule) included?'); } ngZone.runOutsideAngular(() => { const subscription = ngZone.onError.subscribe({ next: (error: any) => { - exceptionHandler.handleError(error); + exceptionHandler!.handleError(error); } }); moduleRef.onDestroy(() => { @@ -474,7 +472,7 @@ export class PlatformRef { subscription.unsubscribe(); }); }); - return _callAndReportToErrorHandler(exceptionHandler, ngZone, () => { + return _callAndReportToErrorHandler(exceptionHandler!, ngZone, () => { const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus); initStatus.runInitializers(); return initStatus.donePromise.then(() => { @@ -755,8 +753,9 @@ export class ApplicationRef { /** * Returns an Observable that indicates when the application is stable or unstable. */ - public readonly isStable = inject(IS_STABLE); + public readonly isStable = inject(ZONE_IS_STABLE_OBSERVABLE); + private readonly _injector = inject(EnvironmentInjector); /** * The `EnvironmentInjector` used to create this application. */ @@ -764,11 +763,6 @@ export class ApplicationRef { return this._injector; } - /** @internal */ - constructor(private _injector: EnvironmentInjector) { - inject(NgZoneChangeDetectionScheduler).initialize(); - } - /** * Bootstrap a component onto the element identified by its selector or, optionally, to a * specified element. @@ -1111,36 +1105,37 @@ function _lastDefined(args: T[]): T|undefined { * * `NgZone` is provided by default today so the default (and only) implementation for this * is calling `ErrorHandler.handleError` outside of the Angular zone. - * - * TODO: When NgZone is off by default, the default behavior should be to just call - * the `ErrorHandler.handleError` directly. */ const INTERNAL_APPLICATION_ERROR_HANDLER = new InjectionToken<(e: any) => void>(NG_DEV_MODE ? 'internal error handler' : '', { providedIn: 'root', factory: () => { - const zone = inject(NgZone); const userErrorHandler = inject(ErrorHandler); - return (e) => zone.runOutsideAngular(() => userErrorHandler.handleError(e)); + return userErrorHandler.handleError.bind(this); } }); +function ngZoneApplicationErrorHandlerFactory() { + const zone = inject(NgZone); + const userErrorHandler = inject(ErrorHandler); + return (e: unknown) => zone.runOutsideAngular(() => userErrorHandler.handleError(e)); +} + @Injectable({providedIn: 'root'}) export class NgZoneChangeDetectionScheduler { private readonly zone = inject(NgZone); - private readonly injector = inject(EnvironmentInjector); + private readonly applicationRef = inject(ApplicationRef); - // Lazy initialization to avoid circular DI since ApplicationRef initializes the scheduler. - // When Zoneless is the default, we can make the opt-in provider function have an - // ENVIRONMENT_INITIALIZER which initializes class instead of `ApplicationRef`. - private applicationRef?: ApplicationRef; private _onMicrotaskEmptySubscription?: Subscription; initialize(): void { + if (this._onMicrotaskEmptySubscription) { + return; + } + this._onMicrotaskEmptySubscription = this.zone.onMicrotaskEmpty.subscribe({ next: () => { this.zone.run(() => { - this.applicationRef ??= this.injector.get(ApplicationRef); this.applicationRef.tick(); }); } @@ -1151,3 +1146,25 @@ export class NgZoneChangeDetectionScheduler { this._onMicrotaskEmptySubscription?.unsubscribe(); } } + +export function provideNgZoneChangeDetection(ngZone: NgZone): StaticProvider[] { + return [ + {provide: NgZone, useValue: ngZone}, + { + provide: ENVIRONMENT_INITIALIZER, + multi: true, + useFactory: () => { + const ngZoneChangeDetectionScheduler = + inject(NgZoneChangeDetectionScheduler, {optional: true}); + if (NG_DEV_MODE && ngZoneChangeDetectionScheduler === null) { + throw new RuntimeError( + RuntimeErrorCode.MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP, + 'No NgZoneChangeDetectionScheduler found in the Dependency Injection tree.'); + } + return () => ngZoneChangeDetectionScheduler!.initialize(); + }, + }, + {provide: INTERNAL_APPLICATION_ERROR_HANDLER, useFactory: ngZoneApplicationErrorHandlerFactory}, + {provide: ZONE_IS_STABLE_OBSERVABLE, useFactory: isStableFactory}, + ]; +} diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts index 5e1891308e4..7b1f317b140 100644 --- a/packages/core/src/core_private_export.ts +++ b/packages/core/src/core_private_export.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -export {ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS, internalCreateApplication as ɵinternalCreateApplication} from './application_ref'; +export {ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS, internalCreateApplication as ɵinternalCreateApplication, provideNgZoneChangeDetection as ɵprovideNgZoneChangeDetection} from './application_ref'; export {APP_ID_RANDOM_PROVIDER as ɵAPP_ID_RANDOM_PROVIDER} from './application_tokens'; export {defaultIterableDiffers as ɵdefaultIterableDiffers, defaultKeyValueDiffers as ɵdefaultKeyValueDiffers} from './change_detection/change_detection'; export {Console as ɵConsole} from './console'; diff --git a/packages/core/src/errors.ts b/packages/core/src/errors.ts index cf674b7a20e..7751204a4d9 100644 --- a/packages/core/src/errors.ts +++ b/packages/core/src/errors.ts @@ -52,7 +52,7 @@ export const enum RuntimeErrorCode { // Bootstrap Errors MULTIPLE_PLATFORMS = 400, PLATFORM_NOT_FOUND = 401, - ERROR_HANDLER_NOT_FOUND = 402, + MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP = 402, BOOTSTRAP_COMPONENTS_NOT_FOUND = -403, PLATFORM_ALREADY_DESTROYED = 404, ASYNC_INITIALIZERS_STILL_RUNNING = 405, diff --git a/packages/core/src/render3/ng_module_ref.ts b/packages/core/src/render3/ng_module_ref.ts index 91005f98b9b..aefa83426df 100644 --- a/packages/core/src/render3/ng_module_ref.ts +++ b/packages/core/src/render3/ng_module_ref.ts @@ -8,7 +8,7 @@ import {createInjectorWithoutInjectorInstances} from '../di/create_injector'; import {Injector} from '../di/injector'; -import {EnvironmentProviders, Provider} from '../di/interface/provider'; +import {EnvironmentProviders, Provider, StaticProvider} from '../di/interface/provider'; import {EnvironmentInjector, getNullInjector, R3Injector} from '../di/r3_injector'; import {Type} from '../interface/type'; import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver'; @@ -32,7 +32,7 @@ import {maybeUnwrapFn} from './util/misc_utils'; */ export function createNgModule( ngModule: Type, parentInjector?: Injector): viewEngine_NgModuleRef { - return new NgModuleRef(ngModule, parentInjector ?? null); + return new NgModuleRef(ngModule, parentInjector ?? null, []); } /** @@ -59,7 +59,8 @@ export class NgModuleRef extends viewEngine_NgModuleRef implements Interna override readonly componentFactoryResolver: ComponentFactoryResolver = new ComponentFactoryResolver(this); - constructor(ngModuleType: Type, public _parent: Injector|null) { + constructor( + ngModuleType: Type, public _parent: Injector|null, additionalProviders: StaticProvider[]) { super(); const ngModuleDef = getNgModuleDef(ngModuleType); ngDevMode && @@ -74,7 +75,8 @@ export class NgModuleRef extends viewEngine_NgModuleRef implements Interna {provide: viewEngine_NgModuleRef, useValue: this}, { provide: viewEngine_ComponentFactoryResolver, useValue: this.componentFactoryResolver - } + }, + ...additionalProviders ], stringify(ngModuleType), new Set(['environment'])) as R3Injector; @@ -108,10 +110,16 @@ export class NgModuleFactory extends viewEngine_NgModuleFactory { } override create(parentInjector: Injector|null): viewEngine_NgModuleRef { - return new NgModuleRef(this.moduleType, parentInjector); + return new NgModuleRef(this.moduleType, parentInjector, []); } } +export function createNgModuleRefWithProviders( + moduleType: Type, parentInjector: Injector|null, + additionalProviders: StaticProvider[]): InternalNgModuleRef { + return new NgModuleRef(moduleType, parentInjector, additionalProviders); +} + class EnvironmentNgModuleRefAdapter extends viewEngine_NgModuleRef { override readonly injector: EnvironmentInjector; override readonly componentFactoryResolver: ComponentFactoryResolver = diff --git a/packages/core/src/zone/ng_zone.ts b/packages/core/src/zone/ng_zone.ts index be1982bb112..d66d9cee1d0 100644 --- a/packages/core/src/zone/ng_zone.ts +++ b/packages/core/src/zone/ng_zone.ts @@ -517,54 +517,59 @@ export class NoopNgZone implements NgZone { * for `NoopNgZone` which is always just an `Observable` of `true`. Additionally, we should consider * whether the property on `NgZone` should be `Observable` or `Signal`. */ -export const IS_STABLE = +export const ZONE_IS_STABLE_OBSERVABLE = new InjectionToken>(ngDevMode ? 'isStable Observable' : '', { providedIn: 'root', - factory: () => { - const zone = inject(NgZone); - let _stable = true; - const isCurrentlyStable = new Observable((observer: Observer) => { - _stable = zone.isStable && !zone.hasPendingMacrotasks && !zone.hasPendingMicrotasks; - zone.runOutsideAngular(() => { - observer.next(_stable); - observer.complete(); - }); + // TODO(atscott): Replace this with a suitable default like `new + // BehaviorSubject(true).asObservable`. Again, long term this won't exist on ApplicationRef at + // all but until we can remove it, we need a default value zoneless. + factory: isStableFactory, + }); + +export function isStableFactory() { + const zone = inject(NgZone); + let _stable = true; + const isCurrentlyStable = new Observable((observer: Observer) => { + _stable = zone.isStable && !zone.hasPendingMacrotasks && !zone.hasPendingMicrotasks; + zone.runOutsideAngular(() => { + observer.next(_stable); + observer.complete(); + }); + }); + + const isStable = new Observable((observer: Observer) => { + // Create the subscription to onStable outside the Angular Zone so that + // the callback is run outside the Angular Zone. + let stableSub: Subscription; + zone.runOutsideAngular(() => { + stableSub = zone.onStable.subscribe(() => { + NgZone.assertNotInAngularZone(); + + // Check whether there are no pending macro/micro tasks in the next tick + // to allow for NgZone to update the state. + scheduleMicroTask(() => { + if (!_stable && !zone.hasPendingMacrotasks && !zone.hasPendingMicrotasks) { + _stable = true; + observer.next(true); + } }); + }); + }); - const isStable = new Observable((observer: Observer) => { - // Create the subscription to onStable outside the Angular Zone so that - // the callback is run outside the Angular Zone. - let stableSub: Subscription; - zone.runOutsideAngular(() => { - stableSub = zone.onStable.subscribe(() => { - NgZone.assertNotInAngularZone(); - - // Check whether there are no pending macro/micro tasks in the next tick - // to allow for NgZone to update the state. - scheduleMicroTask(() => { - if (!_stable && !zone.hasPendingMacrotasks && !zone.hasPendingMicrotasks) { - _stable = true; - observer.next(true); - } - }); - }); - }); - - const unstableSub: Subscription = zone.onUnstable.subscribe(() => { - NgZone.assertInAngularZone(); - if (_stable) { - _stable = false; - zone.runOutsideAngular(() => { - observer.next(false); - }); - } - }); - - return () => { - stableSub.unsubscribe(); - unstableSub.unsubscribe(); - }; + const unstableSub: Subscription = zone.onUnstable.subscribe(() => { + NgZone.assertInAngularZone(); + if (_stable) { + _stable = false; + zone.runOutsideAngular(() => { + observer.next(false); }); - return merge(isCurrentlyStable, isStable.pipe(share())); } }); + + return () => { + stableSub.unsubscribe(); + unstableSub.unsubscribe(); + }; + }); + return merge(isCurrentlyStable, isStable.pipe(share())); +} diff --git a/packages/core/test/application_ref_spec.ts b/packages/core/test/application_ref_spec.ts index 6b31cc7553c..82773e6ab47 100644 --- a/packages/core/test/application_ref_spec.ts +++ b/packages/core/test/application_ref_spec.ts @@ -427,9 +427,7 @@ class SomeComponent { return defaultPlatform.bootstrapModule(EmptyModule) .then(() => fail('expecting error'), (error) => { - expect(error.message) - .toEqual( - 'NG0402: No ErrorHandler. Is platform module (BrowserModule) included?'); + expect(error.message).toMatch(/NG0402/); }); })); diff --git a/packages/core/test/bundling/animations/bundle.golden_symbols.json b/packages/core/test/bundling/animations/bundle.golden_symbols.json index 3a3859f54ab..255a2faa093 100644 --- a/packages/core/test/bundling/animations/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations/bundle.golden_symbols.json @@ -260,6 +260,9 @@ { "name": "INJECTOR_SCOPE" }, + { + "name": "INTERNAL_APPLICATION_ERROR_HANDLER" + }, { "name": "InjectFlags" }, @@ -398,6 +401,9 @@ { "name": "NgZone" }, + { + "name": "NgZoneChangeDetectionScheduler" + }, { "name": "NodeInjector" }, @@ -587,6 +593,9 @@ { "name": "WebAnimationsStyleNormalizer" }, + { + "name": "ZONE_IS_STABLE_OBSERVABLE" + }, { "name": "_CACHED_BODY" }, @@ -884,6 +893,9 @@ { "name": "forEachSingleProvider" }, + { + "name": "formatRuntimeError" + }, { "name": "forwardRef" }, @@ -1157,6 +1169,9 @@ { "name": "isPromise2" }, + { + "name": "isStableFactory" + }, { "name": "isTemplateNode" }, @@ -1238,6 +1253,9 @@ { "name": "ngOnChangesSetInput" }, + { + "name": "ngZoneApplicationErrorHandlerFactory" + }, { "name": "noSideEffects" }, diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 6f3a4780527..2951fe25882 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -161,6 +161,9 @@ { "name": "INJECTOR_SCOPE" }, + { + "name": "INTERNAL_APPLICATION_ERROR_HANDLER" + }, { "name": "InjectFlags" }, @@ -293,6 +296,9 @@ { "name": "NgZone" }, + { + "name": "NgZoneChangeDetectionScheduler" + }, { "name": "NodeInjector" }, @@ -434,6 +440,9 @@ { "name": "ViewRef" }, + { + "name": "ZONE_IS_STABLE_OBSERVABLE" + }, { "name": "_DOM" }, @@ -647,6 +656,9 @@ { "name": "forEachSingleProvider" }, + { + "name": "formatRuntimeError" + }, { "name": "forwardRef" }, @@ -890,6 +902,9 @@ { "name": "isPromise2" }, + { + "name": "isStableFactory" + }, { "name": "isTemplateNode" }, @@ -953,6 +968,9 @@ { "name": "ngOnChangesSetInput" }, + { + "name": "ngZoneApplicationErrorHandlerFactory" + }, { "name": "noSideEffects" }, diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json index ae1cdec1851..c561c90fcec 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -236,6 +236,9 @@ { "name": "INJECTOR_SCOPE" }, + { + "name": "INTERNAL_APPLICATION_ERROR_HANDLER" + }, { "name": "InjectFlags" }, @@ -404,6 +407,9 @@ { "name": "NgZone" }, + { + "name": "NgZoneChangeDetectionScheduler" + }, { "name": "NodeInjector" }, @@ -596,6 +602,9 @@ { "name": "ViewRef" }, + { + "name": "ZONE_IS_STABLE_OBSERVABLE" + }, { "name": "_DOM" }, @@ -911,6 +920,9 @@ { "name": "formGroupNameProvider" }, + { + "name": "formatRuntimeError" + }, { "name": "forwardRef" }, @@ -1253,6 +1265,9 @@ { "name": "isPromise2" }, + { + "name": "isStableFactory" + }, { "name": "isStylingMatch" }, @@ -1379,6 +1394,9 @@ { "name": "ngOnChangesSetInput" }, + { + "name": "ngZoneApplicationErrorHandlerFactory" + }, { "name": "noSideEffects" }, diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json index 03fbfa9ff67..59d87b67dc6 100644 --- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json @@ -221,6 +221,9 @@ { "name": "INJECTOR_SCOPE" }, + { + "name": "INTERNAL_APPLICATION_ERROR_HANDLER" + }, { "name": "InjectFlags" }, @@ -395,6 +398,9 @@ { "name": "NgZone" }, + { + "name": "NgZoneChangeDetectionScheduler" + }, { "name": "NodeInjector" }, @@ -587,6 +593,9 @@ { "name": "ViewRef" }, + { + "name": "ZONE_IS_STABLE_OBSERVABLE" + }, { "name": "_DOM" }, @@ -878,6 +887,9 @@ { "name": "formDirectiveProvider" }, + { + "name": "formatRuntimeError" + }, { "name": "forwardRef" }, @@ -1211,6 +1223,9 @@ { "name": "isPromise2" }, + { + "name": "isStableFactory" + }, { "name": "isStylingMatch" }, @@ -1343,6 +1358,9 @@ { "name": "ngOnChangesSetInput" }, + { + "name": "ngZoneApplicationErrorHandlerFactory" + }, { "name": "noSideEffects" }, diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index aa056848a1d..af77cdd9e4f 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -110,6 +110,9 @@ { "name": "INJECTOR_SCOPE" }, + { + "name": "INTERNAL_APPLICATION_ERROR_HANDLER" + }, { "name": "InjectFlags" }, @@ -152,6 +155,9 @@ { "name": "NG_COMP_DEF" }, + { + "name": "NG_DIR_DEF" + }, { "name": "NG_ELEMENT_ID" }, @@ -173,6 +179,9 @@ { "name": "NG_MOD_DEF" }, + { + "name": "NG_PIPE_DEF" + }, { "name": "NG_PROV_DEF" }, @@ -221,6 +230,9 @@ { "name": "NgZone" }, + { + "name": "NgZoneChangeDetectionScheduler" + }, { "name": "NodeInjector" }, @@ -332,6 +344,9 @@ { "name": "ViewRef" }, + { + "name": "ZONE_IS_STABLE_OBSERVABLE" + }, { "name": "_DOM" }, @@ -494,6 +509,9 @@ { "name": "forEachSingleProvider" }, + { + "name": "formatRuntimeError" + }, { "name": "forwardRef" }, @@ -683,6 +701,9 @@ { "name": "isPromise2" }, + { + "name": "isStableFactory" + }, { "name": "isTypeProvider" }, @@ -731,6 +752,9 @@ { "name": "ngOnChangesSetInput" }, + { + "name": "ngZoneApplicationErrorHandlerFactory" + }, { "name": "noop" }, diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 574adf83b6f..a7604e92ed4 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -275,6 +275,9 @@ { "name": "INJECTOR_SCOPE" }, + { + "name": "INTERNAL_APPLICATION_ERROR_HANDLER" + }, { "name": "INTERNAL_BROWSER_PLATFORM_PROVIDERS" }, @@ -467,6 +470,9 @@ { "name": "NgZone" }, + { + "name": "NgZoneChangeDetectionScheduler" + }, { "name": "NoMatch" }, @@ -800,6 +806,9 @@ { "name": "XSS_SECURITY_URL" }, + { + "name": "ZONE_IS_STABLE_OBSERVABLE" + }, { "name": "_DOM" }, @@ -1562,6 +1571,9 @@ { "name": "isScheduler" }, + { + "name": "isStableFactory" + }, { "name": "isTemplateNode" }, @@ -1682,6 +1694,9 @@ { "name": "ngOnChangesSetInput" }, + { + "name": "ngZoneApplicationErrorHandlerFactory" + }, { "name": "noLeftoversInUrl" }, @@ -1748,6 +1763,9 @@ { "name": "promise" }, + { + "name": "provideNgZoneChangeDetection" + }, { "name": "redirectIfUrlTree" }, diff --git a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json index 498b212ffd1..e342209b39c 100644 --- a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json +++ b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json @@ -146,6 +146,9 @@ { "name": "INJECTOR_SCOPE" }, + { + "name": "INTERNAL_APPLICATION_ERROR_HANDLER" + }, { "name": "INTERNAL_BROWSER_PLATFORM_PROVIDERS" }, @@ -266,6 +269,9 @@ { "name": "NgZone" }, + { + "name": "NgZoneChangeDetectionScheduler" + }, { "name": "NodeInjector" }, @@ -395,6 +401,9 @@ { "name": "ViewRef" }, + { + "name": "ZONE_IS_STABLE_OBSERVABLE" + }, { "name": "_DOM" }, @@ -788,6 +797,9 @@ { "name": "isPromise2" }, + { + "name": "isStableFactory" + }, { "name": "isTemplateNode" }, @@ -845,6 +857,9 @@ { "name": "ngOnChangesSetInput" }, + { + "name": "ngZoneApplicationErrorHandlerFactory" + }, { "name": "nonNull" }, @@ -872,6 +887,9 @@ { "name": "promise" }, + { + "name": "provideNgZoneChangeDetection" + }, { "name": "refCount" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 3cd794b2829..bc74d888d90 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -164,6 +164,9 @@ { "name": "INJECTOR_SCOPE" }, + { + "name": "INTERNAL_APPLICATION_ERROR_HANDLER" + }, { "name": "InjectFlags" }, @@ -308,6 +311,9 @@ { "name": "NgZone" }, + { + "name": "NgZoneChangeDetectionScheduler" + }, { "name": "NodeInjector" }, @@ -509,6 +515,9 @@ { "name": "ViewRef" }, + { + "name": "ZONE_IS_STABLE_OBSERVABLE" + }, { "name": "_DOM" }, @@ -770,6 +779,9 @@ { "name": "forEachSingleProvider" }, + { + "name": "formatRuntimeError" + }, { "name": "forwardRef" }, @@ -1067,6 +1079,9 @@ { "name": "isPromise2" }, + { + "name": "isStableFactory" + }, { "name": "isStylingMatch" }, @@ -1163,6 +1178,9 @@ { "name": "ngOnChangesSetInput" }, + { + "name": "ngZoneApplicationErrorHandlerFactory" + }, { "name": "noSideEffects" }, diff --git a/packages/core/testing/src/test_bed_compiler.ts b/packages/core/testing/src/test_bed_compiler.ts index f526c1d2013..83625d0a898 100644 --- a/packages/core/testing/src/test_bed_compiler.ts +++ b/packages/core/testing/src/test_bed_compiler.ts @@ -7,7 +7,7 @@ */ import {ResourceLoader} from '@angular/compiler'; -import {ApplicationInitStatus, Compiler, COMPILER_OPTIONS, Component, Directive, Injector, InjectorType, LOCALE_ID, ModuleWithComponentFactories, ModuleWithProviders, NgModule, NgModuleFactory, NgZone, Pipe, PlatformRef, Provider, resolveForwardRef, Type, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID, ɵDirectiveDef as DirectiveDef, ɵgetInjectableDef as getInjectableDef, ɵInternalEnvironmentProviders as InternalEnvironmentProviders, ɵisEnvironmentProviders as isEnvironmentProviders, ɵNG_COMP_DEF as NG_COMP_DEF, ɵNG_DIR_DEF as NG_DIR_DEF, ɵNG_INJ_DEF as NG_INJ_DEF, ɵNG_MOD_DEF as NG_MOD_DEF, ɵNG_PIPE_DEF as NG_PIPE_DEF, ɵNgModuleFactory as R3NgModuleFactory, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵNgModuleType as NgModuleType, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵRender3ComponentFactory as ComponentFactory, ɵRender3NgModuleRef as NgModuleRef, ɵsetLocaleId as setLocaleId, ɵtransitiveScopesFor as transitiveScopesFor, ɵɵInjectableDeclaration as InjectableDeclaration} from '@angular/core'; +import {ApplicationInitStatus, Compiler, COMPILER_OPTIONS, Component, Directive, Injector, InjectorType, LOCALE_ID, ModuleWithComponentFactories, ModuleWithProviders, NgModule, NgModuleFactory, NgZone, Pipe, PlatformRef, Provider, resolveForwardRef, Type, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID, ɵDirectiveDef as DirectiveDef, ɵgetInjectableDef as getInjectableDef, ɵInternalEnvironmentProviders as InternalEnvironmentProviders, ɵisEnvironmentProviders as isEnvironmentProviders, ɵNG_COMP_DEF as NG_COMP_DEF, ɵNG_DIR_DEF as NG_DIR_DEF, ɵNG_INJ_DEF as NG_INJ_DEF, ɵNG_MOD_DEF as NG_MOD_DEF, ɵNG_PIPE_DEF as NG_PIPE_DEF, ɵNgModuleFactory as R3NgModuleFactory, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵNgModuleType as NgModuleType, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵprovideNgZoneChangeDetection as provideNgZoneChangeDetection, ɵRender3ComponentFactory as ComponentFactory, ɵRender3NgModuleRef as NgModuleRef, ɵsetLocaleId as setLocaleId, ɵtransitiveScopesFor as transitiveScopesFor, ɵɵInjectableDeclaration as InjectableDeclaration} from '@angular/core'; import {clearResolutionOfComponentResourcesQueue, isComponentDefPendingResolution, resolveComponentResources, restoreComponentResolutionQueue} from '../../src/metadata/resource_loading'; import {ComponentDef, ComponentType} from '../../src/render3'; @@ -287,7 +287,7 @@ export class TestBedCompiler { this.componentToModuleScope.clear(); const parentInjector = this.platform.injector; - this.testModuleRef = new NgModuleRef(this.testModuleType, parentInjector); + this.testModuleRef = new NgModuleRef(this.testModuleType, parentInjector, []); // ApplicationInitStatus.runInitializers() is marked @internal to core. // Cast it to any before accessing it. @@ -748,7 +748,7 @@ export class TestBedCompiler { const ngZone = new NgZone({enableLongStackTrace: true}); const providers: Provider[] = [ - {provide: NgZone, useValue: ngZone}, + provideNgZoneChangeDetection(ngZone), {provide: Compiler, useFactory: () => new R3TestCompiler(this)}, ...this.providers, ...this.providerOverrides, diff --git a/packages/platform-browser/test/browser/bootstrap_spec.ts b/packages/platform-browser/test/browser/bootstrap_spec.ts index 7627bd43d29..96417b90595 100644 --- a/packages/platform-browser/test/browser/bootstrap_spec.ts +++ b/packages/platform-browser/test/browser/bootstrap_spec.ts @@ -437,7 +437,7 @@ function bootstrap( }); }); - it('should throw if no provider', done => { + it('should throw if no provider', async () => { const logger = new MockConsole(); const errorHandler = new ErrorHandler(); (errorHandler as any)._console = logger as any; @@ -460,14 +460,9 @@ function bootstrap( class CustomModule { } - bootstrap(RootCmp, [{provide: ErrorHandler, useValue: errorHandler}], [], [ + await expectAsync(bootstrap(RootCmp, [{provide: ErrorHandler, useValue: errorHandler}], [], [ CustomModule - ]).then(null, (e: Error) => { - const errorMsg = `R3InjectorError(TestModule)[IDontExist -> IDontExist -> IDontExist]: \n`; - expect(e.message).toContain(errorMsg); - done(); - return null; - }); + ])).toBeRejected(); }); if (getDOM().supportsDOMEvents) { diff --git a/packages/platform-browser/testing/src/browser.ts b/packages/platform-browser/testing/src/browser.ts index 258d6ca3d80..9867e1c6814 100644 --- a/packages/platform-browser/testing/src/browser.ts +++ b/packages/platform-browser/testing/src/browser.ts @@ -7,7 +7,7 @@ */ import {PlatformLocation} from '@angular/common'; import {MockPlatformLocation} from '@angular/common/testing'; -import {APP_ID, createPlatformFactory, NgModule, NgZone, PLATFORM_INITIALIZER, platformCore, StaticProvider} from '@angular/core'; +import {APP_ID, createPlatformFactory, NgModule, PLATFORM_INITIALIZER, platformCore, StaticProvider, ɵprovideNgZoneChangeDetection as provideNgZoneChangeDetection} from '@angular/core'; import {BrowserModule, ɵBrowserDomAdapter as BrowserDomAdapter} from '@angular/platform-browser'; import {BrowserDetection, createNgZone} from './browser_util'; @@ -37,7 +37,7 @@ export const platformBrowserTesting = exports: [BrowserModule], providers: [ {provide: APP_ID, useValue: 'a'}, - {provide: NgZone, useFactory: createNgZone}, + provideNgZoneChangeDetection(createNgZone()), {provide: PlatformLocation, useClass: MockPlatformLocation}, ] })