Comparing version 0.1.11 to 0.1.13
282
architect.js
@@ -1,2 +0,1 @@ | ||
/*global require console process setTimeout*/ | ||
( // Module boilerplate to support node.js and AMD. | ||
@@ -11,2 +10,4 @@ (typeof module !== "undefined" && function (m) { module.exports = m(require('events')); }) || | ||
var DEBUG = typeof location != "undefined" && location.href.match(/debug=[123]/) ? true : false; | ||
// Only define Node-style usage using sync I/O if in node. | ||
@@ -21,2 +22,3 @@ if (typeof module === "object") (function () { | ||
var packagePathCache = {}; | ||
var basePath; | ||
@@ -36,2 +38,10 @@ exports.loadConfig = loadConfig; | ||
function resolveConfig(config, base, callback) { | ||
if(typeof base === 'function') { | ||
// probably being called from loadAdditionalConfig, use saved base | ||
callback = base; | ||
base = basePath; | ||
} else { | ||
basePath = base; | ||
} | ||
if (!callback) | ||
@@ -149,3 +159,3 @@ return resolveConfigSync(config, base); | ||
next(null, dirname(packagePath)); | ||
} | ||
} | ||
else { | ||
@@ -187,2 +197,5 @@ resolvePackage(base, modulePath, next); | ||
newPath = resolve(base, packagePath); | ||
if (!existsSync(newPath)) { | ||
newPath = newPath + ".js"; | ||
} | ||
if (existsSync(newPath)) { | ||
@@ -259,3 +272,3 @@ newPath = realpathSync(newPath); | ||
if (nextBase === base) | ||
tryNext(null); | ||
tryNext("/"); // for windows | ||
else | ||
@@ -282,8 +295,6 @@ tryNext(nextBase); | ||
function resolveConfig(config, base, callback) { | ||
if (typeof base == "function") { | ||
callback = base; | ||
base = ""; | ||
} | ||
function resolveConfig(config, base, callback, errback) { | ||
if (typeof base == "function") | ||
return resolveConfig(config, "", arguments[1], arguments[2]); | ||
var paths = [], pluginIndexes = {}; | ||
@@ -308,7 +319,7 @@ config.forEach(function (plugin, index) { | ||
plugin.setup = module; | ||
plugin.provides = module.provides || []; | ||
plugin.consumes = module.consumes || []; | ||
plugin.provides = module.provides || plugin.provides || []; | ||
plugin.consumes = module.consumes || plugin.consumes || []; | ||
}); | ||
callback(null, config); | ||
}); | ||
}, errback); | ||
} | ||
@@ -321,3 +332,3 @@ }()); | ||
// Check a plugin config list for bad dependencies and throw on error | ||
function checkConfig(config) { | ||
function checkConfig(config, lookup) { | ||
@@ -338,6 +349,6 @@ // Check for the required fields in each plugin. | ||
return checkCycles(config); | ||
return checkCycles(config, lookup); | ||
} | ||
function checkCycles(config) { | ||
function checkCycles(config, lookup) { | ||
var plugins = []; | ||
@@ -368,3 +379,3 @@ config.forEach(function(pluginConfig, index) { | ||
var service = consumes[i]; | ||
if (!resolved[service]) { | ||
if (!resolved[service] && (!lookup || !lookup(service))) { | ||
resolvedAll = false; | ||
@@ -393,3 +404,3 @@ } else { | ||
plugin.consumes.forEach(function(name) { | ||
if (unresolved[name] == false) | ||
if (unresolved[name] === false) | ||
return; | ||
@@ -404,12 +415,17 @@ if (!unresolved[name]) | ||
}); | ||
Object.keys(unresolved).forEach(function(name) { | ||
if (unresolved[name] == false) | ||
if (unresolved[name] === false) | ||
delete unresolved[name]; | ||
}); | ||
console.error("Could not resolve dependencies of these plugins:", plugins); | ||
console.error("Resolved services:", Object.keys(resolved)); | ||
console.error("Missing services:", unresolved); | ||
throw new Error("Could not resolve dependencies"); | ||
var unresolvedList = Object.keys(unresolved); | ||
var resolvedList = Object.keys(resolved); | ||
var err = new Error("Could not resolve dependencies\n" | ||
+ (unresolvedList.length ? "Missing services: " + unresolvedList | ||
: "Config contains cyclic dependencies" // TODO print cycles | ||
)); | ||
err.unresolved = unresolvedList; | ||
err.resolved = resolvedList; | ||
throw err; | ||
} | ||
@@ -422,5 +438,8 @@ | ||
var app = this; | ||
app.config = []; | ||
app.destructors = []; | ||
app.services = { | ||
app.config = config; | ||
app.packages = {}; | ||
app.pluginToPackage = {}; | ||
var isAdditionalMode; | ||
var services = app.services = { | ||
hub: { | ||
@@ -433,122 +452,122 @@ on: function (name, callback) { | ||
// Give createApp some time to subscribe to our "ready" event | ||
(typeof process === "object" ? process.nextTick : setTimeout)(function() { | ||
app.loadPlugins(config, function(err) { | ||
if (err) { | ||
throw err; | ||
} | ||
app.emit("ready", app); | ||
}); | ||
}); | ||
} | ||
// Check the config | ||
var sortedPlugins = checkConfig(config); | ||
Architect.prototype = Object.create(EventEmitter.prototype, {constructor:{value:Architect}}); | ||
var destructors = []; | ||
var recur = 0, callnext, ready; | ||
function startPlugins(additional) { | ||
var plugin = sortedPlugins.shift(); | ||
if (!plugin) { | ||
ready = true; | ||
return app.emit(additional ? "ready-additional" : "ready", app); | ||
} | ||
Architect.prototype.destroy = function() { | ||
var app = this; | ||
app.destructors.forEach(function(destroy) { | ||
destroy(); | ||
}); | ||
app.destructors = []; | ||
}; | ||
Architect.prototype.loadPlugins = function(config, callback) { | ||
var app = this; | ||
var sortedConfig; | ||
try { | ||
sortedConfig = checkConfig(config.concat(app.config)); | ||
} | ||
catch(ex) { | ||
return callback(ex); | ||
} | ||
// prevent double loading of plugins | ||
sortedConfig = sortedConfig.filter(function(c) { | ||
return config.indexOf(c) > -1; | ||
}); | ||
var p; | ||
function next(err) { | ||
if (err) { | ||
return callback(err); | ||
var imports = {}; | ||
if (plugin.consumes) { | ||
plugin.consumes.forEach(function (name) { | ||
imports[name] = services[name]; | ||
}); | ||
} | ||
if (p && app.config.indexOf(p) === -1) { | ||
app.config.push(p); | ||
var m = /^plugins\/([^\/]+)|\/plugins\/[^\/]+\/([^\/]+)/.exec(plugin.packagePath); | ||
var packageName = m && (m[1] || m[2]); | ||
if (!app.packages[packageName]) app.packages[packageName] = []; | ||
if (DEBUG) { | ||
recur++; | ||
plugin.setup(plugin, imports, register); | ||
while (callnext && recur <= 1) { | ||
callnext = false; | ||
startPlugins(additional); | ||
} | ||
recur--; | ||
} | ||
else { | ||
try { | ||
recur++; | ||
plugin.setup(plugin, imports, register); | ||
} catch (e) { | ||
e.plugin = plugin; | ||
app.emit("error", e); | ||
throw e; | ||
} finally { | ||
while (callnext && recur <= 1) { | ||
callnext = false; | ||
startPlugins(additional); | ||
} | ||
recur--; | ||
} | ||
} | ||
function register(err, provided) { | ||
if (err) { return app.emit("error", err); } | ||
plugin.provides.forEach(function (name) { | ||
if (!provided.hasOwnProperty(name)) { | ||
var err = new Error("Plugin failed to provide " + name + " service. " + JSON.stringify(plugin)); | ||
err.plugin = plugin; | ||
return app.emit("error", err); | ||
} | ||
services[name] = provided[name]; | ||
app.pluginToPackage[name] = { | ||
path: plugin.packagePath, | ||
package: packageName, | ||
version: plugin.version, | ||
isAdditionalMode: isAdditionalMode | ||
}; | ||
app.packages[packageName].push(name); | ||
app.emit("service", name, services[name], plugin); | ||
}); | ||
if (provided && provided.hasOwnProperty("onDestroy")) | ||
destructors.push(provided.onDestroy); | ||
p = sortedConfig.shift(); | ||
if (!p) { | ||
return callback(); | ||
app.emit("plugin", plugin); | ||
if (recur) return (callnext = true); | ||
startPlugins(additional); | ||
} | ||
app.registerPlugin(p, next); | ||
} | ||
next(); | ||
}; | ||
/** | ||
* Register a plugin in the service | ||
*/ | ||
Architect.prototype.registerPlugin = function(plugin, next) { | ||
var app = this; | ||
var services = app.services; | ||
// Give createApp some time to subscribe to our "ready" event | ||
(typeof process === "object" ? process.nextTick : setTimeout)(startPlugins); | ||
var imports = {}; | ||
if (plugin.consumes) { | ||
plugin.consumes.forEach(function (name) { | ||
imports[name] = services[name]; | ||
this.loadAdditionalPlugins = function(additionalConfig, callback){ | ||
isAdditionalMode = true; | ||
exports.resolveConfig(additionalConfig, function (err, additionalConfig) { | ||
if (err) return callback(err); | ||
app.once(ready ? "ready-additional" : "ready", function(app){ | ||
callback(null, app); | ||
}); // What about error state? | ||
// Check the config - hopefully this works | ||
var _sortedPlugins = checkConfig(additionalConfig, function(name){ | ||
return services[name]; | ||
}); | ||
if (ready) { | ||
sortedPlugins = _sortedPlugins; | ||
// Start Loading additional plugins | ||
startPlugins(true); | ||
} | ||
else { | ||
_sortedPlugins.forEach(function(item){ | ||
sortedPlugins.push(item); | ||
}); | ||
} | ||
}); | ||
} | ||
try { | ||
plugin.setup(plugin, imports, register); | ||
} catch(e) { | ||
return app.emit("error", e); | ||
} | ||
function register(err, provided) { | ||
if (err) { | ||
return app.emit("error", err); | ||
} | ||
plugin.provides.forEach(function (name) { | ||
if (!provided.hasOwnProperty(name)) { | ||
var err = new Error("Plugin failed to provide " + name + " service. " + JSON.stringify(plugin)); | ||
return app.emit("error", err); | ||
} | ||
services[name] = provided[name]; | ||
if (typeof provided[name] != "function") | ||
provided[name].name = name; | ||
app.emit("service", name, services[name]); | ||
this.destroy = function() { | ||
destructors.forEach(function(destroy) { | ||
destroy(); | ||
}); | ||
if (provided && provided.hasOwnProperty("onDestroy")) { | ||
app.destructors.push(provided.onDestroy); | ||
} | ||
plugin.destroy = function() { | ||
if (plugin.provides.length) { | ||
// @todo, make it possible if all consuming plugins are also dead | ||
var err = new Error("Plugins that provide services cannot be destroyed. " + JSON.stringify(plugin)); | ||
return app.emit("error", err); | ||
} | ||
destructors = []; | ||
}; | ||
} | ||
Architect.prototype = Object.create(EventEmitter.prototype, {constructor:{value:Architect}}); | ||
if (provided && provided.hasOwnProperty("onDestroy")) { | ||
app.destructors.splice(app.destructors.indexOf(provided.onDestroy), 1); | ||
provided.onDestroy(); | ||
} | ||
// delete from config | ||
app.config.splice(app.config.indexOf(plugin), 1); | ||
app.emit("destroyed", plugin); | ||
}; | ||
app.emit("plugin", plugin); | ||
next(); | ||
} | ||
}; | ||
Architect.prototype.getService = function(name) { | ||
@@ -595,2 +614,3 @@ if (!this.services[name]) { | ||
return app; | ||
} | ||
@@ -597,0 +617,0 @@ |
{ | ||
"name": "architect", | ||
"description": "A Simple yet powerful plugin system for node applications", | ||
"version": "0.1.11", | ||
"version": "0.1.13", | ||
"author": "ajax.org B.V. <info@ajax.org>", | ||
@@ -6,0 +6,0 @@ "contributors": [ |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
27446
564
0