UNPKG

easyioc

Version:

Stupidly simple recursive IoC service provider.

181 lines (137 loc) 4.35 kB
var _ = require('lodash-node') function isObject(thing){ if (typeof thing === 'object') return {}.toString.apply(thing) === '[object Object]' } var public_methods = { empty: empty, exec: exec, add: add } module.exports = public_methods var modules = {} // This is the master list, it will contain all modules. var adding = {} // Allows add() to be called during exec() execution. var path = [] // How we detect circular dependencies. function Module(name, module){ var loaded = null this.fetch = function(){ if (!loaded) { if (_.isFunction(module)) { loaded = execFuncLoad(name, module) } else if (isObject(module)) loaded = module else throw new Error('"'+name+'" module added with invalid target: "'+module+'"') } return loaded } } function FileGroup (files) { var loaded = null this.fetch = function(){ if (!loaded) { loaded = _.each(files, function(file){ if (_.isFunction(file)) return execFuncLoad(makeId(), file) if (isObject(file)) return file if (!_.isString(file)) throw new Error('easyioc.exec(): Invalid object in array: "'+file+'". Must be either function, string, or object (although the latter is discouraged).') if (!require.resolve(file)) throw new Error('Require could not resolve "'+file+'".') var module = require(file) if (_.isFunction(module)) return execFuncLoad(makeId(), module) else return module }) } return loaded } } function execFuncLoad (name, module) { // This where we detect circular dependency paths. var deps = fetchDeps(module) // deps = _.each(deps, function(dep){ // _.each() doesn't work here... odd. deps = deps.map(function(dep){ if (_.contains(path, dep)) throw new Error('Dependency circle encountered: '+path.concat(dep).join(' ➤ ')) // ▷ ➔ ➤ ▸ ▶ ➠ ⧐ ➞ path.push(dep) var mod = load(name, dep) path.pop() return mod }) return module.apply(new Object, deps) } function load (func, dep) { if (_.has(modules, dep)) return modules[dep].fetch() else throw new Error('Module "'+func+'" requires unknown dependency "'+dep+'".') } function fetchDeps (target) { if (target.length) { var args_regex = /^function\s*[^\(]*\(\s*([^\)]*)\)/m var text = target.toString() return text.match(args_regex)[1].split(',').map(function(e){return e.trim()}) } return [] } // credit: http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript function makeId (n) { n = n || 10 var text = "" var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" for (var i = 0; i < n; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)) return text } function doAdd (name, object) { modules[name] = adding[name] = object } // Exposed methods \\ /** * target can be a * 1) function, * 2) any valid string for require(), * 3) or array of strings for require(). * Name is optional except when target is a string path. * Method is silent when it fails. */ function add (name, target) { var module = target if (_.isArray(name)) doAdd(makeId(), new FileGroup(name)) else if (_.isString(name)) { if (_.isArray(target)) doAdd(name, new FileGroup(target)) else { if (!target) module = require(name) else if (_.isString(target)) module = require(target) else if (_.isFunction(target)) module = target doAdd(name, new Module(name, module)) } } return public_methods } /** * */ function exec(){ var loading = adding adding = {} for (var each in loading) loading[each].fetch() return public_methods } /** * */ function empty(){ adding = {} modules = {} return public_methods }