chore(build): migrated di to TypeScript

This commit is contained in:
vsavkin 2015-04-24 15:19:11 -07:00
parent 649e276610
commit cb87fa0970
22 changed files with 318 additions and 332 deletions

View File

@ -10,6 +10,18 @@ export * from './src/di/decorators';
export {Injector} from './src/di/injector';
export {Binding, ResolvedBinding, Dependency, bind} from './src/di/binding';
export {Key, KeyRegistry} from './src/di/key';
export {KeyMetadataError, NoBindingError, AbstractBindingError, AsyncBindingError, CyclicDependencyError,
InstantiationError, InvalidBindingError, NoAnnotationError} from './src/di/exceptions';
export {
NoBindingError,
AbstractBindingError,
AsyncBindingError,
CyclicDependencyError,
InstantiationError,
InvalidBindingError,
NoAnnotationError
} from './src/di/exceptions';
export {OpaqueToken} from './src/di/opaque_token';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;

View File

@ -1,6 +1,7 @@
import {ProtoRecord} from './proto_record';
import {BaseException} from "angular2/src/facade/lang";
export class ExpressionChangedAfterItHasBeenChecked extends Error {
export class ExpressionChangedAfterItHasBeenChecked extends BaseException {
message:string;
constructor(proto:ProtoRecord, change:any) {
@ -14,7 +15,7 @@ export class ExpressionChangedAfterItHasBeenChecked extends Error {
}
}
export class ChangeDetectionError extends Error {
export class ChangeDetectionError extends BaseException {
message:string;
originalException:any;
location:string;

View File

@ -1,6 +1,6 @@
import {Injectable} from 'angular2/src/di/annotations_impl';
import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection";
import {int, NumberWrapper, StringJoiner, StringWrapper} from "angular2/src/facade/lang";
import {int, NumberWrapper, StringJoiner, StringWrapper, BaseException} from "angular2/src/facade/lang";
export const TOKEN_TYPE_CHARACTER = 1;
export const TOKEN_TYPE_IDENTIFIER = 2;
@ -176,7 +176,7 @@ export const $RBRACE = 125;
const $NBSP = 160;
export class ScannerError extends Error {
export class ScannerError extends BaseException {
message:string;
constructor(message) {
super();

View File

@ -966,7 +966,7 @@ export class ElementInjector extends TreeNode {
}
}
class OutOfBoundsAccess extends Error {
class OutOfBoundsAccess extends BaseException {
message:string;
constructor(index) {
super();
@ -978,7 +978,7 @@ class OutOfBoundsAccess extends Error {
}
}
class QueryError extends Error {
class QueryError extends BaseException {
message:string;
// TODO(rado): pass the names of the active directives.
constructor() {

View File

@ -10,4 +10,4 @@ export {
Optional as OptionalAnnotation,
Injectable as InjectableAnnotation,
DependencyAnnotation, // abstract base class, does not need a decorator
} from './annotations_impl';
} from './annotations_impl';

View File

@ -1,5 +1,10 @@
import {CONST} from "angular2/src/facade/lang";
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
/**
* A parameter annotation that specifies a dependency.
*
@ -11,12 +16,10 @@ import {CONST} from "angular2/src/facade/lang";
*
* @exportedAs angular2/di_annotations
*/
@CONST()
export class Inject {
token;
@CONST()
constructor(token) {
this.token = token;
}
constructor(public token) {}
}
/**
@ -32,12 +35,9 @@ export class Inject {
*
* @exportedAs angular2/di_annotations
*/
@CONST()
export class InjectPromise {
token;
@CONST()
constructor(token) {
this.token = token;
}
constructor(public token) {}
}
/**
@ -53,17 +53,14 @@ export class InjectPromise {
*
* @exportedAs angular2/di_annotations
*/
@CONST()
export class InjectLazy {
token;
@CONST()
constructor(token) {
this.token = token;
}
constructor(public token) {}
}
/**
* A parameter annotation that marks a dependency as optional. {@link Injector} provides `null` if the dependency is not
* found.
* A parameter annotation that marks a dependency as optional. {@link Injector} provides `null` if
* the dependency is not found.
*
* ```
* class AComponent {
@ -75,16 +72,15 @@ export class InjectLazy {
*
* @exportedAs angular2/di_annotations
*/
@CONST()
export class Optional {
@CONST()
constructor() {
}
}
/**
* `DependencyAnnotation` is used by the framework to extend DI.
*
* Only annotations implementing `DependencyAnnotation` are added to the list of dependency properties.
* Only annotations implementing `DependencyAnnotation` are added to the list of dependency
* properties.
*
* For example:
*
@ -108,19 +104,14 @@ export class Optional {
*
* @exportedAs angular2/di_annotations
*/
@CONST()
export class DependencyAnnotation {
@CONST()
constructor() {
}
get token() {
return null;
}
get token() { return null; }
}
/**
* A marker annotation that marks a class as available to `Injector` for creation. Used by tooling for
* generating constructor stubs.
* A marker annotation that marks a class as available to `Injector` for creation. Used by tooling
* for generating constructor stubs.
*
* ```
* class NeedsService {
@ -132,8 +123,6 @@ export class DependencyAnnotation {
* ```
* @exportedAs angular2/di_annotations
*/
@CONST()
export class Injectable {
@CONST()
constructor() {
}
}

View File

@ -2,20 +2,21 @@ import {Type, isBlank, isPresent, CONST} from 'angular2/src/facade/lang';
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {reflector} from 'angular2/src/reflection/reflection';
import {Key} from './key';
import {Inject, InjectLazy, InjectPromise, Optional, DependencyAnnotation} from './annotations_impl';
import {
Inject,
InjectLazy,
InjectPromise,
Optional,
DependencyAnnotation
} from './annotations_impl';
import {NoAnnotationError} from './exceptions';
/**
* @private
*/
export class Dependency {
key:Key;
asPromise:boolean;
lazy:boolean;
optional:boolean;
properties:List;
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean, properties:List) {
constructor(public key: Key, public asPromise: boolean, public lazy: boolean,
public optional: boolean, public properties: List<any>) {
this.key = key;
this.asPromise = asPromise;
this.lazy = lazy;
@ -23,9 +24,7 @@ export class Dependency {
this.properties = properties;
}
static fromKey(key:Key) {
return new Dependency(key, false, false, false, []);
}
static fromKey(key: Key) { return new Dependency(key, false, false, false, []); }
}
var _EMPTY_LIST = []; // TODO: make const when supported
@ -47,8 +46,8 @@ var _EMPTY_LIST = []; // TODO: make const when supported
*
* @exportedAs angular2/di
*/
@CONST()
export class Binding {
/**
* Token used when retrieving this binding. Usually the `Type`.
*/
@ -59,7 +58,8 @@ export class Binding {
*
* ## Example
*
* Becuse `toAlias` and `toClass` are often confused, the example contains both use cases for easy comparison.
* Becuse `toAlias` and `toClass` are often confused, the example contains both use cases for easy
* comparison.
*
* ```javascript
*
@ -83,7 +83,7 @@ export class Binding {
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
* ```
*/
toClass:Type;
toClass: Type;
/**
* Binds a key to a value.
@ -103,12 +103,13 @@ export class Binding {
/**
* Binds a key to the alias for an existing key.
*
* An alias means that {@link Injector} returns the same instance as if the alias token was used. This is in contrast to
* `toClass` where a separate instance of `toClass` is returned.
* An alias means that {@link Injector} returns the same instance as if the alias token was used.
* This is in contrast to `toClass` where a separate instance of `toClass` is returned.
*
* ## Example
*
* Becuse `toAlias` and `toClass` are often confused the example contains both use cases for easy comparison.
* Becuse `toAlias` and `toClass` are often confused the example contains both use cases for easy
* comparison.
*
* ```javascript
*
@ -150,7 +151,7 @@ export class Binding {
* expect(injector.get(String)).toEqual('Value: 3');
* ```
*/
toFactory:Function;
toFactory: Function;
/**
* Binds a key to a function which computes the value asynchronously.
@ -170,17 +171,19 @@ export class Binding {
* injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3'));
* ```
*
* The interesting thing to note is that event though `Number` has an async factory, the `String` factory
* function takes the resolved value. This shows that the {@link Injector} delays executing the `String` factory
* until after the `Number` is resolved. This can only be done if the `token` is retrieved using the
* `asyncGet` API in the {@link Injector}.
* The interesting thing to note is that event though `Number` has an async factory, the `String`
* factory function takes the resolved value. This shows that the {@link Injector} delays
*executing the
*`String` factory
* until after the `Number` is resolved. This can only be done if the `token` is retrieved using
* the `asyncGet` API in the {@link Injector}.
*
*/
toAsyncFactory:Function;
toAsyncFactory: Function;
/**
* Used in conjunction with `toFactory` or `toAsyncFactory` and specifies a set of dependencies (as `token`s) which
* should be injected into the factory function.
* Used in conjunction with `toFactory` or `toAsyncFactory` and specifies a set of dependencies
* (as `token`s) which should be injected into the factory function.
*
* ## Example
*
@ -195,19 +198,11 @@ export class Binding {
* expect(injector.get(String)).toEqual('Value: 3');
* ```
*/
dependencies:List;
dependencies: List<any>;
@CONST()
constructor(
token,
{
toClass,
toValue,
toAlias,
toFactory,
toAsyncFactory,
deps
}) {
constructor(token, {toClass, toValue, toAlias, toFactory, toAsyncFactory, deps}: {
toClass ?: Type, toValue ?: any, toAlias ?: any, toFactory ?: Function,
toAsyncFactory ?: Function, deps ?: List<any>}) {
this.token = token;
this.toClass = toClass;
this.toValue = toValue;
@ -220,10 +215,11 @@ export class Binding {
/**
* Converts the {@link Binding} into {@link ResolvedBinding}.
*
* {@link Injector} internally only uses {@link ResolvedBinding}, {@link Binding} contains convenience binding syntax.
* {@link Injector} internally only uses {@link ResolvedBinding}, {@link Binding} contains
* convenience binding syntax.
*/
resolve(): ResolvedBinding {
var factoryFn:Function;
var factoryFn: Function;
var resolvedDeps;
var isAsync = false;
if (isPresent(this.toClass)) {
@ -244,20 +240,16 @@ export class Binding {
resolvedDeps = _EMPTY_LIST;
}
return new ResolvedBinding(
Key.get(this.token),
factoryFn,
resolvedDeps,
isAsync
);
return new ResolvedBinding(Key.get(this.token), factoryFn, resolvedDeps, isAsync);
}
}
/**
* An internal resolved representation of a {@link Binding} used by the {@link Injector}.
*
* A {@link Binding} is resolved when it has a factory function. Binding to a class, alias, or value, are just convenience
* methods, as {@link Injector} only operates on calling factory functions.
* A {@link Binding} is resolved when it has a factory function. Binding to a class, alias, or
* value, are just convenience methods, as {@link Injector} only operates on calling factory
* functions.
*
* @exportedAs angular2/di
*/
@ -265,24 +257,25 @@ export class ResolvedBinding {
/**
* A key, usually a `Type`.
*/
key:Key;
key: Key;
/**
* Factory function which can return an instance of an object represented by a key.
*/
factory:Function;
factory: Function;
/**
* Arguments (dependencies) to the `factory` function.
*/
dependencies:List<Dependency>;
dependencies: List<Dependency>;
/**
* Specifies whether the `factory` function returns a `Promise`.
*/
providedAsPromise:boolean;
providedAsPromise: boolean;
constructor(key:Key, factory:Function, dependencies:List<Dependency>, providedAsPromise:boolean) {
constructor(key: Key, factory: Function, dependencies: List<Dependency>,
providedAsPromise: boolean) {
this.key = key;
this.factory = factory;
this.dependencies = dependencies;
@ -304,7 +297,7 @@ export class ResolvedBinding {
*
* @exportedAs angular2/di
*/
export function bind(token):BindingBuilder {
export function bind(token): BindingBuilder {
return new BindingBuilder(token);
}
@ -316,16 +309,15 @@ export function bind(token):BindingBuilder {
export class BindingBuilder {
token;
constructor(token) {
this.token = token;
}
constructor(token) { this.token = token; }
/**
* Binds an interface to an implementation / subclass.
*
* ## Example
*
* Because `toAlias` and `toClass` are often confused, the example contains both use cases for easy comparison.
* Because `toAlias` and `toClass` are often confused, the example contains both use cases for
* easy comparison.
*
* ```javascript
*
@ -349,9 +341,7 @@ export class BindingBuilder {
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
* ```
*/
toClass(type:Type):Binding {
return new Binding(this.token, {toClass: type});
}
toClass(type: Type): Binding { return new Binding(this.token, {toClass: type}); }
/**
* Binds a key to a value.
@ -366,19 +356,18 @@ export class BindingBuilder {
* expect(injector.get(String)).toEqual('Hello');
* ```
*/
toValue(value):Binding {
return new Binding(this.token, {toValue: value});
}
toValue(value): Binding { return new Binding(this.token, {toValue: value}); }
/**
* Binds a key to the alias for an existing key.
*
* An alias means that we will return the same instance as if the alias token was used. (This is in contrast to
* `toClass` where a separet instance of `toClass` will be returned.)
* An alias means that we will return the same instance as if the alias token was used. (This is
* in contrast to `toClass` where a separet instance of `toClass` will be returned.)
*
* ## Example
*
* Becuse `toAlias` and `toClass` are often confused, the example contains both use cases for easy comparison.
* Becuse `toAlias` and `toClass` are often confused, the example contains both use cases for easy
* comparison.
*
* ```javascript
*
@ -402,9 +391,7 @@ export class BindingBuilder {
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
* ```
*/
toAlias(aliasToken):Binding {
return new Binding(this.token, {toAlias: aliasToken});
}
toAlias(aliasToken): Binding { return new Binding(this.token, {toAlias: aliasToken}); }
/**
* Binds a key to a function which computes the value.
@ -421,11 +408,8 @@ export class BindingBuilder {
* expect(injector.get(String)).toEqual('Value: 3');
* ```
*/
toFactory(factoryFunction:Function, dependencies:List = null):Binding {
return new Binding(this.token, {
toFactory: factoryFunction,
deps: dependencies
});
toFactory(factoryFunction: Function, dependencies?: List<any>): Binding {
return new Binding(this.token, {toFactory: factoryFunction, deps: dependencies});
}
/**
@ -445,26 +429,24 @@ export class BindingBuilder {
* injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3'));
* ```
*
* The interesting thing to note is that event though `Number` has an async factory, the `String` factory
* function takes the resolved value. This shows that the {@link Injector} delays executing of the `String` factory
* until after the `Number` is resolved. This can only be done if the `token` is retrieved using the
* The interesting thing to note is that event though `Number` has an async factory, the `String`
* factory function takes the resolved value. This shows that the {@link Injector} delays
* executing of the `String` factory
* until after the `Number` is resolved. This can only be done if the `token` is retrieved using
* the `asyncGet` API in the {@link Injector}.
*/
toAsyncFactory(factoryFunction:Function, dependencies:List = null):Binding {
return new Binding(this.token, {
toAsyncFactory: factoryFunction,
deps: dependencies
});
toAsyncFactory(factoryFunction: Function, dependencies?: List<any>): Binding {
return new Binding(this.token, {toAsyncFactory: factoryFunction, deps: dependencies});
}
}
function _constructDependencies(factoryFunction:Function, dependencies:List) {
function _constructDependencies(factoryFunction: Function, dependencies: List<any>) {
return isBlank(dependencies) ?
_dependenciesFor(factoryFunction) :
ListWrapper.map(dependencies, (t) => Dependency.fromKey(Key.get(t)));
_dependenciesFor(factoryFunction) :
ListWrapper.map(dependencies, (t) => Dependency.fromKey(Key.get(t)));
}
function _dependenciesFor(typeOrFunc):List {
function _dependenciesFor(typeOrFunc): List<any> {
var params = reflector.parameters(typeOrFunc);
if (isBlank(params)) return [];
if (ListWrapper.any(params, (p) => isBlank(p))) throw new NoAnnotationError(typeOrFunc);
@ -500,10 +482,9 @@ function _extractToken(typeOrFunc, annotations) {
} else if (paramAnnotation instanceof DependencyAnnotation) {
if (isPresent(paramAnnotation.token)) {
token = paramAnnotation.token;
token = paramAnnotation.token;
}
ListWrapper.push(depProps, paramAnnotation);
}
}
@ -514,6 +495,6 @@ function _extractToken(typeOrFunc, annotations) {
}
}
function _createDependency(token, asPromise, lazy, optional, depProps):Dependency {
function _createDependency(token, asPromise, lazy, optional, depProps): Dependency {
return new Dependency(Key.get(token), asPromise, lazy, optional, depProps);
}

View File

@ -3,7 +3,8 @@ import {
InjectPromiseAnnotation,
InjectLazyAnnotation,
OptionalAnnotation,
InjectableAnnotation} from './annotations';
InjectableAnnotation
} from './annotations';
import {makeDecorator, makeParamDecorator} from '../util/decorators';
export var Inject = makeParamDecorator(InjectAnnotation);

View File

@ -1,9 +1,9 @@
import {ListWrapper, List} from 'angular2/src/facade/collection';
import {stringify} from 'angular2/src/facade/lang';
import {stringify, BaseException} from 'angular2/src/facade/lang';
function findFirstClosedCycle(keys:List):List {
function findFirstClosedCycle(keys: List<any>): List<any> {
var res = [];
for(var i = 0; i < keys.length; ++i) {
for (var i = 0; i < keys.length; ++i) {
if (ListWrapper.contains(res, keys[i])) {
ListWrapper.push(res, keys[i]);
return res;
@ -14,7 +14,7 @@ function findFirstClosedCycle(keys:List):List {
return res;
}
function constructResolvingPath(keys:List):string {
function constructResolvingPath(keys: List<any>): string {
if (keys.length > 1) {
var reversed = findFirstClosedCycle(ListWrapper.reversed(keys));
var tokenStrs = ListWrapper.map(reversed, (k) => stringify(k.token));
@ -30,12 +30,13 @@ function constructResolvingPath(keys:List):string {
*
* @exportedAs angular2/di_errors
*/
export class AbstractBindingError extends Error {
keys:List;
constructResolvingMessage:Function;
message;
export class AbstractBindingError extends BaseException {
name: string;
message: string;
keys: List<any>;
constructResolvingMessage: Function;
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
constructor(key, constructResolvingMessage:Function) {
constructor(key, constructResolvingMessage: Function) {
super();
this.keys = [key];
this.constructResolvingMessage = constructResolvingMessage;
@ -43,26 +44,24 @@ export class AbstractBindingError extends Error {
}
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
addKey(key):void {
addKey(key): void {
ListWrapper.push(this.keys, key);
this.message = this.constructResolvingMessage(this.keys);
}
toString():string {
return this.message;
}
toString(): string { return this.message; }
}
/**
* Thrown when trying to retrieve a dependency by `Key` from {@link Injector}, but the {@link Injector} does not have a
* {@link Binding} for {@link Key}.
* Thrown when trying to retrieve a dependency by `Key` from {@link Injector}, but the
* {@link Injector} does not have a {@link Binding} for {@link Key}.
*
* @exportedAs angular2/di_errors
*/
export class NoBindingError extends AbstractBindingError {
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
constructor(key) {
super(key, function (keys:List) {
super(key, function (keys:List<any>) {
var first = stringify(ListWrapper.first(keys).token);
return `No provider for ${first}!${constructResolvingPath(keys)}`;
});
@ -88,18 +87,17 @@ export class NoBindingError extends AbstractBindingError {
* }).toThrowError(AsycBindingError);
* ```
*
* The above example throws because `String` depends on `Number` which is async. If any binding in the dependency
* graph is async then the graph can only be retrieved using the `asyncGet` API.
* The above example throws because `String` depends on `Number` which is async. If any binding in
* the dependency graph is async then the graph can only be retrieved using the `asyncGet` API.
*
* @exportedAs angular2/di_errors
*/
export class AsyncBindingError extends AbstractBindingError {
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
constructor(key) {
super(key, function (keys:List) {
super(key, function (keys:List<any>) {
var first = stringify(ListWrapper.first(keys).token);
return `Cannot instantiate ${first} synchronously. ` +
`It is provided as a promise!${constructResolvingPath(keys)}`;
return `Cannot instantiate ${first} synchronously. It is provided as a promise!${constructResolvingPath(keys)}`;
});
}
}
@ -125,7 +123,7 @@ export class AsyncBindingError extends AbstractBindingError {
export class CyclicDependencyError extends AbstractBindingError {
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
constructor(key) {
super(key, function (keys:List) {
super(key, function (keys:List<any>) {
return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`;
});
}
@ -134,8 +132,8 @@ export class CyclicDependencyError extends AbstractBindingError {
/**
* Thrown when a constructing type returns with an Error.
*
* The `InstantiationError` class contains the original error plus the dependency graph which caused this object to be
* instantiated.
* The `InstantiationError` class contains the original error plus the dependency graph which caused
* this object to be instantiated.
*
* @exportedAs angular2/di_errors
*/
@ -144,10 +142,9 @@ export class InstantiationError extends AbstractBindingError {
causeKey;
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
constructor(cause, key) {
super(key, function (keys:List) {
super(key, function (keys:List<any>) {
var first = stringify(ListWrapper.first(keys).token);
return `Error during instantiation of ${first}!${constructResolvingPath(keys)}.` +
` ORIGINAL ERROR: ${cause}`;
return `Error during instantiation of ${first}!${constructResolvingPath(keys)}. ORIGINAL ERROR: ${cause}`;
});
this.cause = cause;
this.causeKey = key;
@ -155,39 +152,38 @@ export class InstantiationError extends AbstractBindingError {
}
/**
* Thrown when an object other then {@link Binding} (or `Type`) is passed to {@link Injector} creation.
* Thrown when an object other then {@link Binding} (or `Type`) is passed to {@link Injector}
* creation.
*
* @exportedAs angular2/di_errors
*/
export class InvalidBindingError extends Error {
message:string;
export class InvalidBindingError extends BaseException {
message: string;
constructor(binding) {
super();
this.message = `Invalid binding - only instances of Binding and Type are allowed, got: ${binding}`;
this.message = "Invalid binding - only instances of Binding and Type are allowed, got: " +
binding.toString();
}
toString():string {
return this.message;
}
toString(): string { return this.message; }
}
/**
* Thrown when the class has no annotation information.
*
* Lack of annotation information prevents the {@link Injector} from determining which dependencies need to be injected into
* the constructor.
* Lack of annotation information prevents the {@link Injector} from determining which dependencies
* need to be injected into the constructor.
*
* @exportedAs angular2/di_errors
*/
export class NoAnnotationError extends Error {
message:string;
export class NoAnnotationError extends BaseException {
name: string;
message: string;
constructor(typeOrFunc) {
super();
this.message = `Cannot resolve all parameters for ${stringify(typeOrFunc)}.` +
` Make sure they all have valid type or annotations.`;
this.message = "Cannot resolve all parameters for " + stringify(typeOrFunc) + ". " +
'Make sure they all have valid type or annotations.';
}
toString():string {
return this.message;
}
toString(): string { return this.message; }
}

View File

@ -1,31 +1,37 @@
/// <reference path="../../typings/es6-promise/es6-promise.d.ts" />
import {Map, List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {ResolvedBinding, Binding, BindingBuilder, bind} from './binding';
import {AbstractBindingError, NoBindingError, AsyncBindingError, CyclicDependencyError,
InstantiationError, InvalidBindingError} from './exceptions';
import {
AbstractBindingError,
NoBindingError,
AsyncBindingError,
CyclicDependencyError,
InstantiationError,
InvalidBindingError
} from './exceptions';
import {FunctionWrapper, Type, isPresent, isBlank} from 'angular2/src/facade/lang';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {Key} from './key';
var _constructing = new Object();
var _notFound = new Object();
class _Waiting {
promise:Promise;
constructor(promise:Promise) {
this.promise = promise;
}
promise: Promise<any>;
constructor(promise: Promise<any>) { this.promise = promise; }
}
function _isWaiting(obj):boolean {
function _isWaiting(obj): boolean {
return obj instanceof _Waiting;
}
/**
* A dependency injection container used for resolving dependencies.
*
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the constructor dependencies.
* In typical use, application code asks for the dependencies in the constructor and they are resolved by the
* `Injector`.
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the
* constructor dependencies.
* In typical use, application code asks for the dependencies in the constructor and they are
* resolved by the `Injector`.
*
* ## Example:
*
@ -36,14 +42,15 @@ function _isWaiting(obj):boolean {
* }
*
* class Car {
* constructor(@Inject(Engine) engine) {
* }
* constructor(@Inject(Engine) engine) {
* }
* }
*
* ```
*
* Next we need to write the code that creates and instantiates the `Injector`. We then ask for the `root` object,
* `Car`, so that the `Injector` can recursively build all of that object's dependencies.
* Next we need to write the code that creates and instantiates the `Injector`. We then ask for the
* `root` object, `Car`, so that the `Injector` can recursively build all of that object's
*dependencies.
*
* ```javascript
* main() {
@ -53,71 +60,79 @@ function _isWaiting(obj):boolean {
* var car = injector.get(Car);
* }
* ```
* Notice that we don't use the `new` operator because we explicitly want to have the `Injector` resolve all of the
* object's dependencies automatically.
* Notice that we don't use the `new` operator because we explicitly want to have the `Injector`
* resolve all of the object's dependencies automatically.
*
* @exportedAs angular2/di
*/
export class Injector {
_bindings:List;
_instances:List;
_parent:Injector;
_defaultBindings:boolean;
_bindings: List<any>;
_instances: List<any>;
_parent: Injector;
_defaultBindings: boolean;
_asyncStrategy: _AsyncInjectorStrategy;
_syncStrategy:_SyncInjectorStrategy;
_syncStrategy: _SyncInjectorStrategy;
/**
* Turns a list of binding definitions into an internal resolved list of resolved bindings.
*
* A resolution is a process of flattening multiple nested lists and converting individual bindings into a
* list of {@link ResolvedBinding}s. The resolution can be cached by `resolve` for the {@link Injector} for
* performance-sensitive code.
* A resolution is a process of flattening multiple nested lists and converting individual
* bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve`
* for the {@link Injector} for performance-sensitive code.
*
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a recursive
* list of more bindings.
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
* recursive list of more bindings.
*
* The returned list is sparse, indexed by `id` for the {@link Key}. It is generally not useful to application code
* other than for passing it to {@link Injector} functions that require resolved binding lists, such as
* The returned list is sparse, indexed by `id` for the {@link Key}. It is generally not useful to
*application code
* other than for passing it to {@link Injector} functions that require resolved binding lists,
*such as
* `fromResolvedBindings` and `createChildFromResolved`.
*/
static resolve(bindings:List/*<ResolvedBinding|Binding|Type|List>*/):List<ResolvedBinding> {
static resolve(bindings: List<any>): List<ResolvedBinding> {
var resolvedBindings = _resolveBindings(bindings);
var flatten = _flattenBindings(resolvedBindings, MapWrapper.create());
return _createListOfBindings(flatten);
}
/**
* Resolves bindings and creates an injector based on those bindings. This function is slower than the
* corresponding `fromResolvedBindings` because it needs to resolve bindings first. See `resolve` for the
* {@link Injector}.
* Resolves bindings and creates an injector based on those bindings. This function is slower than
* the corresponding `fromResolvedBindings` because it needs to resolve bindings first. See
*`resolve`
* for the {@link Injector}.
*
* Prefer `fromResolvedBindings` in performance-critical code that creates lots of injectors.
*
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a recursive list of more
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
*recursive list of more
* bindings.
* @param `defaultBindings` Setting to true will auto-create bindings.
*/
static resolveAndCreate(bindings:List/*<ResolvedBinding|Binding|Type|List>*/, {defaultBindings=false}={}): Injector {
static resolveAndCreate(bindings: List<any>, {defaultBindings = false}: any = {}): Injector {
return new Injector(Injector.resolve(bindings), null, defaultBindings);
}
/**
* Creates an injector from previously resolved bindings. This bypasses resolution and flattening. This API is the
* recommended way to construct injectors in performance-sensitive parts.
* Creates an injector from previously resolved bindings. This bypasses resolution and flattening.
* This API is the recommended way to construct injectors in performance-sensitive parts.
*
* @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the {@link Injector}.
* @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the {@link
*Injector}.
* @param `defaultBindings` Setting to true will auto-create bindings.
*/
static fromResolvedBindings(bindings:List<ResolvedBinding>, {defaultBindings=false}={}): Injector {
static fromResolvedBindings(bindings: List<ResolvedBinding>,
{defaultBindings = false}: any = {}): Injector {
return new Injector(bindings, null, defaultBindings);
}
/**
* @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the {@link Injector}.
* @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the {@link
* Injector}.
* @param `parent` Parent Injector or `null` if root Injector.
* @param `defaultBindings` Setting to true will auto-create bindings. (Only use with root injector.)
* @param `defaultBindings` Setting to true will auto-create bindings. (Only use with root
* injector.)
*/
constructor(bindings:List<ResolvedBinding>, parent:Injector, defaultBindings:boolean) {
constructor(bindings: List<ResolvedBinding>, parent: Injector, defaultBindings: boolean) {
this._bindings = bindings;
this._instances = this._createInstances();
this._parent = parent;
@ -129,12 +144,11 @@ export class Injector {
/**
* Retrieves an instance from the injector.
*
* @param `token`: usually the `Type` of an object. (Same as the token used while setting up a binding).
* @param `token`: usually the `Type` of an object. (Same as the token used while setting up a
*binding).
* @returns an instance represented by the token. Throws if not found.
*/
get(token) {
return this._getByKey(Key.get(token), false, false, false);
}
get(token) { return this._getByKey(Key.get(token), false, false, false); }
/**
@ -143,9 +157,7 @@ export class Injector {
* @param `token`: usually a `Type`. (Same as the token used while setting up a binding).
* @returns an instance represented by the token. Returns `null` if not found.
*/
getOptional(token) {
return this._getByKey(Key.get(token), false, false, true);
}
getOptional(token) { return this._getByKey(Key.get(token), false, false, true); }
/**
* Retrieves an instance from the injector asynchronously. Used with asynchronous bindings.
@ -153,40 +165,37 @@ export class Injector {
* @param `token`: usually a `Type`. (Same as token used while setting up a binding).
* @returns a `Promise` which resolves to the instance represented by the token.
*/
asyncGet(token):Promise {
return this._getByKey(Key.get(token), true, false, false);
}
asyncGet(token): Promise<any> { return this._getByKey(Key.get(token), true, false, false); }
/**
* Creates a child injector and loads a new set of bindings into it.
*
* A resolution is a process of flattening multiple nested lists and converting individual bindings into a
* list of {@link ResolvedBinding}s. The resolution can be cached by `resolve` for the {@link Injector} for
* performance-sensitive code.
* A resolution is a process of flattening multiple nested lists and converting individual
* bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve`
* for the {@link Injector} for performance-sensitive code.
*
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a recursive
* list of more bindings.
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
* recursive list of more bindings.
*
*/
resolveAndCreateChild(bindings:List/*<ResolvedBinding|Binding|Type|List>*/):Injector {
resolveAndCreateChild(bindings: List<any>): Injector {
return new Injector(Injector.resolve(bindings), this, false);
}
/**
* Creates a child injector and loads a new set of {@link ResolvedBinding}s into it.
*
* @param `bindings`: A sparse list of {@link ResolvedBinding}s. See `resolve` for the {@link Injector}.
* @param `bindings`: A sparse list of {@link ResolvedBinding}s.
* See `resolve` for the {@link Injector}.
* @returns a new child {@link Injector}.
*/
createChildFromResolved(bindings:List<ResolvedBinding>):Injector {
createChildFromResolved(bindings: List<ResolvedBinding>): Injector {
return new Injector(bindings, this, false);
}
_createInstances():List {
return ListWrapper.createFixedSize(Key.numberOfKeys + 1);
}
_createInstances(): List<any> { return ListWrapper.createFixedSize(Key.numberOfKeys + 1); }
_getByKey(key:Key, returnPromise:boolean, returnLazy:boolean, optional:boolean) {
_getByKey(key: Key, returnPromise: boolean, returnLazy: boolean, optional: boolean) {
if (returnLazy) {
return () => this._getByKey(key, returnPromise, false, optional);
}
@ -210,7 +219,7 @@ export class Injector {
}
}
_resolveDependencies(key:Key, binding:ResolvedBinding, forceAsync:boolean):List {
_resolveDependencies(key: Key, binding: ResolvedBinding, forceAsync: boolean): List<any> {
try {
var getDependency = d => this._getByKey(d.key, forceAsync || d.asPromise, d.lazy, d.optional);
return ListWrapper.map(binding.dependencies, getDependency);
@ -221,44 +230,35 @@ export class Injector {
}
}
_getInstance(key:Key) {
_getInstance(key: Key) {
if (this._instances.length <= key.id) return null;
return ListWrapper.get(this._instances, key.id);
}
_setInstance(key:Key, obj):void {
ListWrapper.set(this._instances, key.id, obj);
}
_setInstance(key: Key, obj): void { ListWrapper.set(this._instances, key.id, obj); }
_getBinding(key:Key) {
var binding = this._bindings.length <= key.id ?
null :
ListWrapper.get(this._bindings, key.id);
_getBinding(key: Key) {
var binding = this._bindings.length <= key.id ? null : ListWrapper.get(this._bindings, key.id);
if (isBlank(binding) && this._defaultBindings) {
return bind(key.token).toClass(key.token).resolve();
var token: any = key.token;
return bind(key.token).toClass(token).resolve();
} else {
return binding;
}
}
_markAsConstructing(key:Key):void {
this._setInstance(key, _constructing);
}
_markAsConstructing(key: Key): void { this._setInstance(key, _constructing); }
_clear(key:Key):void {
this._setInstance(key, null);
}
_clear(key: Key): void { this._setInstance(key, null); }
}
class _SyncInjectorStrategy {
injector:Injector;
constructor(injector:Injector) {
this.injector = injector;
}
injector: Injector;
constructor(injector: Injector) { this.injector = injector; }
readFromCache(key:Key) {
readFromCache(key: Key) {
if (key.token === Injector) {
return this.injector;
}
@ -274,20 +274,20 @@ class _SyncInjectorStrategy {
}
}
instantiate(key:Key) {
instantiate(key: Key) {
var binding = this.injector._getBinding(key);
if (isBlank(binding)) return _notFound;
if (binding.providedAsPromise) throw new AsyncBindingError(key);
//add a marker so we can detect cyclic dependencies
// add a marker so we can detect cyclic dependencies
this.injector._markAsConstructing(key);
var deps = this.injector._resolveDependencies(key, binding, false);
return this._createInstance(key, binding, deps);
}
_createInstance(key:Key, binding:ResolvedBinding, deps:List) {
_createInstance(key: Key, binding: ResolvedBinding, deps: List<any>) {
try {
var instance = FunctionWrapper.apply(binding.factory, deps);
this.injector._setInstance(key, instance);
@ -301,12 +301,10 @@ class _SyncInjectorStrategy {
class _AsyncInjectorStrategy {
injector:Injector;
constructor(injector:Injector) {
this.injector = injector;
}
injector: Injector;
constructor(injector: Injector) { this.injector = injector; }
readFromCache(key:Key) {
readFromCache(key: Key) {
if (key.token === Injector) {
return PromiseWrapper.resolve(this.injector);
}
@ -324,31 +322,30 @@ class _AsyncInjectorStrategy {
}
}
instantiate(key:Key) /* Promise?? */ {
instantiate(key: Key) /* Promise?? */ {
var binding = this.injector._getBinding(key);
if (isBlank(binding)) return _notFound;
//add a marker so we can detect cyclic dependencies
// add a marker so we can detect cyclic dependencies
this.injector._markAsConstructing(key);
var deps = this.injector._resolveDependencies(key, binding, true);
var depsPromise = PromiseWrapper.all(deps);
var promise = PromiseWrapper
.then(depsPromise, null, (e) => this._errorHandler(key, e))
.then(deps => this._findOrCreate(key, binding, deps))
.then(instance => this._cacheInstance(key, instance));
var promise = PromiseWrapper.then(depsPromise, null, (e) => this._errorHandler(key, e))
.then(deps => this._findOrCreate(key, binding, deps))
.then(instance => this._cacheInstance(key, instance));
this.injector._setInstance(key, new _Waiting(promise));
return promise;
}
_errorHandler(key:Key, e):Promise {
_errorHandler(key: Key, e): Promise<any> {
if (e instanceof AbstractBindingError) e.addKey(key);
return PromiseWrapper.reject(e);
}
_findOrCreate(key:Key, binding:ResolvedBinding, deps:List) {
_findOrCreate(key: Key, binding: ResolvedBinding, deps: List<any>) {
try {
var instance = this.injector._getInstance(key);
if (!_isWaiting(instance)) return instance;
@ -365,7 +362,7 @@ class _AsyncInjectorStrategy {
}
}
function _resolveBindings(bindings:List): List {
function _resolveBindings(bindings: List<any>): List<ResolvedBinding> {
var resolvedList = ListWrapper.createFixedSize(bindings.length);
for (var i = 0; i < bindings.length; i++) {
var unresolved = bindings[i];
@ -379,7 +376,7 @@ function _resolveBindings(bindings:List): List {
} else if (unresolved instanceof List) {
resolved = _resolveBindings(unresolved);
} else if (unresolved instanceof BindingBuilder) {
throw new InvalidBindingError('BindingBuilder with ' + unresolved.token + ' token');
throw new InvalidBindingError(unresolved.token);
} else {
throw new InvalidBindingError(unresolved);
}
@ -388,14 +385,15 @@ function _resolveBindings(bindings:List): List {
return resolvedList;
}
function _createListOfBindings(flattenedBindings):List {
function _createListOfBindings(flattenedBindings): List<any> {
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v);
return bindings;
}
function _flattenBindings(bindings:List, res:Map):Map {
ListWrapper.forEach(bindings, function (b) {
function _flattenBindings(bindings: List<ResolvedBinding /* | List<any>*/>,
res: Map<number, ResolvedBinding>): Map<number, ResolvedBinding> {
ListWrapper.forEach(bindings, function(b) {
if (b instanceof ResolvedBinding) {
MapWrapper.set(res, b.key.id, b);
} else if (b instanceof List) {

View File

@ -16,42 +16,34 @@ import {stringify} from 'angular2/src/facade/lang';
* @exportedAs angular2/di
*/
export class Key {
token;
id/* :int */;
constructor(token, id/* :int */) {
token: Object;
id: number;
constructor(token: Object, id: number) {
this.token = token;
this.id = id;
}
get displayName() {
return stringify(this.token);
}
get displayName() { return stringify(this.token); }
/**
* Retrieves a `Key` for a token.
*/
static get(token):Key {
return _globalKeyRegistry.get(token);
}
static get(token): Key { return _globalKeyRegistry.get(token); }
/**
* @returns the number of keys registered in the system.
*/
static get numberOfKeys()/* :int */ {
return _globalKeyRegistry.numberOfKeys;
}
static get numberOfKeys(): number { return _globalKeyRegistry.numberOfKeys; }
}
/**
* @private
*/
export class KeyRegistry {
_allKeys:Map;
constructor() {
this._allKeys = MapWrapper.create();
}
_allKeys: Map<Object, Key>;
constructor() { this._allKeys = MapWrapper.create(); }
get(token):Key {
get(token: Object): Key {
if (token instanceof Key) return token;
if (MapWrapper.contains(this._allKeys, token)) {
@ -63,9 +55,7 @@ export class KeyRegistry {
return newKey;
}
get numberOfKeys()/* :int */ {
return MapWrapper.size(this._allKeys);
}
get numberOfKeys() /* :int */ { return MapWrapper.size(this._allKeys); }
}
var _globalKeyRegistry = new KeyRegistry();

View File

@ -1,16 +0,0 @@
/**
*
*
* @exportedAs angular2/di
*/
export class OpaqueToken {
_desc:string;
constructor(desc:string){
this._desc = `Token(${desc})`;
}
toString():string {
return this._desc;
}
}

View File

@ -0,0 +1,12 @@
/**
*
*
* @exportedAs angular2/di
*/
export class OpaqueToken {
_desc: string;
constructor(desc: string) { this._desc = `Token(${desc})`; }
toString(): string { return this._desc; }
}

View File

@ -10,6 +10,8 @@ import {int, global, isPresent} from 'angular2/src/facade/lang';
import {List} from 'angular2/src/facade/collection';
import * as Rx from 'rx';
export var Promise = (<any>global).Promise;
export class PromiseWrapper {
static resolve(obj): Promise<any> { return Promise.resolve(obj); }

View File

@ -161,7 +161,7 @@ class FunctionWrapper {
class BaseException extends Error {
final String message;
BaseException(this.message);
BaseException([this.message]);
String toString() {
return this.message;

View File

@ -9,6 +9,18 @@ export var __esModule = true;
export var Type = Function;
export type Type = typeof Function;
export class BaseException extends Error {
message;
stack;
constructor(message?: string) {
super(message);
this.message = message;
this.stack = (<any>new Error()).stack;
}
toString(): string { return this.message; }
}
export var Math = _global.Math;
export var Date = _global.Date;
@ -28,7 +40,9 @@ if (assertionsEnabled_) {
}
export {int};
export class CONST {}
export function CONST() {
return (target) => target;
};
export class ABSTRACT {}
export class IMPLEMENTS {}
@ -111,10 +125,10 @@ export class StringJoiner {
toString(): string { return this.parts.join(""); }
}
export class NumberParseError implements Error {
export class NumberParseError extends BaseException {
name: string;
constructor(public message: string) {}
constructor(public message: string) { super(); }
toString() { return this.message; }
}
@ -191,9 +205,6 @@ export class FunctionWrapper {
static apply(fn: Function, posArgs) { return fn.apply(null, posArgs); }
}
// No subclass so that we preserve error stack.
export var BaseException = Error;
// JS has NaN !== NaN
export function looseIdentical(a, b): boolean {
return a === b || typeof a === "number" && typeof b === "number" && isNaN(a) && isNaN(b);

View File

@ -0,0 +1 @@
library util_decorators;

View File

@ -1,12 +1,16 @@
export var __esModule = true;
import {global} from 'angular2/src/facade/lang';
export function makeDecorator(annotationCls) {
return function(...args) {
return function() {
var args = arguments;
var Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators';
}
var annotationInstance = new annotationCls(...args);
var annotationInstance = Object.create(annotationCls);
annotationInstance.call(annotationInstance, args);
return function(cls) {
var annotations = Reflect.getMetadata('annotations', cls);
annotations = annotations || [];
@ -18,12 +22,14 @@ export function makeDecorator(annotationCls) {
}
export function makeParamDecorator(annotationCls) {
return function(...args) {
return function() {
var args = arguments;
var Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using parameter decorators';
}
var annotationInstance = new annotationCls(...args);
var annotationInstance = Object.create(annotationCls);
annotationInstance.call(annotationInstance, args);
return function(cls, unusedKey, index) {
var parameters = Reflect.getMetadata('parameters', cls);
parameters = parameters || [];

View File

@ -77,6 +77,7 @@ class NoAnnotations {
export function main() {
describe('injector', function () {
it('should instantiate a class without dependencies', function () {
var injector = Injector.resolveAndCreate([Engine]);
var engine = injector.get(Engine);
@ -211,7 +212,7 @@ export function main() {
.toThrowError('Invalid binding - only instances of Binding and Type are allowed, got: blah');
expect(() => Injector.resolveAndCreate([bind("blah")]))
.toThrowError('Invalid binding - only instances of Binding and Type are allowed, ' +
'got: BindingBuilder with blah token');
'got: blah');
});
it('should provide itself', function () {

View File

@ -22,6 +22,7 @@ class SpyTestObj extends SpyObject {
noSuchMethod(m){return super.noSuchMethod(m)}
}
export function main() {
describe('test_lib', () => {
describe('equality', () => {

View File

@ -9509,7 +9509,7 @@
},
"typescript": {
"version": "1.5.0-beta",
"resolved": "git://github.com/alexeagle/TypeScript#cddc1867c44b6bed9095dc30f0b0f552cbc003a9"
"resolved": "git://github.com/alexeagle/TypeScript#be9a7edff73ac2592e508732c771c85357041385"
},
"vinyl": {
"version": "0.4.6",

2
npm-shrinkwrap.json generated
View File

@ -14695,7 +14695,7 @@
"typescript": {
"version": "1.5.0-beta",
"from": "git://github.com/alexeagle/TypeScript#error_is_class",
"resolved": "git://github.com/alexeagle/TypeScript#cddc1867c44b6bed9095dc30f0b0f552cbc003a9"
"resolved": "git://github.com/alexeagle/TypeScript#be9a7edff73ac2592e508732c771c85357041385"
},
"vinyl": {
"version": "0.4.6",