diff --git a/packages/core/src/di/r3_injector.ts b/packages/core/src/di/r3_injector.ts index 2776e85bb2..132c3891fb 100644 --- a/packages/core/src/di/r3_injector.ts +++ b/packages/core/src/di/r3_injector.ts @@ -289,10 +289,6 @@ export class R3Injector { return false; } - // Track the InjectorType and add a provider for it. - this.injectorDefTypes.add(defType); - this.records.set(defType, makeRecord(def.factory, NOT_YET)); - // Add providers in the same way that @NgModule resolution did: // First, include providers from any imports. @@ -330,6 +326,10 @@ export class R3Injector { } } } + // Track the InjectorType and add a provider for it. It's important that this is done after the + // def's imports. + this.injectorDefTypes.add(defType); + this.records.set(defType, makeRecord(def.factory, NOT_YET)); // Next, include providers listed on the definition itself. const defProviders = def.providers; diff --git a/packages/core/test/acceptance/ng_module_spec.ts b/packages/core/test/acceptance/ng_module_spec.ts index cd21f1617e..ec9c5be0d8 100644 --- a/packages/core/test/acceptance/ng_module_spec.ts +++ b/packages/core/test/acceptance/ng_module_spec.ts @@ -7,9 +7,9 @@ */ import {CommonModule} from '@angular/common'; -import {CUSTOM_ELEMENTS_SCHEMA, Component, NO_ERRORS_SCHEMA, NgModule, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelement as element} from '@angular/core'; - +import {CUSTOM_ELEMENTS_SCHEMA, Component, Injectable, NO_ERRORS_SCHEMA, NgModule, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelement as element} from '@angular/core'; import {TestBed} from '@angular/core/testing'; +import {expect} from '@angular/platform-browser/testing/src/matchers'; import {modifiedInIvy, onlyInIvy} from '@angular/private/testing'; describe('NgModule', () => { @@ -77,6 +77,25 @@ describe('NgModule', () => { }); }); + it('initializes the module imports before the module itself', () => { + @Injectable() + class Service { + initializations: string[] = []; + } + @NgModule({providers: [Service]}) + class RoutesModule { + constructor(service: Service) { service.initializations.push('RoutesModule'); } + } + + @NgModule({imports: [RoutesModule]}) + class AppModule { + constructor(service: Service) { service.initializations.push('AppModule'); } + } + + TestBed.configureTestingModule({imports: [AppModule]}); + expect(TestBed.inject(Service).initializations).toEqual(['RoutesModule', 'AppModule']); + }); + describe('schemas', () => { onlyInIvy('Unknown property warning logged instead of throwing an error') .it('should throw on unknown props if NO_ERRORS_SCHEMA is absent', () => { diff --git a/packages/core/test/di/r3_injector_spec.ts b/packages/core/test/di/r3_injector_spec.ts index 292bd079d1..fb48a634b8 100644 --- a/packages/core/test/di/r3_injector_spec.ts +++ b/packages/core/test/di/r3_injector_spec.ts @@ -267,6 +267,30 @@ describe('InjectorDef-based createInjector()', () => { injector = createInjector(Module); }); + it('initializes imported modules before the module being declared', () => { + const moduleRegistrations: string[] = []; + + class ChildModule { + static ɵinj = ɵɵdefineInjector({ + factory: () => new ChildModule(), + imports: undefined, + providers: [], + }); + constructor() { moduleRegistrations.push('ChildModule'); } + } + + class RootModule { + static ɵinj = ɵɵdefineInjector({ + factory: () => new RootModule(), + imports: [ChildModule], + providers: [], + }); + constructor() { moduleRegistrations.push('RootModule'); } + } + createInjector(RootModule); + expect(moduleRegistrations).toEqual(['ChildModule', 'RootModule']); + }); + it('injects a simple class', () => { const instance = injector.get(Service); expect(instance instanceof Service).toBeTruthy();