diff --git a/karma-js.conf.js b/karma-js.conf.js index 0028c738c2..4c63692ba0 100644 --- a/karma-js.conf.js +++ b/karma-js.conf.js @@ -70,6 +70,7 @@ module.exports = function(config) { 'dist/all/@angular/compiler/test/aot/**', 'dist/all/@angular/compiler/test/render3/**', 'dist/all/@angular/core/test/bundling/**', + 'dist/all/@angular/core/test/render3/**', 'dist/all/@angular/elements/schematics/**', 'dist/all/@angular/examples/**/e2e_test/*', 'dist/all/@angular/language-service/**', diff --git a/packages/core/src/render3/jit/fields.ts b/packages/core/src/render3/jit/fields.ts index 6ddef85fee..416c702a50 100644 --- a/packages/core/src/render3/jit/fields.ts +++ b/packages/core/src/render3/jit/fields.ts @@ -12,5 +12,6 @@ const TARGET = {} as any; export const NG_COMPONENT_DEF = getClosureSafeProperty({ngComponentDef: TARGET}, TARGET); export const NG_DIRECTIVE_DEF = getClosureSafeProperty({ngDirectiveDef: TARGET}, TARGET); +export const NG_INJECTOR_DEF = getClosureSafeProperty({ngInjectorDef: TARGET}, TARGET); export const NG_PIPE_DEF = getClosureSafeProperty({ngPipeDef: TARGET}, TARGET); export const NG_MODULE_DEF = getClosureSafeProperty({ngModuleDef: TARGET}, TARGET); diff --git a/packages/core/src/render3/jit/module.ts b/packages/core/src/render3/jit/module.ts index 004cf80e06..823f30a001 100644 --- a/packages/core/src/render3/jit/module.ts +++ b/packages/core/src/render3/jit/module.ts @@ -6,24 +6,25 @@ * found in the LICENSE file at https://angular.io/license */ -import {Expression, R3NgModuleMetadata, WrappedNodeExpr, compileNgModule as compileR3NgModule, jitExpression} from '@angular/compiler'; +import {Expression, R3InjectorMetadata, R3NgModuleMetadata, WrappedNodeExpr, compileInjector, compileNgModule as compileR3NgModule, jitExpression} from '@angular/compiler'; import {ModuleWithProviders, NgModule, NgModuleDefInternal, NgModuleTransitiveScopes} from '../../metadata/ng_module'; import {Type} from '../../type'; import {ComponentDefInternal} from '../interfaces/definition'; import {angularCoreEnv} from './environment'; -import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from './fields'; +import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_INJECTOR_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from './fields'; +import {reflectDependencies} from './util'; const EMPTY_ARRAY: Type[] = []; export function compileNgModule(type: Type, ngModule: NgModule): void { const declarations: Type[] = flatten(ngModule.declarations || EMPTY_ARRAY); - let def: any = null; + let ngModuleDef: any = null; Object.defineProperty(type, NG_MODULE_DEF, { get: () => { - if (def === null) { + if (ngModuleDef === null) { const meta: R3NgModuleMetadata = { type: wrap(type), bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY).map(wrap), @@ -35,9 +36,32 @@ export function compileNgModule(type: Type, ngModule: NgModule): void { emitInline: true, }; const res = compileR3NgModule(meta); - def = jitExpression(res.expression, angularCoreEnv, `ng://${type.name}/ngModuleDef.js`); + ngModuleDef = + jitExpression(res.expression, angularCoreEnv, `ng://${type.name}/ngModuleDef.js`); } - return def; + return ngModuleDef; + }, + }); + + let ngInjectorDef: any = null; + Object.defineProperty(type, NG_INJECTOR_DEF, { + get: () => { + if (ngInjectorDef === null) { + const meta: R3InjectorMetadata = { + name: type.name, + type: wrap(type), + deps: reflectDependencies(type), + providers: new WrappedNodeExpr(ngModule.providers || EMPTY_ARRAY), + imports: new WrappedNodeExpr([ + ngModule.imports || EMPTY_ARRAY, + ngModule.exports || EMPTY_ARRAY, + ]), + }; + const res = compileInjector(meta); + ngInjectorDef = + jitExpression(res.expression, angularCoreEnv, `ng://${type.name}/ngInjectorDef.js`); + } + return ngInjectorDef; }, }); diff --git a/packages/core/test/render3/ivy/BUILD.bazel b/packages/core/test/render3/ivy/BUILD.bazel index a6e21830bd..dd78fb48fa 100644 --- a/packages/core/test/render3/ivy/BUILD.bazel +++ b/packages/core/test/render3/ivy/BUILD.bazel @@ -35,10 +35,3 @@ jasmine_node_test( ":ivy_node_lib", ], ) - -ts_web_test_suite( - name = "ivy_web", - deps = [ - ":ivy_lib", - ], -) diff --git a/packages/core/test/render3/ivy/jit_spec.ts b/packages/core/test/render3/ivy/jit_spec.ts index 6d31afb2bf..919df879d8 100644 --- a/packages/core/test/render3/ivy/jit_spec.ts +++ b/packages/core/test/render3/ivy/jit_spec.ts @@ -6,6 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ +import 'reflect-metadata'; + +import {InjectorDef, defineInjectable} from '@angular/core/src/di/defs'; import {Injectable} from '@angular/core/src/di/injectable'; import {inject, setCurrentInjector} from '@angular/core/src/di/injector'; import {ivyEnabled} from '@angular/core/src/ivy_switch'; @@ -140,6 +143,31 @@ ivyEnabled && describe('render3 jit', () => { expect(moduleDef.declarations[0]).toBe(Cmp); }); + it('compiles a module to an ngInjectorDef with the providers', () => { + class Token { + static ngInjectableDef = defineInjectable({ + providedIn: 'root', + factory: () => 'default', + }); + } + + @NgModule({ + providers: [{provide: Token, useValue: 'test'}], + }) + class Module { + constructor(public token: Token) {} + } + + const injectorDef: InjectorDef = (Module as any).ngInjectorDef; + const instance = injectorDef.factory(); + + // Since the instance was created outside of an injector using the module, the + // injection will use the default provider, not the provider from the module. + expect(instance.token).toBe('default'); + + expect(injectorDef.providers).toEqual([{provide: Token, useValue: 'test'}]); + }); + it('patches a module onto the component', () => { @Component({ template: 'foo', diff --git a/packages/core/test/render3/jit_environment_spec.ts b/packages/core/test/render3/jit_environment_spec.ts index b88abe1a84..0927739564 100644 --- a/packages/core/test/render3/jit_environment_spec.ts +++ b/packages/core/test/render3/jit_environment_spec.ts @@ -14,6 +14,7 @@ import {angularCoreEnv} from '../../src/render3/jit/environment'; const INTERFACE_EXCEPTIONS = new Set([ 'ComponentDef', 'DirectiveDef', + 'InjectorDef', 'NgModuleDef', ]);