/**
* @module flitter-di/src/DependencyInjector
*/
/**
* @type {typeof module:flitter-di/src/Container~Container}
*/
const Container = require('./Container')
/** Manages services and injects classes from its service container. */
class DependencyInjector {
constructor(container = new Container()) {
/**
* The service container used by this dependency injector.
* @type {module:flitter-di/src/Container~Container}
*/
this.container = container
this.container.di = this
}
/**
* Instantiate the passed in class. If it is injectable, it will be injected.
* @param {typeof module:flitter-di/src/Injectable~Injectable} Class
* @param {...*} args - additional arguments to be passed to the constructor of the class
* @returns {*} - the injected static reference to the Class
*/
make(Class, ...args) {
if ( this.__is_injectable(Class) ) {
this.inject(Class)
}
return new Class(...args)
}
/**
* Inject the passed in Class with the IoC items it requires.
* @param {typeof module:flitter-di/src/Injectable~Injectable} Class
* @returns {*}
*/
inject(Class) {
if ( !this.__is_injectable(Class) ) {
throw new TypeError(`Cannot inject non-injectable class: ${Class.name}`)
}
Class.__inject(this.container)
return Class
}
/**
* Fetch a service by name. If no name is provided, return the service
* proxy container. This container has getters for all the services by
* name.
* @deprecated - prefer DependencyInjector.get. This will be removed in the future.
* @param {string} name - the name of the service
* @returns {module:flitter-di/src/Service~Service|undefined|Proxy} - the service instance or service container proxy
*/
service(name) {
return this.container.get(name)
}
/**
* Fetch an IoC item by name.
* @param {string} name
* @returns {*}
*/
get(name) {
return this.container.get(name)
}
/**
* Verify that the injector's container has a service or set of services.
* @param {string|Array<string>} name - service name or array of service names
* @returns {boolean} - true if the container has the service(s)
*/
has(name) {
if ( Array.isArray(name) ) {
return name.every(x => this.container.has(x))
}
return this.container.has(name)
}
/**
* If called, this method will extend the global nodejs require() method to
* check for injectable classes. If a require value is injectable, it will be
* automatically injected with the services from this DI.
*/
inject_globally() {
const Module = require('module')
const original_require = Module.prototype.require
const di = this
di._original_require = original_require
Module.prototype.require = function() {
const value = original_require.apply(this, arguments)
return di.__is_injectable(value) ? di.inject(value) : value
}
}
/**
* Verify that a class is injectable. This means that it has a static __inject
* method and that method takes at least one argument. In almost all cases, this
* should be satisfied by using the Injectable base class.
* @param {*} Class - the class to check
* @returns {boolean} - true if the class is injectable
* @private
*/
__is_injectable(Class) {
return (
typeof Class === 'function'
&& typeof Class.__inject === 'function'
&& Class.__inject.length > 0
)
}
}
module.exports = exports = DependencyInjector