inversify / inversifyjs Goto Github PK
View Code? Open in Web Editor NEWA powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
Home Page: http://inversify.io/
License: MIT License
A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
Home Page: http://inversify.io/
License: MIT License
The idea is to allow developers to bind multiple implementation types (classes) to one service type (interface). A "constraint" function is used to decide whether InversifyJS should inject one implementation type (class) or another based on the context of the injection.
The context of the injection will be provided via the Reflection metadata API.
See #14
Initial API thoughts:
https://gist.github.com/remojansen/dbd57b72622ebf1129f8
I have installed Inversify version 1.2.0 from NPM and have saved it to my dependencies
in package.json, and I have installed the typings with TSD also.
When I import Inject
from the inversify
module, I get an error Cannot find module 'inversify'
.
/// <reference path="../typings/inversify/inversify.d.ts" />
import { Inject } from 'inversify';
The ///
reference is pointing to the correct typings file.
declare module inversify {
should be
declare module 'inversify' {
So that the following will compile
import inversify = require('inversify');
Right now its an ambient internal module and needs to be an ambient external module.
See 'Working with Other JavaScript Libraries' at http://www.typescriptlang.org/Handbook#modules
At the moment, too much is being distributed with the bower distribution.
Included in the distribution is the TypeScript source, the Karma configuration, and the test files.
The package size could be slimmed down considerably by only distributing the compiled JS, the LICENSE and the bower.json.
To allow the minifiers to rename the function parameters and still be able to inject the right services, the function needs to be annotated.
Will be implemented using Reflection Metadata API. Useful links:
microsoft/TypeScript#2249
https://www.npmjs.com/package/reflect-metadata
https://github.com/matjaz/property-DI/
I may be wrong but from what I see dependencies are being resolved at runtime right? If that's the case, wouldn't it be possible to "dump" a file with all the dependencies resolved?
Problem: Currently we rely on the constructor argument names to inject dependencies. This causes problems with minification (as the argument name is changed) and injecting two dependencies of the same type into a class (as they both can't have the same argument name).
Proposed Solution: Decorators
As an addition to being able to resolve the dependencies of the class by argument name, it should also possible to do so via decorator (see example below)
class A {
constructor (@Inject("IB") be: IB) {
be.fancy();
}
}
interface IB {
fancy(): void;
}
class B implements IB {
fancy() {
console.log("this does something I promise");
}
}
The decorator should be given the string which relates to the binding registered in the kernel i.e. in this scenario...
let kernel = new inversify.Kernel();
kernel.bind(new inversify.TypeBinding<IB>("IB", B));
In terms of solutions I have already developed a brief concept so I suggest that we add the following...
let Inject = function (typeIdentifier: string) {
return function (target: InjectableConstructorInterface, propertyName: string, argumentIndex: number) {
//if there are no decorated argument types associated with this type
//then add them as the argument names (as currently implemented)
if (!target.argumentTypes) {
// Regular expressions used to get a list containing
// the names of the arguments of a function
let STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
let ARGUMENT_NAMES = /([^\s,]+)/g;
let fnStr = target.toString().replace(STRIP_COMMENTS, '');
let argsInit = fnStr.indexOf('(') + 1;
let argsEnd = fnStr.indexOf(')');
target.argumentTypes = fnStr.slice(argsInit, argsEnd).match(ARGUMENT_NAMES);
}
//override assumed type with the decorated type
target.argumentTypes[argumentIndex] = typeIdentifier;
};
};
export { Inject };
then adjust how _getConstructorArguments works slightly to always return the argumentTypes array in favor of the new decorator defined arguments if these are available.
interface InjectableConstructorInterface {
argumentTypes: Array<string>;
}
private _getConstructorArguments(func : InjectableConstructorInterface) {
if (func.argumentTypes) {
return func.argumentTypes;
}
... (continues to execute the existing code)
What are your thoughts @remojansen? I can have this code ready in a PR if you're happy, I'm also happy to discuss naming or tweaks/additions in functionality.
Consider using node-introspect as suggested by @orzarchi at #23 (comment)
Is there anyway to use the container to inject commonjs modules in to your classes as well? Based on the .d.ts, whatever you pass in as the implementation type must have a constructor. Essentially, I would like to use the container to inject modules like express in to the constructors of my classes.
Also, is there any way to support dependencies with callbacks? What if a class needs to do some IO ops when it is being constructed. I would guess these needs to be synchronous, otherwise the container might inject the dependency before its ready.
The JavaScript module ecosystem is a mess these days. For module definitions, we have AMD, UMD, CommonJS, globals, and ES6 modules 1. For distribution, we have npm, Bower, and jspm, as well as CDNs like cdnjs, jsDelivr, and Github itself. For translating between Node and browser code, we have Browserify, Webpack, and Rollup.
http://nolanlawson.com/2015/10/19/the-struggles-of-publishing-a-javascript-library
2.0.0-alpha.0
is not correct semantic versioning.
We should instead use 2-alpha.0.0
as we are in the alpha stage of version 2.
*I put this question on Stackoverflow, but there's no InversifyJS tag there yet: http://stackoverflow.com/questions/35149514/why-does-inversifyjs-not-compile-this-attribute
I'm copying the Inversify documentation almost exactly (version 1.2.2) and it fails to compile, giving me Typescript error TS1238. Why?
'use strict';
import './tools/typescriptImports';
import IServer = require('./iServer.ts');
import IDatabase = require('../database/iDatabase');
import express = require('express');
import bodyParser = require('body-parser');
import config = require('../config');
import types = require('../tools/types.ts');
import _ = require('lodash');
import async_ = require('async');
import { Inject } from "inversify";
@Inject("IDatabase") // <- error TS1238: Unable to resolve signature of class decorator when called as an expression. Supplied parameters do not match any signature of call target.
class Server implements IServer
{
db : IDatabase;
expressApp : express.Express;
constructor(db : IDatabase)
{
this.db = db;
..
It would be good to demonstrate how Inversify can be used in an angular (preferably angular 2) as it will help people understand from a wider perspective the importance of DI.
example
export class MyClass extends AnotherClass implements IMyClass {
constructor(
IShouldbeInjected: IShouldbeInjected ) {
var options: any = {}
options.el = '#mydiv';
options.className = "row";
super(options);
this.childView = ChildView;
this.collection = IShouldbeInjected;
}
}
comes from kernel.ts _getConstructorArguments at line 96 in
if ('function' === typeof Map &&
fnStr.indexOf("class") !== -1 &&
fnStr.indexOf("constructor") === -1) {
result = null;
}
you probably have to replace fnStr.indexOf("class") with fnStr.indexOf("class ")
I'm just going to create some issues for some required examples.
We need a Node/express example at:
https://github.com/inversify/inversify-code-samples
I've been doing some analysis and we should be able to integrate InversifyJS with Express without problems.
Feel free to send PRs.
Some ES7 Decorator examples would be awesome!
It would also be nice to see how u could take advantage of meta-data proposal too - https://github.com/rbuckton/ReflectDecorators
Hi guys!
I have added a label to track all the .d.ts related issues:
https://github.com/inversify/InversifyJS/labels/.d.ts
I think we need to focus on fixing this issues before we add new features.
Also we need to do something so this issues can be spotted in the future, at the moment the issues are are there but the Travis CI build thinks that everything is OK.
So we need integration tests so the build ensures that we don't merge to master broken code:
Any ideas about how to implement it? @inversify/collaborators
We need to remove the old way of detecting injections (function.toString() + regex).
Can we add:
console.log("WARNING! Injections without inject decorator won't be supported in the upcoming releases");
I'm just going to create some issues for some required examples.
We need an Angular 1.x example at:
https://github.com/inversify/inversify-code-samples
Feel free to send PRs.
Hey @Jameskmonger,
As discussed we should be running automated scripts prepublish to ensure breaking builds cannot be published.
For now just run gulp test
and we will add more checks as necessary to ensure InversifyJS is as stable as possible.
My recent #32 pull request failed for fix the issue that I was attempting to fix. It seems that you can't use
declare module 'inversify'
inside the .d.ts used by the typings declared in package.json, which was the reason for the extra inversify-node.d.ts
the typings declared in package.json should remove the need to use /// <reference calls.
Using node 4.0 and native ES6 classes:
class A{
}
class B{
constructor(a){}
}
var kernel = new inversify.Kernel();
kernel.bind(new inversify.TypeBinding('a', A));
kernel.bind(new inversify.TypeBinding('b', B));
kernel.resolve('b');
results in Error: Could not resolve service class
Hi guys,
I'm going to share with you my plans for 2.0
...
I was thinking about finishing the 2.0
branch and doing a massive pull request but I changed my mind...
I'm going to use the 2.0
branch just as a reference and I'm going to be working on a fork to do small merges.
So instead of doing a massive pull request to move from 1.x
to 2.0
, I would like to move from the current version to 2.0
slowly.
The roadmap in my head looks as follows:
NOTE: This is just s roadmap, final APIs will probably be different and some of the items in the roadmap may never be implemented.
src/
├── activation
│ ├── metadata.ts
│ ├── metadata_keys.ts
│ ├── queryable_string.ts
│ └── target.ts
├── bindings
│ ├── binding.ts
│ └── binding_scope.ts
├── decorators
│ ├── decorator_utils.ts
│ ├── inject.ts
│ ├── named.ts
│ ├── paramnames.ts
│ └── tagged.ts
├── interfaces
│ ├── activation
│ │ ├── metadata.d.ts
│ │ ├── queryable_string.d.ts
│ │ └── target.d.ts
│ ├── bindings
│ │ └── binding.d.ts
│ ├── decorators
│ │ └── inject.d.ts
│ ├── interfaces.d.ts
│ └── kernel
│ ├── kernel.d.ts
│ ├── key_value_pair.d.ts
│ └── lookup.d.ts
├── inversify.ts
└── kernel
├── kernel.ts
├── key_value_pair.ts
└── lookup.ts
Metadata
and Target
.kernel.bind<X>("X").to().inTransientScope()
Type X has a cyclic dependency on type Y
new Kernel(parentKernel)
bind<X>("X").toValue(someInstance)
.bind<X>("X").toFactory(someFactory)
.bind<X>("X").toProvider(someFactoryAsync)
.kernel.bind(new Binding<IWeapon>("IWeapon").to(Katana));
kernel.bind(new Binding<IWeapon>("IWeapon").to(Shuriken));
@inject("IWeapon","IWeapon")
class Warrior {
private _primaryWeapon : IWeapon;
private _secondaryWeapon : IWeapon;
constructor(
@Tagged("canThrow", false) primary : IWeapon,
@Tagged("canThrow", true) secondary : IWeapon) {
// ...
}
}
kernel.bind<IWeapon>("IWeapon").to(Katana).tagged("canThrow", false);
kernel.bind<IWeapon>("IWeapon").to(Shuriken).tagged("canThrow", true);
// We should also be able to do:
var canThrow = Tagged.bind(null, ["canThrow", false]);
@inject("IWeapon","IWeapon")
class Warrior {
private _primaryWeapon : IWeapon;
private _secondaryWeapon : IWeapon;
constructor(
@canThrow() primary : IWeapon,
@canThrow() secondary : IWeapon) {
// ...
}
}
@inject("IWeapon","IWeapon")
class Soldier {
private _mainWeapon : IWeapon;
private _secondaryWeapon : IWeapon;
constructor(
@named("main") weapon: IWeapon,
@named("secondary") weapon: IWeapon
) {
this._mainWeapon = main;
this._secondaryWeapon = secondary;
}
}
kernel.bind<IWeapon>("IWeapon").to(Katana).named("main");
kernel.bind<IWeapon>("IWeapon").to(Shuriken).named("secondary");
kernel.bind<IWeapon>("IWeapon").to(Katana).named("main");
kernel.bind<IWeapon>("IWeapon").to(Shuriken).named("secondary");
@inject("IWeapon[]")
.when(request => { return request.target.name.equal("katana") })
.whenInjectedInto(SomeClass)
when(request => {})
the requests object will provide access to the depth
, parentContext
& parentRequest
values.when(request => { return request.parentRequests.target.name.equal("katana") })
My plan is to implement 1.3 and then I will start creating tickets and accepting pull requests for all the other releases.
I think guys is really important that we spend the time necessary to create a good architecture don't be afraid to ask for advice from the community.
I'm going to document the glossary and an architecture overview.
Thanks for being part the team 👪 !
We will use the following technology stack:
http://nightwatchjs.org/
https://saucelabs.com/opensauce/
integration tests will be under e2e
folder and integration examples will be located under samples
folder.
There's no InversifyJS Stackoverflow tag yet.
Please add something to the documentation saying where users can ask questions: http://inversify.io/.
Either:
I get this error when I compile with typescript v1.7.3
error TS2656: Exported external package typings file '<path>/node_modules/inversify/type_definitions/inversify.d.ts' is not a module. Please contact the package author to update the package definition.
I made it work by manualy removing "typings": "type_definitions/inversify.d.ts"
from inversify package.json and importing inversify.d.ts file in my tsd.d.ts file
this project is promising, but is possible to be used without Typescript, at least for a start?
Me, as many javascript coders out there, might be interested in Typescript in the near future, but for now a DI library without dependencies is even more attractive :)
When adding a binding allow "transaction" as scope.
Users can then create a "transaction" with:
kernel.beginTransaction("inteface");
And finish it with:
kernel.endTransaction("inteface");
Between beginTransaction
and endTransaction
if resolve
is invoked:
kernel.resolve<inteface>("inteface");
The IoC container will use singleton scope
.
If resolve
is invoked before beginTransaction
has been invoked the IoC container will use transient scope
.
This is useful when we want a module to be a singleton
within a scope
. If we are in a scope
(e.g.controller
) and we navigate to a different scope
(e.g. another controller
) we might want our module to stop using singleton scope
or create a new singleton
within the new scope
. All we need to do is to invoke beginTransaction
when creating a scope (e.g. navigating to a controller) and endTransaction
when leaving the scope (e.g. disposing the controller).
It has been announced than tsd is going to be deprecated:
I would like to switch to typings.
this is on my list of TODOs but if someone want to do PR it will be more than welcome :)
Thanks!
I've downloaded the latest version of Inversify (1.2.0) and have Typescript (1.7.3). But when trying to use your documentation to change just two files in my project and compile with tsc I get:
error TS2656: Exported external package typings file 'node_modules/inversify/type_definitions/inversify.d.ts' is not a module. Please contact the package author to update the package definition.
Is there a typo in the latest version of Inversify perhaps?
I'm referencing Inversify like this:
/// <reference path="../../node_modules/inversify/type_definitions/inversify.d.ts" />
and using it like this:
import { TypeBinding, Kernel, TypeBindingScopeEnum } from "inversify";
import IDatabase = require('../database/iDatabase');
import Database = require('../database/Database');
class Ioc
{
public container : Kernel;
constructor()
{
let c = new Kernel();
c.bind(new TypeBinding<IDatabase>("IDatabase", Database, TypeBindingScopeEnum.Transient));
this.container = c;
}
}
export = Ioc;
Makes quite difficult tracking dependencies, basically the current package in npm is on 1.2.2 but you miss tag 1.2.2 (and anything lesser than that until 1.0.2)
Should be a good idea including at least the latest one to address it
I'm just going to create some issues for some required examples.
We need an Ember.js example at:
https://github.com/inversify/inversify-code-samples
I've been doing some analysis and we should be able to integrate InversifyJS with Ember.js without problems.
Feel free to send PRs.
I'm just going to create some issues for some required examples.
We need a Knockout.js example at:
https://github.com/inversify/inversify-code-samples
I've been doing some analysis and we should be able to integrate InversifyJS with Knockout.js without problems.
Feel free to send PRs.
At the moment, we are using Karma to test the project after it has been passed through browserify. This means that we test some of the code added by browserify, which we shouldn't be testing (such as var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f
).
This results in skewed test coverage results (of 99.78%) because some of the lines are not possible to test with Karma.
By using Mocha, we can directly test our compiled commonjs code, which will give us a true reflection of our test coverage.
At the moment, too much is being distributed with the npm distribution.
Included in the distribution is the TypeScript source, the Karma configuration, and the test files.
The package size could be slimmed down considerably by only distributing the compiled JS and the package.json.
If I have simple class which is not implementing any interface.
export class Something{
constructor(*/../*) { }
public static CustomMethodFromNotReallySomething(*/params/*) { }
}
And a module which exports NotReallySomething
relying on third party node module
export var NotReallySomething= GetNotReallySomethingFromThirdParty();
In my app I am swapping Something
with NotReallySomething
manually using imports so this way is working for me but I am thinking to improve that so just wondering is there any way I can configure inversify to create a single instantiate of Something
or NotReallySomething
so I can use it in my calling module like var something = kernel.instance('SomethingOrNotReallySomething');
by configuring inversify.config.ts
something like:
if (app.get('env') === 'dev') {
kernel.bind(new TypeInstance<Something>('SomethingOrNotReallySomething'));
}
else if (app.get('env') === 'stg') {
kernel.bind(new TypeInstance<NotReallySomething>('SomethingOrNotReallySomething'));
}
We need to upgrade the marionette example at https://github.com/inversify/inversify-code-samples to use the latest version of its dependencies. This includes:
So there is a good bit of work in this upgrade.
There is an error in the build logs:
https://travis-ci.org/inversify/InversifyJS/builds/103853460
[14:19:34] Starting 'build-test'...
test/inject_annotation.test.ts(1,1): error TS6053: File '/home/travis/build/inversify/InversifyJS/typings/tsd.d.ts' not found.
test/inject_annotation.test.ts(3,24): error TS2307: Cannot find module 'chai'.
test/inject_annotation.test.ts(6,1): error TS2304: Cannot find name 'describe'.
test/inject_annotation.test.ts(7,4): error TS2304: Cannot find name 'it'.
test/inject_annotation.test.ts(19,4): error TS2304: Cannot find name 'it'.
test/inject_annotation.test.ts(32,4): error TS2304: Cannot find name 'it'.
test/inject_annotation.test.ts(45,4): error TS2304: Cannot find name 'it'.
test/inject_annotation.test.ts(58,4): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(1,1): error TS6053: File '/home/travis/build/inversify/InversifyJS/typings/tsd.d.ts' not found.
test/kernel.test.ts(3,24): error TS2307: Cannot find module 'chai'.
test/kernel.test.ts(9,1): error TS2304: Cannot find name 'describe'.
test/kernel.test.ts(11,3): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(23,3): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(51,3): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(66,3): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(87,3): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(111,3): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(131,3): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(143,3): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(177,3): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(213,3): error TS2304: Cannot find name 'it'.
test/kernel.test.ts(249,3): error TS2304: Cannot find name 'it'.
test/lookup.test.ts(1,1): error TS6053: File '/home/travis/build/inversify/InversifyJS/typings/tsd.d.ts' not found.
test/lookup.test.ts(3,24): error TS2307: Cannot find module 'chai'.
test/lookup.test.ts(6,1): error TS2304: Cannot find name 'describe'.
test/lookup.test.ts(8,3): error TS2304: Cannot find name 'it'.
test/lookup.test.ts(15,3): error TS2304: Cannot find name 'it'.
test/lookup.test.ts(20,3): error TS2304: Cannot find name 'it'.
test/lookup.test.ts(25,3): error TS2304: Cannot find name 'it'.
test/lookup.test.ts(34,3): error TS2304: Cannot find name 'it'.
test/type_binding.test.ts(1,1): error TS6053: File '/home/travis/build/inversify/InversifyJS/typings/tsd.d.ts' not found.
test/type_binding.test.ts(3,24): error TS2307: Cannot find module 'chai'.
test/type_binding.test.ts(7,1): error TS2304: Cannot find name 'describe'.
test/type_binding.test.ts(9,3): error TS2304: Cannot find name 'it'.
test/type_binding.test.ts(30,3): error TS2304: Cannot find name 'it'.
test/type_binding.test.ts(38,3): error TS2304: Cannot find name 'it'.
[14:19:34] TypeScript: 36 semantic errors
[14:19:34] TypeScript: emit succeeded (with errors)
I think this issues was introduced by #39
It looks like is missing the tsd.d.ts
file:
'/home/travis/build/inversify/InversifyJS/typings/tsd.d.ts' not found.
We need to fix it but even more important is to ensure that the Travis CI build fails if there are compilation errors.
How does inversify manage cyclic dependencies?
Could not find anything in the documentation yet.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.