fire-up
Version:
Fire Up! is a dependency injection container designed specifically for node.js with a powerful but sleek API.
230 lines (176 loc) • 9.2 kB
JavaScript
;
var _ = require('lodash');
var descriptor = require('./descriptor.js');
var constants = require('../definitions/constants.js');
function instanceToFactoryAdapter() {
/* jshint validthis: true */
if (this.type === constants.MODULE_TYPE_MULTIPLE_INSTANCES) {
return _.cloneDeep(this.instance);
} else {
return this.instance;
}
}
function constructorToFactoryAdapter() {
/* jshint validthis: true, -W061 */
if (arguments.length === 0) {
return new this._constructor();
}
var q = [];
for ( var i = 0; i < arguments.length; i+=1 ) {
q.push("arguments[" + i + "]");
}
return eval("new this._constructor(" + q.join(",") + ")");
}
function validateModule(fireUp, module, registryId) {
var err, i;
if (_.isPlainObject(module) === false) {
err = new fireUp.errors.ConfigError("The following module does not export an object: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, "At minimum the following statement is required: module.exports = { implements: '<module interface>', factory: <function> };");
throw err;
}
if (_.isUndefined(module.implements)) {
err = new fireUp.errors.ConfigError("The following module does not export the exports.implements property: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, "You may assign a string denoting the interface implemented by the module or an array of strings for multiple interfaces.");
throw err;
} else if (_.isString(module.implements) === false && _.isArray(module.implements) === false) {
err = new fireUp.errors.ConfigError("The following module exports the exports.implements property in the wrong format: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, "You may assign a string denoting the interface implemented by the module or an array of strings for multiple interfaces.");
throw err;
}
function validateModuleImplementsEntry(entry, i, registryId) {
if (_.isString(entry) === false || entry.length === 0 || descriptor.validateInterfaceName(entry) === false) {
var err = new fireUp.errors.ConfigError("The following module exports an invalid interface name " + (i ? "at index " + i + " of" : "through") + " the exports.implements property: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, 'A valid interface name must not contain whitespaces, stars, nor parenthesises.');
throw err;
}
}
if (_.isArray(module.implements)) {
for ( i = 0; i < module.implements.length; i+=1 ) {
validateModuleImplementsEntry(module.implements[i], i, registryId);
}
if (_.uniq(module.implements).length < module.implements.length) {
err = new fireUp.errors.ConfigError("The following module exports duplicated interface names in the exports.implements property: %s", registryId);
fireUp.log.error(err);
throw err;
}
} else {
validateModuleImplementsEntry(module.implements, null, registryId);
}
function validateModuleInjectEntry(entry, i, registryId) {
if (_.isString(entry) === false || entry.length === 0 || descriptor.validateModuleReference(entry) === false) {
var err = new fireUp.errors.ConfigError("The following module exports an invalid module reference " + (i ? "at index " + i + " of" : "through") + " the exports.inject property: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, 'A valid module reference must not contain whitespaces and use parenthesises only at the end.');
if (entry.match(/\*/)) {
fireUp.log.debug({}, "The star selector is only available as a ':*' suffix.");
}
throw err;
}
}
if (_.isUndefined(module.inject) === false) {
if (_.isString(module.inject) === false && _.isArray(module.inject) === false) {
err = new fireUp.errors.ConfigError("The following module exports the exports.inject property in the wrong format: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, "You should assign an array of strings that reference the interfaces for which an implementation shall be injected.");
throw err;
}
if (_.isArray(module.inject)) {
for ( i = 0; i < module.inject.length; i+=1 ) {
validateModuleInjectEntry(module.inject[i], i, registryId);
}
} else {
validateModuleInjectEntry(module.inject, null, registryId);
}
var injectedOwnInterfaces = _.intersection(
_.isArray(module.implements) ? module.implements : [module.implements],
_.isArray(module.inject) ? module.inject : [module.inject]
);
if (injectedOwnInterfaces.length > 0) {
err = new fireUp.errors.ConfigError("The following module get one or more of its own interfaces injected: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({ injectedOwnInterfaces: injectedOwnInterfaces }, "Please make sure that exports.implements and exports.inject are distinct.");
throw err;
}
}
if (_.isUndefined(module.type) === false) {
if (_.isString(module.type) === false ||
(module.type !== fireUp.constants.MODULE_TYPE_SINGLETON && module.type !== fireUp.constants.MODULE_TYPE_MULTIPLE_INSTANCES)) {
err = new fireUp.errors.ConfigError("The following module exports an invalid module type through the exports.type property: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, "Please use either '%s' (default) or '%s'.", fireUp.constants.MODULE_TYPE_SINGLETON, fireUp.constants.MODULE_TYPE_MULTIPLE_INSTANCES);
throw err;
}
}
if (_.isUndefined(module.factory) && _.isUndefined(module._constructor) && _.isUndefined(module.instance)) {
err = new fireUp.errors.ConfigError("The following module did not export any of the properties exports.factory, exports._constructor, or exports.instance: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, "For full flexibility it is recommended to use exports.factory.");
throw err;
}
if ((_.isUndefined(module.factory) === false && _.isUndefined(module.instance) === false && module.factory !== instanceToFactoryAdapter) ||
(_.isUndefined(module.factory) === false && _.isUndefined(module._constructor) === false && module.factory !== constructorToFactoryAdapter) ||
(_.isUndefined(module.instance) === false && _.isUndefined(module._constructor) === false)) {
err = new fireUp.errors.ConfigError("The following module exports more than one of the properties exports.factory, exports._constructor, and exports.instance: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, "Please define only one of those properties.");
throw err;
}
if (_.isUndefined(module.instance) === false) {
if (_.isUndefined(module.inject) === false) {
err = new fireUp.errors.ConfigError("The following module defines the exports.inject property although it provides the module instance through exports.instance: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, "You may provide a factory through exports.factory that builds the module instance based on the injected dependencies.");
throw err;
}
var instanceIsThenable = false;
try {
instanceIsThenable = _.isFunction(module.instance.then);
} catch (e) { }
if (instanceIsThenable) {
err = new fireUp.errors.ConfigError("The following module exports a Thenable through the exports.instance property: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, "This is currently not supported. Open an issue on GitHub if that would be helpful to you.");
throw err;
}
}
if (_.isUndefined(module._constructor) === false) {
if (_.isFunction(module._constructor) === false) {
err = new fireUp.errors.ConfigError("The following module exports a value through the exports._constructor property that is not a function: %s", registryId);
fireUp.log.error(err);
throw err;
}
var constructorIsThenable = false;
try {
constructorIsThenable = _.isFunction(module._constructor.then);
} catch (e) { }
if (constructorIsThenable) {
err = new fireUp.errors.ConfigError("The following module exports a Thenable through the exports._constructor property: %s", registryId);
fireUp.log.error(err);
fireUp.log.debug({}, "This is currently not supported. Open an issue on GitHub if that would be helpful to you.");
throw err;
}
}
if (_.isUndefined(module.factory) === false) {
if (_.isFunction(module.factory) === false) {
err = new fireUp.errors.ConfigError("The following module exports a value through the exports.factory property that is not a function: %s", registryId);
fireUp.log.error(err);
throw err;
}
}
}
function addFactoryAdapterIfNeeded(module) {
if (_.isUndefined(module.instance) === false) {
module.factory = instanceToFactoryAdapter;
} else if (_.isUndefined(module._constructor) === false) {
module.factory = constructorToFactoryAdapter;
}
return module;
}
module.exports = {
validateModule: validateModule,
addFactoryAdapterIfNeeded: addFactoryAdapterIfNeeded
};