we-core
Version:
We.js is a node.js framework for build real time applications, sites or blogs!
353 lines (307 loc) • 9.33 kB
JavaScript
/**
* We.js Plugin Class
*
*
*/
var _ = require('lodash'),
path = require('path'),
fs = require('fs'),
async = require('async'),
requireAll = require('require-all');
module.exports = function (we) {
/**
* We.js plugin Class constructor
*
* @param {string} name plugin npm pakage name
* @param {string} projectPath project path where the plugin is instaled
*/
function Plugin(pluginPath) {
this.pluginPath = pluginPath;
this.we = we;
this.events = this.we.events;
this.hooks = this.we.hooks;
this.router = this.we.router;
/**
* We.js Assets
* @type {Object}
*/
this.assets = this.we.view.assets;
this.addJs = this.we.view.assets.addJs;
this.addCss = this.we.view.assets.addCss;
this['package.json'] = require( path.join( pluginPath , 'package.json') );
this.controllersPath = path.join( this.pluginPath, this.controllerFolder );
this.modelsPath = path.join( this.pluginPath, this.modelFolder );
this.templatesPath = this.pluginPath + '/server/templates';
this.helpersPath = this.pluginPath + '/server/helpers';
this.resourcesPath = this.pluginPath + '/server/resources';
this.routesPath = this.pluginPath + '/server/routes';
this.helpers = {};
this.layouts = {};
this.templates = {};
/**
* Default plugin config object
*
* @type {Object}
*/
this.configs = {};
/**
* Default plugin resources
*
* @type {Object}
*/
this.controllers = {};
this.models = {};
this.routes = {};
this.appFiles = [];
this.appAdminFiles = [];
}
/**
* Default initializer function, override in plugin.js file if need
*
* @param {Object} we we.js object
* @param {Function} cb callback
*/
Plugin.prototype.init = function initPlugin(we, cb) { return cb(); }
/**
* Set plugin config
* @param {Object} config
*/
Plugin.prototype.setConfigs = function setConfigs(configs) {
this.configs = configs;
}
// default plugin paths
Plugin.prototype.controllerFolder = 'server/controllers';
Plugin.prototype.modelFolder = 'server/models';
/**
* Set plugin routes
*
* @param {object} routes
*/
Plugin.prototype.setRoutes = function setRoutes(routes) {
var routePaths = Object.keys(routes);
var routePath;
for (var i = routePaths.length - 1; i >= 0; i--) {
routePath = routePaths[i];
this.setRoute(routePath, routes[routePath]);
}
}
/**
* Set one route in plugin routes
* @param {string} path route path
* @param {object} configs route configs how will be avaible as res.locals
*/
Plugin.prototype.setRoute = function(routePath, configs) {
this.routes[routePath] = configs;
this.routes[routePath].path = routePath;
this.routes[routePath].pluginPath = this.pluginPath;
}
Plugin.prototype.setResource = function(opts) {
var router = this.we.router;
var fullName = (opts.namePrefix || '') + opts.name;
// first save resource in name or merge route options if exists
if (!router.resourcesByName[fullName]) {
router.resourcesByName[fullName] = opts;
} else {
_.merge(router.resourcesByName[fullName], opts);
}
if (opts.parent) {
// is subroute
if (!router.resourcesByName[opts.parent]) {
// parent dont are set
// temporary create parent resource wit subroutes attr
router.resourcesByName[opts.parent] = { subRoutes: {} };
// add reference to route in parent subroutes
router.resourcesByName[opts.parent].subRoutes[fullName] = router.resourcesByName[fullName];
} else {
// parent resource is set
if (!router.resourcesByName[opts.parent].subRoutes) {
// add subRoutes object if dont are set
router.resourcesByName[opts.parent].subRoutes = {};
}
if (!router.resourcesByName[opts.parent].subRoutes[fullName]) {
router.resourcesByName[opts.parent].subRoutes[fullName] = {};
}
// add reference to route in parent resource subroutes
router.resourcesByName[opts.parent].subRoutes[fullName] = router.resourcesByName[fullName];
}
} else {
// is route route
router.resources[fullName] = router.resourcesByName[fullName];
}
}
/**
* Set plugin helpers DEPRECATED!
* Now this feature have a auto load!
* @param {object} helpers
*/
Plugin.prototype.setHelpers = function setHelpers() {
this.we.log.warn('DEPRECATED! Plugin.setHelpers in plugin: ', this.pluginPath);
}
/**
* Set plugin layouts
* @param {object} layouts
*/
Plugin.prototype.setLayouts = function setLayouts(layouts) {
this.layouts = layouts;
}
/**
* Set plugin templates DEPRECATED!
* Now this feature have a auto load!
* @param {object} templates
*/
Plugin.prototype.setTemplates = function setTemplates() {
this.we.log.warn('DEPRECATED! Plugin.setTemplates in plugin: ', this.pluginPath);
}
/**
* Load all we.js plugin features
*
* Auto load avaible for plugins, helpers ...
*
*/
Plugin.prototype.loadFeatures = function loadFeatures(we, cb) {
var self = this;
async.parallel([
function loadControllesAndModels(done) {
self.loadControllers(we);
self.loadModels(we);
done();
},
function loadPluginTemplates (done) {
if (we.view.loadFromCache()) return done();
self.loadTemplates(done);
},
function loadHelpers(done) {
self.loadHelpers(done);
},
function loaderHookExtensor(done) {
we.hooks.trigger('plugin:load:features', {
plugin: self,
we: we
}, done);
},
// load resources then the routes
function loadResources(done) {
self.loadResources(function(){
self.loadRoutes(done);
});
}
], function (err){
if (err) return cb(err);
if (self.layouts) _.merge(we.view.configuration.layouts, self.layouts);
cb();
});
}
/**
* load plugin controllers
*/
Plugin.prototype.loadControllers = function loadControllers() {
try {
this.controllers = requireAll({
dirname : this.controllersPath,
filter : /(.+)\.js$/
});
} catch(e) {
if (e.code !== 'ENOENT') throw e;
}
};
/**
* load plugin models
*/
Plugin.prototype.loadModels = function loadModels(we) {
try {
this.models = requireAll({
dirname : this.modelsPath,
filter : /(.+)\.js$/,
resolve : function (model) {
return model(we);
}
});
} catch(e) {
if (e.code !== 'ENOENT') throw e;
}
};
/**
* Load all plugin template paths
*
* Runs in we.js bootstrap
*/
Plugin.prototype.loadTemplates = function loadTemplates (cb) {
var we = this.we;
var self = this, templateName;
// load template folders
we.utils.listFilesRecursive(this.templatesPath, function (err, list){
for (var i = 0; i < list.length; i++) {
if (list[i].indexOf('.hbs', list[i].length - 4) >-1) {
templateName = list[i].substring(0, list[i].length-4).substring(self.templatesPath.length+1);
// ensures that template names always have / slashes
if (path.sep != '/') templateName = templateName.split(path.sep).join('/');
self.templates[templateName] = list[i];
we.view.configuration.templates[templateName] = list[i];
}
}
cb();
});
}
/**
* Load helpers from folder server/helpers
*
* @param {Object} we
* @param {Function} cb callback
*/
Plugin.prototype.loadHelpers = function loadHelpers(cb) {
var we = this.we;
var self = this, name, file;
fs.readdir(this.helpersPath , function (err, list){
if (err) {
if (err.code === 'ENOENT') return cb();
return cb(err);
}
for (var i = 0; i < list.length; i++) {
if (list[i].indexOf('.js', list[i].length - 3) >-1) {
name = list[i].substring(0, list[i].length-3);
file = self.helpersPath +'/'+list[i];
self.helpers[name] = file;
we.view.configuration.helpers[name] = file;
}
}
cb();
});
}
/**
* Load route resources from folder server/resources
*
* @param {Function} cb callback
*/
Plugin.prototype.loadResources = function loadResources(cb) {
var self = this;
fs.readdir(this.resourcesPath , function (err, list){
if (err) {
if (err.code === 'ENOENT') return cb();
throw err;
}
for (var i = 0; i < list.length; i++) {
self.setResource(require(self.resourcesPath+'/'+list[i]));
}
cb();
});
}
/**
* Load routes from folder server/routes
*
* @param {Function} cb callback
*/
Plugin.prototype.loadRoutes = function loadRoutes(cb) {
var self = this;
fs.readdir(this.routesPath , function (err, list) {
if (err) {
if (err.code === 'ENOENT') return cb();
throw err;
}
for (var i = 0; i < list.length; i++) {
self.setRoutes(require(self.routesPath+'/'+list[i]));
}
cb();
});
}
return Plugin;
}