prodio
Version:
Simplified project management
353 lines (330 loc) • 9.69 kB
JavaScript
(function(){
'use strict';
var async = require('async');
var path = require('path');
var fs = require('fs');
var vm = require('vm');
var semver = require('semver');
var noop = function noop(){};
var DEFAULTS = require('./defaults');
var ERRORS = require('./errors');
var _defaults = function _defaults(src, overrides){
var keys = Object.keys(src);
var dst = overrides;
keys.forEach(function _defaultsKeys(key){
if(dst[key] === void(0)){
dst[key] = src[key];
}else if(src[key] && typeof(src[key])==='object'){
dst[key] = _defaults(src[key], dst[key]||{});
}
});
return dst;
};
var Plugger = function Plugger(options){
var self = this;
self.plugins = {};
self.types = {};
self.roles = {};
self.options = _defaults(DEFAULTS, options||{});
};
Plugger.prototype.plugin = function plugin(location){
var self = this;
return (self.plugins[location]||{}).plugin;
};
Plugger.prototype.plugins = function plugins(){
var self = this;
var results = [], keys = Object.keys(self.plugins);
keys.forEach(function plugins_keys(key){
results.push(self.plugins[key].plugin);
});
var r;
results.sort(_libsSort);
return results;
};
Plugger.prototype.status = function status(location){
var self = this;
return (self.plugins[location]||{status: 'unknown'}).status;
};
Plugger.prototype.location = function location(location){
var self = this;
return (self.plugins[location]||{}).location;
};
Plugger.prototype.details = function details(location){
var self = this;
if(location !== void 0){
return self.plugins[location];
}else{
return self.plugins;
}
};
Plugger.prototype.status = function status(location, status){
var self = this;
if(!self.plugins[location]){
if(status){
throw new ERRORS.PluginNotLoaded(location);
}
return 'unknown';
}
if(status){
self.plugins[plugin].status = status;
}
return self.plugins[location].status;
};
/*
load(callback)
load(library, callback)
load([library, library] callback)
*/
var _requires = function _requires(a, b){
if(a.requires instanceof Array){
if(a.requires.indexOf(b.name) > -1){
return 1;
}
}
if(b.requires instanceof Array){
if(b.requires.indexOf(a.name) > -1){
return -1;
}
}
return 0;
};
var _libsSort = function _libsSort(a, b){
var r = _requires(a, b);
if(r !== 0){
return r;
}
if(a.priority > b.priority){
return 1;
}else if(a.priority < b.priority){
return -1;
}else{
return 0;
}
};
var _findFile = function _findFile(file, location){
try{
var folder = path.resolve(location, file);
var stat = fs.lstatSync(folder);
if(stat.isDirectory()){
if(fs.existsSync(folder+path.sep+'plugger.json')){
return folder+path.sep+'plugger.json';
}else if(fs.existsSync(folder+path.sep+'index.js')){
return folder+path.sep+'index.js';
}
}else if(stat.isFile()){
return folder;
}
}catch(e){
if(fs.existsSync(folder+'.js')){
return folder+'.js';
}
}
try{
file = require.resolve(file);
var loc = loc.split(/[\\\/]/gi);
loc.pop();
while(loc.length>0){
if(fs.existsSync(loc.join(path.sep)+path.sep+'package.json')){
if(fs.existsSync(loc.join(path.sep)+path.sep+'plugger.json')){
return loc.join(path.sep)+path.sep+'plugger.json';
}
break;
}
loc.pop();
}
return file;
}catch(e){
return new ERRORS.FileNotFound(file, location);
}
};
var _require = require;
var _loadLib = function _loadLib(root, name, callback){
var file = _findFile(name, root);// || new ERRORS.FileNotFound(name, root);
if(file instanceof ERRORS.FileNotFound){
return callback(file);
}
var isPlugger = file && !!file.match(/plugger.json$/i);
var lib;
var done = function _loadLib_done(lib){
process.nextTick(function done_nextTick(){
if(typeof(lib)==='function'){
lib = {
priority: 0,
version: '0.0.0',
plugin: lib
};
}
if(lib.version === void(0)){
lib.version = '0.0.0';
}
lib.name = name;
if(!semver.valid(lib.version)){
return callback(new ERRORS.InvalidSemVer(name, lib.version), lib);
}
return callback(null, lib);
});
};
if(isPlugger){
try{
var sandbox = {}, location;
var folder = file.split(/[\\\/]/gi);
folder.pop();
folder = folder.join(path.sep)+path.sep;
vm.runInNewContext('this.config = '+fs.readFileSync(file), sandbox);
lib = sandbox.config||{};
location = folder + (lib.main||'index.js');
lib.plugin = _require(location);
return done(lib);
}catch(e){
e.plugin = name;
e.location = location || folder;
return callback(e);
}
}else{
try{
lib = _require(file);
return done(lib);
}catch(e){
e.plugin = name;
e.location = file;
return callback(e);
}
try{
lib = _require(file);
}catch(e){
try{
lib = _require(name);
}catch(e){
e.plugin = name;
e.location = path.resolve(root, name);
return callback(e);
}
}
return done(lib);
}
};
var _loadFolder = function _loadFolder(root, callback){
var self = this;
fs.readdir(root, function _loadFolder_readdir(err, files){
if(err){
return callback(err);
}
_loadArray.call(self, root, files, callback);
});
};
var _loadArray = function _loadArray(root, libs, callback){
var self = this;
if(arguments.length===2){
callback = libs;
libs = root;
root = self.options.path;
}
var stack = [].concat(libs);
var errs = [], _libs = [];
var done = function _loadArray_done(){
var r;
_libs.sort(_libsSort);
callback(errs.length?errs:null, _libs);
};
var next = function _loadArray_next(){
if(stack.length){
process.nextTick(function doLoad_nextTick(){
doLoad();
});
}else{
done();
}
};
var doLoad = function _loadArray_doLoad(){
var name = stack.shift();
var needsLoad = !self.plugins[name];
var _lib = self.plugins[name] = {name: name, status: 'loading'};
if(!needsLoad){
return next();
}
_loadLib(root, name, function(err, lib){
if(lib){
lib.status = 'ready';
lib.name = name;
if(Array.isArray(lib.requires)){
Array.prototype.push.apply(stack, lib.requires);
}
if(lib.priority === void(0)){
lib.priority = self.options.defaultPriority;
}
self.plugins[name] = _lib = lib;
if(lib.role){
if(!self.roles[lib.role]){
self.roles[lib.role] = [];
}
self.roles[lib.role].push(lib);
}
if(lib.type){
if(!self.roles[lib.type]){
self.roles[lib.type] = [];
}
self.roles[lib.type].push(lib);
}
_libs.push(lib);
}
if(err){
err.plugin = err.plugin || name;
err.location = err.location || root;
_lib.status = 'error';
_lib.error = err;
errs.push(err);
}
next();
});
};
doLoad();
};
Plugger.prototype.load = function load(location, callback){
var self = this, defaultPriority = self.options.defaultPriority;
var doCallback = function(errs, pkgs){
(callback||noop)(errs, pkgs);
};
switch(typeof(location)){
case('string'):
return _loadArray.call(self, [location], doCallback);
case('function'):
callback = location;
location = '';
return _loadFolder.call(self, self.options.path, doCallback);
default:
return _loadArray.call(self, location, doCallback);
}
};
var _validateRequires = function _validateRequires(requires, callback){
var self = this;
var i, l=requires.length;
var errors = [];
for(i=0; i<l; i++){
if(!self.plugins[requires[i]]){
errors.push(new ERRORS.PluginDoesNotExist(requires[i]));//new Error('Plugin '+requires[i]+' does not exist'));
}
}
return callback(errors.length?errors:null, !errors.length);
};
Plugger.prototype.safe = function safe(root, name, callback){
var self = this;
if(arguments.length===2){
callback = name;
name = root;
root = self.options.path;
}
_loadLib(root, name, function(err, lib){
if(err){
return callback(err, false);
}
if(!lib){
return callback(new Error('Something went really wrong'), false);
}
if(!Array.isArray(lib.requires)){
return callback(null, true);
}
_validateRequires.call(self, lib.requires, callback);
});
};
module.exports = Plugger;
})();