-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixed #1
- Loading branch information
Showing
37 changed files
with
481 additions
and
295 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ "excludeFiles": | ||
[ "node_modules/**" | ||
, "coverage" | ||
] | ||
, "requireLineFeedAtFileEnd": true | ||
, "disallowMultipleLineBreaks": true | ||
, "requireMultipleVarDecl": true | ||
, "disallowEmptyBlocks": true | ||
, "disallowSpaceAfterObjectKeys": true | ||
, "disallowCommaBeforeLineBreak": true | ||
, "disallowTrailingWhitespace": true | ||
, "requireCapitalizedConstructors": true | ||
, "requireSpacesInsideObjectBrackets": "all" | ||
, "requireSpacesInsideArrayBrackets": "all" | ||
, "validateLineBreaks": "LF" | ||
, "requireSpaceBeforeBinaryOperators": [ "+", "-", "/", "*", "=", "==", "===", "!=", "!==" ] | ||
, "requireSpaceAfterBinaryOperators": [ "+", "-", "/", "*", "=", "==", "===", "!=", "!==" ] | ||
, "requireSpaceAfterKeywords": [ "if", "else", "for", "while", "do", "switch", "try", "catch" ] | ||
, "disallowSpaceAfterPrefixUnaryOperators": [ "++", "--", "+", "-", "~", "!" ] | ||
, "disallowSpaceBeforePostfixUnaryOperators": [ "++", "--" ] | ||
, "requireSpaceBeforeBinaryOperators": [ "+", "-", "/", "*", "=", "==", "===", "!=", "!==" ] | ||
, "requireSpaceAfterBinaryOperators": [ "+", "-", "/", "*", "=", "==", "===", "!=", "!==" ] | ||
, "disallowKeywordsOnNewLine": [ "else" ] | ||
, "requireSpacesInFunctionExpression": { "beforeOpeningCurlyBrace": true } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
language: node_js | ||
node_js: | ||
- 0.10 | ||
- 0.11 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,229 +1,67 @@ | ||
var glob = require('glob') | ||
, async = require('async') | ||
, unique = require('lodash.uniq') | ||
, serviceLocator = require('service-locator')() | ||
, callbackNames = require('./lib/callback-names') | ||
, pluginables = {} | ||
, pluginNames = [] | ||
, preloadPlugins = require('./lib/preload-plugins') | ||
, loadPlugins = require('./lib/load-plugins') | ||
, validatePlugin = require('./lib/validate-plugin') | ||
, beforeLoad = [] | ||
, createServiceLocator = require('service-locator') | ||
, serviceLocator | ||
|
||
module.exports = function (list, cb) { | ||
serviceLocator = createServiceLocator() | ||
|
||
// TODO Don't assume it is a glob, allow other options! | ||
async.waterfall( | ||
[ function (callback) { | ||
[ function (cb) { | ||
if (Array.isArray(list)) { | ||
var files = [] | ||
async.each(list, function (pattern, eachCb) { | ||
glob(pattern, function (error, found) { | ||
if (error) return eachCb(error) | ||
files = files.concat(found) | ||
eachCb() | ||
eachCb(error) | ||
}) | ||
}, function (error) { | ||
callback(error, files) | ||
cb(error, files) | ||
}) | ||
} else { | ||
glob(list, callback) | ||
glob(list, cb) | ||
} | ||
} | ||
, handleFiles | ||
, discoverDependencies | ||
, validateDependencies | ||
, handleDependencies | ||
, load | ||
], cb) | ||
} | ||
|
||
// Mainly for tests! | ||
module.exports.reset = function () { | ||
pluginables = {} | ||
pluginNames = [] | ||
serviceLocator = require('service-locator')() | ||
} | ||
|
||
module.exports.getPlugins = function () { | ||
return serviceLocator | ||
} | ||
|
||
module.exports.register = function (plugin) { | ||
pluginNames.push(plugin.name) | ||
pluginables[plugin.name] = plugin | ||
} | ||
|
||
function handleFiles(files, cb) { | ||
async.each(files, function (file, callback) { | ||
validatePlugin(file, function (error, plugin) { | ||
if (error) return callback(error) | ||
|
||
// Plugin is valid, so add it to the list | ||
pluginables[plugin.name] = plugin | ||
pluginNames.push(plugin.name) | ||
, function (files, cb) { | ||
if (!files || files.length === 0) return cb(new Error('No pluginables found')) | ||
|
||
callback() | ||
}) | ||
}, cb) | ||
|
||
} | ||
|
||
function validatePlugin(file, cb) { | ||
var plugin = require(file) | ||
, error = null | ||
|
||
if (!plugin.name) { | ||
error = new Error('No name defined in ' + file) | ||
} else if (typeof plugin.init !== 'function') { | ||
error = new Error('Expected an exported init function in ' + file) | ||
} | ||
// TODO Add more validation checks | ||
|
||
cb(error, plugin) | ||
} | ||
var plugins = [] | ||
|
||
function discoverDependencies(cb) { | ||
async.each(pluginNames, function (name, callback) { | ||
var plugin = pluginables[name] | ||
async.each(files, function (file, callback) { | ||
validatePlugin(file, function (error, plugin) { | ||
if (error) return callback(error) | ||
|
||
if (plugin.dependencies && Array.isArray(plugin.dependencies)) { | ||
return callback() | ||
} | ||
|
||
// TODO move into own file for tests | ||
plugin.dependencies = plugin.init.toString() | ||
.replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg,'') | ||
.match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1] | ||
.split(/,/) | ||
|
||
if (plugin.dependencies.length === 1 && plugin.dependencies[0] === '') { | ||
plugin.dependencies = null | ||
} | ||
|
||
callback() | ||
}, cb) | ||
} | ||
|
||
function validateDependencies(cb) { | ||
async.each(pluginNames, function (name, callback) { | ||
var plugin = pluginables[name] | ||
, error = null | ||
|
||
if (!plugin.dependencies || plugin.dependencies.length === 0) return callback() | ||
|
||
if (plugin.async) { | ||
plugin.dependencies.pop() | ||
} else if (typeof plugin.async === 'undefined') { | ||
var lastArg = plugin.dependencies[plugin.dependencies.length - 1] | ||
|
||
if (callbackNames.indexOf(lastArg) !== -1) { | ||
plugin.async = true | ||
plugin.dependencies.pop() | ||
} | ||
} | ||
|
||
plugin.dependencies.forEach(function (dependency) { | ||
if (pluginNames.indexOf(dependency) === -1) { | ||
error = new Error(name + ' has an unknown dependency ' + dependency) | ||
return | ||
} | ||
}) | ||
|
||
// pluginables[name] = plugin | ||
|
||
callback(error) | ||
}, cb) | ||
} | ||
plugins.push(plugin) | ||
|
||
function handleDependencies(cb) { | ||
// TODO Look into tree structure for parallel loading where possible | ||
var loadOrder = [] | ||
, toCheck = pluginNames.slice() | ||
, error = null | ||
|
||
// TODO Optimise | ||
pluginNames.forEach(function (name) { | ||
// Load non-dependent first | ||
var plugin = pluginables[name] | ||
|
||
if (!plugin.dependencies || plugin.dependencies.length === 0) { | ||
loadOrder.push(name) | ||
toCheck.splice(toCheck.indexOf(name), 1) | ||
} | ||
}) | ||
|
||
// TODO Make this far more robust | ||
toCheck.forEach(function (name) { | ||
var plugin = pluginables[name] | ||
loadOrder = loadOrder.concat(plugin.dependencies, name) | ||
}) | ||
|
||
loadOrder = unique(loadOrder) | ||
|
||
cb(error, loadOrder) | ||
|
||
} | ||
|
||
function load(loadOrder, cb) { | ||
async.eachSeries(loadOrder, function (name, callback) { | ||
var plugin = pluginables[name] | ||
|
||
if (plugin.async) { | ||
loadAsync(plugin, callback) | ||
} else { | ||
try { | ||
loadSync(plugin) | ||
} catch (e) { | ||
return callback(e) | ||
} | ||
callback() | ||
}) | ||
}, function (error) { | ||
plugins = plugins.concat(beforeLoad) | ||
beforeLoad = [] | ||
|
||
callback() | ||
cb(error, plugins) | ||
}) | ||
} | ||
}, cb) | ||
} | ||
|
||
function getDependencyInstances(plugin) { | ||
if (!plugin.dependencies) return [] | ||
|
||
var instances = [] | ||
|
||
plugin.dependencies.forEach(function (name) { | ||
instances.push(serviceLocator[name]) | ||
}) | ||
|
||
return instances | ||
} | ||
|
||
function loadAsync(plugin, cb) { | ||
var args = getDependencyInstances(plugin).concat(function (error, instance) { | ||
, preloadPlugins | ||
, loadPlugins.bind(null, serviceLocator) | ||
], function (error, instances) { | ||
if (error) return cb(error) | ||
|
||
// Only register if truey | ||
if (instance) { | ||
serviceLocator.register(plugin.name, instance) | ||
} | ||
|
||
cb() | ||
}) | ||
|
||
var error = null | ||
serviceLocator = instances | ||
|
||
args.forEach(function (arg) { | ||
if (typeof arg === 'undefined') error = new Error('Undefined argument found for ' + plugin.name) | ||
cb(null, instances) | ||
}) | ||
|
||
if (error) return cb(error) | ||
|
||
plugin.init.apply(null, args) | ||
} | ||
|
||
function loadSync(plugin) { | ||
var args = getDependencyInstances(plugin) | ||
|
||
args.forEach(function (arg) { | ||
if (typeof arg === 'undefined') throw new Error('Undefined argument found for ' + plugin.name) | ||
}) | ||
|
||
var instance = plugin.init.apply(null, args) | ||
module.exports.getPlugins = function () { | ||
return serviceLocator | ||
} | ||
|
||
// Only register if truey | ||
if (instance) { | ||
serviceLocator.register(plugin.name, instance) | ||
} | ||
module.exports.registerBeforeLoad = function (plugin) { | ||
beforeLoad.push(plugin) | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module.exports = function (serviceLocator, plugin) { | ||
if (!plugin.dependencies) return [] | ||
|
||
var instances = [] | ||
|
||
plugin.dependencies.forEach(function (name) { | ||
instances.push(serviceLocator[name]) | ||
}) | ||
|
||
return instances | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module.exports = function (fn) { | ||
var args = fn | ||
.toString() | ||
.replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg,'') | ||
.match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1] | ||
.split(/,/) | ||
|
||
if (args.length === 1 && !args[0]) args = [] | ||
|
||
return args | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
module.exports = function (fn) { | ||
var name = null | ||
|
||
if (!fn) return name | ||
|
||
// TODO optimise | ||
var names = fn | ||
.toString() | ||
.replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg,'') | ||
.match(/^function(\w*)/) | ||
|
||
if (!names || names.length !== 2 || !names[1]) return name | ||
|
||
name = names[1] | ||
|
||
return name | ||
} |
Oops, something went wrong.