feat(ivy): @NgModule -> ngInjectorDef compilation (#22458)

This adds compilation of @NgModule providers and imports into
ngInjectorDef statements in generated code. All @NgModule annotations
will be compiled and the @NgModule decorators removed from the
resultant js output.

All @Injectables will also be compiled in Ivy mode, and the decorator
removed.

PR Close #22458
This commit is contained in:
Alex Rickabaugh
2018-02-16 08:45:21 -08:00
committed by Miško Hevery
parent 688096b7a3
commit 6ef9f2278f
48 changed files with 2044 additions and 206 deletions

View File

@ -13,13 +13,13 @@
*/
export * from './di/metadata';
export {defineInjectable, Injectable, InjectableDecorator, InjectableProvider, InjectableType} from './di/injectable';
export * from './di/defs';
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
export {inject, InjectFlags, Injector} from './di/injector';
export {Injectable, InjectableDecorator, InjectableProvider} from './di/injectable';
export {inject, InjectFlags, INJECTOR, Injector} from './di/injector';
export {ReflectiveInjector} from './di/reflective_injector';
export {StaticProvider, ValueProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
export {createInjector} from './di/r3_injector';
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './di/reflective_provider';
export {ReflectiveKey} from './di/reflective_key';
export {InjectionToken} from './di/injection_token';

View File

@ -0,0 +1,140 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Type} from '../type';
import {ClassProvider, ClassSansProvider, ConstructorProvider, ConstructorSansProvider, ExistingProvider, ExistingSansProvider, FactoryProvider, FactorySansProvider, StaticClassProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider';
/**
* Information about how a type or `InjectionToken` interfaces with the DI system.
*
* At a minimum, this includes a `factory` which defines how to create the given type `T`, possibly
* requesting injection of other types if necessary.
*
* Optionally, a `providedIn` parameter specifies that the given type belongs to a particular
* `InjectorDef`, `NgModule`, or a special scope (e.g. `'root'`). A value of `null` indicates
* that the injectable does not belong to any scope.
*
* This type is typically generated by the Angular compiler, but can be hand-written if needed.
*
* @experimental
*/
export interface InjectableDef<T> {
providedIn: InjectorType<any>|'root'|'any'|null;
factory: () => T;
}
/**
* Information about the providers to be included in an `Injector` as well as how the given type
* which carries the information should be created by the DI system.
*
* An `InjectorDef` can import other types which have `InjectorDefs`, forming a deep nested
* structure of providers with a defined priority (identically to how `NgModule`s also have
* an import/dependency structure).
*
* @experimental
*/
export interface InjectorDef<T> {
factory: () => T;
// TODO(alxhub): Narrow down the type here once decorators properly change the return type of the
// class they are decorating (to add the ngInjectableDef property for example).
providers: (Type<any>|ValueProvider|ExistingProvider|FactoryProvider|ConstructorProvider|
StaticClassProvider|ClassProvider|any[])[];
imports: (InjectorType<any>|InjectorTypeWithProviders<any>)[];
}
/**
* A `Type` which has an `InjectableDef` static field.
*
* `InjectableDefType`s contain their own Dependency Injection metadata and are usable in an
* `InjectorDef`-based `StaticInjector.
*
* @experimental
*/
export interface InjectableType<T> extends Type<T> { ngInjectableDef: InjectableDef<T>; }
/**
* A type which has an `InjectorDef` static field.
*
* `InjectorDefTypes` can be used to configure a `StaticInjector`.
*
* @experimental
*/
export interface InjectorType<T> extends Type<T> { ngInjectorDef: InjectorDef<T>; }
/**
* Describes the `InjectorDef` equivalent of a `ModuleWithProviders`, an `InjectorDefType` with an
* associated array of providers.
*
* Objects of this type can be listed in the imports section of an `InjectorDef`.
*
* @experimental
*/
export interface InjectorTypeWithProviders<T> {
ngModule: InjectorType<T>;
providers?: (Type<any>|ValueProvider|ExistingProvider|FactoryProvider|ConstructorProvider|
StaticClassProvider|ClassProvider|any[])[];
}
/**
* Construct an `InjectableDef` which defines how a token will be constructed by the DI system, and
* in which injectors (if any) it will be available.
*
* This should be assigned to a static `ngInjectableDef` field on a type, which will then be an
* `InjectableType`.
*
* Options:
* * `providedIn` determines which injectors will include the injectable, by either associating it
* with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be
* provided in the `'root'` injector, which will be the application-level injector in most apps.
* * `factory` gives the zero argument function which will create an instance of the injectable.
* The factory can call `inject` to access the `Injector` and request injection of dependencies.
*
* @experimental
*/
export function defineInjectable<T>(opts: {
providedIn?: Type<any>| 'root' | null,
factory: () => T,
}): InjectableDef<T> {
return {
providedIn: (opts.providedIn as InjectorType<any>| 'root' | null | undefined) || null,
factory: opts.factory,
};
}
/**
* Construct an `InjectorDef` which configures an injector.
*
* This should be assigned to a static `ngInjectorDef` field on a type, which will then be an
* `InjectorType`.
*
* Options:
*
* * `factory`: an `InjectorType` is an instantiable type, so a zero argument `factory` function to
* create the type must be provided. If that factory function needs to inject arguments, it can
* use the `inject` function.
* * `providers`: an optional array of providers to add to the injector. Each provider must
* either have a factory or point to a type which has an `ngInjectableDef` static property (the
* type must be an `InjectableType`).
* * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s
* whose providers will also be added to the injector. Locally provided types will override
* providers from imports.
*
* @experimental
*/
export function defineInjector(options: {factory: () => any, providers?: any[], imports?: any[]}):
InjectorDef<any> {
return {
factory: options.factory,
providers: options.providers || [],
imports: options.imports || [],
};
}

View File

@ -11,6 +11,7 @@ import {Type} from '../type';
import {makeDecorator, makeParamDecorator} from '../util/decorators';
import {getClosureSafeProperty} from '../util/property';
import {InjectableDef, InjectableType, defineInjectable} from './defs';
import {inject, injectArgs} from './injector';
import {ClassSansProvider, ConstructorProvider, ConstructorSansProvider, ExistingProvider, ExistingSansProvider, FactoryProvider, FactorySansProvider, StaticClassProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider';
@ -108,22 +109,6 @@ export function convertInjectableProviderToFactory(
}
}
/**
* Construct an `InjectableDef` which defines how a token will be constructed by the DI system, and
* in which injectors (if any) it will be available.
*
* @experimental
*/
export function defineInjectable<T>(opts: {
providedIn?: Type<any>| 'root' | null,
factory: () => T,
}): InjectableDef<T> {
return {
providedIn: opts.providedIn || null,
factory: opts.factory,
};
}
/**
* Injectable decorator and metadata.
*
@ -132,21 +117,16 @@ export function defineInjectable<T>(opts: {
*/
export const Injectable: InjectableDecorator = makeDecorator(
'Injectable', undefined, undefined, undefined,
(injectableType: Type<any>,
(injectableType: InjectableType<any>,
options: {providedIn?: Type<any>| 'root' | null} & InjectableProvider) => {
if (options && options.providedIn) {
(injectableType as InjectableType<any>).ngInjectableDef = defineInjectable({
if (options && options.providedIn !== undefined) {
injectableType.ngInjectableDef = defineInjectable({
providedIn: options.providedIn,
factory: convertInjectableProviderToFactory(injectableType, options)
});
}
});
export interface InjectableDef<T> {
providedIn: Type<any>|'root'|null;
factory: () => T;
}
/**
* Type representing injectable service.
*

View File

@ -8,7 +8,7 @@
import {Type} from '../type';
import {InjectableDef, defineInjectable} from './injectable';
import {InjectableDef, defineInjectable} from './defs';
/**
* Creates a token that can be used in a DI Provider.
@ -26,8 +26,24 @@ import {InjectableDef, defineInjectable} from './injectable';
* // myInterface is inferred to be MyInterface.
* ```
*
* When creating an `InjectionToken`, you can optionally specify a factory function which returns
* (possibly by creating) a default value of the parameterized type `T`. This sets up the
* `InjectionToken` using this factory as a provider as if it was defined explicitly in the
* application's root injector. If the factory function, which takes zero arguments, needs to inject
* dependencies, it can do so using the `inject` function. See below for an example.
*
* Additionally, if a `factory` is specified you can also specify the `providedIn` option, which
* overrides the above behavior and marks the token as belonging to a particular `@NgModule`. As
* mentioned above, `'root'` is the default value for `providedIn`.
*
* ### Example
*
* #### Tree-shakeable InjectionToken
*
* {@example core/di/ts/injector_spec.ts region='ShakeableInjectionToken'}
*
* #### Plain InjectionToken
*
* {@example core/di/ts/injector_spec.ts region='InjectionToken'}
*
* @stable
@ -54,3 +70,7 @@ export class InjectionToken<T> {
toString(): string { return `InjectionToken ${this._desc}`; }
}
export interface InjectableDefToken<T> extends InjectionToken<T> {
ngInjectableDef: InjectableDef<T>;
}

View File

@ -8,6 +8,8 @@
import {Type} from '../type';
import {stringify} from '../util';
import {InjectableDef, defineInjectable} from './defs';
import {resolveForwardRef} from './forward_ref';
import {InjectionToken} from './injection_token';
import {Inject, Optional, Self, SkipSelf} from './metadata';
@ -17,7 +19,17 @@ export const SOURCE = '__source';
const _THROW_IF_NOT_FOUND = new Object();
export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
class _NullInjector implements Injector {
/**
* An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
*
* Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
* project.
*
* @experimental
*/
export const INJECTOR = new InjectionToken<Injector>('INJECTOR');
export class NullInjector implements Injector {
get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
if (notFoundValue === _THROW_IF_NOT_FOUND) {
throw new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
@ -48,7 +60,7 @@ class _NullInjector implements Injector {
*/
export abstract class Injector {
static THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
static NULL: Injector = new _NullInjector();
static NULL: Injector = new NullInjector();
/**
* Retrieves an instance from the injector based on the provided token.
@ -87,6 +99,11 @@ export abstract class Injector {
return new StaticInjector(options.providers, options.parent, options.name || null);
}
}
static ngInjectableDef = defineInjectable({
providedIn: 'any' as any,
factory: () => inject(INJECTOR),
});
}
@ -100,7 +117,7 @@ const MULTI_PROVIDER_FN = function(): any[] {
return Array.prototype.slice.call(arguments);
};
const GET_PROPERTY_NAME = {} as any;
const USE_VALUE =
export const USE_VALUE =
getClosureSafeProperty<ValueProvider>({provide: String, useValue: GET_PROPERTY_NAME});
const NG_TOKEN_PATH = 'ngTokenPath';
const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
@ -127,6 +144,8 @@ export class StaticInjector implements Injector {
const records = this._records = new Map<any, Record>();
records.set(
Injector, <Record>{token: Injector, fn: IDENT, deps: EMPTY, value: this, useNew: false});
records.set(
INJECTOR, <Record>{token: Injector, fn: IDENT, deps: EMPTY, value: this, useNew: false});
recursivelyProcessProviders(records, providers);
}

View File

@ -0,0 +1,408 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {OnDestroy} from '../metadata/lifecycle_hooks';
import {Type} from '../type';
import {stringify} from '../util';
import {InjectableDef, InjectableType, InjectorDef, InjectorType, InjectorTypeWithProviders} from './defs';
import {resolveForwardRef} from './forward_ref';
import {InjectableDefToken, InjectionToken} from './injection_token';
import {INJECTOR, InjectFlags, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, inject, injectArgs, setCurrentInjector} from './injector';
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, Provider, StaticClassProvider, TypeProvider, ValueProvider} from './provider';
import {APP_ROOT} from './scope';
/**
* Internal type for a single provider in a deep provider array.
*/
type SingleProvider = TypeProvider | ValueProvider | ClassProvider | ConstructorProvider |
ExistingProvider | FactoryProvider | StaticClassProvider;
/**
* Marker which indicates that a value has not yet been created from the factory function.
*/
const NOT_YET = {};
/**
* Marker which indicates that the factory function for a token is in the process of being called.
*
* If the injector is asked to inject a token with its value set to CIRCULAR, that indicates
* injection of a dependency has recursively attempted to inject the original token, and there is
* a circular dependency among the providers.
*/
const CIRCULAR = {};
const EMPTY_ARRAY = [] as any[];
/**
* A lazily initialized NullInjector.
*/
let NULL_INJECTOR: Injector|undefined = undefined;
function getNullInjector(): Injector {
if (NULL_INJECTOR === undefined) {
NULL_INJECTOR = new NullInjector();
}
return NULL_INJECTOR;
}
/**
* An entry in the injector which tracks information about the given token, including a possible
* current value.
*/
interface Record<T> {
factory: (() => T)|undefined;
value: T|{};
multi: any[]|undefined;
}
/**
* Create a new `Injector` which is configured using `InjectorDefType`s.
*
* @experimental
*/
export function createInjector(
defType: /* InjectorDefType<any> */ any, parent: Injector | null = null): Injector {
parent = parent || getNullInjector();
return new R3Injector(defType, parent);
}
export class R3Injector {
/**
* Map of tokens to records which contain the instances of those tokens.
*/
private records = new Map<Type<any>|InjectionToken<any>, Record<any>>();
/**
* The transitive set of `InjectorDefType`s which define this injector.
*/
private injectorDefTypes = new Set<InjectorType<any>>();
/**
* Set of values instantiated by this injector which contain `ngOnDestroy` lifecycle hooks.
*/
private onDestroy = new Set<OnDestroy>();
/**
* Flag indicating this injector provides the APP_ROOT_SCOPE token, and thus counts as the
* root scope.
*/
private readonly isRootInjector: boolean;
/**
* Flag indicating that this injector was previously destroyed.
*/
private destroyed = false;
constructor(def: InjectorType<any>, readonly parent: Injector) {
// Start off by creating Records for every provider declared in every InjectorDefType
// included transitively in `def`.
deepForEach(
[def], injectorDef => this.processInjectorType(injectorDef, new Set<InjectorType<any>>()));
// Make sure the INJECTOR token provides this injector.
this.records.set(INJECTOR, makeRecord(undefined, this));
// Detect whether this injector has the APP_ROOT_SCOPE token and thus should provide
// any injectable scoped to APP_ROOT_SCOPE.
this.isRootInjector = this.records.has(APP_ROOT);
// Eagerly instantiate the InjectorDefType classes themselves.
this.injectorDefTypes.forEach(defType => this.get(defType));
}
/**
* Destroy the injector and release references to every instance or provider associated with it.
*
* Also calls the `OnDestroy` lifecycle hooks of every instance that was created for which a
* hook was found.
*/
destroy(): void {
this.assertNotDestroyed();
// Set destroyed = true first, in case lifecycle hooks re-enter destroy().
this.destroyed = true;
try {
// Call all the lifecycle hooks.
this.onDestroy.forEach(service => service.ngOnDestroy());
} finally {
// Release all references.
this.records.clear();
this.onDestroy.clear();
this.injectorDefTypes.clear();
}
}
get<T>(
token: Type<T>|InjectionToken<T>, notFoundValue: any = THROW_IF_NOT_FOUND,
flags = InjectFlags.Default): T {
this.assertNotDestroyed();
// Set the injection context.
const previousInjector = setCurrentInjector(this);
try {
// Check for the SkipSelf flag.
if (!(flags & InjectFlags.SkipSelf)) {
// SkipSelf isn't set, check if the record belongs to this injector.
let record: Record<T>|undefined = this.records.get(token);
if (record === undefined) {
// No record, but maybe the token is scoped to this injector. Look for an ngInjectableDef
// with a scope matching this injector.
const def = couldBeInjectableType(token) &&
(token as InjectableType<any>| InjectableDefToken<any>).ngInjectableDef ||
undefined;
if (def !== undefined && this.injectableDefInScope(def)) {
// Found an ngInjectableDef and it's scoped to this injector. Pretend as if it was here
// all along.
record = injectableDefRecord(token);
this.records.set(token, record);
}
}
// If a record was found, get the instance for it and return it.
if (record !== undefined) {
return this.hydrate(token, record);
}
}
// Select the next injector based on the Self flag - if self is set, the next injector is
// the NullInjector, otherwise it's the parent.
let next = !(flags & InjectFlags.Self) ? this.parent : getNullInjector();
return this.parent.get(token, notFoundValue);
} finally {
// Lastly, clean up the state by restoring the previous injector.
setCurrentInjector(previousInjector);
}
}
private assertNotDestroyed(): void {
if (this.destroyed) {
throw new Error('Injector has already been destroyed.');
}
}
/**
* Add an `InjectorDefType` or `InjectorDefTypeWithProviders` and all of its transitive providers
* to this injector.
*/
private processInjectorType(
defOrWrappedDef: InjectorType<any>|InjectorTypeWithProviders<any>,
parents: Set<InjectorType<any>>) {
defOrWrappedDef = resolveForwardRef(defOrWrappedDef);
// Either the defOrWrappedDef is an InjectorDefType (with ngInjectorDef) or an
// InjectorDefTypeWithProviders (aka ModuleWithProviders). Detecting either is a megamorphic
// read, so care is taken to only do the read once.
// First attempt to read the ngInjectorDef.
let def = (defOrWrappedDef as InjectorType<any>).ngInjectorDef as(InjectorDef<any>| undefined);
// If that's not present, then attempt to read ngModule from the InjectorDefTypeWithProviders.
const ngModule =
(def == null) && (defOrWrappedDef as InjectorTypeWithProviders<any>).ngModule || undefined;
// Determine the InjectorDefType. In the case where `defOrWrappedDef` is an `InjectorDefType`,
// then this is easy. In the case of an InjectorDefTypeWithProviders, then the definition type
// is the `ngModule`.
const defType: InjectorType<any> =
(ngModule === undefined) ? (defOrWrappedDef as InjectorType<any>) : ngModule;
// If defOrWrappedType was an InjectorDefTypeWithProviders, then .providers may hold some
// extra providers.
const providers =
(ngModule !== undefined) && (defOrWrappedDef as InjectorTypeWithProviders<any>).providers ||
EMPTY_ARRAY;
// Finally, if defOrWrappedType was an `InjectorDefTypeWithProviders`, then the actual
// `InjectorDef` is on its `ngModule`.
if (ngModule !== undefined) {
def = ngModule.ngInjectorDef;
}
// If no definition was found, throw.
if (def == null) {
throw new Error(`Type ${stringify(defType)} is missing an ngInjectorDef definition.`);
}
// Check for circular dependencies.
if (parents.has(defType)) {
throw new Error(`Circular dependency: type ${stringify(defType)} ends up importing itself.`);
}
// Track the InjectorDefType and add a provider for it.
this.injectorDefTypes.add(defType);
this.records.set(defType, makeRecord(def.factory));
// Add providers in the same way that @NgModule resolution did:
// First, include providers from any imports.
if (def.imports != null) {
// Before processing defType's imports, add it to the set of parents. This way, if it ends
// up deeply importing itself, this can be detected.
parents.add(defType);
try {
deepForEach(def.imports, imported => this.processInjectorType(imported, parents));
} finally {
// Remove it from the parents set when finished.
parents.delete(defType);
}
}
// Next, include providers listed on the definition itself.
if (def.providers != null) {
deepForEach(def.providers, provider => this.processProvider(provider));
}
// Finally, include providers from an InjectorDefTypeWithProviders if there was one.
deepForEach(providers, provider => this.processProvider(provider));
}
/**
* Process a `SingleProvider` and add it.
*/
private processProvider(provider: SingleProvider): void {
// Determine the token from the provider. Either it's its own token, or has a {provide: ...}
// property.
provider = resolveForwardRef(provider);
let token: any = isTypeProvider(provider) ? provider : resolveForwardRef(provider.provide);
// Construct a `Record` for the provider.
const record = providerToRecord(provider);
if (!isTypeProvider(provider) && provider.multi === true) {
// If the provider indicates that it's a multi-provider, process it specially.
// First check whether it's been defined already.
let multiRecord = this.records.get(token);
if (multiRecord) {
// It has. Throw a nice error if
if (multiRecord.multi === undefined) {
throw new Error(`Mixed multi-provider for ${token}.`);
}
} else {
token = provider;
multiRecord = makeRecord(undefined, NOT_YET, true);
multiRecord.factory = () => injectArgs(multiRecord !.multi !);
this.records.set(token, multiRecord);
}
token = provider;
multiRecord.multi !.push(provider);
}
const existing = this.records.get(token);
if (existing && existing.multi !== undefined) {
throw new Error(`Mixed multi-provider for ${token}`);
}
this.records.set(token, record);
}
private hydrate<T>(token: Type<T>|InjectionToken<T>, record: Record<T>): T {
if (record.value === CIRCULAR) {
throw new Error(`Circular dep for ${stringify(token)}`);
} else if (record.value === NOT_YET) {
record.value = CIRCULAR;
record.value = record.factory !();
}
if (typeof record.value === 'object' && record.value && hasOnDestroy(record.value)) {
this.onDestroy.add(record.value);
}
return record.value as T;
}
private injectableDefInScope(def: InjectableDef<any>): boolean {
if (!def.providedIn) {
return false;
} else if (typeof def.providedIn === 'string') {
return def.providedIn === 'any' || (def.providedIn === 'root' && this.isRootInjector);
} else {
return this.injectorDefTypes.has(def.providedIn);
}
}
}
function injectableDefRecord(token: Type<any>| InjectionToken<any>): Record<any> {
const def = (token as InjectableType<any>).ngInjectableDef;
if (def === undefined) {
throw new Error(`Type ${stringify(token)} is missing an ngInjectableDef definition.`);
}
return makeRecord(def.factory);
}
function providerToRecord(provider: SingleProvider): Record<any> {
let token = resolveForwardRef(provider);
let value: any = NOT_YET;
let factory: (() => any)|undefined = undefined;
if (isTypeProvider(provider)) {
return injectableDefRecord(provider);
} else {
token = resolveForwardRef(provider.provide);
if (isValueProvider(provider)) {
value = provider.useValue;
} else if (isExistingProvider(provider)) {
factory = () => inject(provider.useExisting);
} else if (isFactoryProvider(provider)) {
factory = () => provider.useFactory(...injectArgs(provider.deps || []));
} else {
const classRef = (provider as StaticClassProvider | ClassProvider).useClass || token;
if (hasDeps(provider)) {
factory = () => new (classRef)(...injectArgs(provider.deps));
} else {
return injectableDefRecord(classRef);
}
}
}
return makeRecord(factory, value);
}
function makeRecord<T>(
factory: (() => T) | undefined, value: T | {} = NOT_YET, multi: boolean = false): Record<T> {
return {
factory: factory,
value: value,
multi: multi ? [] : undefined,
};
}
function deepForEach<T>(input: (T | any[])[], fn: (value: T) => void): void {
input.forEach(value => Array.isArray(value) ? deepForEach(value, fn) : fn(value));
}
function isValueProvider(value: SingleProvider): value is ValueProvider {
return USE_VALUE in value;
}
function isExistingProvider(value: SingleProvider): value is ExistingProvider {
return !!(value as ExistingProvider).useExisting;
}
function isFactoryProvider(value: SingleProvider): value is FactoryProvider {
return !!(value as FactoryProvider).useFactory;
}
function isClassProvider(value: SingleProvider): value is ClassProvider {
return !!(value as ClassProvider).useClass;
}
function isTypeProvider(value: SingleProvider): value is TypeProvider {
return typeof value === 'function';
}
function hasDeps(value: ClassProvider | ConstructorProvider | StaticClassProvider):
value is ClassProvider&{deps: any[]} {
return !!(value as any).deps;
}
function hasOnDestroy(value: any): value is OnDestroy {
return typeof value === 'object' && value != null && (value as OnDestroy).ngOnDestroy &&
typeof(value as OnDestroy).ngOnDestroy === 'function';
}
function couldBeInjectableType(value: any): value is Type<any>|InjectionToken<any> {
return (typeof value === 'function') ||
(typeof value === 'object' && value instanceof InjectionToken);
}

View File

@ -6,10 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Provider} from '../di';
import {InjectorDef, InjectorType, defineInjector} from '../di/defs';
import {convertInjectableProviderToFactory} from '../di/injectable';
import {Provider} from '../di/provider';
import {Type} from '../type';
import {TypeDecorator, makeDecorator} from '../util/decorators';
/**
* A wrapper around a module that also includes the providers.
*
@ -190,5 +193,17 @@ export interface NgModule {
* @stable
* @Annotation
*/
export const NgModule: NgModuleDecorator =
makeDecorator('NgModule', (ngModule: NgModule) => ngModule);
export const NgModule: NgModuleDecorator = makeDecorator(
'NgModule', (ngModule: NgModule) => ngModule, undefined, undefined,
(moduleType: InjectorType<any>, metadata: NgModule) => {
let imports = (metadata && metadata.imports) || [];
if (metadata && metadata.exports) {
imports = [...imports, metadata.exports];
}
moduleType.ngInjectorDef = defineInjector({
factory: convertInjectableProviderToFactory(moduleType, {useClass: moduleType}),
providers: metadata && metadata.providers,
imports: imports,
});
});

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectableDef} from '../di/defs';
import {resolveForwardRef} from '../di/forward_ref';
import {InjectableDef} from '../di/injectable';
import {InjectFlags, Injector, setCurrentInjector} from '../di/injector';
import {INJECTOR, InjectFlags, Injector, setCurrentInjector} from '../di/injector';
import {APP_ROOT} from '../di/scope';
import {NgModuleRef} from '../linker/ng_module_factory';
import {stringify} from '../util';

View File

@ -8,7 +8,8 @@
import {isDevMode} from '../application_ref';
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node';
import {InjectableType, Injector} from '../di';
import {Injector} from '../di';
import {InjectableType} from '../di/injectable';
import {ErrorHandler} from '../error_handler';
import {ComponentFactory} from '../linker/component_factory';
import {NgModuleRef} from '../linker/ng_module_factory';