Designing a toy dependency injection library Just for learning purpose I thought to write a simple and useful dependency injection service which would just do dependency injection but also it should expose its core too which would let the client code to do some advance stuffs.

I took inspiration from here but I think it does too much apart from that I think that the Factory and Provider thing can be done via calling resolve method in my library.

I need to know if I am missing something or can I improve my code to handle more cases which I don't know yet.

const RE = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;

const Color = {
  RED: 'RED',
  GREEN: 'GREEN',
  BLUE: 'BLUE',
};

class Door {
  constructor(color) {
    this.color = color;
  }

  open() {
    console.log('Kitchen door is open now!');
  }
}


class Stove {
  constructor() {
  }

  on() {
    console.log('Gas stove is turned on!');
  }

  off() {
    console.log('Gas stove is now off!');
  }
}

class TeaMaker {
  constructor(door, stove) {
    this.door  = door;
    this.stove = stove;
  }

  makeTea() {
    this.door.open();
    this.stove.on();
    console.log('Put water on stop');
    this.stove.off();
    console.log('Enjoying tea :)');
  }
}

const injector = {
  dependencies: {},
  register: (key, value) =&gt; {
    injector.dependencies[key] = value;
  },
  resolve: (deps, fn, scope={}) =&gt; {
    let args = [];
    for (let d of deps) {
      if (injector.dependencies[d]) {
        args.push(injector.dependencies[d]);
      } else {
        throw new Error(`Unkown Dep: ${d}`);
      }
    }

    return () =&gt; {
      return fn.call(scope, ...args, ...arguments);
    };
  },
  get: (fn) =&gt; {
    let deps = fn.toString().match(RE)[1].replace(/ /g, '').split(',');
    let args = [];
    for (let d of deps) {
      if (injector.dependencies[d]) {
        args.push(injector.dependencies[d]);
      } else {
        throw new Error(`Unkown Dep: ${d}`);
      }
    }
    return new fn(...args);
  }
};


injector.register('door',  new Door(Color.RED));
injector.register('stove', new Stove());

let makeTea = injector.resolve(['door', 'stove'], function(door, stove) {
  door.open();
  stove.on();
  console.log('Put water on stop');
  stove.off();
  console.log('Enjoying tea :)');
});



makeTea();
let teaMaker = injector.get(TeaMaker);
teaMaker.makeTea();


Output:

Kitchen door is open now!
Gas stove is turned on!
Put water on stop
Gas stove is now off!
Enjoying tea :)
Kitchen door is open now!
Gas stove is turned on!
Put water on stop
Gas stove is now off!
Enjoying tea :)
In your injector you're exposing the dependencies object, in a way that all could access.

You should consider to hide it instead, so people will use just the API as it should be.

This happened because you choose to define your component just with a plain object approach. 

If you want to write a lib or an utility, it is better to use one of the module pattern.

For example wrapping your code in a function if you mean to use it within a browser:

!function () {
    const dependencies = {};

    const injector = {
      // my code here
    };

    return injector;
 }()


Or with the node modules if you use in node:

const dependencies = {}

const injector = {
    // your code here..
}

export default injector


So you could expose just the API function you want.

This helps the developer to better understand how you design the lib to be used.