app-container
Version:
asynchronous IoC container for node.js applications
122 lines (109 loc) • 4.12 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _debug = require('debug');
var _debug2 = _interopRequireDefault(_debug);
var _lodash = require('lodash');
var _error = require('./error');
var _error2 = _interopRequireDefault(_error);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const debug = (0, _debug2.default)('app-container:component');
class Component {
/**
* Constructor function
* @param {Object} params - parameters
* @param {Object} params.container - parent container reference
* @param {String} params.name - unique component name
* @param {Function|Object} params.mod - raw module definition
* @param {Object} [params.options={}] - component options
* @param {String|String[]|Object} params.options.require - module dependencies
* @param {Boolean} [params.options.singleton=false] - singleton flag
* @param {String} params.options.type - component type
* @return {Component}
*/
constructor({
container, mod, name, options = {}
}) {
this.container = container;
this.mod = mod;
this.instances = [];
this.options = options;
/* eslint-disable no-param-reassign */
if (typeof name !== 'string') {
throw new Error('Component constructor missing required "name" option');
}
if (!this.options.type && typeof mod === 'object' && !Array.isArray(mod)) {
this.options.type = 'object';
}
if (this.options.require && typeof this.options.require === 'string') {
this.options.require = [this.options.require];
}
this.name = name;
this.singleton = options.singleton || false;
}
/**
* Load an instance of the underlying module
* @param {Container} container - parent container instance
* @return {Bluebird}
*/
load(container) {
debug(`loading component: ${this.name}`);
if (this.singleton && this.instances.length) {
return _bluebird2.default.resolve(this.instances[0]);
}
return this.create(container).then(instance => {
this.instances.push(instance);
return instance;
}).catch(e => {
if (!(e instanceof _error2.default)) {
const err = new _error2.default(`Error loading component (${this.name}): ${e.message}`);
err.stack = e.stack;
throw err;
}
throw e;
});
}
/**
* Create a new instance of the underlying module
* @param {Container} container - parent container instance
* @return {Bluebird}
*/
create(container) {
debug(`creating new component: ${this.name}`);
return _bluebird2.default.try(() => {
const { require: req } = this.options;
if (req) {
const deps = Array.isArray(req) ? req : [req];
return container.load(...deps);
}
return _bluebird2.default.resolve([]);
}).then(result => {
const deps = Array.isArray(result) ? result : [result];
let instance;
if (this.options.type === 'object') {
instance = typeof this.mod === 'object' ? (0, _lodash.cloneDeep)(this.mod) : this.mod;
if (typeof result === 'object' && !Array.isArray(result)) {
(0, _lodash.extend)(instance, result);
}
} else if (this.options.type === 'constructor') {
const Mod = this.mod;
instance = new Mod(...deps);
} else {
instance = this.mod(...deps);
}
return instance;
}).then(instance => {
const { init } = this.options;
if (init && typeof instance[init] === 'function') {
debug(`initializing new component: ${this.name}`);
return _bluebird2.default.try(() => instance[init]()).return(instance);
}
return instance;
});
}
}
exports.default = Component;
module.exports = exports.default;