easy-di
v4.0.1
Published
Simple DI module for anything javascript
Downloads
9
Maintainers
Readme
Easy Dependency Injector
Simple dependency injection module written in vanilla js.
Why?
There's atleast a thousand implementations of a dependency injection module. Why another?
Honestly, most of them were too heavy for my taste, and didn't provide some of the quirks I'd wanted.
Motivation
Most dependency injection libraries/frameworks have multiple ways of doing the same exact thing. Take angularjs for example. It has at least 3 different ways to declare dependencies for a provider. Which way to use? Are there performance implications? Unexpected side effects? Um. yes (minification being the big one). So you have to choose carefuly.
I wanted a DI module that was declarative from top to bottom. Where there was only one semantic way to perform each operation. Where there is no need to specify anything about how you structure your code and didn't introduce complexity for what should be returned as a factory or a singleton or whatever. Since I couldn't find anything that fit my criteria - I decided to wire up my own!
Install
yarn (preferred)
yarn add easy-di
npm
npm install --save easy-di
Usage
const easyDi = require('easy-di')
Here's the suggested workflow
- Load the
easy-di
library - Load all of the dependencies into an
easyDi
module of your choice- Make sure to define a main node for your module
- Don't worry about the order of declaration (with the exception of resolve)
- Just declare what you want, where you want, when you want.
- Finally - resolve your module
Basic Usage
//file main.js
easyDi
.module('app')
.main(( one, two, three ) => {
console.log('node one', one)
console.log('node two', two)
console.log('node three', three)
})
//file one.js
easyDi
.module('app')
.define(function one() { return 'ONE'})
//file two.js
easyDi
.module('app')
.define(function two(three) { return three + ' AND TWO' })
//file three.js
easyDi
.module('app')
.define(function three(one) { return one + ' AND THREE' })
//file resolve.js
easyDi
.module('app')
.resolve()
// or
easyDi
.resolve('app')
// node one ONE
// node two ONE AND THREE AND TWO
// node three ONE AND THREE
Using shared modules
Its possible to define multiple modules and use them respectively in your core application. Be it third party, or just your effort to modularize the code base, using shared modules can be extremely powerful and effective
e.g. lets say you have something like this
lib1/
lib2/
app/
where
- the lib1 directory defines a module called lib1
- the lib2 directory defines a module called lib2
- the app directory contains our application specific module
You can directly use dependencies defined in that library in your module using the following syntax.
Say lib1 defines the doSomething service, we can use it like so
easyDi
.module('app')
.include('lib1')
.define(function root(doSomething) {
return doSomething()
})
.main(root => {
console.log(root) // equals the result of doSomething()
})
.resolve()
Defining an injector function
.define()
When defining a dependency the injector functions name is used as the actual name of the dependency. If the name property does not exist/is empty, an error will be thrown
i.e.
// correct
easyDi
.module('app')
.define(function hello(world) {
return world + 1
})
//or
const hello = (world) => world + 1
easyDi
.module('app')
.define(hello)
// incorrect
easyDi
.module('app')
.define(function (world) {
return world + 1
})
//or
easyDi
.module('app')
.define((world) => world + 1)
.main()
The main injector function is exactly the same in syntax as a normal dependency injector function, but does not have to be named, or can have any name.
Think of main as the entry point of the module (the function that will be called by .resolve(). If this is not defined and .resolve() is called, an error will be thrown.
.resolve()
This function will finally resolve the module you want to use. An error will be thrown if the entry point main is not defined.
Features
The biggest take away from this DI module is that the dependencies can declared whenever and wherever.
It doesn't matter the order. Even the injector module doesn't need to "prepared" in order to use it.
easyDi
takes care of that for you.
- "asynchronous" loading (just call "resolve" when you're ready)
- no build dependencies
- no transpiler nonsense
- auto instantiation of the module/container
- nothing happens until "resolve" is called
- LIGHT WEIGHT
Caveats
There is no system of automagically requiring the files you have defined. I suggest using a package like glob to require all the necessary files for the module/libraries you have defined.
API
The complete API and internals documentation is here
Build/Test
Download
git clone https://github.com/navneetgarg123/easy-di.git
cd easy-di
Setup
yarn install
Test
yarn test
Documentation
yarn docs
cd pages/api
Contributing
Issues and pull requests welcome :)
- Fork it
- Create your feature branch (git checkout -b feature/my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin feature/my-new-feature)
- Create a new Pull Request