easy-di

4.0.1 • Public • Published

Easy Dependency Injector

Build Status

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

Package Sidebar

Install

npm i easy-di

Weekly Downloads

1

Version

4.0.1

License

ISC

Last publish

Collaborators

  • antiokus314