npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tornadodi

v1.2.4

Published

Easiest dependency injection system

Downloads

79

Readme

TornadoDI :tornado:

TornadoDI provide a modern lite way to deal with dependency injection into your Typescript or javascript project

Build Status Coverage Status GitHub license npm version

Table of Contents

Prerequisites

If you are working on a javascript project, you have to install few babel plugins

npm i --save-dev @babel/cli @babel/core @babel/plugin-proposal-decorators @babel/preset-env

and configure your .babelrc as the following

{
  "presets": ["@babel/env"],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }]
  ]
}

Installation

To use TornadoDI you have to install the library first.

npm i tornadodi

Features

Decorators

To free the power of TornadoDI some decorators are provided in order to rich the purpose of the dependency injection.

@Injectable()

The @Injectable() decorator is the more important one, it is through this decorator than TornadoDI will be able to know about what as to be injected in the class to resolve.

// Typescript
import { Injectable } from 'tornadodi';

@Injectable()
export class Foo { } 
// Javascript
const Injectable = require('tornadodi').Injectable;

@Injectable()
class Foo { }

module.exports = Foo;

@Inject()

The @Inject() decorator will allow you to specify a token or a class to be inject for a specific constructor decorator. To specify a token during the registration, refer you to the following section Register a class

// Typescript
import { Injectable, Inject } from 'tornadodi';

@Injectable()
export class Foo { 
    constructor(
        private bar: Bar, 
        @Inject(Symbol('InjectByCustomToken')) private soo: Soo,
        @Inject('InjectByCustomToken2) private fii: Fii) { }
} 
Take as parameter

Only one parameter.

| Parameter type | Example | |:---:|:---:| | any | Symbol('fooToken') Or 'fooToken' or a class etc. |

@Dependencies()

In javascript you will not be able to emit design:paramtypes metadata from a class. In order to be able to specify the injection you can use the @Dependencies() decorator which will be able to populate this specific metadata and allow TornadoDI to deal with the injection.

// Javascript
const Injectable = require('tornadodi').Injectable;
const Dependencies = require('tornadodi').Dependencies;

@Injectable()
@Dependencies('barToken')
export class Foo { 
    constructor(bar)
} 

module.exports = Foo;
Take as parameter

Any number of parameter.

| Parameter type | Example | |:---:|:---:| | any | Symbol('fooToken') Or 'fooToken' or a class etc. |

Registering and resolving

During the time you are using TornadoDI you will be able to register any classes at any time and resolve them when you want. The resolution is made only when it is asked for and never during the registration for performance purpose.

Register a class

After that you have created your class and used the appropriate decorator (see below), you will be able to register all of them. Registering a class does not mean that she's resolved as the same time (We will see how the resolution work in the Resolve dependencies section).

To register a new class or multiple classes you have two possibilities.

registerAsSingleton

The registerAsSingleton method will provide you a way to explicitly register a class that can be instantiated only once in the whole container. The singleton will be used as well to instantiate other classes with always the same instance if they inject this singleton.

To use this methods see the following example.

// Typescript
import Bar from './bar.service';
import Foo from './foo.service';
import { Tornado } from 'tornadodi';

const bootstrap = () => {
    // Register a class
    Tornado.registerAsSingleton<Foo>(Foo);
    // Register multiple class
    Tornado.registerAsSingleton([Foo, Bar]);
    // Register multiple class and use custom token with metatype
    Tornado.registerAsSingleton([Foo, { token: Bar, metatype: Bar}]);
    // Register multiple class and use custom token with static value through useValue
    Tornado.registerAsSingleton([Foo, { token: Symbol('t2'), useValue: 42 }]);
    // Register multiple class and use custom token with factory
    Tornado.registerAsSingleton([Foo, { token: 't3', useFactory: (foo) => foo.method(), inject: [Foo] }]);
};
bootstrap();

// Javascript
const Bar = require('./bar.service');
const Foo = require('./foo.service');
const Tornado = require('tornadodi').Tornado;

const bootstrap = () => {
    // Register a class
    Tornado.registerAsSingleton(Foo);
    // Register multiple class
    Tornado.registerAsSingleton([Foo, Bar]);
    // Register multiple class and use custom token with metatype
    Tornado.registerAsSingleton([Foo, { token: Bar, metatype: Bar}]);
    // Register multiple class and use custom token with static value through useValue
    Tornado.registerAsSingleton([Foo, { token: Symbol('t2'), useValue: 42 }]);
    // Register multiple class and use custom token with factory
    Tornado.registerAsSingleton([Foo, { token: 't3', useFactory: (foo) => foo.method(), inject: [Foo] }]);
};
bootstrap();
Take as parameter

Only one parameter.

| Parameter type | Example | |:---:|:---:| | { token: any; metatype: Metatype<T> } | { token: Foo, metatype: Foo } | | { token: any; useValue: any } | { token: Symbol('MyToken'), useValue: 42 } | | { token: any; useFactory: (...args: any[]) => any, inject?: any[] } | { token: 'MyToken', useFactory: () => 'value' } | | Metatype<T> | Foo | | Array</* Previous types */> | [{ token: Foo, metatype: Foo }, Bar] |

register

Otherwise, the register method will provide you the possibility to register a class as a non singleton one. In order words, each time you will ask to resolve it, you will get a new instance of it and, each time the class is injected, a new instance will be used.

To use this methods see the following example.

// Typescript
import Bar from './bar.service';
import Foo from './foo.service';
import { Tornado } from 'tornadodi';

const bootstrap = () => {
    // Register a class
    Tornado.register<Foo>(Foo);
    // Register multiple class
    Tornado.register([Foo, Bar]);
    // Register multiple class and use custom token with metatype
    Tornado.register([Foo, { token: Bar, metatype: Bar}]);
    // Register multiple class and use custom token with static value through useValue
    Tornado.register([Foo, { token: Symbol('t2'), useValue: 42 }]);
    // Register multiple class and use custom token with factory
    Tornado.register([Foo, { token: 't3', useFactory: (foo) => foo.method(), inject: [Foo] }]);
};
bootstrap();
// Javascript
const Bar = require('./bar.service');
const Foo = require('./foo.service');
const Tornado = require('tornadodi').Tornado;

const bootstrap = () => {
    // Register a class
    Tornado.register(Foo);
    // Register multiple class
    Tornado.register([Foo, Bar]);
    // Register multiple class and use custom token with metatype
    Tornado.register([Foo, { token: Bar, metatype: Bar}]);
    // Register multiple class and use custom token with static value through useValue
    Tornado.register([Foo, { token: Symbol('t2'), useValue: 42 }]);
    // Register multiple class and use custom token with factory
    Tornado.register([Foo, { token: 't3', useFactory: (foo) => foo.method(), inject: [Foo] }]);
};
bootstrap();
Take as parameter

Only one parameter.

| Parameter type | Example | |:---:|:---:| | { token: any; metatype: Metatype<T> } | { token: Foo, metatype: Foo } | | { token: any; useValue: any } | { token: Symbol('MyToken'), useValue: 42 } | | { token: any; useFactory: (...args: any[]) => any, inject?: any[] } | { token: 'MyToken', useFactory: () => 'value' } | | Metatype<T> | Foo | | Array</* Previous types */> | [{ token: Foo, metatype: Foo }, Bar] |

Resolve a class

After have been registering the different classes, you will be able to resolve them. The resolution of any classes is made when you call the resolve method. That means than the dependency resolution is lazy and apply when it requested.

The resolution will resolve the class and it's dependencies, if they are register as singleton the next resolve will return the same instance as the previous one. See the following example.

// Typescript
import Bar from './bar.service';
import Foo from './foo.service';
import { Tornado } from 'tornadodi';

const bootstrap = () => {
    Tornado.register([Foo, Bar]);
    // Resolve dependency by giving a class to resolve
    const foo = Tornado.resolve<Foo>(Foo);
    // Resolve dependency by giving a Symbol to resolve
    const bar = Tornado.resolve<Bar>(Symbol('barToken'));
    // Resolve dependency by giving a string to resolve
    const anotherBar = Tornado.resolve<Bar>('barToken');
};
bootstrap();
// Javascript
const Bar = require('./bar.service');
const Foo = require('./foo.service');
const Tornado = require('tornadodi').Tornado;

const bootstrap = () => {
    Tornado.register([Foo, Bar]);
    // Resolve dependency by giving a class to resolve
    const foo = Tornado.resolve(Foo);
    // Resolve dependency by giving a class to resolve
    const bar = Tornado.resolve(Symbol('barToken'));
    // Resolve dependency by giving a string to resolve
    const anotherBar = Tornado.resolve<Bar>('barToken');
};
bootstrap();
Take as parameter

Only one parameter.

| Parameter type | Example | |:---:|:---:| | any | Symbol('fooToken') Or 'fooToken' or a class etc. |

Clear the container

TornadoDI as it's own container, which is not accessible to the user natively. If you want at any time clear all the dependencies registered into the container, you can call the clear method as the following example.

// Typescript
import Bar from './bar.service';
import Foo from './foo.service';
import { Tornado } from 'tornadodi';

const bootstrap = () => {
    Tornado.register([{ token: 'foo', metatype: Foo }, Bar]);
    const foo = Tornado.resolve<Foo>('foo');
    const bar = Tornado.resolve<Bar>(Bar);
    console.log(Tornado.getContainerSize()); // result: 2;
    
    // Reset container
    Tornado.clear();
    console.log(Tornado.getContainerSize()); // result: 0;
};
bootstrap();
// Javascript
const Bar = require('./bar.service');
const Foo = require('./foo.service');
const Tornado = require('tornadodi').Tornado

const bootstrap = () => {
    Tornado.register([{ token: 'foo', metatype: Foo }, Bar]);
    const foo = Tornado.resolve('foo');
    const bar = Tornado.resolve(Bar);
    console.log(Tornado.getContainerSize()); // result: 2;
    
    // Reset container
    Tornado.clear();
    console.log(Tornado.getContainerSize()); // result: 0;
};
bootstrap();

Scoped container

In TornadoDI you are able to switch container at any time to work with. in every features that we have seen previously in Features from registering and resolving section. You are able to specify as a second argument, the scope that you want to target. It will switch automatically on this scoped container to process the action. If there is no specified scope, the default container will be used. See the following example.

// Typescript
Tornado.registerAsSingleton([{ token: Foo, metatype: Foo }, Bar], 'scoped');
Tornado.register([{ token: Foo, metatype: Foo }, Bar], 'scoped');
Tornado.resolve<Bar>(Bar, 'scoped')
// Javascript
Tornado.registerAsSingleton([{ token: Foo, metatype: Foo }, Bar], 'scoped');
Tornado.register([{ token: Foo, metatype: Foo }, Bar], 'scoped');
Tornado.resolve(Bar, 'scoped')

Team

  • Adrien de Peretti

License

This project is licensed under the MIT License - see the LICENSE.md file for details