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 {Injector} from './src/di/injector';
export {Binding, ResolvedBinding, Dependency, bind} from './src/di/binding'; export {Binding, ResolvedBinding, Dependency, bind} from './src/di/binding';
export {Key, KeyRegistry} from './src/di/key'; export {Key, KeyRegistry} from './src/di/key';
export {KeyMetadataError, NoBindingError, AbstractBindingError, AsyncBindingError, CyclicDependencyError, export {
InstantiationError, InvalidBindingError, NoAnnotationError} from './src/di/exceptions'; NoBindingError,
AbstractBindingError,
AsyncBindingError,
CyclicDependencyError,
InstantiationError,
InvalidBindingError,
NoAnnotationError
} from './src/di/exceptions';
export {OpaqueToken} from './src/di/opaque_token'; 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 {ProtoRecord} from './proto_record';
import {BaseException} from "angular2/src/facade/lang";
export class ExpressionChangedAfterItHasBeenChecked extends Error { export class ExpressionChangedAfterItHasBeenChecked extends BaseException {
message:string; message:string;
constructor(proto:ProtoRecord, change:any) { 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; message:string;
originalException:any; originalException:any;
location:string; location:string;

View File

@ -1,6 +1,6 @@
import {Injectable} from 'angular2/src/di/annotations_impl'; import {Injectable} from 'angular2/src/di/annotations_impl';
import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection"; 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_CHARACTER = 1;
export const TOKEN_TYPE_IDENTIFIER = 2; export const TOKEN_TYPE_IDENTIFIER = 2;
@ -176,7 +176,7 @@ export const $RBRACE = 125;
const $NBSP = 160; const $NBSP = 160;
export class ScannerError extends Error { export class ScannerError extends BaseException {
message:string; message:string;
constructor(message) { constructor(message) {
super(); super();

View File

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

View File

@ -10,4 +10,4 @@ export {
Optional as OptionalAnnotation, Optional as OptionalAnnotation,
Injectable as InjectableAnnotation, Injectable as InjectableAnnotation,
DependencyAnnotation, // abstract base class, does not need a decorator 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"; 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. * A parameter annotation that specifies a dependency.
* *
@ -11,12 +16,10 @@ import {CONST} from "angular2/src/facade/lang";
* *
* @exportedAs angular2/di_annotations * @exportedAs angular2/di_annotations
*/ */
@CONST()
export class Inject { export class Inject {
token; constructor(public token) {}
@CONST()
constructor(token) {
this.token = token;
}
} }
/** /**
@ -32,12 +35,9 @@ export class Inject {
* *
* @exportedAs angular2/di_annotations * @exportedAs angular2/di_annotations
*/ */
@CONST()
export class InjectPromise { export class InjectPromise {
token; constructor(public token) {}
@CONST()
constructor(token) {
this.token = token;
}
} }
/** /**
@ -53,17 +53,14 @@ export class InjectPromise {
* *
* @exportedAs angular2/di_annotations * @exportedAs angular2/di_annotations
*/ */
@CONST()
export class InjectLazy { export class InjectLazy {
token; constructor(public token) {}
@CONST()
constructor(token) {
this.token = token;
}
} }
/** /**
* A parameter annotation that marks a dependency as optional. {@link Injector} provides `null` if the dependency is not * A parameter annotation that marks a dependency as optional. {@link Injector} provides `null` if
* found. * the dependency is not found.
* *
* ``` * ```
* class AComponent { * class AComponent {
@ -75,16 +72,15 @@ export class InjectLazy {
* *
* @exportedAs angular2/di_annotations * @exportedAs angular2/di_annotations
*/ */
@CONST()
export class Optional { export class Optional {
@CONST()
constructor() {
}
} }
/** /**
* `DependencyAnnotation` is used by the framework to extend DI. * `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: * For example:
* *
@ -108,19 +104,14 @@ export class Optional {
* *
* @exportedAs angular2/di_annotations * @exportedAs angular2/di_annotations
*/ */
@CONST()
export class DependencyAnnotation { export class DependencyAnnotation {
@CONST() get token() { return null; }
constructor() {
}
get token() {
return null;
}
} }
/** /**
* A marker annotation that marks a class as available to `Injector` for creation. Used by tooling for * A marker annotation that marks a class as available to `Injector` for creation. Used by tooling
* generating constructor stubs. * for generating constructor stubs.
* *
* ``` * ```
* class NeedsService { * class NeedsService {
@ -132,8 +123,6 @@ export class DependencyAnnotation {
* ``` * ```
* @exportedAs angular2/di_annotations * @exportedAs angular2/di_annotations
*/ */
@CONST()
export class Injectable { 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 {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {reflector} from 'angular2/src/reflection/reflection'; import {reflector} from 'angular2/src/reflection/reflection';
import {Key} from './key'; 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'; import {NoAnnotationError} from './exceptions';
/** /**
* @private * @private
*/ */
export class Dependency { export class Dependency {
key:Key; constructor(public key: Key, public asPromise: boolean, public lazy: boolean,
asPromise:boolean; public optional: boolean, public properties: List<any>) {
lazy:boolean;
optional:boolean;
properties:List;
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean, properties:List) {
this.key = key; this.key = key;
this.asPromise = asPromise; this.asPromise = asPromise;
this.lazy = lazy; this.lazy = lazy;
@ -23,9 +24,7 @@ export class Dependency {
this.properties = properties; this.properties = properties;
} }
static fromKey(key:Key) { static fromKey(key: Key) { return new Dependency(key, false, false, false, []); }
return new Dependency(key, false, false, false, []);
}
} }
var _EMPTY_LIST = []; // TODO: make const when supported var _EMPTY_LIST = []; // TODO: make const when supported
@ -47,8 +46,8 @@ var _EMPTY_LIST = []; // TODO: make const when supported
* *
* @exportedAs angular2/di * @exportedAs angular2/di
*/ */
@CONST()
export class Binding { export class Binding {
/** /**
* Token used when retrieving this binding. Usually the `Type`. * Token used when retrieving this binding. Usually the `Type`.
*/ */
@ -59,7 +58,8 @@ export class Binding {
* *
* ## Example * ## 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 * ```javascript
* *
@ -83,7 +83,7 @@ export class Binding {
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true); * expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
* ``` * ```
*/ */
toClass:Type; toClass: Type;
/** /**
* Binds a key to a value. * Binds a key to a value.
@ -103,12 +103,13 @@ export class Binding {
/** /**
* Binds a key to the alias for an existing key. * 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 * An alias means that {@link Injector} returns the same instance as if the alias token was used.
* `toClass` where a separate instance of `toClass` is returned. * This is in contrast to `toClass` where a separate instance of `toClass` is returned.
* *
* ## Example * ## 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 * ```javascript
* *
@ -150,7 +151,7 @@ export class Binding {
* expect(injector.get(String)).toEqual('Value: 3'); * expect(injector.get(String)).toEqual('Value: 3');
* ``` * ```
*/ */
toFactory:Function; toFactory: Function;
/** /**
* Binds a key to a function which computes the value asynchronously. * 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')); * 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 * The interesting thing to note is that event though `Number` has an async factory, the `String`
* function takes the resolved value. This shows that the {@link Injector} delays executing the `String` factory * factory function takes the resolved value. This shows that the {@link Injector} delays
* until after the `Number` is resolved. This can only be done if the `token` is retrieved using the *executing the
* `asyncGet` API in the {@link Injector}. *`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 * Used in conjunction with `toFactory` or `toAsyncFactory` and specifies a set of dependencies
* should be injected into the factory function. * (as `token`s) which should be injected into the factory function.
* *
* ## Example * ## Example
* *
@ -195,19 +198,11 @@ export class Binding {
* expect(injector.get(String)).toEqual('Value: 3'); * expect(injector.get(String)).toEqual('Value: 3');
* ``` * ```
*/ */
dependencies:List; dependencies: List<any>;
@CONST() constructor(token, {toClass, toValue, toAlias, toFactory, toAsyncFactory, deps}: {
constructor( toClass ?: Type, toValue ?: any, toAlias ?: any, toFactory ?: Function,
token, toAsyncFactory ?: Function, deps ?: List<any>}) {
{
toClass,
toValue,
toAlias,
toFactory,
toAsyncFactory,
deps
}) {
this.token = token; this.token = token;
this.toClass = toClass; this.toClass = toClass;
this.toValue = toValue; this.toValue = toValue;
@ -220,10 +215,11 @@ export class Binding {
/** /**
* Converts the {@link Binding} into {@link ResolvedBinding}. * 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 { resolve(): ResolvedBinding {
var factoryFn:Function; var factoryFn: Function;
var resolvedDeps; var resolvedDeps;
var isAsync = false; var isAsync = false;
if (isPresent(this.toClass)) { if (isPresent(this.toClass)) {
@ -244,20 +240,16 @@ export class Binding {
resolvedDeps = _EMPTY_LIST; resolvedDeps = _EMPTY_LIST;
} }
return new ResolvedBinding( return new ResolvedBinding(Key.get(this.token), factoryFn, resolvedDeps, isAsync);
Key.get(this.token),
factoryFn,
resolvedDeps,
isAsync
);
} }
} }
/** /**
* An internal resolved representation of a {@link Binding} used by the {@link Injector}. * 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 * A {@link Binding} is resolved when it has a factory function. Binding to a class, alias, or
* methods, as {@link Injector} only operates on calling factory functions. * value, are just convenience methods, as {@link Injector} only operates on calling factory
* functions.
* *
* @exportedAs angular2/di * @exportedAs angular2/di
*/ */
@ -265,24 +257,25 @@ export class ResolvedBinding {
/** /**
* A key, usually a `Type`. * 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 which can return an instance of an object represented by a key.
*/ */
factory:Function; factory: Function;
/** /**
* Arguments (dependencies) to the `factory` function. * Arguments (dependencies) to the `factory` function.
*/ */
dependencies:List<Dependency>; dependencies: List<Dependency>;
/** /**
* Specifies whether the `factory` function returns a `Promise`. * 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.key = key;
this.factory = factory; this.factory = factory;
this.dependencies = dependencies; this.dependencies = dependencies;
@ -304,7 +297,7 @@ export class ResolvedBinding {
* *
* @exportedAs angular2/di * @exportedAs angular2/di
*/ */
export function bind(token):BindingBuilder { export function bind(token): BindingBuilder {
return new BindingBuilder(token); return new BindingBuilder(token);
} }
@ -316,16 +309,15 @@ export function bind(token):BindingBuilder {
export class BindingBuilder { export class BindingBuilder {
token; token;
constructor(token) { constructor(token) { this.token = token; }
this.token = token;
}
/** /**
* Binds an interface to an implementation / subclass. * Binds an interface to an implementation / subclass.
* *
* ## Example * ## 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 * ```javascript
* *
@ -349,9 +341,7 @@ export class BindingBuilder {
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true); * expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
* ``` * ```
*/ */
toClass(type:Type):Binding { toClass(type: Type): Binding { return new Binding(this.token, {toClass: type}); }
return new Binding(this.token, {toClass: type});
}
/** /**
* Binds a key to a value. * Binds a key to a value.
@ -366,19 +356,18 @@ export class BindingBuilder {
* expect(injector.get(String)).toEqual('Hello'); * expect(injector.get(String)).toEqual('Hello');
* ``` * ```
*/ */
toValue(value):Binding { toValue(value): Binding { return new Binding(this.token, {toValue: value}); }
return new Binding(this.token, {toValue: value});
}
/** /**
* Binds a key to the alias for an existing key. * 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 * An alias means that we will return the same instance as if the alias token was used. (This is
* `toClass` where a separet instance of `toClass` will be returned.) * in contrast to `toClass` where a separet instance of `toClass` will be returned.)
* *
* ## Example * ## 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 * ```javascript
* *
@ -402,9 +391,7 @@ export class BindingBuilder {
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true); * expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
* ``` * ```
*/ */
toAlias(aliasToken):Binding { toAlias(aliasToken): Binding { return new Binding(this.token, {toAlias: aliasToken}); }
return new Binding(this.token, {toAlias: aliasToken});
}
/** /**
* Binds a key to a function which computes the value. * Binds a key to a function which computes the value.
@ -421,11 +408,8 @@ export class BindingBuilder {
* expect(injector.get(String)).toEqual('Value: 3'); * expect(injector.get(String)).toEqual('Value: 3');
* ``` * ```
*/ */
toFactory(factoryFunction:Function, dependencies:List = null):Binding { toFactory(factoryFunction: Function, dependencies?: List<any>): Binding {
return new Binding(this.token, { return new Binding(this.token, {toFactory: factoryFunction, deps: dependencies});
toFactory: factoryFunction,
deps: dependencies
});
} }
/** /**
@ -445,26 +429,24 @@ export class BindingBuilder {
* injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3')); * 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 * The interesting thing to note is that event though `Number` has an async factory, the `String`
* function takes the resolved value. This shows that the {@link Injector} delays executing of the `String` factory * factory function takes the resolved value. This shows that the {@link Injector} delays
* until after the `Number` is resolved. This can only be done if the `token` is retrieved using the * 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}. * the `asyncGet` API in the {@link Injector}.
*/ */
toAsyncFactory(factoryFunction:Function, dependencies:List = null):Binding { toAsyncFactory(factoryFunction: Function, dependencies?: List<any>): Binding {
return new Binding(this.token, { return new Binding(this.token, {toAsyncFactory: factoryFunction, deps: dependencies});
toAsyncFactory: factoryFunction,
deps: dependencies
});
} }
} }
function _constructDependencies(factoryFunction:Function, dependencies:List) { function _constructDependencies(factoryFunction: Function, dependencies: List<any>) {
return isBlank(dependencies) ? return isBlank(dependencies) ?
_dependenciesFor(factoryFunction) : _dependenciesFor(factoryFunction) :
ListWrapper.map(dependencies, (t) => Dependency.fromKey(Key.get(t))); ListWrapper.map(dependencies, (t) => Dependency.fromKey(Key.get(t)));
} }
function _dependenciesFor(typeOrFunc):List { function _dependenciesFor(typeOrFunc): List<any> {
var params = reflector.parameters(typeOrFunc); var params = reflector.parameters(typeOrFunc);
if (isBlank(params)) return []; if (isBlank(params)) return [];
if (ListWrapper.any(params, (p) => isBlank(p))) throw new NoAnnotationError(typeOrFunc); if (ListWrapper.any(params, (p) => isBlank(p))) throw new NoAnnotationError(typeOrFunc);
@ -503,7 +485,6 @@ function _extractToken(typeOrFunc, annotations) {
token = paramAnnotation.token; token = paramAnnotation.token;
} }
ListWrapper.push(depProps, paramAnnotation); 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); return new Dependency(Key.get(token), asPromise, lazy, optional, depProps);
} }

View File

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

View File

@ -1,9 +1,9 @@
import {ListWrapper, List} from 'angular2/src/facade/collection'; 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 = []; var res = [];
for(var i = 0; i < keys.length; ++i) { for (var i = 0; i < keys.length; ++i) {
if (ListWrapper.contains(res, keys[i])) { if (ListWrapper.contains(res, keys[i])) {
ListWrapper.push(res, keys[i]); ListWrapper.push(res, keys[i]);
return res; return res;
@ -14,7 +14,7 @@ function findFirstClosedCycle(keys:List):List {
return res; return res;
} }
function constructResolvingPath(keys:List):string { function constructResolvingPath(keys: List<any>): string {
if (keys.length > 1) { if (keys.length > 1) {
var reversed = findFirstClosedCycle(ListWrapper.reversed(keys)); var reversed = findFirstClosedCycle(ListWrapper.reversed(keys));
var tokenStrs = ListWrapper.map(reversed, (k) => stringify(k.token)); var tokenStrs = ListWrapper.map(reversed, (k) => stringify(k.token));
@ -30,12 +30,13 @@ function constructResolvingPath(keys:List):string {
* *
* @exportedAs angular2/di_errors * @exportedAs angular2/di_errors
*/ */
export class AbstractBindingError extends Error { export class AbstractBindingError extends BaseException {
keys:List; name: string;
constructResolvingMessage:Function; message: string;
message; keys: List<any>;
constructResolvingMessage: Function;
// TODO(tbosch): Can't do key:Key as this results in a circular dependency! // TODO(tbosch): Can't do key:Key as this results in a circular dependency!
constructor(key, constructResolvingMessage:Function) { constructor(key, constructResolvingMessage: Function) {
super(); super();
this.keys = [key]; this.keys = [key];
this.constructResolvingMessage = constructResolvingMessage; 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! // 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); ListWrapper.push(this.keys, key);
this.message = this.constructResolvingMessage(this.keys); this.message = this.constructResolvingMessage(this.keys);
} }
toString():string { toString(): string { return this.message; }
return this.message;
}
} }
/** /**
* Thrown when trying to retrieve a dependency by `Key` from {@link Injector}, but the {@link Injector} does not have a * Thrown when trying to retrieve a dependency by `Key` from {@link Injector}, but the
* {@link Binding} for {@link Key}. * {@link Injector} does not have a {@link Binding} for {@link Key}.
* *
* @exportedAs angular2/di_errors * @exportedAs angular2/di_errors
*/ */
export class NoBindingError extends AbstractBindingError { export class NoBindingError extends AbstractBindingError {
// TODO(tbosch): Can't do key:Key as this results in a circular dependency! // TODO(tbosch): Can't do key:Key as this results in a circular dependency!
constructor(key) { constructor(key) {
super(key, function (keys:List) { super(key, function (keys:List<any>) {
var first = stringify(ListWrapper.first(keys).token); var first = stringify(ListWrapper.first(keys).token);
return `No provider for ${first}!${constructResolvingPath(keys)}`; return `No provider for ${first}!${constructResolvingPath(keys)}`;
}); });
@ -88,18 +87,17 @@ export class NoBindingError extends AbstractBindingError {
* }).toThrowError(AsycBindingError); * }).toThrowError(AsycBindingError);
* ``` * ```
* *
* The above example throws because `String` depends on `Number` which is async. If any binding in the dependency * The above example throws because `String` depends on `Number` which is async. If any binding in
* graph is async then the graph can only be retrieved using the `asyncGet` API. * the dependency graph is async then the graph can only be retrieved using the `asyncGet` API.
* *
* @exportedAs angular2/di_errors * @exportedAs angular2/di_errors
*/ */
export class AsyncBindingError extends AbstractBindingError { export class AsyncBindingError extends AbstractBindingError {
// TODO(tbosch): Can't do key:Key as this results in a circular dependency! // TODO(tbosch): Can't do key:Key as this results in a circular dependency!
constructor(key) { constructor(key) {
super(key, function (keys:List) { super(key, function (keys:List<any>) {
var first = stringify(ListWrapper.first(keys).token); var first = stringify(ListWrapper.first(keys).token);
return `Cannot instantiate ${first} synchronously. ` + return `Cannot instantiate ${first} synchronously. It is provided as a promise!${constructResolvingPath(keys)}`;
`It is provided as a promise!${constructResolvingPath(keys)}`;
}); });
} }
} }
@ -125,7 +123,7 @@ export class AsyncBindingError extends AbstractBindingError {
export class CyclicDependencyError extends AbstractBindingError { export class CyclicDependencyError extends AbstractBindingError {
// TODO(tbosch): Can't do key:Key as this results in a circular dependency! // TODO(tbosch): Can't do key:Key as this results in a circular dependency!
constructor(key) { constructor(key) {
super(key, function (keys:List) { super(key, function (keys:List<any>) {
return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`; 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. * 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 * The `InstantiationError` class contains the original error plus the dependency graph which caused
* instantiated. * this object to be instantiated.
* *
* @exportedAs angular2/di_errors * @exportedAs angular2/di_errors
*/ */
@ -144,10 +142,9 @@ export class InstantiationError extends AbstractBindingError {
causeKey; causeKey;
// TODO(tbosch): Can't do key:Key as this results in a circular dependency! // TODO(tbosch): Can't do key:Key as this results in a circular dependency!
constructor(cause, key) { constructor(cause, key) {
super(key, function (keys:List) { super(key, function (keys:List<any>) {
var first = stringify(ListWrapper.first(keys).token); var first = stringify(ListWrapper.first(keys).token);
return `Error during instantiation of ${first}!${constructResolvingPath(keys)}.` + return `Error during instantiation of ${first}!${constructResolvingPath(keys)}. ORIGINAL ERROR: ${cause}`;
` ORIGINAL ERROR: ${cause}`;
}); });
this.cause = cause; this.cause = cause;
this.causeKey = key; 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 * @exportedAs angular2/di_errors
*/ */
export class InvalidBindingError extends Error { export class InvalidBindingError extends BaseException {
message:string; message: string;
constructor(binding) { constructor(binding) {
super(); 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 { toString(): string { return this.message; }
return this.message;
}
} }
/** /**
* Thrown when the class has no annotation information. * 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 * Lack of annotation information prevents the {@link Injector} from determining which dependencies
* the constructor. * need to be injected into the constructor.
* *
* @exportedAs angular2/di_errors * @exportedAs angular2/di_errors
*/ */
export class NoAnnotationError extends Error { export class NoAnnotationError extends BaseException {
message:string; name: string;
message: string;
constructor(typeOrFunc) { constructor(typeOrFunc) {
super(); super();
this.message = `Cannot resolve all parameters for ${stringify(typeOrFunc)}.` + this.message = "Cannot resolve all parameters for " + stringify(typeOrFunc) + ". " +
` Make sure they all have valid type or annotations.`; 'Make sure they all have valid type or annotations.';
} }
toString():string { toString(): string { return this.message; }
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 {Map, List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {ResolvedBinding, Binding, BindingBuilder, bind} from './binding'; import {ResolvedBinding, Binding, BindingBuilder, bind} from './binding';
import {AbstractBindingError, NoBindingError, AsyncBindingError, CyclicDependencyError, import {
InstantiationError, InvalidBindingError} from './exceptions'; AbstractBindingError,
NoBindingError,
AsyncBindingError,
CyclicDependencyError,
InstantiationError,
InvalidBindingError
} from './exceptions';
import {FunctionWrapper, Type, isPresent, isBlank} from 'angular2/src/facade/lang'; 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'; import {Key} from './key';
var _constructing = new Object(); var _constructing = new Object();
var _notFound = new Object(); var _notFound = new Object();
class _Waiting { class _Waiting {
promise:Promise; promise: Promise<any>;
constructor(promise:Promise) { constructor(promise: Promise<any>) { this.promise = promise; }
this.promise = promise;
}
} }
function _isWaiting(obj):boolean { function _isWaiting(obj): boolean {
return obj instanceof _Waiting; return obj instanceof _Waiting;
} }
/** /**
* A dependency injection container used for resolving dependencies. * A dependency injection container used for resolving dependencies.
* *
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the constructor dependencies. * An `Injector` is a replacement for a `new` operator, which can automatically resolve the
* In typical use, application code asks for the dependencies in the constructor and they are resolved by the * constructor dependencies.
* `Injector`. * In typical use, application code asks for the dependencies in the constructor and they are
* resolved by the `Injector`.
* *
* ## Example: * ## Example:
* *
@ -42,8 +48,9 @@ function _isWaiting(obj):boolean {
* *
* ``` * ```
* *
* Next we need to write the code that creates and instantiates the `Injector`. We then ask for the `root` object, * Next we need to write the code that creates and instantiates the `Injector`. We then ask for the
* `Car`, so that the `Injector` can recursively build all of that object's dependencies. * `root` object, `Car`, so that the `Injector` can recursively build all of that object's
*dependencies.
* *
* ```javascript * ```javascript
* main() { * main() {
@ -53,71 +60,79 @@ function _isWaiting(obj):boolean {
* var car = injector.get(Car); * 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 * Notice that we don't use the `new` operator because we explicitly want to have the `Injector`
* object's dependencies automatically. * resolve all of the object's dependencies automatically.
* *
* @exportedAs angular2/di * @exportedAs angular2/di
*/ */
export class Injector { export class Injector {
_bindings:List; _bindings: List<any>;
_instances:List; _instances: List<any>;
_parent:Injector; _parent: Injector;
_defaultBindings:boolean; _defaultBindings: boolean;
_asyncStrategy: _AsyncInjectorStrategy; _asyncStrategy: _AsyncInjectorStrategy;
_syncStrategy:_SyncInjectorStrategy; _syncStrategy: _SyncInjectorStrategy;
/** /**
* Turns a list of binding definitions into an internal resolved list of resolved bindings. * 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 * A resolution is a process of flattening multiple nested lists and converting individual
* list of {@link ResolvedBinding}s. The resolution can be cached by `resolve` for the {@link Injector} for * bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve`
* performance-sensitive code. * for the {@link Injector} for performance-sensitive code.
* *
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a recursive * @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
* list of more bindings. * 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 * The returned list is sparse, indexed by `id` for the {@link Key}. It is generally not useful to
* other than for passing it to {@link Injector} functions that require resolved binding lists, such as *application code
* other than for passing it to {@link Injector} functions that require resolved binding lists,
*such as
* `fromResolvedBindings` and `createChildFromResolved`. * `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 resolvedBindings = _resolveBindings(bindings);
var flatten = _flattenBindings(resolvedBindings, MapWrapper.create()); var flatten = _flattenBindings(resolvedBindings, MapWrapper.create());
return _createListOfBindings(flatten); return _createListOfBindings(flatten);
} }
/** /**
* Resolves bindings and creates an injector based on those bindings. This function is slower than the * Resolves bindings and creates an injector based on those bindings. This function is slower than
* corresponding `fromResolvedBindings` because it needs to resolve bindings first. See `resolve` for the * the corresponding `fromResolvedBindings` because it needs to resolve bindings first. See
* {@link Injector}. *`resolve`
* for the {@link Injector}.
* *
* Prefer `fromResolvedBindings` in performance-critical code that creates lots of injectors. * 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. * bindings.
* @param `defaultBindings` Setting to true will auto-create 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); return new Injector(Injector.resolve(bindings), null, defaultBindings);
} }
/** /**
* Creates an injector from previously resolved bindings. This bypasses resolution and flattening. This API is the * Creates an injector from previously resolved bindings. This bypasses resolution and flattening.
* recommended way to construct injectors in performance-sensitive parts. * 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. * @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); 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 `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._bindings = bindings;
this._instances = this._createInstances(); this._instances = this._createInstances();
this._parent = parent; this._parent = parent;
@ -129,12 +144,11 @@ export class Injector {
/** /**
* Retrieves an instance from the 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. * @returns an instance represented by the token. Throws if not found.
*/ */
get(token) { get(token) { return this._getByKey(Key.get(token), false, false, false); }
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). * @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. * @returns an instance represented by the token. Returns `null` if not found.
*/ */
getOptional(token) { getOptional(token) { return this._getByKey(Key.get(token), false, false, true); }
return this._getByKey(Key.get(token), false, false, true);
}
/** /**
* Retrieves an instance from the injector asynchronously. Used with asynchronous bindings. * 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). * @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. * @returns a `Promise` which resolves to the instance represented by the token.
*/ */
asyncGet(token):Promise { asyncGet(token): Promise<any> { return this._getByKey(Key.get(token), true, false, false); }
return this._getByKey(Key.get(token), true, false, false);
}
/** /**
* Creates a child injector and loads a new set of bindings into it. * 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 * A resolution is a process of flattening multiple nested lists and converting individual
* list of {@link ResolvedBinding}s. The resolution can be cached by `resolve` for the {@link Injector} for * bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve`
* performance-sensitive code. * for the {@link Injector} for performance-sensitive code.
* *
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a recursive * @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
* list of more bindings. * 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); return new Injector(Injector.resolve(bindings), this, false);
} }
/** /**
* Creates a child injector and loads a new set of {@link ResolvedBinding}s into it. * 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}. * @returns a new child {@link Injector}.
*/ */
createChildFromResolved(bindings:List<ResolvedBinding>):Injector { createChildFromResolved(bindings: List<ResolvedBinding>): Injector {
return new Injector(bindings, this, false); return new Injector(bindings, this, false);
} }
_createInstances():List { _createInstances(): List<any> { return ListWrapper.createFixedSize(Key.numberOfKeys + 1); }
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) { if (returnLazy) {
return () => this._getByKey(key, returnPromise, false, optional); 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 { try {
var getDependency = d => this._getByKey(d.key, forceAsync || d.asPromise, d.lazy, d.optional); var getDependency = d => this._getByKey(d.key, forceAsync || d.asPromise, d.lazy, d.optional);
return ListWrapper.map(binding.dependencies, getDependency); 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; if (this._instances.length <= key.id) return null;
return ListWrapper.get(this._instances, key.id); return ListWrapper.get(this._instances, key.id);
} }
_setInstance(key:Key, obj):void { _setInstance(key: Key, obj): void { ListWrapper.set(this._instances, key.id, obj); }
ListWrapper.set(this._instances, key.id, obj);
}
_getBinding(key:Key) { _getBinding(key: Key) {
var binding = this._bindings.length <= key.id ? var binding = this._bindings.length <= key.id ? null : ListWrapper.get(this._bindings, key.id);
null :
ListWrapper.get(this._bindings, key.id);
if (isBlank(binding) && this._defaultBindings) { 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 { } else {
return binding; return binding;
} }
} }
_markAsConstructing(key:Key):void { _markAsConstructing(key: Key): void { this._setInstance(key, _constructing); }
this._setInstance(key, _constructing);
}
_clear(key:Key):void { _clear(key: Key): void { this._setInstance(key, null); }
this._setInstance(key, null);
}
} }
class _SyncInjectorStrategy { class _SyncInjectorStrategy {
injector:Injector; injector: Injector;
constructor(injector:Injector) { constructor(injector: Injector) { this.injector = injector; }
this.injector = injector;
}
readFromCache(key:Key) { readFromCache(key: Key) {
if (key.token === Injector) { if (key.token === Injector) {
return this.injector; return this.injector;
} }
@ -274,20 +274,20 @@ class _SyncInjectorStrategy {
} }
} }
instantiate(key:Key) { instantiate(key: Key) {
var binding = this.injector._getBinding(key); var binding = this.injector._getBinding(key);
if (isBlank(binding)) return _notFound; if (isBlank(binding)) return _notFound;
if (binding.providedAsPromise) throw new AsyncBindingError(key); 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); this.injector._markAsConstructing(key);
var deps = this.injector._resolveDependencies(key, binding, false); var deps = this.injector._resolveDependencies(key, binding, false);
return this._createInstance(key, binding, deps); return this._createInstance(key, binding, deps);
} }
_createInstance(key:Key, binding:ResolvedBinding, deps:List) { _createInstance(key: Key, binding: ResolvedBinding, deps: List<any>) {
try { try {
var instance = FunctionWrapper.apply(binding.factory, deps); var instance = FunctionWrapper.apply(binding.factory, deps);
this.injector._setInstance(key, instance); this.injector._setInstance(key, instance);
@ -301,12 +301,10 @@ class _SyncInjectorStrategy {
class _AsyncInjectorStrategy { class _AsyncInjectorStrategy {
injector:Injector; injector: Injector;
constructor(injector:Injector) { constructor(injector: Injector) { this.injector = injector; }
this.injector = injector;
}
readFromCache(key:Key) { readFromCache(key: Key) {
if (key.token === Injector) { if (key.token === Injector) {
return PromiseWrapper.resolve(this.injector); return PromiseWrapper.resolve(this.injector);
} }
@ -324,18 +322,17 @@ class _AsyncInjectorStrategy {
} }
} }
instantiate(key:Key) /* Promise?? */ { instantiate(key: Key) /* Promise?? */ {
var binding = this.injector._getBinding(key); var binding = this.injector._getBinding(key);
if (isBlank(binding)) return _notFound; 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); this.injector._markAsConstructing(key);
var deps = this.injector._resolveDependencies(key, binding, true); var deps = this.injector._resolveDependencies(key, binding, true);
var depsPromise = PromiseWrapper.all(deps); var depsPromise = PromiseWrapper.all(deps);
var promise = PromiseWrapper var promise = PromiseWrapper.then(depsPromise, null, (e) => this._errorHandler(key, e))
.then(depsPromise, null, (e) => this._errorHandler(key, e))
.then(deps => this._findOrCreate(key, binding, deps)) .then(deps => this._findOrCreate(key, binding, deps))
.then(instance => this._cacheInstance(key, instance)); .then(instance => this._cacheInstance(key, instance));
@ -343,12 +340,12 @@ class _AsyncInjectorStrategy {
return promise; return promise;
} }
_errorHandler(key:Key, e):Promise { _errorHandler(key: Key, e): Promise<any> {
if (e instanceof AbstractBindingError) e.addKey(key); if (e instanceof AbstractBindingError) e.addKey(key);
return PromiseWrapper.reject(e); return PromiseWrapper.reject(e);
} }
_findOrCreate(key:Key, binding:ResolvedBinding, deps:List) { _findOrCreate(key: Key, binding: ResolvedBinding, deps: List<any>) {
try { try {
var instance = this.injector._getInstance(key); var instance = this.injector._getInstance(key);
if (!_isWaiting(instance)) return instance; 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); var resolvedList = ListWrapper.createFixedSize(bindings.length);
for (var i = 0; i < bindings.length; i++) { for (var i = 0; i < bindings.length; i++) {
var unresolved = bindings[i]; var unresolved = bindings[i];
@ -379,7 +376,7 @@ function _resolveBindings(bindings:List): List {
} else if (unresolved instanceof List) { } else if (unresolved instanceof List) {
resolved = _resolveBindings(unresolved); resolved = _resolveBindings(unresolved);
} else if (unresolved instanceof BindingBuilder) { } else if (unresolved instanceof BindingBuilder) {
throw new InvalidBindingError('BindingBuilder with ' + unresolved.token + ' token'); throw new InvalidBindingError(unresolved.token);
} else { } else {
throw new InvalidBindingError(unresolved); throw new InvalidBindingError(unresolved);
} }
@ -388,14 +385,15 @@ function _resolveBindings(bindings:List): List {
return resolvedList; return resolvedList;
} }
function _createListOfBindings(flattenedBindings):List { function _createListOfBindings(flattenedBindings): List<any> {
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1); var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v); MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v);
return bindings; return bindings;
} }
function _flattenBindings(bindings:List, res:Map):Map { function _flattenBindings(bindings: List<ResolvedBinding /* | List<any>*/>,
ListWrapper.forEach(bindings, function (b) { res: Map<number, ResolvedBinding>): Map<number, ResolvedBinding> {
ListWrapper.forEach(bindings, function(b) {
if (b instanceof ResolvedBinding) { if (b instanceof ResolvedBinding) {
MapWrapper.set(res, b.key.id, b); MapWrapper.set(res, b.key.id, b);
} else if (b instanceof List) { } else if (b instanceof List) {

View File

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

View File

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

View File

@ -9,6 +9,18 @@ export var __esModule = true;
export var Type = Function; export var Type = Function;
export type Type = typeof 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 Math = _global.Math;
export var Date = _global.Date; export var Date = _global.Date;
@ -28,7 +40,9 @@ if (assertionsEnabled_) {
} }
export {int}; export {int};
export class CONST {} export function CONST() {
return (target) => target;
};
export class ABSTRACT {} export class ABSTRACT {}
export class IMPLEMENTS {} export class IMPLEMENTS {}
@ -111,10 +125,10 @@ export class StringJoiner {
toString(): string { return this.parts.join(""); } toString(): string { return this.parts.join(""); }
} }
export class NumberParseError implements Error { export class NumberParseError extends BaseException {
name: string; name: string;
constructor(public message: string) {} constructor(public message: string) { super(); }
toString() { return this.message; } toString() { return this.message; }
} }
@ -191,9 +205,6 @@ export class FunctionWrapper {
static apply(fn: Function, posArgs) { return fn.apply(null, posArgs); } 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 // JS has NaN !== NaN
export function looseIdentical(a, b): boolean { export function looseIdentical(a, b): boolean {
return a === b || typeof a === "number" && typeof b === "number" && isNaN(a) && isNaN(b); 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'; import {global} from 'angular2/src/facade/lang';
export function makeDecorator(annotationCls) { export function makeDecorator(annotationCls) {
return function(...args) { return function() {
var args = arguments;
var Reflect = global.Reflect; var Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) { if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators'; 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) { return function(cls) {
var annotations = Reflect.getMetadata('annotations', cls); var annotations = Reflect.getMetadata('annotations', cls);
annotations = annotations || []; annotations = annotations || [];
@ -18,12 +22,14 @@ export function makeDecorator(annotationCls) {
} }
export function makeParamDecorator(annotationCls) { export function makeParamDecorator(annotationCls) {
return function(...args) { return function() {
var args = arguments;
var Reflect = global.Reflect; var Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) { if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using parameter decorators'; 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) { return function(cls, unusedKey, index) {
var parameters = Reflect.getMetadata('parameters', cls); var parameters = Reflect.getMetadata('parameters', cls);
parameters = parameters || []; parameters = parameters || [];

View File

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

View File

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

View File

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

2
npm-shrinkwrap.json generated
View File

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