refactor(ivy): refactor more files in DI to prepare it for bazel packages (#28098)

PR Close #28098
This commit is contained in:
Miško Hevery
2019-01-11 16:07:01 -08:00
committed by Andrew Kushnir
parent 6a9a48b0ac
commit 978ffa9d32
29 changed files with 194 additions and 112 deletions

View File

@ -7,11 +7,10 @@
*/
import {Type} from '../interface/type';
import {compileInjectable as render3CompileInjectable} from '../render3/jit/injectable';
import {TypeDecorator, makeDecorator} from '../util/decorators';
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueSansProvider,} from './interface/provider';
import {InjectableType, defineInjectable, getInjectableDef, InjectableDef} from './interface/defs';
import {InjectableDef, InjectableType, defineInjectable, getInjectableDef} from './interface/defs';
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueSansProvider} from './interface/provider';
import {compileInjectable as render3CompileInjectable} from './jit/injectable';
import {convertInjectableProviderToFactory} from './util';

View File

@ -0,0 +1,33 @@
/**
* @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 '../../interface/type';
import {inject} from '../injector_compatibility';
import {defineInjectable, defineInjector, getInjectableDef, getInjectorDef} from '../interface/defs';
/**
* A mapping of the @angular/core API surface used in generated expressions to the actual symbols.
*
* This should be kept up to date with the public exports of @angular/core.
*/
export const angularCoreDiEnv: {[name: string]: Function} = {
'defineInjectable': defineInjectable,
'defineInjector': defineInjector,
'inject': inject,
'ɵgetFactoryOf': getFactoryOf,
};
function getFactoryOf<T>(type: Type<any>): ((type: Type<T>| null) => T)|null {
const typeAny = type as any;
const def = getInjectableDef<T>(typeAny) || getInjectorDef<T>(typeAny);
if (!def || def.factory === undefined) {
return null;
}
return def.factory;
}

View File

@ -0,0 +1,100 @@
/**
* @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 {R3InjectableMetadataFacade, getCompilerFacade} from '../../compiler/compiler_facade';
import {Type} from '../../interface/type';
import {getClosureSafeProperty} from '../../util/property';
import {Injectable} from '../injectable';
import {NG_INJECTABLE_DEF} from '../interface/defs';
import {ClassSansProvider, ExistingSansProvider, FactorySansProvider, ValueProvider, ValueSansProvider} from '../interface/provider';
import {angularCoreDiEnv} from './environment';
import {convertDependencies, reflectDependencies} from './util';
/**
* Compile an Angular injectable according to its `Injectable` metadata, and patch the resulting
* `ngInjectableDef` onto the injectable type.
*/
export function compileInjectable(type: Type<any>, srcMeta?: Injectable): void {
let def: any = null;
// if NG_INJECTABLE_DEF is already defined on this class then don't overwrite it
if (type.hasOwnProperty(NG_INJECTABLE_DEF)) return;
Object.defineProperty(type, NG_INJECTABLE_DEF, {
get: () => {
if (def === null) {
// Allow the compilation of a class with a `@Injectable()` decorator without parameters
const meta: Injectable = srcMeta || {providedIn: null};
const hasAProvider = isUseClassProvider(meta) || isUseFactoryProvider(meta) ||
isUseValueProvider(meta) || isUseExistingProvider(meta);
const compilerMeta: R3InjectableMetadataFacade = {
name: type.name,
type: type,
typeArgumentCount: 0,
providedIn: meta.providedIn,
ctorDeps: reflectDependencies(type),
userDeps: undefined
};
if ((isUseClassProvider(meta) || isUseFactoryProvider(meta)) && meta.deps !== undefined) {
compilerMeta.userDeps = convertDependencies(meta.deps);
}
if (!hasAProvider) {
// In the case the user specifies a type provider, treat it as {provide: X, useClass: X}.
// The deps will have been reflected above, causing the factory to create the class by
// calling
// its constructor with injected deps.
compilerMeta.useClass = type;
} else if (isUseClassProvider(meta)) {
// The user explicitly specified useClass, and may or may not have provided deps.
compilerMeta.useClass = meta.useClass;
} else if (isUseValueProvider(meta)) {
// The user explicitly specified useValue.
compilerMeta.useValue = meta.useValue;
} else if (isUseFactoryProvider(meta)) {
// The user explicitly specified useFactory.
compilerMeta.useFactory = meta.useFactory;
} else if (isUseExistingProvider(meta)) {
// The user explicitly specified useExisting.
compilerMeta.useExisting = meta.useExisting;
} else {
// Can't happen - either hasAProvider will be false, or one of the providers will be set.
throw new Error(`Unreachable state.`);
}
def = getCompilerFacade().compileInjectable(
angularCoreDiEnv, `ng://${type.name}/ngInjectableDef.js`, compilerMeta);
}
return def;
},
});
}
type UseClassProvider = Injectable & ClassSansProvider & {deps?: any[]};
const USE_VALUE =
getClosureSafeProperty<ValueProvider>({provide: String, useValue: getClosureSafeProperty});
function isUseClassProvider(meta: Injectable): meta is UseClassProvider {
return (meta as UseClassProvider).useClass !== undefined;
}
function isUseValueProvider(meta: Injectable): meta is Injectable&ValueSansProvider {
return USE_VALUE in meta;
}
function isUseFactoryProvider(meta: Injectable): meta is Injectable&FactorySansProvider {
return (meta as FactorySansProvider).useFactory !== undefined;
}
function isUseExistingProvider(meta: Injectable): meta is Injectable&ExistingSansProvider {
return (meta as ExistingSansProvider).useExisting !== undefined;
}

View File

@ -0,0 +1,77 @@
/**
* @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 {CompilerFacade, R3DependencyMetadataFacade, getCompilerFacade} from '../../compiler/compiler_facade';
import {Type} from '../../interface/type';
import {ReflectionCapabilities} from '../../reflection/reflection_capabilities';
import {Attribute, Host, Inject, Optional, Self, SkipSelf} from '../metadata';
let _reflect: ReflectionCapabilities|null = null;
export function getReflect(): ReflectionCapabilities {
return (_reflect = _reflect || new ReflectionCapabilities());
}
export function reflectDependencies(type: Type<any>): R3DependencyMetadataFacade[] {
return convertDependencies(getReflect().parameters(type));
}
export function convertDependencies(deps: any[]): R3DependencyMetadataFacade[] {
const compiler = getCompilerFacade();
return deps.map(dep => reflectDependency(compiler, dep));
}
function reflectDependency(compiler: CompilerFacade, dep: any | any[]): R3DependencyMetadataFacade {
const meta: R3DependencyMetadataFacade = {
token: null,
host: false,
optional: false,
resolved: compiler.R3ResolvedDependencyType.Token,
self: false,
skipSelf: false,
};
function setTokenAndResolvedType(token: any): void {
meta.resolved = compiler.R3ResolvedDependencyType.Token;
meta.token = token;
}
if (Array.isArray(dep)) {
if (dep.length === 0) {
throw new Error('Dependency array must have arguments.');
}
for (let j = 0; j < dep.length; j++) {
const param = dep[j];
if (param === undefined) {
// param may be undefined if type of dep is not set by ngtsc
continue;
} else if (param instanceof Optional || param.__proto__.ngMetadataName === 'Optional') {
meta.optional = true;
} else if (param instanceof SkipSelf || param.__proto__.ngMetadataName === 'SkipSelf') {
meta.skipSelf = true;
} else if (param instanceof Self || param.__proto__.ngMetadataName === 'Self') {
meta.self = true;
} else if (param instanceof Host || param.__proto__.ngMetadataName === 'Host') {
meta.host = true;
} else if (param instanceof Inject) {
meta.token = param.token;
} else if (param instanceof Attribute) {
if (param.attributeName === undefined) {
throw new Error(`Attribute name must be defined.`);
}
meta.token = param.attributeName;
meta.resolved = compiler.R3ResolvedDependencyType.Attribute;
} else {
setTokenAndResolvedType(param);
}
}
} else {
setTokenAndResolvedType(dep);
}
return meta;
}

View File

@ -206,3 +206,67 @@ export interface Host {}
* @publicApi
*/
export const Host: HostDecorator = makeParamDecorator('Host');
/**
* Type of the Attribute decorator / constructor function.
*
* @publicApi
*/
export interface AttributeDecorator {
/**
* Specifies that a constant attribute value should be injected.
*
* The directive can inject constant string literals of host element attributes.
*
* @usageNotes
* ### Example
*
* Suppose we have an `<input>` element and want to know its `type`.
*
* ```html
* <input type="text">
* ```
*
* A decorator can inject string literal `text` like so:
*
* {@example core/ts/metadata/metadata.ts region='attributeMetadata'}
*
* ### Example as TypeScript Decorator
*
* {@example core/ts/metadata/metadata.ts region='attributeFactory'}
*
* ### Example as ES5 annotation
*
* ```
* var MyComponent = function(title) {
* ...
* };
*
* MyComponent.annotations = [
* new ng.Component({...})
* ]
* MyComponent.parameters = [
* [new ng.Attribute('title')]
* ]
* ```
*/
(name: string): any;
new (name: string): Attribute;
}
/**
* Type of the Attribute metadata.
*
* @publicApi
*/
export interface Attribute { attributeName?: string; }
/**
* Attribute decorator and metadata.
*
* @Annotation
* @publicApi
*/
export const Attribute: AttributeDecorator =
makeParamDecorator('Attribute', (attributeName?: string) => ({attributeName}));

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {OnDestroy} from '../interface/lifecycle_hooks';
import {Type} from '../interface/type';
import {OnDestroy} from '../metadata/lifecycle_hooks';
import {stringify} from '../util/stringify';
import {resolveForwardRef} from './forward_ref';

View File

@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {wrappedError} from '../error_handler';
import {Type} from '../interface/type';
import {ERROR_ORIGINAL_ERROR} from '../util/errors';
import {ERROR_ORIGINAL_ERROR, wrappedError} from '../util/errors';
import {stringify} from '../util/stringify';
import {ReflectiveInjector} from './reflective_injector';