diff --git a/.gitignore b/.gitignore
index 6d040bb53..8248d8e2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,8 @@ typings
dist
+src/*.js
src/**/*.js
+test/*.js
test/**/*.js
type_definitions/*.js
diff --git a/.travis.yml b/.travis.yml
index 0a40d3b77..eb97c3f12 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,12 @@
language: node_js
node_js:
- stable
-- 4.2.3
-- 4.2.0
-- 4.1.2
-- 4.1.0
-- 4.0.0
+- 5.4.1
+- 5.4.0
+- 5.3.0
+- 5.2.0
+- 5.1.1
- '0.12'
-- '0.10'
before_install:
- npm install -g typings
- typings install
\ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
index 6f09f126b..10c029c7f 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -9,7 +9,7 @@ var gulp = require("gulp"),
buffer = require("vinyl-buffer"),
tslint = require("gulp-tslint"),
tsc = require("gulp-typescript"),
- coveralls = require('gulp-coveralls'),
+ coveralls = require("gulp-coveralls"),
uglify = require("gulp-uglify"),
typedoc = require("gulp-typedoc"),
rename = require("gulp-rename"),
@@ -46,7 +46,7 @@ gulp.task("build-source", function() {
"node_modules/reflect-metadata/reflect-metadata.d.ts"
])
.pipe(tsc(tsProject))
- .on('error', function (err) {
+ .on("error", function (err) {
process.exit(1);
})
.js.pipe(gulp.dest("src/"));
@@ -61,7 +61,7 @@ gulp.task("build-test", function() {
"node_modules/reflect-metadata/reflect-metadata.d.ts"
])
.pipe(tsc(tsTestProject))
- .on('error', function (err) {
+ .on("error", function (err) {
process.exit(1);
})
.js.pipe(gulp.dest("test/"));
@@ -72,7 +72,7 @@ var tsTypeDefinitionsProject = tsc.createProject("tsconfig.json");
gulp.task("build-type-definitions", function() {
return gulp.src("type_definitions/**/*.ts")
.pipe(tsc(tsTypeDefinitionsProject))
- .on('error', function (err) {
+ .on("error", function (err) {
process.exit(1);
})
.js.pipe(gulp.dest("type_definitions/"));
@@ -114,7 +114,7 @@ gulp.task("document", function () {
gulp.task("bundle", function () {
var b = browserify({
- standalone : 'inversify',
+ standalone : "inversify",
entries: "src/inversify.js",
debug: true
});
@@ -131,15 +131,15 @@ gulp.task("bundle", function () {
//******************************************************************************
gulp.task("mocha", function() {
return gulp.src([
- 'node_modules/reflect-metadata/Reflect.js',
- 'test/**/*.test.js'
+ "node_modules/reflect-metadata/Reflect.js",
+ "test/**/*.test.js"
])
- .pipe(mocha({ui: 'bdd'}))
+ .pipe(mocha({ui: "bdd"}))
.pipe(istanbul.writeReports());
});
gulp.task("istanbul:hook", function() {
- return gulp.src(['src/**/*.js'])
+ return gulp.src(["src/**/*.js"])
// Covering files
.pipe(istanbul())
// Force `require` to return covered files
@@ -168,7 +168,7 @@ gulp.task("compress", function() {
return gulp.src("bundled/src/inversify.js")
.pipe(uglify({ preserveComments : false }))
.pipe(rename({
- extname: '.min.js'
+ extname: ".min.js"
}))
.pipe(gulp.dest("dist/"))
});
diff --git a/package.json b/package.json
index 0dc2cf2e8..6faea28a4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "inversify",
- "version": "2.0.0-alpha.1",
+ "version": "2.0.0-alpha.2",
"description": "A lightweight IoC container written in TypeScript.",
"main": "dist/inversify.js",
"typings": "type_definitions/inversify-npm.d.ts",
@@ -32,10 +32,9 @@
},
"homepage": "http://inversify.io",
"engines": {},
- "dependencies": {
- "reflect-metadata": "^0.1.3"
- },
+ "dependencies": {},
"devDependencies": {
+ "bluebird": "^3.3.3",
"browserify": "^13.0.0",
"chai": "^3.4.1",
"gulp": "^3.9.0",
@@ -50,6 +49,7 @@
"gulp-uglify": "^1.5.1",
"istanbul": "^0.4.2",
"mocha": "^2.3.4",
+ "reflect-metadata": "^0.1.3",
"run-sequence": "^1.1.5",
"sinon": "^1.17.3",
"tslint": "^3.2.2",
diff --git a/src/activation/decorator_utils.ts b/src/activation/decorator_utils.ts
index f897b2033..38c57f9a4 100644
--- a/src/activation/decorator_utils.ts
+++ b/src/activation/decorator_utils.ts
@@ -1,7 +1,5 @@
///
-import "reflect-metadata";
-
function tagParameter(target: any, targetKey: string, index: number, metadata: IMetadata) {
let metadataKey = "inversify:tagged";
let paramsMetadata: Object = null;
diff --git a/src/bindings/binding.ts b/src/bindings/binding.ts
index 38f4a1161..0dc4a8a76 100644
--- a/src/bindings/binding.ts
+++ b/src/bindings/binding.ts
@@ -16,7 +16,7 @@ class Binding implements IBinding {
public runtimeIdentifier: string;
// The constructor of a class which must implement T
- public implementationType: { new(): T; };
+ public implementationType: INewable;
// Cache used to allow singleton scope and BindingType.Value bindings
public cache: T;
@@ -28,15 +28,18 @@ class Binding implements IBinding {
public type: BindingType;
// A factory method used in BindingType.Factory bindings
- public factory: (context) => T;
+ public factory: IFactoryCreator;
+
+ // An async factory method used in BindingType.Provider bindings
+ public provider: IProviderCreator;
constructor(runtimeIdentifier: string) {
this.runtimeIdentifier = runtimeIdentifier;
- this.type = BindingType.Instance;
+ this.scope = BindingScope.Transient;
+ this.type = BindingType.Invalid;
this.implementationType = null;
this.cache = null;
this.factory = null;
- this.scope = BindingScope.Transient;
}
}
diff --git a/src/bindings/binding_type.ts b/src/bindings/binding_type.ts
index 789e3b4e2..b0878e050 100644
--- a/src/bindings/binding_type.ts
+++ b/src/bindings/binding_type.ts
@@ -1,10 +1,12 @@
///
enum BindingType {
+ Invalid,
Instance,
Value,
Constructor,
- Factory
+ Factory,
+ Provider
}
export default BindingType;
diff --git a/src/constants/error_msgs.ts b/src/constants/error_msgs.ts
index 4e5bcb922..64c532632 100644
--- a/src/constants/error_msgs.ts
+++ b/src/constants/error_msgs.ts
@@ -8,4 +8,5 @@ export const AMBIGUOUS_MATCH = "Ambiguous match found for service:";
export const CANNOT_UNBIND = "Could not unbind service:";
export const NOT_REGISTERED = "No bindigns found for service:";
export const CIRCULAR_DEPENDENCY = "Circular dependency found between services:";
-export const NOT_IMPLEMENTED = "Sorry, this feature is not fully implemented yet";
+export const NOT_IMPLEMENTED = "Sorry, this feature is not fully implemented yet.";
+export const INVALID_BINDING_TYPE = "Invalid binding type:";
diff --git a/src/interfaces/bindings/binding.d.ts b/src/interfaces/bindings/binding.d.ts
index 807e93e0b..92a7fc094 100644
--- a/src/interfaces/bindings/binding.d.ts
+++ b/src/interfaces/bindings/binding.d.ts
@@ -1,7 +1,10 @@
+///
+
interface IBinding {
runtimeIdentifier: string;
- implementationType: { new(): T; };
- factory: (context) => T;
+ implementationType: INewable;
+ factory: IFactoryCreator;
+ provider: IProviderCreator;
cache: T;
scope: number; // BindingScope
type: number; // BindingType
diff --git a/src/interfaces/bindings/factory.d.ts b/src/interfaces/bindings/factory.d.ts
new file mode 100644
index 000000000..47aec0545
--- /dev/null
+++ b/src/interfaces/bindings/factory.d.ts
@@ -0,0 +1,5 @@
+///
+
+interface IFactory extends Function {
+ (): T;
+}
diff --git a/src/interfaces/bindings/factory_creator.d.ts b/src/interfaces/bindings/factory_creator.d.ts
new file mode 100644
index 000000000..d3f10e064
--- /dev/null
+++ b/src/interfaces/bindings/factory_creator.d.ts
@@ -0,0 +1,5 @@
+///
+
+interface IFactoryCreator extends Function {
+ (context: IContext): IFactory;
+}
diff --git a/src/interfaces/bindings/newable.d.ts b/src/interfaces/bindings/newable.d.ts
new file mode 100644
index 000000000..40ab73c67
--- /dev/null
+++ b/src/interfaces/bindings/newable.d.ts
@@ -0,0 +1,5 @@
+///
+
+interface INewable {
+ new(...args: any[]): T;
+}
diff --git a/src/interfaces/bindings/provider.d.ts b/src/interfaces/bindings/provider.d.ts
new file mode 100644
index 000000000..bffba00f5
--- /dev/null
+++ b/src/interfaces/bindings/provider.d.ts
@@ -0,0 +1,5 @@
+///
+
+interface IProvider extends Function {
+ (): Promise;
+}
diff --git a/src/interfaces/bindings/provider_creator.d.ts b/src/interfaces/bindings/provider_creator.d.ts
new file mode 100644
index 000000000..85a1c5463
--- /dev/null
+++ b/src/interfaces/bindings/provider_creator.d.ts
@@ -0,0 +1,5 @@
+///
+
+interface IProviderCreator extends Function {
+ (context: IContext): IProvider;
+}
diff --git a/src/interfaces/interfaces.d.ts b/src/interfaces/interfaces.d.ts
index 26bf18e20..100dfd92c 100644
--- a/src/interfaces/interfaces.d.ts
+++ b/src/interfaces/interfaces.d.ts
@@ -1,26 +1,38 @@
+
+// KERNEL
///
///
///
///
///
+// PLANNING
///
///
+///
+///
+///
+///
+// RESOLUTION
///
+// BINDINGS
///
///
+///
+///
+///
+///
+///
+// ACTIVATION
///
-///
-///
-///
-///
-
+// MIDDLEWARE
///
+// SYNTAX
///
///
///
diff --git a/src/interfaces/syntax/binding_to_syntax.d.ts b/src/interfaces/syntax/binding_to_syntax.d.ts
index 1f3530d9d..091f0450e 100644
--- a/src/interfaces/syntax/binding_to_syntax.d.ts
+++ b/src/interfaces/syntax/binding_to_syntax.d.ts
@@ -4,6 +4,7 @@
interface IBindingToSyntax {
to(constructor: { new(...args: any[]): T; }): IBindingInSyntax;
toValue(value: T): IBindingWhenSyntax;
- toConstructor(constructor: { new(...args: any[]): T; }): IBindingWhenSyntax;
- toFactory(factory: (context) => T): IBindingWhenSyntax;
+ toConstructor(constructor: INewable): IBindingWhenSyntax;
+ toFactory(factory: IFactoryCreator): IBindingWhenSyntax;
+ toProvider(provider: IProviderCreator): IBindingWhenSyntax;
}
diff --git a/src/inversify.js b/src/inversify.js
deleted file mode 100644
index 645894c50..000000000
--- a/src/inversify.js
+++ /dev/null
@@ -1,13 +0,0 @@
-"use strict";
-var kernel_1 = require("./kernel/kernel");
-exports.Kernel = kernel_1.default;
-var inject_1 = require("./activation/inject");
-exports.Inject = inject_1.default;
-var tagged_1 = require("./activation/tagged");
-exports.Tagged = tagged_1.default;
-var named_1 = require("./activation/named");
-exports.Named = named_1.default;
-var paramnames_1 = require("./activation/paramnames");
-exports.ParamNames = paramnames_1.default;
-var decorator_utils_1 = require("./activation/decorator_utils");
-exports.decorate = decorator_utils_1.decorate;
diff --git a/src/planning/planner.ts b/src/planning/planner.ts
index c4e1406e0..f6a286758 100644
--- a/src/planning/planner.ts
+++ b/src/planning/planner.ts
@@ -1,12 +1,12 @@
///
-import "reflect-metadata";
import Plan from "./plan";
import Context from "./context";
import Request from "./request";
import Target from "./target";
import * as METADATA_KEY from "../constants/metadata_keys";
import * as ERROR_MSGS from "../constants/error_msgs";
+import BindingType from "../bindings/binding_type";
class Planner implements IPlanner {
@@ -58,16 +58,19 @@ class Planner implements IPlanner {
} else {
- // TODO 2.0.0-alpha.2 handle value, factory, etc here
+ // Use the only active binding to create a child request
let binding = bindings[0];
-
let childRequest = parentRequest.addChildRequest(target.service.value(), binding, target);
- let subDependencies = this._getDependencies(binding.implementationType);
+ // Only try to plan sub-dependencies when binding type is BindingType.Instance
+ if (binding.type === BindingType.Instance) {
- subDependencies.forEach((d, index) => {
- this._createSubRequest(childRequest, d);
- });
+ // Create child requests for sub-dependencies if any
+ let subDependencies = this._getDependencies(binding.implementationType);
+ subDependencies.forEach((d, index) => {
+ this._createSubRequest(childRequest, d);
+ });
+ }
}
} catch (error) {
if (error instanceof RangeError) {
diff --git a/src/resolution/resolver.ts b/src/resolution/resolver.ts
index 61492c207..50ed3b635 100644
--- a/src/resolution/resolver.ts
+++ b/src/resolution/resolver.ts
@@ -1,6 +1,8 @@
///
import BindingScope from "../bindings/binding_scope";
+import BindingType from "../bindings/binding_type";
+import * as ERROR_MSGS from "../constants/error_msgs";
class Resolver implements IResolver {
@@ -12,31 +14,53 @@ class Resolver implements IResolver {
public resolve(context: IContext): Service {
let rootRequest = context.plan.rootRequest;
- return this._construct(rootRequest);
+ return this._inject(rootRequest);
}
- private _construct(request) {
+ private _inject(request: IRequest) {
let childRequests = request.childRequests;
let binding = request.bindings[0]; // TODO handle multi-injection
- let constr = binding.implementationType;
- let isSingleton = binding.scope === BindingScope.Singleton;
- if (isSingleton && binding.cache !== null) {
- return binding.cache;
- }
+ switch (binding.type) {
+ case BindingType.Value:
+ return binding.cache;
+
+ case BindingType.Constructor:
+ return binding.implementationType;
+
+ case BindingType.Factory:
+ return binding.factory(request.parentContext);
+
+ case BindingType.Provider:
+ return binding.provider(request.parentContext);
+
+ case BindingType.Instance:
+ let constr = binding.implementationType;
+ let isSingleton = binding.scope === BindingScope.Singleton;
+
+ if (isSingleton && binding.cache !== null) {
+ return binding.cache;
+ }
+
+ if (childRequests.length > 0) {
+ let injections = childRequests.map((childRequest) => {
+ return this._inject(childRequest);
+ });
+ let instance = this._createInstance(constr, injections);
+ if (isSingleton) { binding.cache = instance; }
+ return instance;
+ } else {
+ let instance = new constr();
+ if (isSingleton) { binding.cache = instance; }
+ return instance;
+ }
- if (childRequests.length > 0) {
- let injections = childRequests.map((childRequest) => {
- return this._construct(childRequest);
- });
- let instance = this._createInstance(constr, injections);
- if (isSingleton) { binding.cache = instance; }
- return instance;
- } else {
- let instance = new constr();
- if (isSingleton) { binding.cache = instance; }
- return instance;
+ case BindingType.Invalid:
+ default:
+ // The user probably created a binding but didn't finish it
+ // e.g. kernel.bind("ISomething"); missing BindingToSyntax
+ throw new Error(`${ERROR_MSGS.INVALID_BINDING_TYPE} ${request.service}`);
}
}
diff --git a/src/syntax/binding_to_syntax.ts b/src/syntax/binding_to_syntax.ts
index a050e80e8..41c824f89 100644
--- a/src/syntax/binding_to_syntax.ts
+++ b/src/syntax/binding_to_syntax.ts
@@ -24,15 +24,21 @@ class BindingToSyntax implements IBindingToSyntax {
return new BindingWhenSyntax(this._binding);
}
- public toConstructor(constructor: { new(...args: any[]): T; }): IBindingWhenSyntax {
+ public toConstructor(constructor: INewable): IBindingWhenSyntax {
this._binding.type = BindingType.Constructor;
- this._binding.implementationType = constructor;
+ this._binding.implementationType = constructor;
return new BindingWhenSyntax(this._binding);
}
- public toFactory(factory: (context) => T): IBindingWhenSyntax {
+ public toFactory(factory: IFactoryCreator): IBindingWhenSyntax {
this._binding.type = BindingType.Factory;
- this._binding.factory = factory;
+ this._binding.factory = factory;
+ return new BindingWhenSyntax(this._binding);
+ }
+
+ public toProvider(provider: IProviderCreator) {
+ this._binding.type = BindingType.Provider;
+ this._binding.provider = provider;
return new BindingWhenSyntax(this._binding);
}
diff --git a/test/planning/planner.test.ts b/test/planning/planner.test.ts
index 8345db20e..d7fda31cf 100644
--- a/test/planning/planner.test.ts
+++ b/test/planning/planner.test.ts
@@ -1,6 +1,7 @@
///
import { expect } from "chai";
+import * as sinon from "sinon";
import Planner from "../../src/planning/planner";
import Context from "../../src/planning/context";
import Kernel from "../../src/kernel/kernel";
@@ -13,6 +14,16 @@ import * as ERROR_MSGS from "../../src/constants/error_msgs";
describe("Planner", () => {
+ let sandbox: Sinon.SinonSandbox;
+
+ beforeEach(() => {
+ sandbox = sinon.sandbox.create();
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+ });
+
it("Should be able to create instances of Context", () => {
let kernel = new Kernel();
@@ -40,7 +51,8 @@ describe("Planner", () => {
public handler: IKatanaHandler;
public blade: IKatanaBlade;
public constructor(handler: IKatanaHandler, blade: IKatanaBlade) {
- // DO NOTHING
+ this.handler = handler;
+ this.blade = blade;
}
}
@@ -55,7 +67,8 @@ describe("Planner", () => {
public katana: IKatana;
public shuriken: IShuriken;
public constructor(katana: IKatana, shuriken: IShuriken) {
- // DO NOTHING
+ this.katana = katana;
+ this.shuriken = shuriken;
}
}
@@ -184,7 +197,8 @@ describe("Planner", () => {
public katana: IWeapon;
public shuriken: IWeapon;
public constructor(katana: IWeapon, shuriken: IWeapon) {
- // DO NOTHING
+ this.katana = katana;
+ this.shuriken = shuriken;
}
}
@@ -258,4 +272,75 @@ describe("Planner", () => {
});
+ it("Should only plan sub-dependencies when binding type is BindingType.Instance", () => {
+
+ interface IKatanaBlade {}
+ class KatanaBlade implements IKatanaBlade {}
+
+ interface IKatanaHandler {}
+ class KatanaHandler implements IKatanaHandler {}
+
+ interface IKatana {}
+
+ @Inject("IKatanaHandler", "IKatanaBlade")
+ @ParamNames("handler", "blade")
+ class Katana implements IKatana {
+ public handler: IKatanaHandler;
+ public blade: IKatanaBlade;
+ public constructor(handler: IKatanaHandler, blade: IKatanaBlade) {
+ this.handler = handler;
+ this.blade = blade;
+ }
+ }
+
+ interface IShuriken {}
+ class Shuriken implements IShuriken {}
+
+ interface INinja {}
+
+ @Inject("IFactory", "IShuriken")
+ @ParamNames("katanaFactory", "shuriken")
+ class Ninja implements INinja {
+ public katanaFactory: IFactory;
+ public shuriken: IShuriken;
+ public constructor(katanaFactory: IFactory, shuriken: IShuriken) {
+ this.katanaFactory = katanaFactory;
+ this.shuriken = shuriken;
+ }
+ }
+
+ let ninjaId = "INinja";
+ let shurikenId = "IShuriken";
+ let katanaId = "IKatana";
+ let katanaHandlerId = "IKatanaHandler";
+ let katanaBladeId = "IKatanaBlade";
+ let katanaFactoryId = "IFactory";
+
+ let kernel = new Kernel();
+ kernel.bind(ninjaId).to(Ninja);
+ kernel.bind(shurikenId).to(Shuriken);
+ kernel.bind(katanaBladeId).to(Katana);
+ kernel.bind(katanaBladeId).to(KatanaBlade);
+ kernel.bind(katanaHandlerId).to(KatanaHandler);
+ kernel.bind>(katanaFactoryId).toFactory((context) => {
+ return () => {
+ return context.kernel.get(katanaId);
+ };
+ });
+
+ let _kernel: any = kernel;
+ let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0];
+ let planner = new Planner();
+ let context = planner.createContext(kernel);
+ let actualPlan = planner.createPlan(context, ninjaBinding);
+
+ expect(actualPlan.rootRequest.service).eql(ninjaId);
+ expect(actualPlan.rootRequest.childRequests[0].service).eql(katanaFactoryId);
+ expect(actualPlan.rootRequest.childRequests[0].childRequests.length).eql(0); // IMPORTANT!
+ expect(actualPlan.rootRequest.childRequests[1].service).eql(shurikenId);
+ expect(actualPlan.rootRequest.childRequests[1].childRequests.length).eql(0);
+ expect(actualPlan.rootRequest.childRequests[2]).eql(undefined);
+
+ });
+
});
diff --git a/test/resolution/resolver.test.ts b/test/resolution/resolver.test.ts
index 92689c6a2..a5fb592ee 100644
--- a/test/resolution/resolver.test.ts
+++ b/test/resolution/resolver.test.ts
@@ -10,6 +10,8 @@ import Plan from "../../src/planning/plan";
import Target from "../../src/planning/target";
import Inject from "../../src/activation/inject";
import ParamNames from "../../src/activation/paramnames";
+import * as ERROR_MSGS from "../../src/constants/error_msgs";
+import BindingType from "../../src/bindings/binding_type";
describe("Resolver", () => {
@@ -23,7 +25,7 @@ describe("Resolver", () => {
sandbox.restore();
});
- it("Should be able to resolve a basic plan", () => {
+ it("Should be able to resolve BindingType.Instance bindings", () => {
interface IKatanaBlade {}
class KatanaBlade implements IKatanaBlade {}
@@ -221,4 +223,409 @@ describe("Resolver", () => {
});
+ it("Should throw when an invalid BindingType is detected", () => {
+
+ interface IKatana {}
+ class Katana implements IKatana {}
+
+ interface IShuriken {}
+ class Shuriken implements IShuriken {}
+
+ interface INinja {
+ katana: IKatana;
+ shuriken: IShuriken;
+ }
+
+ @Inject("IKatana", "IShuriken")
+ @ParamNames("katana", "shuriken")
+ class Ninja implements INinja {
+ public katana: IKatana;
+ public shuriken: IShuriken;
+ public constructor(katana: IKatana, shuriken: IShuriken) {
+ this.katana = katana;
+ this.shuriken = shuriken;
+ }
+ }
+
+ // kernel and bindings
+ let ninjaId = "INinja";
+ let kernel = new Kernel();
+ let _kernel: any = kernel;
+ kernel.bind(ninjaId); // IMPORTAN! (Invalid binding)
+ let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0];
+
+ // context and plan
+ let planner = new Planner();
+ let context = planner.createContext(kernel);
+ let ninjaRequest = new Request(ninjaId, context, null, ninjaBinding, null);
+ let plan = new Plan(context, ninjaRequest);
+ context.addPlan(plan);
+
+ // resolver
+ let resolver = new Resolver([]);
+ let _resolver: any = resolver;
+ let _inject = _resolver._inject;
+
+ let throwFunction = () => {
+ _inject(ninjaRequest);
+ };
+
+ expect(ninjaRequest.bindings[0].type).eql(BindingType.Invalid);
+ expect(throwFunction).to.throw(`${ERROR_MSGS.INVALID_BINDING_TYPE} ${ninjaId}`);
+
+ });
+
+ it("Should be able to resolve BindingType.Value bindings", () => {
+
+ interface IKatanaBlade {}
+ class KatanaBlade implements IKatanaBlade {}
+
+ interface IKatanaHandler {}
+ class KatanaHandler implements IKatanaHandler {}
+
+ interface IKatana {
+ handler: IKatanaHandler;
+ blade: IKatanaBlade;
+ }
+
+ class Katana implements IKatana {
+ public handler: IKatanaHandler;
+ public blade: IKatanaBlade;
+ public constructor(handler: IKatanaHandler, blade: IKatanaBlade) {
+ this.handler = handler;
+ this.blade = blade;
+ }
+ }
+
+ interface IShuriken {}
+ class Shuriken implements IShuriken {}
+
+ interface INinja {
+ katana: IKatana;
+ shuriken: IShuriken;
+ }
+
+ @Inject("IKatana", "IShuriken")
+ @ParamNames("katana", "shuriken")
+ class Ninja implements INinja {
+ public katana: IKatana;
+ public shuriken: IShuriken;
+ public constructor(katana: IKatana, shuriken: IShuriken) {
+ this.katana = katana;
+ this.shuriken = shuriken;
+ }
+ }
+
+ let ninjaId = "INinja";
+ let shurikenId = "IShuriken";
+ let katanaId = "IKatana";
+
+ let kernel = new Kernel();
+ kernel.bind(ninjaId).to(Ninja);
+ kernel.bind(shurikenId).to(Shuriken);
+ kernel.bind(katanaId).toValue(new Katana(new KatanaHandler(), new KatanaBlade())); // IMPORTANT!
+
+ let _kernel: any = kernel;
+ let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0];
+ let katanaBinding = _kernel._bindingDictionary.get(katanaId)[0];
+ let shurikenBinding = _kernel._bindingDictionary.get(shurikenId)[0];
+
+ let planner = new Planner();
+ let context = planner.createContext(kernel);
+
+ /*
+ * Plan (request tree):
+ *
+ * Ninja (target "null", no metadata)
+ * -- Katana (target "katama", no metadata)
+ * -- Shuriken (target "shuriken", no metadata)
+ */
+ let ninjaRequest = new Request(ninjaId, context, null, ninjaBinding, null);
+ let plan = new Plan(context, ninjaRequest);
+ plan.rootRequest.addChildRequest(katanaId, katanaBinding, new Target("katana", katanaId));
+ plan.rootRequest.addChildRequest(shurikenId, shurikenBinding, new Target("shuriken", shurikenId));
+ context.addPlan(plan);
+
+ let resolver = new Resolver();
+ let ninja = resolver.resolve(context);
+
+ expect(ninja instanceof Ninja).eql(true);
+ expect(ninja.katana instanceof Katana).eql(true);
+ expect(ninja.katana.handler instanceof KatanaHandler).eql(true);
+ expect(ninja.katana.blade instanceof KatanaBlade).eql(true);
+ expect(ninja.shuriken instanceof Shuriken).eql(true);
+
+ });
+
+ it("Should be able to resolve BindingType.Constructor bindings", () => {
+
+ interface IKatanaBlade {}
+ class KatanaBlade implements IKatanaBlade {}
+
+ interface IKatanaHandler {}
+ class KatanaHandler implements IKatanaHandler {}
+
+ interface IKatana {
+ handler: IKatanaHandler;
+ blade: IKatanaBlade;
+ }
+
+ @Inject("IKatanaHandler", "IKatanaBlade")
+ @ParamNames("handler", "blade")
+ class Katana implements IKatana {
+ public handler: IKatanaHandler;
+ public blade: IKatanaBlade;
+ public constructor(handler: IKatanaHandler, blade: IKatanaBlade) {
+ this.handler = handler;
+ this.blade = blade;
+ }
+ }
+
+ interface IShuriken {}
+ class Shuriken implements IShuriken {}
+
+ interface INinja {
+ katana: IKatana;
+ shuriken: IShuriken;
+ }
+
+ @Inject("IKatana", "IShuriken")
+ @ParamNames("katana", "shuriken")
+ class Ninja implements INinja {
+ public katana: IKatana;
+ public shuriken: IShuriken;
+ public constructor(Katana: INewable, shuriken: IShuriken) {
+ this.katana = new Katana(new KatanaHandler(), new KatanaBlade()); // IMPORTANT!
+ this.shuriken = shuriken;
+ }
+ }
+
+ let ninjaId = "INinja";
+ let shurikenId = "IShuriken";
+ let newableKatanaId = "INewable";
+
+ let kernel = new Kernel();
+ kernel.bind(ninjaId).to(Ninja);
+ kernel.bind(shurikenId).to(Shuriken);
+ kernel.bind>(newableKatanaId).toConstructor(Katana); // IMPORTANT!
+
+ let _kernel: any = kernel;
+ let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0];
+ let newableKatanaBinding = _kernel._bindingDictionary.get(newableKatanaId)[0];
+ let shurikenBinding = _kernel._bindingDictionary.get(shurikenId)[0];
+
+ let planner = new Planner();
+ let context = planner.createContext(kernel);
+
+ let ninjaRequest = new Request(ninjaId, context, null, ninjaBinding, null);
+ let plan = new Plan(context, ninjaRequest);
+ plan.rootRequest.addChildRequest(newableKatanaId, newableKatanaBinding, new Target("Katana", newableKatanaId));
+ plan.rootRequest.addChildRequest(shurikenId, shurikenBinding, new Target("shuriken", shurikenId));
+ context.addPlan(plan);
+
+ let resolver = new Resolver();
+ let ninja = resolver.resolve(context);
+
+ expect(ninja instanceof Ninja).eql(true);
+ expect(ninja.katana instanceof Katana).eql(true);
+ expect(ninja.katana.handler instanceof KatanaHandler).eql(true);
+ expect(ninja.katana.blade instanceof KatanaBlade).eql(true);
+ expect(ninja.shuriken instanceof Shuriken).eql(true);
+
+ });
+
+ it("Should be able to resolve BindingType.Factory bindings", () => {
+
+ interface IKatanaBlade {}
+ class KatanaBlade implements IKatanaBlade {}
+
+ interface IKatanaHandler {}
+ class KatanaHandler implements IKatanaHandler {}
+
+ interface IKatana {
+ handler: IKatanaHandler;
+ blade: IKatanaBlade;
+ }
+
+ interface IKatanaFactory extends Function {
+ (): IKatana;
+ }
+
+ @Inject("IKatanaHandler", "IKatanaBlade")
+ @ParamNames("handler", "blade")
+ class Katana implements IKatana {
+ public handler: IKatanaHandler;
+ public blade: IKatanaBlade;
+ public constructor(handler: IKatanaHandler, blade: IKatanaBlade) {
+ this.handler = handler;
+ this.blade = blade;
+ }
+ }
+
+ interface IShuriken {}
+ class Shuriken implements IShuriken {}
+
+ interface INinja {
+ katana: IKatana;
+ shuriken: IShuriken;
+ }
+
+ @Inject("IKatana", "IShuriken")
+ @ParamNames("katana", "shuriken")
+ class Ninja implements INinja {
+ public katana: IKatana;
+ public shuriken: IShuriken;
+ public constructor(makeKatana: IKatanaFactory, shuriken: IShuriken) {
+ this.katana = makeKatana(); // IMPORTANT!
+ this.shuriken = shuriken;
+ }
+ }
+
+ let ninjaId = "INinja";
+ let shurikenId = "IShuriken";
+ let katanaFactoryId = "IFactory";
+ let katanaId = "IKatana";
+ let katanaHandlerId = "IKatanaHandler";
+ let katanaBladeId = "IKatanaBlade";
+
+ let kernel = new Kernel();
+ kernel.bind(ninjaId).to(Ninja);
+ kernel.bind(shurikenId).to(Shuriken);
+ kernel.bind(katanaId).to(Katana);
+ kernel.bind(katanaBladeId).to(KatanaBlade);
+ kernel.bind(katanaHandlerId).to(KatanaHandler);
+
+ kernel.bind>(katanaFactoryId).toFactory((context: IContext) => {
+ return () => {
+ return context.kernel.get(katanaId);
+ };
+ });
+
+ let _kernel: any = kernel;
+ let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0];
+ let katanaFactoryBinding = _kernel._bindingDictionary.get(katanaFactoryId)[0];
+ let shurikenBinding = _kernel._bindingDictionary.get(shurikenId)[0];
+
+ let planner = new Planner();
+ let context = planner.createContext(kernel);
+
+ let ninjaRequest = new Request(ninjaId, context, null, ninjaBinding, null);
+ let plan = new Plan(context, ninjaRequest);
+ plan.rootRequest.addChildRequest(katanaFactoryId, katanaFactoryBinding, new Target("makeKatana", katanaFactoryId));
+ plan.rootRequest.addChildRequest(shurikenId, shurikenBinding, new Target("shuriken", shurikenId));
+ context.addPlan(plan);
+
+ let resolver = new Resolver();
+ let ninja = resolver.resolve(context);
+
+ expect(ninja instanceof Ninja).eql(true);
+ expect(ninja.katana instanceof Katana).eql(true);
+ expect(ninja.katana.handler instanceof KatanaHandler).eql(true);
+ expect(ninja.katana.blade instanceof KatanaBlade).eql(true);
+ expect(ninja.shuriken instanceof Shuriken).eql(true);
+
+ });
+
+ it("Should be able to resolve BindingType.Provider bindings", (done) => {
+
+ interface IKatanaBlade {}
+ class KatanaBlade implements IKatanaBlade {}
+
+ interface IKatanaHandler {}
+ class KatanaHandler implements IKatanaHandler {}
+
+ interface IKatana {
+ handler: IKatanaHandler;
+ blade: IKatanaBlade;
+ }
+
+ interface IKatanaFactory extends Function {
+ (): IKatana;
+ }
+
+ @Inject("IKatanaHandler", "IKatanaBlade")
+ @ParamNames("handler", "blade")
+ class Katana implements IKatana {
+ public handler: IKatanaHandler;
+ public blade: IKatanaBlade;
+ public constructor(handler: IKatanaHandler, blade: IKatanaBlade) {
+ this.handler = handler;
+ this.blade = blade;
+ }
+ }
+
+ interface IShuriken {}
+ class Shuriken implements IShuriken {}
+
+ interface INinja {
+ katana: IKatana;
+ katanaProvider: IProvider;
+ shuriken: IShuriken;
+ }
+
+ @Inject("IKatana", "IShuriken")
+ @ParamNames("katana", "shuriken")
+ class Ninja implements INinja {
+ public katana: IKatana;
+ public katanaProvider: IProvider;
+ public shuriken: IShuriken;
+ public constructor(katanaProvider: IProvider, shuriken: IShuriken) {
+ this.katana = null;
+ this.katanaProvider = katanaProvider;
+ this.shuriken = shuriken;
+ }
+ }
+
+ let ninjaId = "INinja";
+ let shurikenId = "IShuriken";
+ let katanaFactoryId = "IFactory";
+ let katanaId = "IKatana";
+ let katanaHandlerId = "IKatanaHandler";
+ let katanaBladeId = "IKatanaBlade";
+
+ let kernel = new Kernel();
+ kernel.bind(ninjaId).to(Ninja);
+ kernel.bind(shurikenId).to(Shuriken);
+ kernel.bind(katanaId).to(Katana);
+ kernel.bind(katanaBladeId).to(KatanaBlade);
+ kernel.bind(katanaHandlerId).to(KatanaHandler);
+
+ kernel.bind>(katanaFactoryId).toProvider((context: IContext) => {
+ return () => {
+ return new Promise((resolve) => {
+ // Using setTimeout to simulate complex initialization
+ setTimeout(() => { resolve(context.kernel.get(katanaId)); }, 100);
+ });
+ };
+ });
+
+ let _kernel: any = kernel;
+ let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0];
+ let katanaFactoryBinding = _kernel._bindingDictionary.get(katanaFactoryId)[0];
+ let shurikenBinding = _kernel._bindingDictionary.get(shurikenId)[0];
+
+ let planner = new Planner();
+ let context = planner.createContext(kernel);
+
+ let ninjaRequest = new Request(ninjaId, context, null, ninjaBinding, null);
+ let plan = new Plan(context, ninjaRequest);
+ plan.rootRequest.addChildRequest(katanaFactoryId, katanaFactoryBinding, new Target("makeKatana", katanaFactoryId));
+ plan.rootRequest.addChildRequest(shurikenId, shurikenBinding, new Target("shuriken", shurikenId));
+ context.addPlan(plan);
+
+ let resolver = new Resolver();
+ let ninja = resolver.resolve(context);
+
+ expect(ninja instanceof Ninja).eql(true);
+ expect(ninja.shuriken instanceof Shuriken).eql(true);
+ ninja.katanaProvider().then((katana) => {
+ ninja.katana = katana;
+ expect(ninja.katana instanceof Katana).eql(true);
+ expect(ninja.katana.handler instanceof KatanaHandler).eql(true);
+ expect(ninja.katana.blade instanceof KatanaBlade).eql(true);
+ done();
+ });
+
+ });
+
});
diff --git a/test/syntax/binding_to_syntax.test.ts b/test/syntax/binding_to_syntax.test.ts
index 7249e0531..719011c20 100644
--- a/test/syntax/binding_to_syntax.test.ts
+++ b/test/syntax/binding_to_syntax.test.ts
@@ -31,22 +31,39 @@ describe("BindingToSyntax", () => {
let binding = new Binding(ninjaIdentifier);
let bindingToSyntax = new BindingToSyntax(binding);
- expect(binding.type).eql(BindingType.Instance);
+ expect(binding.type).eql(BindingType.Invalid);
bindingToSyntax.to(Ninja);
expect(binding.type).eql(BindingType.Instance);
+ expect(binding.implementationType).not.to.eql(null);
bindingToSyntax.toValue(new Ninja());
expect(binding.type).eql(BindingType.Value);
+ expect(binding.cache instanceof Ninja).eql(true);
- bindingToSyntax.toConstructor(Ninja);
+ bindingToSyntax.toConstructor(Ninja);
expect(binding.type).eql(BindingType.Constructor);
+ expect(binding.implementationType).not.to.eql(null);
- bindingToSyntax.toConstructor(Ninja);
- expect(binding.type).eql(BindingType.Constructor);
+ bindingToSyntax.toFactory((context) => {
+ return () => {
+ return new Ninja();
+ };
+ });
- bindingToSyntax.toFactory((context) => { return new Ninja(); });
expect(binding.type).eql(BindingType.Factory);
+ expect(binding.factory).not.to.eql(null);
+
+ bindingToSyntax.toProvider((context) => {
+ return () => {
+ return new Promise((resolve) => {
+ resolve(new Ninja());
+ });
+ };
+ });
+
+ expect(binding.type).eql(BindingType.Provider);
+ expect(binding.provider).not.to.eql(null);
});
diff --git a/type_definitions/inversify-global-test.ts b/type_definitions/inversify-global-test.ts
index 4b7617caf..b782b001b 100644
--- a/type_definitions/inversify-global-test.ts
+++ b/type_definitions/inversify-global-test.ts
@@ -75,4 +75,25 @@ module inversify_global_test {
let ninja2 = kernel2.get("INinja");
console.log(ninja2);
+ // binding types
+ kernel2.bind("IKatana").to(Katana);
+ kernel2.bind("IKatana").toValue(new Katana());
+
+ kernel2.bind<__inversify.INewable>("IKatana").toConstructor(Katana);
+
+ kernel2.bind<__inversify.IFactory>("IKatana").toFactory((context) => {
+ return () => {
+ return kernel2.get("IKatana");
+ };
+ });
+
+ kernel2.bind<__inversify.IProvider>("IKatana").toProvider((context) => {
+ return () => {
+ return new Promise((resolve) => {
+ let katana = kernel2.get("IKatana");
+ resolve(katana);
+ });
+ };
+ });
+
}
diff --git a/type_definitions/inversify-global.d.ts b/type_definitions/inversify-global.d.ts
index 570d3bcd8..9817daec7 100644
--- a/type_definitions/inversify-global.d.ts
+++ b/type_definitions/inversify-global.d.ts
@@ -1,8 +1,10 @@
-// Type definitions for inversify 1.2.2
+// Type definitions for inversify 2.0.0-alpha.2
// Project: https://github.com/inversify/InversifyJS
// Definitions by: inversify
// Definitions: https://github.com/borisyankov/DefinitelyTyped
+///
+
declare namespace inversify {
export interface IMiddleware extends Function {
@@ -34,11 +36,32 @@ declare namespace inversify {
when(constraint: Constraint): void;
}
+ interface IFactoryCreator extends Function {
+ (context: IContext): IFactory;
+ }
+
+ interface IProviderCreator extends Function {
+ (context: IContext): IProvider;
+ }
+
+ export interface IFactory extends Function {
+ (): T;
+ }
+
+ export interface IProvider extends Function {
+ (): Promise;
+ }
+
+ export interface INewable {
+ new(...args: any[]): T;
+ }
+
interface IBindingToSyntax {
to(constructor: { new(...args: any[]): T; }): IBindingInSyntax;
toValue(value: T): IBindingWhenSyntax;
- toConstructor(constructor: { new(...args: any[]): T; }): IBindingWhenSyntax;
- toFactory(factory: (context) => T): IBindingWhenSyntax;
+ toConstructor(constructor: INewable): IBindingWhenSyntax;
+ toFactory(factory: IFactoryCreator): IBindingWhenSyntax;
+ toProvider(provider: IProviderCreator): IBindingWhenSyntax;
}
interface IBindingInSyntax {
@@ -49,7 +72,8 @@ declare namespace inversify {
export interface IBinding {
runtimeIdentifier: string;
implementationType: { new(): T; };
- factory: (context) => T;
+ factory: IFactoryCreator;
+ provider: IProviderCreator;
cache: T;
scope: number; // BindingScope
type: number; // BindingType
diff --git a/type_definitions/inversify-npm.d.ts b/type_definitions/inversify-npm.d.ts
index 426c09397..ffa43911f 100644
--- a/type_definitions/inversify-npm.d.ts
+++ b/type_definitions/inversify-npm.d.ts
@@ -1,8 +1,10 @@
-// Type definitions for inversify 1.2.2
+// Type definitions for inversify 2.0.0-alpha.2
// Project: https://github.com/inversify/InversifyJS
// Definitions by: inversify
// Definitions: https://github.com/borisyankov/DefinitelyTyped
+///
+
interface IMiddleware extends Function {
(...args: any[]): any;
}
@@ -32,11 +34,32 @@ interface IBindingWhenSyntax {
when(constraint: Constraint): void;
}
+interface IFactoryCreator extends Function {
+ (context: IContext): IFactory;
+}
+
+interface IProviderCreator extends Function {
+ (context: IContext): IProvider;
+}
+
+interface IFactory extends Function {
+ (): T;
+}
+
+interface IProvider extends Function {
+ (): Promise;
+}
+
+interface INewable {
+ new(...args: any[]): T;
+}
+
interface IBindingToSyntax {
to(constructor: { new(...args: any[]): T; }): IBindingInSyntax;
toValue(value: T): IBindingWhenSyntax;
- toConstructor(constructor: { new(...args: any[]): T; }): IBindingWhenSyntax;
- toFactory(factory: (context) => T): IBindingWhenSyntax;
+ toConstructor(constructor: INewable): IBindingWhenSyntax;
+ toFactory(factory: IFactoryCreator): IBindingWhenSyntax;
+ toProvider(provider: IProviderCreator): IBindingWhenSyntax;
}
interface IBindingInSyntax {
@@ -47,7 +70,8 @@ interface IBindingInSyntax {
interface IBinding {
runtimeIdentifier: string;
implementationType: { new(): T; };
- factory: (context) => T;
+ factory: IFactoryCreator;
+ provider: IProviderCreator;
cache: T;
scope: number; // BindingScope
type: number; // BindingType
diff --git a/type_definitions/inversify-test.ts b/type_definitions/inversify-test.ts
index 8c7d26f47..17ace77b0 100644
--- a/type_definitions/inversify-test.ts
+++ b/type_definitions/inversify-test.ts
@@ -1,6 +1,6 @@
///
-import { Kernel, Inject, IKernel, IKernelOptions, IKernelModule } from "inversify";
+import { Kernel, Inject, IKernel, IKernelOptions, INewable, IKernelModule, IFactory, IProvider } from "inversify";
module inversify_external_module_test {
@@ -73,4 +73,25 @@ module inversify_external_module_test {
let ninja2 = kernel2.get("INinja");
console.log(ninja2);
+ // binding types
+ kernel2.bind("IKatana").to(Katana);
+ kernel2.bind("IKatana").toValue(new Katana());
+
+ kernel2.bind>("IKatana").toConstructor(Katana);
+
+ kernel2.bind>("IKatana").toFactory((context) => {
+ return () => {
+ return kernel2.get("IKatana");
+ };
+ });
+
+ kernel2.bind>("IKatana").toProvider((context) => {
+ return () => {
+ return new Promise((resolve) => {
+ let katana = kernel2.get("IKatana");
+ resolve(katana);
+ });
+ };
+ });
+
}
diff --git a/type_definitions/inversify.d.ts b/type_definitions/inversify.d.ts
index ea304678a..a361fccfa 100644
--- a/type_definitions/inversify.d.ts
+++ b/type_definitions/inversify.d.ts
@@ -1,8 +1,10 @@
-// Type definitions for inversify 1.2.2
+// Type definitions for inversify 2.0.0-alpha.2
// Project: https://github.com/inversify/InversifyJS
// Definitions by: inversify
// Definitions: https://github.com/borisyankov/DefinitelyTyped
+///
+
declare namespace __inversify {
export interface IMiddleware extends Function {
@@ -34,11 +36,32 @@ declare namespace __inversify {
when(constraint: Constraint): void;
}
+ interface IFactoryCreator extends Function {
+ (context: IContext): IFactory;
+ }
+
+ interface IProviderCreator extends Function {
+ (context: IContext): IProvider;
+ }
+
+ export interface IFactory extends Function {
+ (): T;
+ }
+
+ export interface IProvider extends Function {
+ (): Promise;
+ }
+
+ export interface INewable {
+ new(...args: any[]): T;
+ }
+
interface IBindingToSyntax {
to(constructor: { new(...args: any[]): T; }): IBindingInSyntax;
toValue(value: T): IBindingWhenSyntax;
- toConstructor(constructor: { new(...args: any[]): T; }): IBindingWhenSyntax;
- toFactory(factory: (context) => T): IBindingWhenSyntax;
+ toConstructor(constructor: INewable): IBindingWhenSyntax;
+ toFactory(factory: IFactoryCreator): IBindingWhenSyntax;
+ toProvider(provider: IProviderCreator): IBindingWhenSyntax;
}
interface IBindingInSyntax {
@@ -49,7 +72,8 @@ declare namespace __inversify {
export interface IBinding {
runtimeIdentifier: string;
implementationType: { new(): T; };
- factory: (context) => T;
+ factory: IFactoryCreator;
+ provider: IProviderCreator;
cache: T;
scope: number; // BindingScope
type: number; // BindingType
diff --git a/typings.json b/typings.json
index 52d066398..d7f3ddaa0 100644
--- a/typings.json
+++ b/typings.json
@@ -3,6 +3,7 @@
"dependencies": {},
"devDependencies": {},
"ambientDependencies": {
+ "bluebird": "github:DefinitelyTyped/DefinitelyTyped/bluebird/bluebird.d.ts#dd328830dddffbe19e9addd7cf8532cbd3600816",
"chai": "github:DefinitelyTyped/DefinitelyTyped/chai/chai.d.ts#1914a00b3c740348dae407ee0d2be89b0b58ad7f",
"mocha": "github:DefinitelyTyped/DefinitelyTyped/mocha/mocha.d.ts#d6dd320291705694ba8e1a79497a908e9f5e6617",
"sinon": "github:DefinitelyTyped/DefinitelyTyped/sinon/sinon.d.ts#6e7d83167d86d857817bd492786f8304f5c4a0e4"
diff --git a/wallaby.js b/wallaby.js
index c219b359c..eafc03ac3 100644
--- a/wallaby.js
+++ b/wallaby.js
@@ -12,7 +12,8 @@ module.exports = function (wallaby) {
files : [
{ pattern: "src/**/*.ts", load: false },
{ pattern: "typings/browser.d.ts", load: false },
- { pattern: "node_modules/reflect-metadata/reflect-metadata.d.ts", load: false }
+ { pattern: "node_modules/reflect-metadata/reflect-metadata.d.ts", load: false },
+ { pattern: "node_modules/reflect-metadata/Reflect.js", load: true }
],
tests: [
{ pattern: "test/**/*.ts", load: false },