Socket
Socket
Sign inDemoInstall

clout-js

Package Overview
Dependencies
118
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.1.0-beta.2 to 2.1.0-beta.3

.eslintignore

32

Gruntfile.js

@@ -7,19 +7,19 @@ /*!

module.exports = function(grunt) {
grunt.initConfig({
jsdoc : {
dist : {
src: ['bin/**/*.js', 'hooks/**/*.js', 'hookslib/**/*.js', 'lib/**/*.js', 'test/lib.js', 'index.js', 'README.md'],
options: {
tutorials: 'tutorials/',
destination: 'docs',
template: './node_modules/minami'
}
}
}
});
module.exports = function gruntConfig(grunt) {
grunt.initConfig({
jsdoc: {
dist: {
src: ['bin/**/*.js', 'hooks/**/*.js', 'hookslib/**/*.js', 'lib/**/*.js', 'test/lib.js', 'index.js', 'README.md'],
options: {
tutorials: 'tutorials/',
destination: 'docs',
template: './node_modules/minami',
},
},
},
});
grunt.loadNpmTasks('grunt-jsdoc');
grunt.registerTask('gendoc', ['jsdoc']);
grunt.registerTask('defualt', ['jsdoc']);
grunt.loadNpmTasks('grunt-jsdoc');
grunt.registerTask('gendoc', ['jsdoc']);
grunt.registerTask('defualt', ['jsdoc']);
};

@@ -13,27 +13,27 @@ /*!

module.exports = {
/**
* initialize apis from application paths
* @property {event} event start
* @property {priority} priority API
*/
initialize: {
event: 'start',
priority: 'API',
fn: function (next) {
let clientApiFolder = path.resolve(this.rootDirectory, 'apis');
let apiDirs = this.modules
.map((moduleInfo) => path.resolve(moduleInfo.path, 'apis'))
.concat([clientApiFolder]);
/**
* initialize apis from application paths
* @property {event} event start
* @property {priority} priority API
*/
initialize: {
event: 'start',
priority: 'API',
fn(next) {
const clientApiFolder = path.resolve(this.rootDirectory, 'apis');
const apiDirs = this.modules
.map(moduleInfo => path.resolve(moduleInfo.path, 'apis'))
.concat([clientApiFolder]);
if (!this.core) {
this.core = {};
}
if (!this.core) {
this.core = {};
}
this.core.api = new CloutApiRoutes(this);
this.core.api.loadAPIsFromDirs(apiDirs);
this.core.api.attachRouterToApp();
this.core.api = new CloutApiRoutes(this);
this.core.api.loadAPIsFromDirs(apiDirs);
this.core.api.attachRouterToApp();
next();
}
}
next();
},
},
};

@@ -10,20 +10,19 @@ /*!

*/
const debug = require('debug')('clout:hook/config');
module.exports = {
/**
* add config to application locals
* @property {event} event start
* @property {priority} priority 25
*/
middleware: {
event: 'start',
priority: 25,
fn: function (next) {
!this.app.locals && (this.app.locals = {});
this.app.locals.config = this.config;
this.app.request.clout = this;
next();
}
}
/**
* add config to application locals
* @property {event} event start
* @property {priority} priority 25
*/
middleware: {
event: 'start',
priority: 25,
fn(next) {
this.app.locals = this.app.locals || {};
this.app.locals.config = this.config;
this.app.request.clout = this;
next();
},
},
};

@@ -12,82 +12,85 @@ /*!

const debug = require('debug')('clout:hook/controllers');
const utils = require('../lib/utils');
const express = require('express');
const router = express.Router();
const async = require('async');
const Q = require('q');
const utils = require('../lib/utils');
module.exports = {
/**
* initialize controllers from application paths
* @property {event} event start
* @property {priority} priority CONTROLLER
*/
initialize: {
event: 'start',
priority: 'CONTROLLER',
fn: function (next) {
var self = this;
/**
* initialize controllers from application paths
* @property {event} event start
* @property {priority} priority CONTROLLER
*/
initialize: {
event: 'start',
priority: 'CONTROLLER',
fn(next) {
const self = this;
try {
function loadController(dir) {
let name = dir.split('controllers/')[1].replace('.js', '');
debug('loading controller %s', name);
let controller = require(dir);
function loadController(dir) {
const name = dir.split('controllers/')[1].replace('.js', '');
debug('loading controller %s', name);
const controller = require(dir);
if (!controller.path) { return; }
if (!controller.path) { return; }
let hooks = controller.hooks || [];
let method = controller.method ? controller.method.toLowerCase() : 'all';
const hooks = controller.hooks || [];
const method = controller.method ? controller.method.toLowerCase() : 'all';
// log endpoint request
router[method](controller.path, function (req, res, next) {
req.logger.info('Endpoint [%s] %s', req.method, req.path);
debug('Endpoint [%s] %s', req.method, req.path);
next();
});
// log endpoint request
router[method](controller.path, (req, res, done) => {
req.logger.info('Endpoint [%s] %s', req.method, req.path);
debug('Endpoint [%s] %s', req.method, req.path);
done();
});
// load hook first
hooks.forEach(function (hook) {
router[method](controller.path, function (req) {
hook.name && debug('hook:', hook.name);
hook.apply(this, arguments);
});
});
// load hook first
hooks.forEach((hook) => {
router[method](controller.path, function cloutHook(...args) {
if (hook.name) {
debug('hook:', hook.name);
}
// load controller
if (controller.fn) {
debug('loaded endpoint [%s] %s', method, controller.path);
router[method](controller.path, function (req) {
debug('loading controller %s:%s', name);
controller.fn.apply(this, arguments);
});
}
}
hook.apply(this, args);
});
});
function loadControllersFromDirectory(dir) {
let dirs = utils.getGlobbedFiles(path.join(dir, '**/**.js'));
dirs.forEach(loadController);
return Promise.resolve();
}
// load controller
if (controller.fn) {
debug('loaded endpoint [%s] %s', method, controller.path);
router[method](controller.path, function cloutController(...args) {
debug('loading controller %s:%s', name);
controller.fn.apply(this, args);
});
}
}
debug('loading controllers');
// 1) load module controllers
async.each(this.modules, function (module, next) {
loadControllersFromDirectory(path.join(module.path, 'controllers')).then(function () {
next(null);
}, next);
}, function done(err) {
if (err) { throw new Error(err); }
// 2) load application controllers
loadControllersFromDirectory(path.join(self.rootDirectory, 'controllers')).then(function () {
debug('attached router');
self.app.use('/', router);
next();
}, next);
});
} catch(e) {
console.log(e);
}
}
}
function loadControllersFromDirectory(dir) {
const dirs = utils.getGlobbedFiles(path.join(dir, '**/**.js'));
dirs.forEach(loadController);
return Promise.resolve();
}
try {
debug('loading controllers');
// 1) load module controllers
async.each(this.modules, (module, done) => {
loadControllersFromDirectory(path.join(module.path, 'controllers'))
.then(() => done())
.catch(err => done(err));
}, (err) => {
if (err) { throw new Error(err); }
// 2) load application controllers
loadControllersFromDirectory(path.join(self.rootDirectory, 'controllers')).then(() => {
debug('attached router');
self.app.use('/', router);
next();
}, next);
});
} catch (e) {
console.error(e);
}
},
},
};

@@ -10,123 +10,125 @@ /*!

*/
const
debug = require('debug')('clout:hook/engines'),
fs = require('fs-extra'),
path = require('path');
const debug = require('debug')('clout:hook/engines');
const fs = require('fs-extra');
const path = require('path');
module.exports = {
/**
* initialize engine mechanist
* @property {event} event start
* @property {priority} priority 2
*/
initialize: {
event: 'start',
priority: 2,
fn: function (next) {
var self = this;
debug('initialize engines');
!this.app.engines && (this.app.engines = {});
/**
* initialize engine mechanist
* @property {event} event start
* @property {priority} priority 2
*/
initialize: {
event: 'start',
priority: 2,
fn(next) {
const self = this;
debug('initialize engines');
this.app.engines = this.app.engines || {};
Object.defineProperty(this.app.engines, 'add', {
value: function add(ext, engine) {
debug('adding engine %s', ext);
self.app.engines[ext] = engine;
self.app.engine(ext, engine);
}
});
Object.defineProperty(this.app.engines, 'add', {
value: function add(ext, engine) {
debug('adding engine %s', ext);
self.app.engines[ext] = engine;
self.app.engine(ext, engine);
},
});
next();
}
},
/**
* attach EJS engine
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
html: {
event: 'start',
priority: 'MIDDLEWARE',
fn: function (next) {
debug('adding ejs engine for html');
this.app.engines.add('html', require('ejs').__express);
next();
}
},
/**
* attach EJS engine
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
ejs: {
event: 'start',
priority: 'MIDDLEWARE',
fn: function (next) {
debug('adding ejs engine');
this.app.engines.add('ejs', require('ejs').__express);
next();
}
},
/**
* attach HBS engine
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
hbs: {
event: 'start',
priority: 'MIDDLEWARE',
fn: function (next) {
debug('adding hbs engine');
this.app.engines.add('hbs', require('hbs').__express);
next();
}
},
next();
},
},
/**
* attach EJS engine
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
html: {
event: 'start',
priority: 'MIDDLEWARE',
fn(next) {
debug('adding ejs engine for html');
this.app.engines.add('html', require('ejs').__express);
next();
},
},
/**
* attach EJS engine
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
ejs: {
event: 'start',
priority: 'MIDDLEWARE',
fn(next) {
debug('adding ejs engine');
this.app.engines.add('ejs', require('ejs').__express);
next();
},
},
/**
* attach HBS engine
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
hbs: {
event: 'start',
priority: 'MIDDLEWARE',
fn(next) {
debug('adding hbs engine');
this.app.engines.add('hbs', require('hbs').__express);
next();
},
},
/**
* attach rendering mechanism
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
render: {
event: 'start',
priority: 'MIDDLEWARE',
fn: function (next) {
this.app._render = this.app.render;
this.app.render = function (view, opts, cb) {
var ext = path.extname(view),
engines = this.engines,
dirs = this.get('views'),
queue = [],
found = false;
// if no extension, try each
if (!ext || !engines[ext]) {
Object.keys(engines).forEach(function (ext) {
queue.push(view + '.' + ext);
dirs.forEach(function (dir) {
queue.push(path.join(dir, view + '.' + ext));
});
});
}
// queue directly
queue.push(view);
dirs.forEach(function (dir) {
queue.push(path.join(dir, view));
});
// run search
do {
var dir = queue.shift();
if (fs.existsSync(dir)) {
found = true;
view = dir;
}
} while (!found && queue.length > 0);
// not found
if (!found) {
return cb(new Error('Unable to find layout "' + view + '"'));
}
// do original render
this._render.call(this, view, opts, cb);
};
next();
}
},
/**
* attach rendering mechanism
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
render: {
event: 'start',
priority: 'MIDDLEWARE',
fn(next) {
this.app._render = this.app.render;
this.app.render = function cloutRender(view, opts, cb) {
const ext = path.extname(view);
const {engines} = this;
const dirs = this.get('views');
const queue = [];
// if no extension, try each
if (!ext || !engines[ext]) {
Object.keys(engines).forEach((engineExt) => {
queue.push(`${view}.${engineExt}`);
dirs.forEach((dir) => {
queue.push(path.join(dir, `${view}.${engineExt}`));
});
});
}
// queue directly
queue.push(view);
dirs.forEach((dir) => {
queue.push(path.join(dir, view));
});
const viewFilePath = queue.find(dir => fs.existsSync(dir));
// not found
if (!viewFilePath) {
return cb(new Error(`Unable to find layout "${viewFilePath}"`));
}
// do original render
return this._render.call(this, viewFilePath, opts, (err, response) => {
if (err) {
console.error({err}, 'failed to render');
}
cb(err, response);
});
};
next();
},
},
};

@@ -23,231 +23,235 @@ /*!

module.exports = {
/**
* Initialize express application
* @property {event} event start
* @property {priority} priority 1
*/
initialize: {
event: 'start',
priority: 1,
fn: function (next) {
// TODO:-
// - move to Clout.js for initialization
this.app = express();
this.app.use((req, resp, next) => {
resp.setHeader('x-powered-by', 'Clout-JS');
resp.setHeader('clout-version', `${this.package.version}`);
resp.setHeader('clout-env', this.config.env);
next();
});
/**
* Initialize express application
* @property {event} event start
* @property {priority} priority 1
*/
initialize: {
event: 'start',
priority: 1,
fn(next) {
// TODO:-
// - move to Clout.js for initialization
this.app = express();
this.app.use((req, resp, done) => {
resp.setHeader('x-powered-by', 'Clout-JS');
resp.setHeader('clout-version', `${this.package.version}`);
resp.setHeader('clout-env', this.config.env);
done();
});
// request parsing
this.app.use(bodyParser.json());
debug('loaded bodyParser.json()');
this.app.use(bodyParser.urlencoded({
extended: true
}));
debug('loaded bodyParser.urlencoded()');
this.app.use(bodyParser.text({}));
debug('loaded bodyParser.text()');
this.app.use(bodyParser.raw({}));
debug('loaded bodyParser.raw()');
this.app.use(cookieParser());
next();
}
},
/**
* attach compression mechanism
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
compress: {
event: 'start',
priority: 'MIDDLEWARE',
fn: function (next) {
debug('appending compression to middleware');
this.app.use(compress());
next();
}
},
/**
* attach session mechanism
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
session: {
event: 'start',
priority: 'MIDDLEWARE',
fn: function (next) {
let sessionConf = this.config.session || {};
// request parsing
this.app.use(bodyParser.json());
debug('loaded bodyParser.json()');
this.app.use(bodyParser.urlencoded({
extended: true,
}));
debug('loaded bodyParser.urlencoded()');
this.app.use(bodyParser.text({}));
debug('loaded bodyParser.text()');
this.app.use(bodyParser.raw({}));
debug('loaded bodyParser.raw()');
this.app.use(cookieParser());
next();
},
},
/**
* attach compression mechanism
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
compress: {
event: 'start',
priority: 'MIDDLEWARE',
fn(next) {
debug('appending compression to middleware');
this.app.use(compress());
next();
},
},
/**
* attach session mechanism
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
session: {
event: 'start',
priority: 'MIDDLEWARE',
fn(next) {
const sessionConf = this.config.session || {};
if (!sessionConf.secret) {
this.logger.warn('session.secret is undefined');
sessionConf.secret = '1c6bf8c5cef18097a5389c3ca6d73328';
}
if (!sessionConf.secret) {
this.logger.warn('session.secret is undefined');
sessionConf.secret = '1c6bf8c5cef18097a5389c3ca6d73328';
}
if (!sessionConf.resave) {
sessionConf.resave = true;
}
if (!sessionConf.resave) {
sessionConf.resave = true;
}
if (!sessionConf.saveUninitialized) {
sessionConf.saveUninitialized = false;
}
if (!sessionConf.saveUninitialized) {
sessionConf.saveUninitialized = false;
}
this.config.session = sessionConf;
this.app.session = session(sessionConf);
this.app.use(this.app.session);
next();
}
},
/**
* attach public folders
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
publicFolders: {
event: 'start',
priority: 'MIDDLEWARE',
fn: function (next) {
let useDir = (dir) => {
if (!fs.existsSync(dir)) { return; }
debug('appending public dir %s', dir);
this.app.use(express.static(dir));
}
this.config.session = sessionConf;
this.app.session = session(sessionConf);
this.app.use(this.app.session);
next();
},
},
/**
* attach public folders
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
publicFolders: {
event: 'start',
priority: 'MIDDLEWARE',
fn(next) {
const useDir = (dir) => {
if (!fs.existsSync(dir)) { return; }
debug('appending public dir %s', dir);
this.app.use(express.static(dir));
};
// application public folder
useDir(path.join(this.rootDirectory, 'public'));
// application public folder
useDir(path.join(this.rootDirectory, 'public'));
// modules
this.modules.forEach(module => useDir(path.join(module.path, 'public')));
// modules
this.modules.forEach(module => useDir(path.join(module.path, 'public')));
// clout public folder
useDir(path.join(__dirname, '../resources/public'));
// clout public folder
useDir(path.join(__dirname, '../resources/public'));
next();
}
},
/**
* attach views folders
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
views: {
event: 'start',
priority: 'MIDDLEWARE',
fn: function (next) {
let views = [];
next();
},
},
/**
* attach views folders
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
views: {
event: 'start',
priority: 'MIDDLEWARE',
fn(next) {
const views = [];
function useDir(dir) {
if (!fs.existsSync(dir)) { return; }
debug('appending views dir %s', dir);
views.push(dir);
}
function useDir(dir) {
if (!fs.existsSync(dir)) { return; }
debug('appending views dir %s', dir);
views.push(dir);
}
// application public folder
useDir(path.join(this.rootDirectory, 'views'));
// application public folder
useDir(path.join(this.rootDirectory, 'views'));
// modules
this.modules.forEach(module => useDir(path.join(module.path, 'views')));
// modules
this.modules.forEach(module => useDir(path.join(module.path, 'views')));
// clout public folder
useDir(path.join(__dirname, '../resources/views'));
// clout public folder
useDir(path.join(__dirname, '../resources/views'));
// set views
this.app.set('views', views);
next();
}
},
request: {
event: 'start',
priority: 'MIDDLEWARE',
fn: function (next) {
// TODO:-
// - Support converting form data
// - Support multipart data
next();
}
},
/**
* attach clout response mechanism
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
response: {
event: 'start',
priority: 'MIDDLEWARE',
fn: function (next) {
var httpResponseMap = this.config.httpResponseMap;
// set views
this.app.set('views', views);
next();
},
},
request: {
event: 'start',
priority: 'MIDDLEWARE',
fn(next) {
// TODO:-
// - Support converting form data
// - Support multipart data
next();
},
},
/**
* attach clout response mechanism
* @property {event} event start
* @property {priority} priority MIDDLEWARE
*/
response: {
event: 'start',
priority: 'MIDDLEWARE',
fn(next) {
const {httpResponseMap} = this.config;
function jsonFormat(method, context, payload) {
return () => {
context
.type('json')
.status(method.code)
.send(JSON.stringify(payload.data));
}
}
function jsonFormat(method, context, payload) {
return () => {
context
.type('json')
.status(method.code)
.send(JSON.stringify(payload.data));
};
}
function htmlFormat(method, context, payload) {
return () => {
!method.render && (method.render = DEFAULT_HTML_RENDER);
context
.status(method.code)
.render(method.render, {
data: payload.data
});
}
}
function htmlFormat(_method, context, payload) {
return () => {
const method = _method.render
? _method
: {..._method, ...{render: DEFAULT_HTML_RENDER}};
// TODO:-
// - refactor to add support for more file types (CSV, XML)
// - success: false should point to an error html response
for (let methodName in httpResponseMap) {
let method = httpResponseMap[methodName];
context
.status(method.code)
.render(method.render, {
data: payload.data,
});
};
}
if (typeof express.response[methodName] !== 'undefined') {
debug('overiding express response method `%s`', methodName);
}
// TODO:-
// - refactor to add support for more file types (CSV, XML)
// - success: false should point to an error html response
Object.keys(httpResponseMap).forEach((methodName) => {
const method = httpResponseMap[methodName];
express.response[methodName] = function (data) {
let deffered = Q.defer();
let payload = {
data: data,
code: method.code,
success: method.method
};
if (typeof express.response[methodName] !== 'undefined') {
debug('overiding express response method `%s`', methodName);
}
// bind our formaters
let jsonFormatFn = jsonFormat(method, this, payload);
let htmlFormatFn = htmlFormat(method, this, payload);
express.response[methodName] = function cloutResponse(data) {
const deffered = Q.defer();
const payload = {
data,
code: method.code,
success: method.method,
};
// let express choose the format
this.format({
text: jsonFormatFn,
json: jsonFormatFn,
html: htmlFormatFn,
default: htmlFormatFn
});
// bind our formaters
const jsonFormatFn = jsonFormat(method, this, payload);
const htmlFormatFn = htmlFormat(method, this, payload);
deffered.resolve();
return deffered.promise;
};
}
next();
}
},
/**
* attach error handling
* @property {event} event start
*/
errorHandler: {
event: 'start',
fn: function (next) {
this.app.use(function (err, req, resp, next) {
if (!err) { return next(); }
req.logger.error(err.stack);
resp.error(err);
});
next();
}
}
// let express choose the format
this.format({
text: jsonFormatFn,
json: jsonFormatFn,
html: htmlFormatFn,
default: htmlFormatFn,
});
deffered.resolve();
return deffered.promise;
};
});
next();
},
},
/**
* attach error handling
* @property {event} event start
*/
errorHandler: {
event: 'start',
fn(next) {
this.app.use((err, req, resp, done) => {
if (!err) { return done(); }
req.logger.error(err.stack);
return resp.error(err);
});
next();
},
},
};

@@ -10,62 +10,61 @@ /*!

*/
const
debug = require('debug')('clout:hook/models'),
utils = require('../lib/utils'),
Q = require('q'),
path = require('path');
const debug = require('debug')('clout:hook/models');
const path = require('path');
const utils = require('../lib/utils');
module.exports = {
/**
* initialize models
* @property {event} event start
* @property {priority} priority MODEL
*/
initialize: {
event: 'start',
priority: 'MODEL',
fn: function (next) {
debug('initialize models');
this.models = {};
// append to middleware
this.app.request.models = this.models;
next();
}
},
/**
* load models from application paths
* @property {event} event start
* @property {priority} priority MODEL + 2
*/
loadModels: {
event: 'start',
priority: 18,
fn: function (next) {
var self = this;
/**
* initialize models
* @property {event} event start
* @property {priority} priority MODEL
*/
initialize: {
event: 'start',
priority: 'MODEL',
fn(next) {
debug('initialize models');
this.models = {};
// append to middleware
this.app.request.models = this.models;
next();
},
},
/**
* load models from application paths
* @property {event} event start
* @property {priority} priority MODEL + 2
*/
loadModels: {
event: 'start',
priority: 18,
fn(next) {
const self = this;
function loadModelsFromDir(dir) {
var dirs = utils.getGlobbedFiles(path.join(dir, '**/**.js'));
dirs.forEach(function (dir) {
var modelName = dir.split('models/')[1].replace('.js', '');
debug('loading model %s', modelName);
if (self.models.hasOwnProperty(modelName)) {
throw new Error('Cannot load model `' + modelName + '` as it already exists');
}
try {
self.models[modelName] = require(dir);
} catch (e) {
throw new Error('Error loading model `' + modelName + '`: ' + e);
}
});
}
function loadModelsFromDir(dir) {
const dirs = utils.getGlobbedFiles(path.join(dir, '**/**.js'));
dirs.forEach((filePath) => {
const modelName = filePath.split('models/')[1].replace('.js', '');
debug('loading model %s', modelName);
if (self.models[modelName]) {
throw new Error(`Cannot load model \`${modelName}\` as it already exists`);
}
debug('loading models');
// 1) load module hooks
this.modules.forEach(function (module) {
loadModelsFromDir(path.join(module.path, 'models'));
});
// 2) load application hooks
loadModelsFromDir(path.join(self.rootDirectory, 'models'));
next();
}
}
try {
self.models[modelName] = require(filePath);
} catch (e) {
throw new Error(`Error loading model \`${modelName}\`: ${e}`);
}
});
}
debug('loading models');
// 1) load module hooks
this.modules.forEach((module) => {
loadModelsFromDir(path.join(module.path, 'models'));
});
// 2) load application hooks
loadModelsFromDir(path.join(self.rootDirectory, 'models'));
next();
},
},
};

@@ -10,87 +10,90 @@ /*!

*/
const
debug = require('debug')('clout:hook/server'),
https = require('https');
const debug = require('debug')('clout:hook/server');
const https = require('https');
module.exports = {
/**
* http instance
* @property {event} event start
* @property {priority} priority 25
*/
http: {
event: 'start',
priority: 25,
fn: function (next) {
var self = this,
port = process.env.PORT || this.config.http && this.config.http.port || 8080;
this.server.http = this.app.listen(port, function () {
if (self.server.http.address()) {
debug('http server started on port %s', self.server.http.address().port);
}
next();
});
}
},
/**
* stop http instance
* @property {event} event stop
* @property {priority} priority 25
*/
httpStop: {
event: 'stop',
priority: 25,
fn: function (next) {
let http = this.server.http;
/**
* http instance
* @property {event} event start
* @property {priority} priority 25
*/
http: {
event: 'start',
priority: 25,
fn(next) {
const self = this;
const port = process.env.PORT || (this.config.http && this.config.http.port) || 8080;
if (http) {
let port = http.address().port;
http.close();
debug('http server stopped on port %s', port);
}
this.server.http = this.app.listen(port, () => {
if (self.server.http.address()) {
debug('http server started on port %s', self.server.http.address().port);
}
next();
});
},
},
/**
* stop http instance
* @property {event} event stop
* @property {priority} priority 25
*/
httpStop: {
event: 'stop',
priority: 25,
fn(next) {
const {http} = this.server;
next();
}
},
/**
* https instance
* @property {event} event start
* @property {priority} priority 25
*/
https: {
event: 'start',
priority: 25,
fn: function (next) {
if (!this.config.https) { return next(); }
debug('Securely using https protocol');
var port = process.env.SSLPORT || this.config.https.port || 8443,
conf = this.config.https;
if (http) {
const {port} = http.address();
if (!conf) { return next(); }
http.close();
debug('http server stopped on port %s', port);
}
this.server.https = https.createServer(conf, this.app).listen();
debug('https server started on port %s', this.server.https.address().port);
next();
}
},
/**
* stop https instance
* @property {event} event stop
* @property {priority} priority 25
*/
httpsStop: {
event: 'stop',
priority: 25,
fn: function (next) {
let https = this.server.https;
next();
},
},
/**
* https instance
* @property {event} event start
* @property {priority} priority 25
*/
https: {
event: 'start',
priority: 25,
fn(next) {
if (!this.config.https) { return next(); }
if (https) {
let port = https.address().port;
https.close();
debug('https server stopped on port %s', port);
}
debug('Securely using https protocol');
const conf = this.config.https;
next();
}
},
if (!conf) { return next(); }
const port = process.env.SSLPORT || this.config.https.port || 8443;
this.server.https = https.createServer(conf, this.app).listen(port);
debug('https server started on port %s', this.server.https.address().port);
return next();
},
},
/**
* stop https instance
* @property {event} event stop
* @property {priority} priority 25
*/
httpsStop: {
event: 'stop',
priority: 25,
fn(next) {
if (this.server.https) {
const {port} = this.server.https.address();
this.server.https.close();
debug('https server stopped on port %s', port);
}
next();
},
},
};

@@ -1,22 +0,23 @@

const {safePromisifyCallFn} = require('../../lib/utils');
const CloutAPIRoute = require('../CloutApiRoute');
module.exports = {
name: 'api',
fn(apiRoute) {
const apiPath = this.path && `${this.path}.:acceptType?`;
name: 'api',
fn(fn) {
const apiPath = this.path && `${this.path}.:acceptType?`;
this.methods.forEach((method) => {
// attach logging
this.router[method](apiPath, function (req, resp, next) {
req.logger.info('Endpoint [%s] /api%s', req.method, req.path);
next();
});
function attachHook(method, hookFn) {
return this.router[method](apiPath, this.handlePromisePostTriggers(hookFn));
}
// attach hooks
this.hooks.map((hookFn) => this.router[method](apiPath, this.handlePromisePostTriggers(hookFn)));
this.methods.forEach((method) => {
// attach logging
this.router[method](apiPath, (req, resp, next) => {
req.logger.info('Endpoint [%s] /api%s', req.method, req.path);
next();
});
this.router[method](apiPath, this.handlePromisePostTriggers(this.fn));
});
}
// attach hooks
this.hooks.map(hookFn => attachHook(method, hookFn));
this.router[method](apiPath, this.handlePromisePostTriggers(fn));
});
},
};
const path = require('path');
const {getGlobbedFiles} = require('../../lib/utils');
const { getGlobbedFiles } = require('../../lib/utils');

@@ -7,13 +7,13 @@ module.exports = {};

function getApiTypes() {
const apiTypeGlob = path.resolve(__dirname, './**/*.js');
const apiTypeFilePaths = getGlobbedFiles(apiTypeGlob);
return apiTypeFilePaths.reduce((acc, filePath) => {
const {name} = path.parse(filePath);
acc[name] = require(filePath);
return acc;
}, module.exports);
const apiTypeGlob = path.resolve(__dirname, './**/*.js');
const apiTypeFilePaths = getGlobbedFiles(apiTypeGlob);
return apiTypeFilePaths.reduce((acc, filePath) => {
const { name } = path.parse(filePath);
acc[name] = require(filePath);
return acc;
}, module.exports);
}
getApiTypes()
getApiTypes();

@@ -1,29 +0,29 @@

const {safePromisifyCallFn} = require('../../lib/utils');
const CloutAPIRoute = require('../CloutApiRoute');
const request = require('express/lib/request');
const { safePromisifyCallFn } = require('../../lib/utils');
const request = require('express/lib/request')
const expressRequestParam = request.param;
request.param = function (name) {
const {_params} = this;
request.param = function cloutParam(name) {
const { _params } = this;
return _params[name];
}
return _params[name] || expressRequestParam.apply(this, [name]);
};
module.exports = {
fn(fn) {
const key = this.result || this.param;
fn(fn) {
const key = this.result || this.param;
async function cloutApiParam(req, resp, next, ...args) {
req._params = req._paramsparams || {};
async function cloutApiParam(req, resp, next, ...args) {
req._params = req._paramsparams || {};
try {
req._params[key] = await safePromisifyCallFn(fn, this, [req, resp, null, ...args]);;
next();
} catch (err) {
next(err);
}
}
try {
req._params[key] = await safePromisifyCallFn(fn, this, [req, resp, null, ...args]);
next();
} catch (err) {
next(err);
}
}
this.router.param(this.param, cloutApiParam);
}
this.router.param(this.param, cloutApiParam);
},
};

@@ -6,3 +6,3 @@ /*!

*/
const {safePromisifyCallFn} = require('../lib/utils');
const { safePromisifyCallFn } = require('../lib/utils');
const types = require('./apiType');

@@ -12,4 +12,4 @@

const TYPES_DEFINITION = {
API: 'api',
PARAM: 'param'
API: 'api',
PARAM: 'param',
};

@@ -23,3 +23,3 @@ const DEFAULT_TYPE = TYPES_DEFINITION.API;

class CloutApiRoute {
/**
/**
* @constructor

@@ -36,66 +36,68 @@ * @param {object} _opts

*/
constructor(_opts) {
this._opts = _opts;
constructor(_opts) {
this._opts = _opts;
this.type = (this._opts.type || DEFAULT_TYPE).toLowerCase();
this.isPublicFacing = this.type.includes('api');
this.type = (this._opts.type || DEFAULT_TYPE).toLowerCase();
this.isPublicFacing = this.type.includes('api');
switch (this.type) {
case TYPES_DEFINITION.PARAM:
this.param = this._opts.param;
this.result = this._opts.result;
break;
default:
this.path = this._opts.path;
this.hooks = this._opts.hooks || [];
this.methods = (this._opts.methods || [this._opts.method || DEFAULT_METHOD]).map((method) => method.toLowerCase());
this.params = this._opts.params; // TODO:- time to start packing modules? clout-swagger
break;
}
const methods = this._opts.methods || [this._opts.method || DEFAULT_METHOD];
// Documentation Specific
this.group = this._opts.group;
this.name = this._opts.name;
this.description = this._opts.description;
// What actually runs
this.fn = this._opts.fn;
switch (this.type) {
case TYPES_DEFINITION.PARAM:
this.param = this._opts.param;
this.result = this._opts.result;
break;
default:
this.path = this._opts.path;
this.hooks = this._opts.hooks || [];
this.methods = (methods).map(method => method.toLowerCase());
this.params = this._opts.params; // TODO:- time to start packing modules? clout-swagger
break;
}
/**
// Documentation Specific
this.group = this._opts.group;
this.name = this._opts.name;
this.description = this._opts.description;
// What actually runs
this.fn = this._opts.fn;
}
/**
* handles router method in a promise
* @param {*} fn RouterCallback
*/
handlePromisePostTriggers(fn) {
const {isPublicFacing} = this;
return function (req, resp, next, ...args) {
safePromisifyCallFn(fn, this, [req, resp, null, ...args])
.then((data) => {
if (isPublicFacing) {
return resp.ok(data);
}
handlePromisePostTriggers(fn) {
const { isPublicFacing } = this;
return function postPromiseHandle(req, resp, next, ...args) {
safePromisifyCallFn(fn, this, [req, resp, null, ...args])
.then((data) => {
if (isPublicFacing) {
return resp.ok(data);
}
next(null, data)
})
.catch((err) => next(err));
}
}
return next(null, data);
})
.catch(err => next(err));
};
}
/**
/**
* attach express router
* @param {object} router express router
*/
attachRouter(router) {
this.router = router;
attachRouter(router) {
this.router = router;
const matchedApiType = types[this.type];
const matchedApiType = types[this.type];
if (matchedApiType) {
const cloutApi = matchedApiType.fn.apply(this, [this.fn]);
} else {
console.error(`unrecognised type ${this.type}`);
}
if (matchedApiType) {
matchedApiType.fn.apply(this, [this.fn]);
} else {
console.error(`unrecognised type ${this.type}`);
}
};
}
}
module.exports = CloutApiRoute;

@@ -8,6 +8,6 @@ /*!

const { merge } = require('lodash');
const CloutApiRoute = require('../hookslib/CloutApiRoute');
const express = require('express');
const path = require('path');
const CloutApiRoute = require('./CloutApiRoute');
const utils = require('../lib/utils');
const path = require('path');

@@ -17,2 +17,31 @@ const PRIORITIZED_FILES = ['index.js', 'clout.hook.js'];

/**
* Sort array by PRIORITIZED_FILES
* @param {string} a string
* @param {string} b string
*/
function sortByPriority(a, b) {
const keys = {a, b};
const getPriorityFor = key => PRIORITIZED_FILES.indexOf(keys[key]);
const priorityIndexForA = getPriorityFor(a);
const bPriority = getPriorityFor(b);
const weight = {
a: priorityIndexForA + 1,
b: bPriority + 1,
};
const bBiggerThanA = weight.b > weight.a;
const aBiggerThanB = weight.a > weight.b;
if (aBiggerThanB) {
return 1;
}
if (bBiggerThanA) {
return -1;
}
return 0;
}
/**
* CloutApiRoutes

@@ -22,116 +51,102 @@ * @class

class CloutApiRoutes {
/**
/**
* @constructor
* @param {object} app clout instance
*/
constructor(app) {
this.clout = app;
this.config = {
basePath: '/api',
acceptTypes: {
json: 'application/json',
html: 'text/html'
}
};
this.routes = {};
this.router = express.Router();
this.initializeAcceptTypeHandler();
constructor(app) {
this.clout = app;
this.config = {
basePath: '/api',
acceptTypes: {
json: 'application/json',
html: 'text/html',
},
};
this.routes = {};
this.router = express.Router();
this.initializeAcceptTypeHandler();
this.clout.logger.debug('Module CloutApiRoutes loaded');
}
this.clout.logger.debug('Module CloutApiRoutes loaded');
}
/**
/**
* Clout-JS handler for custom content requests
*/
initializeAcceptTypeHandler() {
this.router.param('acceptType', (req, resp, next, type) => {
let acceptType = this.config.acceptTypes[type];
initializeAcceptTypeHandler() {
this.router.param('acceptType', (req, resp, next, type) => {
const acceptType = this.config.acceptTypes[type];
req.logger.info(`handling param '${acceptType}'`);
req.logger.info(`handling param '${acceptType}'`);
if (acceptType) {
req.headers['accept'] = `${acceptType},` + req.headers['accept'];
}
if (acceptType) {
req.headers.accept = `${acceptType},${req.headers.accept}`;
}
next();
});
}
next();
});
}
/**
/**
* Attaches router to clout-app
*/
attachRouterToApp() {
let basePath = this.config.basePath;
attachRouterToApp() {
const {basePath} = this.config;
this.clout.app.use(basePath, this.router);
this.clout.logger.debug(`router attached at ${basePath}`);
}
this.clout.app.use(basePath, this.router);
this.clout.logger.debug(`router attached at ${basePath}`);
}
/**
/**
* Add CloutApiRouter to router
* @param {object} CloutApiRouter
*/
addRoute(cloutRoute) {
if (!this.routes[cloutRoute.group]) {
this.routes[cloutRoute.group] = [];
}
this.routes[cloutRoute.group].push(cloutRoute);
cloutRoute.attachRouter(this.router);
addRoute(cloutRoute) {
if (!this.routes[cloutRoute.group]) {
this.routes[cloutRoute.group] = [];
}
/**
* Load APIs from a file
* @param {string} filePath
*/
loadAPIFromFile(filePath) {
let groupName = path.basename(filePath).replace('.js', '');
let apis = require(filePath);
this.routes[cloutRoute.group].push(cloutRoute);
this.clout.logger.debug(`loading API from file ${filePath}`);
cloutRoute.attachRouter(this.router);
}
return Object.keys(apis).map((apiName) => {
let opts = merge({
name: apiName,
group: groupName
}, apis[apiName]);
/**
* Load APIs from a file
* @param {string} filePath
*/
loadAPIFromFile(filePath) {
const groupName = path.basename(filePath).replace('.js', '');
const apis = require(filePath);
return this.addRoute(new CloutApiRoute(opts));
});
}
this.clout.logger.debug(`loading API from file ${filePath}`);
/**
* Finds all the **.js files inside a directory and loads it
* @param {string} dir path containing directory of APIs
*/
loadAPIsFromDir(dir) {
let globbedDirs = utils.getGlobbedFiles(path.join(dir, '**/**.js'));
return Object.keys(apis).map((apiName) => {
const opts = merge({
name: apiName,
group: groupName,
}, apis[apiName]);
return globbedDirs.sort((a, b) => {
const aPriority = PRIORITIZED_FILES.indexOf(a) !== -1;
const bPriority = PRIORITIZED_FILES.indexOf(b) !== -1;
const weight = {a: 0, b: 0};
return this.addRoute(new CloutApiRoute(opts));
});
}
if (aPriority !== -1) {
weight.a = aPriority + 1;
}
if (bPriority !== -1) {
weight.b = aPriority + 1;
}
/**
* Finds all the **.js files inside a directory and loads it
* @param {string} dir path containing directory of APIs
*/
loadAPIsFromDir(dir) {
const globbedDirs = utils.getGlobbedFiles(path.join(dir, '**/**.js'));
return weight.a > weight.b ? 1 : weight.b > weight.a ? -1 : 0;
}).map((filePath) => this.loadAPIFromFile(filePath));
}
return globbedDirs.sort(sortByPriority).map(filePath => this.loadAPIFromFile(filePath));
}
/**
* Finds all the **.js files inside an array of directories and loads it
* @param {array} dirs array of paths containing directory of APIs
*/
loadAPIsFromDirs(dirs) {
return dirs.map((dir) => this.loadAPIsFromDir(dir));
}
};
/**
* Finds all the **.js files inside an array of directories and loads it
* @param {array} dirs array of paths containing directory of APIs
*/
loadAPIsFromDirs(dirs) {
return dirs.map(dir => this.loadAPIsFromDir(dir));
}
}
module.exports = CloutApiRoutes;

@@ -1,2 +0,2 @@

import '@types/express';
import {Express} from 'express';

@@ -29,2 +29,1 @@ declare global {

}

@@ -12,5 +12,5 @@ /*!

if (process.env.APPLICATION_DIR) {
applicationDir = process.env.APPLICATION_DIR;
applicationDir = process.env.APPLICATION_DIR;
} else if (module && module.parent) {
applicationDir = path.dirname(module.parent.filename);
applicationDir = path.dirname(module.parent.filename);
}

@@ -24,3 +24,3 @@

if (!applicationDir) {
throw new Error('application not found');
throw new Error('application not found');
}

@@ -27,0 +27,0 @@

@@ -12,4 +12,3 @@ /*!

const fs = require('fs-extra');
const EventEmitter = require('events').EventEmitter;
const util = require('util');
const { EventEmitter } = require('events');

@@ -23,2 +22,3 @@ const debug = require('debug')('clout:core');

const Config = require('./Config');
const CloutApiRoute = require('../hookslib/CloutApiRoute');

@@ -36,7 +36,7 @@ /**

const CORE_PRIORITY = {
CONFIG: 5,
MIDDLEWARE: 10,
MODEL: 15,
API: 20,
CONTROLLER: 25
CONFIG: 5,
MIDDLEWARE: 10,
MODEL: 15,
API: 20,
CONTROLLER: 25,
};

@@ -51,401 +51,359 @@

class Clout extends EventEmitter {
/**
* @constructor
* @param {path} rootDirectory application directory
*/
constructor(rootDirectory) {
super();
this.handleProcess();
/**
* @constructor
* @param {path} rootDirectory application directory
*/
constructor(rootDirectory) {
super();
this.process = process;
this.handleProcess();
this.rootDirectory = null;
this.package = {};
this.applicationPackage = {};
this.config = {};
this.logger = {debug: debug};
this.app = null;
this.server = {};
this.modules = [];
this.moduleCache = [];
this.rootDirectory = null;
this.package = {};
this.applicationPackage = {};
this.config = {};
this.logger = { debug };
this.app = null;
this.server = {};
this.modules = [];
this.moduleCache = [];
// expose core libraries
this.utils = utils;
this.async = async;
this._ = _;
this.fs = fs;
// expose core libraries
this.utils = utils;
this.async = async;
this._ = _;
this.fs = fs;
// allow application hooks (Synchronous)
this.CORE_PRIORITY = CORE_PRIORITY;
this.hooks = {
start: [],
stop: [],
reload: []
};
// allow application hooks (Synchronous)
this.CORE_PRIORITY = CORE_PRIORITY;
this.hooks = {
start: [],
stop: [],
reload: [],
};
// Load clout configuration
this.config = new Config();
this.config.loadFromDir(path.join(__dirname, '../resources/conf'));
// Load clout configuration
this.config = new Config();
this.config.loadFromDir(path.join(__dirname, '../resources/conf'));
this.applicationPackage = {};
this.applicationPackage = {};
// load clout package.json
this.package = require(path.join(__dirname, '../package.json'));
// load clout package.json
this.package = require(path.join(__dirname, '../package.json'));
// load clout modules
if (this.package.modules) {
this.addModules(this.package.modules);
}
// load clout modules
if (this.package.modules) {
this.addModules(this.package.modules);
}
if (rootDirectory) {
// set application root directory
this.rootDirectory = path.resolve(rootDirectory);
if (rootDirectory) {
// set application root directory
this.rootDirectory = path.resolve(rootDirectory);
// load application manifest
['package.json', 'clout.json'].forEach((fileName) => {
let filePath = path.resolve(this.rootDirectory, fileName);
if (!fs.existsSync(filePath)) {
return debug(`${fileName} not found`);
}
// load application manifest
['package.json', 'clout.json'].forEach((fileName) => {
const filePath = path.resolve(this.rootDirectory, fileName);
if (!fs.existsSync(filePath)) {
return debug(`${fileName} not found`);
}
_.merge(this.applicationPackage, require(filePath));
});
return _.merge(this.applicationPackage, require(filePath));
});
process.title = `[clout-js v${this.package.version}] ${this.applicationPackage.name}`;
this.process.title = `[clout-js v${this.package.version}] ${this.applicationPackage.name}`;
// add rootdir to node_modules
module.paths.unshift(path.join(this.rootDirectory, 'node_modules'));
// add rootdir to node_modules
module.paths.unshift(path.join(this.rootDirectory, 'node_modules'));
// load modules from application manifest
if (this.applicationPackage.modules) {
this.addModules(this.applicationPackage.modules);
}
}
// load modules from application manifest
if (this.applicationPackage.modules) {
this.addModules(this.applicationPackage.modules);
}
}
// append module configuration
this.modules.forEach((module) => this.config.loadFromDir(path.join(module.path, 'conf')));
// append module configuration
this.modules.forEach(module => this.config.loadFromDir(path.join(module.path, 'conf')));
// append application configuration (Overrides module conf)
this.config.loadFromDir(path.join(this.rootDirectory, 'conf'));
// append application configuration (Overrides module conf)
this.config.loadFromDir(path.join(this.rootDirectory, 'conf'));
// initialize logger
this.logger = new Logger(this);
// initialize logger
this.logger = new Logger(this);
// 1) load core hooks
// 2) load application hooks
// 3) load module hooks
this.loadHooksFromDir(CLOUT_MODULE_PATH)
.then(this.loadHooksFromDir(this.rootDirectory))
.then(() => new Promise((resolve, reject) => {
async.each(this.modules, (module, next) => {
this.loadHooksFromDir(module.path)
.then(() => next())
.catch((err) => {
console.error(err);
next()
});
}, (err) => {
if (err) { return reject(err); }
resolve();
});
// 1) load core hooks
// 2) load application hooks
// 3) load module hooks
this.loadHooksFromDir(CLOUT_MODULE_PATH)
.then(this.loadHooksFromDir(this.rootDirectory))
.then(async () => this.modules.map(async module => this.loadHooksFromDir(module.path)))
.then((moduleHooks) => {
this.initialized = true;
return moduleHooks;
})
.catch(err => console.error(err));
}
this.initialized = true;
}))
.catch((err) => console.error(err));
}
/**
* hook into clout runtime
* @param {string} event event name
* @param {Function} fn function to execute
* @param {String} fn._name hook name
* @param {String} fn.group hook group
* @param {Number} priority function priority
* @param {Boolean} override override existing
* @example
* // register a function to the hook
* clout.registerHook('start', function (next) {
* next();
* });
* // invoking an error in clout runtime
* clout.registerHook('start', function (next) {
* next(new Error('Error executing function'));
* });
*/
registerHook(event, fn, priority, override) {
const hasPriority = typeof priority !== 'undefined';
const hasEvent = this.hooks[event];
debug('registerHook:event=%s:fn:priority=%s', event, hasEvent, priority);
/**
* hook into clout runtime
* @param {string} event event name
* @param {Function} fn function to execute
* @param {String} fn._name hook name
* @param {String} fn.group hook group
* @param {Number} priority function priority
* @param {Boolean} override override existing
* @example
* // register a function to the hook
* clout.registerHook('start', function (next) {
* next();
* });
* // invoking an error in clout runtime
* clout.registerHook('start', function (next) {
* next(new Error('Error executing function'));
* });
*/
registerHook(event, fn, priority, override) {
debug('registerHook:event=%s:fn:priority=%s', event, priority);
if (!hasEvent) {
throw new Error('Invalid Hook Event');
}
if (!this.hooks.hasOwnProperty(event)) {
throw new Error('Invalid Hook Event');
}
if (hasPriority) {
fn.priority = priority;
}
typeof priority !== 'undefined' && (fn.priority = priority);
// find existing, override
if (override === true) {
debug('override');
for (let i = 0, l = this.hooks[event].length; i < l; i += 1) {
const hook = this.hooks[event][i];
if (hook._name !== null && hook._name === fn._name && hook.group === fn.group) {
debug('match found, overriden');
this.hooks[event][i] = fn;
return;
}
}
}
// find existing, override
if (override === true) {
debug('override');
for (var i = 0, l = this.hooks[event].length; i < l; ++i) {
var hook = this.hooks[event][i];
if (hook._name !== null && hook._name === fn._name && hook.group === fn.group) {
debug('match found, overriden');
this.hooks[event][i] = fn;
return;
}
}
}
// push is no priority
if (!fn.priority) {
debug('push hook (no priority)');
this.hooks[event].push(fn);
return;
}
// push is no priority
if (!fn.priority) {
debug('push hook (no priority)');
return this.hooks[event].push(fn);
}
// find the correct place to register hook
for (let i = 0, l = this.hooks[event].length; i < l; i += 1) {
const tmpPriority = this.hooks[event][i].priority || 99999;
// find the correct place to register hook
for (var i = 0, l = this.hooks[event].length; i < l; ++i) {
var tmp_p = this.hooks[event][i].priority || 99999;
if (fn.priority < tmp_p) {
debug('push hook at index %s', String(i));
return this.hooks[event].splice(i, 0, fn);
}
}
if (fn.priority < tmpPriority) {
debug('push hook at index %s', String(i));
this.hooks[event].splice(i, 0, fn);
return;
}
}
debug('push hook (lowest priority yet)');
return this.hooks[event].push(fn);
}
debug('push hook (lowest priority yet)');
this.hooks[event].push(fn);
}
/**
* Loads hooks from directory
* @param {Path} dir directory
* @return {Promise} promise
*/
loadHooksFromDir(dir) {
var glob = path.join(dir, '/hooks/**/*.js'),
files = utils.getGlobbedFiles(glob);
/**
* Loads hooks from directory
* @param {Path} dir directory
* @return {Promise} promise
*/
loadHooksFromDir(dir) {
const glob = path.join(dir, '/hooks/**/*.js');
const files = utils.getGlobbedFiles(glob);
debug('loadHooksFromDir: %s', dir);
debug('loadHooksFromDir: %s', dir);
return new Promise((resolve, reject) => {
async.each(files, (file, next) => {
debug('loading hooks from file: %s', String(file));
return new Promise((resolve, reject) => {
async.each(files, (file, next) => {
debug('loading hooks from file: %s', String(file));
let hooks = require(file);
let keys = Object.keys(hooks);
const hooks = require(file);
const keys = Object.keys(hooks);
keys.forEach((key) => {
let hook = hooks[key];
let args = [];
keys.forEach((key) => {
const hook = hooks[key];
const args = [];
debug('Loading hook: %s', key);
debug('Loading hook: %s', key);
// create args
if (!hook.event || !hook.fn) {
throw new Error('Hook missing attributes');
}
hook.fn.group = file.split('hooks/')[1].replace('.js', '');
hook.fn._name = key;
args.push(hook.event);
args.push(hook.fn);
if (typeof hook.priority !== 'undefined') {
if (typeof hook.priority === 'string') {
if (!this.CORE_PRIORITY.hasOwnProperty(hook.priority)) {
throw "Invalid priority type";
}
hook.priority = this.CORE_PRIORITY[hook.priority];
}
args.push(hook.priority);
} else {
args.push(null);
}
if (hook.override) {
args.push(true);
}
this.registerHook.apply(this, args);
});
next();
}, function done(err) {
if (err) {
debug(err);
return reject(err);
}
debug('all hooks loaded from %s', dir);
resolve();
});
});
}
// create args
if (!hook.event || !hook.fn) {
throw new Error('Hook missing attributes');
}
hook.fn.group = file.split('hooks/')[1].replace('.js', '');
hook.fn._name = key;
args.push(hook.event);
args.push(hook.fn);
if (typeof hook.priority !== 'undefined') {
if (typeof hook.priority === 'string') {
if (!this.CORE_PRIORITY[hook.priority]) {
throw new Error('Invalid priority type');
}
hook.priority = this.CORE_PRIORITY[hook.priority];
}
args.push(hook.priority);
} else {
args.push(null);
}
if (hook.override) {
args.push(true);
}
addModules(modules) {
debug('loading modules', JSON.stringify(modules));
modules.forEach((moduleName) => this.addModule(moduleName));
}
this.registerHook(...args);
});
next();
}, (err) => {
if (err) {
debug(err);
return reject(err);
}
debug('all hooks loaded from %s', dir);
resolve();
});
});
}
/**
* Load clout-js node module
* @param {string} moduleName clout node module name
*/
addModule(moduleName) {
if (!!~this.moduleCache.indexOf(moduleName)) {
debug('module: %s already loaded', moduleName);
return;
}
addModules(modules) {
debug('loading modules', JSON.stringify(modules));
modules.forEach(moduleName => this.addModule(moduleName));
}
this.logger.debug('loading module: %s', moduleName);
this.moduleCache.push(moduleName);
/**
* Load clout-js node module
* @param {string} moduleName clout node module name
*/
addModule(moduleName) {
if (this.moduleCache.includes(moduleName)) {
debug('module: %s already loaded', moduleName);
return;
}
let cloutModule = {
name: moduleName,
path: path.dirname(require.resolve(moduleName)),
manifest: {}
};
this.logger.debug('loading module: %s', moduleName);
this.moduleCache.push(moduleName);
this.modules.push(cloutModule);
debug(cloutModule);
const cloutModule = {
name: moduleName,
path: path.dirname(require.resolve(moduleName)),
manifest: {},
};
// load module manifest
['package.json', 'clout.json'].forEach((fileName) => {
let filePath = path.resolve(cloutModule.path, fileName);
if (!fs.existsSync(filePath)) {
return debug(`${fileName} not found`);
}
this.modules.push(cloutModule);
debug(cloutModule);
_.merge(cloutModule.manifest, require(filePath));
});
// load module manifest
['package.json', 'clout.json'].forEach((fileName) => {
const filePath = path.resolve(cloutModule.path, fileName);
if (!fs.existsSync(filePath)) {
return debug(`${fileName} not found`);
}
// load module modules
if (cloutModule.manifest.modules) {
debug('%s loading modules %s', moduleName, manifest.modules);
this.addModules(manifest.modules);
}
}
_.merge(cloutModule.manifest, require(filePath));
});
/**
* Start clout
* @return {Promise} returns a promise
*/
start() {
this.emit('initialized');
// load module modules
if (cloutModule.manifest.modules) {
debug('%s loading modules %s', moduleName, cloutModule.manifest.modules);
this.addModules(cloutModule.manifest.modules);
}
}
if (!this.initialized) {
return new Promise((resolve) => {
setTimeout(() => resolve(this.start()), 100);
});
}
/**
* Start clout
* @return {Promise} returns a promise
*/
start() {
this.emit('initialized');
this.emit('start');
if (!this.initialized) {
return new Promise((resolve) => {
setTimeout(() => resolve(this.start()), 100);
});
}
return new Promise((resolve, reject) => {
process.nextTick(() => {
async.eachLimit(this.hooks.start, 1, (hook, next) => {
debug('executing', hook.name || hook._name, hook.group);
let hookResponse = hook.apply(this, [next]);
this.emit('start');
// support promises
if (typeof hookResponse === 'object') {
hookResponse.then(next, (err) => next(null, err));
}
}, (err) => {
if (err) {
debug(err);
return reject(err);
}
resolve();
this.emit('started');
});
});
});
}
return new Promise((resolve, reject) => {
process.nextTick(() => {
async.eachLimit(this.hooks.start, 1, (hook, next) => {
debug('executing', hook.name || hook._name, hook.group);
const hookResponse = hook.apply(this, [next]);
// TODO:- investigate if we still need this?
/**
* Add API
* @param {string} path api path
* @param {function} fn express function
*/
addApi(path, fn) {
this.app.use(path, function (req, resp, next) {
let promiseResponse = fn.apply(this, arguments);
// support promises
if (typeof hookResponse === 'object') {
hookResponse.then(next, err => next(null, err));
}
}, (err) => {
if (err) {
debug(err);
return reject(err);
}
resolve();
this.emit('started');
});
});
});
}
// support for prmises
// bind to app.use
if (String(promiseResponse) === '[object Promise]') {
promiseResponse
.then((payload) => {
switch (Object.prototype.toString.call(payload)) {
case '[object Object]':
case '[object String]':
resp.success(payload);
break;
case '[object Undefined]':
break;
default:
console.error('type not supported');
resp.error('response type is invalid');
break;
}
next();
})
.catch((payload) => {
switch (Object.prototype.toString.call(payload)) {
case '[object Object]':
case '[object String]':
resp.error(payload);
break;
case '[object Undefined]':
break;
default:
console.error('type not supported');
resp.error('response type is invalid');
break;
}
next();
});
}
});
// TODO:- investigate if we still need this?
/**
* Add API
* @param {string} path api path
* @param {function} fn express function
*/
addApi(apiPath, fn) {
if (this.core.api) {
this.core.addRoute(new CloutApiRoute({
path: apiPath,
fn,
}));
}
}
return new Promise.resolve();
}
/**
* Stop clout
* @return {Promise} returns a promise
*/
stop() {
this.emit('stop');
return new Promise((resolve, reject) => {
async.eachLimit(this.hooks.stop, 1, (hook, next) => {
hook.apply(this, [next]);
}, (err) => {
if (err) {
debug(err);
return reject(err);
}
/**
* Stop clout
* @return {Promise} returns a promise
*/
stop() {
this.emit('stop');
return new Promise((resolve, reject) => {
async.eachLimit(this.hooks.stop, 1, (hook, next) => {
hook.apply(this, [next]);
}, (err) => {
if (err) {
debug(err);
return reject(err);
}
resolve();
this.emit('stopped');
});
});
}
resolve();
this.emit('stopped');
});
});
}
/**
* Reload clout
* @return {Promise} returns a promise
*/
reload() {
this.emit('reload');
/**
* Reload clout
* @return {Promise} returns a promise
*/
reload() {
this.emit('reload');
return this.stop()
.then(this.start)
.then(() => this.emit('reloaded'));
}
return this.stop()
.then(this.start)
.then(() => {
deferred.resolve();
this.emit('reloaded');
});
}
handleProcess() {
process.on('unhandledRejection', (err) => {
console.error(err);
});
process.on('uncaughtException', (err) => {
console.log(err);
process.exit(0);
});
}
handleProcess() {
this.process.on('unhandledRejection', err => console.error(err));
this.process.on('uncaughtException', (err) => {
console.error(err);
process.exit(0);
});
}
}

@@ -452,0 +410,0 @@

@@ -7,72 +7,77 @@ /*!

const path = require('path');
const fs = require('fs');
const _ = require('lodash');
const async = require('async');
const utils = require('./utils');
const debug = require('debug')('clout:commands');
const prompt = require('prompt');
const utils = require('./utils');
module.exports = function config(clout) {
var COMMANDS_DIR = path.join(__dirname, 'commands');
debug('COMMANDS_DIR: %s', COMMANDS_DIR);
const COMMANDS_DIR = path.join(__dirname, 'commands');
debug('COMMANDS_DIR: %s', COMMANDS_DIR);
prompt.message = "";
prompt.delimiter = "";
prompt.message = '';
prompt.delimiter = '';
var globPattern = COMMANDS_DIR + '**/*.js';
utils.getGlobbedFiles(globPattern).forEach(function (filePath) {
var commandConf = require(filePath),
command = clout.program.command(commandConf.command).desc(commandConf.desc);
const globPattern = `${COMMANDS_DIR}**/*.js`;
utils.getGlobbedFiles(globPattern).forEach((filePath) => {
const commandConf = require(filePath);
const command = clout.program.command(commandConf.command).desc(commandConf.desc);
debug('creating command `%s`: %s', commandConf.command, commandConf.desc);
debug('creating command `%s`: %s', commandConf.command, commandConf.desc);
// load options
commandConf.options && commandConf.options.forEach(function (option) {
debug('option: %s', option);
command.option.apply(command, option);
});
// we may add custom loaders for command actions
command.action(function (argv) {
if (commandConf.banner !== false) {
// print banner
console.log('\n %s %s', clout.package.name.cyan, clout.package.version);
console.log(' %s\n', clout.package.description.grey);
}
async.series([
function ensureMissingOptions(next) {
var missingOptions = [];
// check missing options
commandConf.required && commandConf.required.forEach(function (required) {
var value = argv.param.apply(argv, typeof required.option === 'string' ? [required.option] : required.option);
if (!value) {
missingOptions.push(required);
}
});
// load options
if (commandConf.options) {
commandConf.options.forEach((option) => {
debug('option: %s', option);
command.option(...option);
});
}
if (missingOptions.length === 0) {
return next();
}
// we may add custom loaders for command actions
command.action((argv) => {
if (commandConf.banner !== false) {
// print banner
console.log('\n %s %s', clout.package.name.cyan, clout.package.version);
console.log(' %s\n', clout.package.description.grey);
}
async.series([
function ensureMissingOptions(next) {
const missingOptions = [];
// check missing options
if (commandConf.required) {
commandConf.required.forEach((required) => {
const value = argv.param(...typeof required.option === 'string' ? [required.option] : required.option);
if (!value) {
missingOptions.push(required);
}
});
}
// prompt for missing
prompt.get(missingOptions, function (err, result) {
if (!result) {
// probably a SIGKILL
console.log('');
return;
}
_.merge(argv.params, result);
next();
});
prompt.start();
}
], function (err) {
if (err) {
console.error(err.red);
return;
}
this.prompt = prompt;
commandConf.action.apply(this, [argv]);
});
});
});
if (missingOptions.length === 0) {
return next();
}
// prompt for missing
prompt.get(missingOptions, (err, result) => {
if (!result) {
// probably a SIGKILL
console.log('');
return;
}
_.merge(argv.params, result);
next();
});
prompt.start();
},
], function onError(err) {
if (err) {
console.error(err.red);
return;
}
this.prompt = prompt;
commandConf.action.apply(this, [argv]);
});
});
});
};

@@ -6,93 +6,96 @@ /*!

*/
const
_ = require('lodash'),
debug = require('debug')('clout:install'),
path = require('path'),
async = require('async'),
fs = require('fs-extra'),
exec = require('child_process').exec;
const _ = require('lodash');
const debug = require('debug')('clout:install');
const path = require('path');
const async = require('async');
const fs = require('fs-extra');
const {exec} = require('child_process');
module.exports = {
command: 'install',
desc: 'install a clout module',
options: [
['--name', 'service name:'],
['--projectDir', 'Project Directory'],
['--workspaceDir', 'Workspace Directory'],
['--module', 'Workspace Directory (required)']
],
required: [
{
description: 'Module Name:',
option: 'module',
name: 'module',
required: true
}
],
action: function (argv) {
var serviceName = argv.param('name'),
serviceId = serviceName && serviceName.replace(' ', '-').toLowerCase(),
projectDir = undefined,
moduleName = argv.param('module'),
pkg = {},
cloutPkg = {};
command: 'install',
desc: 'install a clout module',
options: [
['--name', 'service name:'],
['--projectDir', 'Project Directory'],
['--workspaceDir', 'Workspace Directory'],
['--module', 'Workspace Directory (required)'],
],
required: [
{
description: 'Module Name:',
option: 'module',
name: 'module',
required: true,
},
],
action(argv) {
const serviceName = argv.param('name');
const serviceId = serviceName && serviceName.replace(' ', '-').toLowerCase();
const moduleName = argv.param('module');
let pkg = {};
const cloutPkg = {};
let projectDir;
debug('serviceName: %s', serviceName);
debug('serviceId: %s', serviceId);
// get projectDirectory
if (argv.param('projectDir')) {
projectDir = path.resolve(projectDir);
} else if (argv.param('workspaceDir')) {
projectDir = path.join(argv.param('workspaceDir'), serviceId);
} else {
serviceId && (projectDir = path.join(process.cwd(), serviceId));
if (!serviceId || !fs.existsSync(projectDir)) {
projectDir = process.cwd();
}
}
debug('serviceName: %s', serviceName);
debug('serviceId: %s', serviceId);
// get projectDirectory
if (argv.param('projectDir')) {
projectDir = path.resolve(projectDir);
} else if (argv.param('workspaceDir')) {
projectDir = path.join(argv.param('workspaceDir'), serviceId);
} else {
if (serviceId) {
projectDir = path.join(process.cwd(), serviceId);
}
debug('projectDir: %s', projectDir);
if (!serviceId || !fs.existsSync(projectDir)) {
projectDir = process.cwd();
}
}
async.series([
// check if project already exists
function checkIfProjectExists(next) {
debug('projectDir exists? %s', fs.existsSync(projectDir));
if (!fs.existsSync(projectDir)) {
return next('Project does not exist');
}
return next();
},
// install module
function (next) {
// run npm install
console.log('Installing project dependencies');
exec('cd "' + projectDir + '" && npm install ' + moduleName + ' --save', function (error, stdout, stderr) {
next();
});
},
// save module information
function (next) {
var pkgPath = path.join(projectDir, 'package.json'),
cloutPkgPath = path.join(projectDir, 'clout.json');
// load files
fs.existsSync(pkgPath) && (pkg = require(pkgPath));
fs.existsSync(cloutPkgPath) && (pkg = require(cloutPkg));
if (pkg.modules) {
// save here
pkg.modules.push(moduleName);
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, '\t'));
return next();
}
!cloutPkg.modules && (cloutPkg.modules = []);
cloutPkg.modules.push(moduleName);
fs.writeFileSync(cloutPkgPath, JSON.stringify(cloutPkg, null, '\t'));
next();
}
], function (err) {
if (err) {
return console.error(err.red);
}
console.error('Module Installed');
});
}
debug('projectDir: %s', projectDir);
async.series([
// check if project already exists
function checkIfProjectExists(next) {
debug('projectDir exists? %s', fs.existsSync(projectDir));
if (!fs.existsSync(projectDir)) {
return next('Project does not exist');
}
return next();
},
// install module
function (next) {
// run npm install
console.log('Installing project dependencies');
exec(`cd "${projectDir}" && npm install ${moduleName} --save`, (error, stdout, stderr) => {
next();
});
},
// save module information
function (next) {
let pkgPath = path.join(projectDir, 'package.json'),
cloutPkgPath = path.join(projectDir, 'clout.json');
// load files
fs.existsSync(pkgPath) && (pkg = require(pkgPath));
fs.existsSync(cloutPkgPath) && (pkg = require(cloutPkg));
if (pkg.modules) {
// save here
pkg.modules.push(moduleName);
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, '\t'));
return next();
}
!cloutPkg.modules && (cloutPkg.modules = []);
cloutPkg.modules.push(moduleName);
fs.writeFileSync(cloutPkgPath, JSON.stringify(cloutPkg, null, '\t'));
next();
},
], (err) => {
if (err) {
return console.error(err.red);
}
console.error('Module Installed');
});
},
};

@@ -7,130 +7,130 @@ /*!

const
_ = require('lodash'),
debug = require('debug')('clout:new'),
path = require('path'),
async = require('async'),
ejs = require('ejs'),
utils = require('../utils'),
fs = require('fs-extra'),
exec = require('child_process').exec
_ = require('lodash'),
debug = require('debug')('clout:new'),
path = require('path'),
async = require('async'),
ejs = require('ejs'),
utils = require('../utils'),
fs = require('fs-extra'),
exec = require('child_process').exec;
const
TEMPLATES_DIR = path.join(__dirname, '../../resources/templates/'),
BASE_TEMPLATE = path.join(TEMPLATES_DIR, 'base');
TEMPLATES_DIR = path.join(__dirname, '../../resources/templates/'),
BASE_TEMPLATE = path.join(TEMPLATES_DIR, 'base');
module.exports = {
command: 'new',
desc: 'create a new service',
options: [
['--name', 'service name:'],
['--template', 'template:'],
['--projectDir', 'Project Directory'],
['--workspaceDir', 'Workspace Directory'],
['-f, --force', 'Force project creation']
],
required: [
{
description: 'Service Name:',
option: 'name',
name: 'name',
required: true
}
],
action: function (argv) {
var serviceName = argv.param('name'),
template = argv.param('template') || 'sample',
serviceId = serviceName.replace(' ', '-').toLowerCase(),
projectDir = undefined;
command: 'new',
desc: 'create a new service',
options: [
['--name', 'service name:'],
['--template', 'template:'],
['--projectDir', 'Project Directory'],
['--workspaceDir', 'Workspace Directory'],
['-f, --force', 'Force project creation'],
],
required: [
{
description: 'Service Name:',
option: 'name',
name: 'name',
required: true,
},
],
action(argv) {
let serviceName = argv.param('name'),
template = argv.param('template') || 'sample',
serviceId = serviceName.replace(' ', '-').toLowerCase(),
projectDir;
debug('serviceName: %s', serviceName);
debug('serviceId: %s', serviceId);
// get projectDirectory
if (argv.param('projectDir')) {
projectDir = path.resolve(projectDir);
} else if (argv.param('workspaceDir')) {
projectDir = path.join(argv.param('workspaceDir'), serviceId);
} else {
projectDir = path.join(process.cwd(), serviceId);
}
debug('serviceName: %s', serviceName);
debug('serviceId: %s', serviceId);
// get projectDirectory
if (argv.param('projectDir')) {
projectDir = path.resolve(projectDir);
} else if (argv.param('workspaceDir')) {
projectDir = path.join(argv.param('workspaceDir'), serviceId);
} else {
projectDir = path.join(process.cwd(), serviceId);
}
debug('projectDir: %s', projectDir);
debug('projectDir: %s', projectDir);
async.series([
// check if project already exists
function checkIfProjectExists(next) {
debug('projectDir exists? %s', fs.existsSync(projectDir));
if (!fs.existsSync(projectDir)) {
return next();
}
console.warn('Project already exists'.yellow);
if (argv.mode('force', 'f')) {
console.warn('Forcing project creation'.yellow);
return next();
}
prompt.get([{
description: 'Would you like to overide the existing project:',
option: 'force',
name: 'force'
}], function (err, result) {
if (!result) {
// probably a SIGKILL
console.log('');
return;
}
if (!!result.force && ['y', 'Y', 'yes', 'YES', 'true', 'TRUE', 't', 'T'].indexOf(result.force) > -1) {
// carry on
return next();
}
console.error('Project could not be created :('.red);
});
prompt.start();
},
// create Project in directory
function copyBaseProjectAndRender(next) {
debug('copySync BASE_TEMPLATE: %s -> projectDir', BASE_TEMPLATE);
fs.copySync(BASE_TEMPLATE, projectDir);
var EJSData = {
serviceName: serviceName,
serviceId: serviceId,
author: 'Muhammad Dadu'
};
debug('EJSData: %s', JSON.stringify(EJSData));
utils.getGlobbedFiles(projectDir + '/*.ejs').forEach(function load(filePath) {
var file = fs.readFileSync(filePath, 'ascii'),
rendered = ejs.render(file, EJSData),
newFilePath = filePath.replace('.ejs', '');
fs.writeFileSync(newFilePath, rendered);
fs.removeSync(filePath);
});
next();
},
function copyTemplate(next) {
var templateDir = path.join(TEMPLATES_DIR, template);
if (!fs.existsSync(templateDir)) {
return next('Template `' + template + '` not found');
}
fs.copySync(templateDir, projectDir);
next();
},
function (next) {
var cloutConf_json = {
nodes: 1
};
fs.writeFileSync(path.join(projectDir, 'clout.json'), JSON.stringify(cloutConf_json, null, '\t'));
next();
},
function (next) {
// run npm install
console.log('Installing project dependencies');
exec('cd "' + projectDir + '" && npm install', function (error, stdout, stderr) {
next();
});
}
], function (err) {
if (err) {
return console.error(err.red);
}
console.error('Project Created');
});
}
async.series([
// check if project already exists
function checkIfProjectExists(next) {
debug('projectDir exists? %s', fs.existsSync(projectDir));
if (!fs.existsSync(projectDir)) {
return next();
}
console.warn('Project already exists'.yellow);
if (argv.mode('force', 'f')) {
console.warn('Forcing project creation'.yellow);
return next();
}
prompt.get([{
description: 'Would you like to overide the existing project:',
option: 'force',
name: 'force',
}], (err, result) => {
if (!result) {
// probably a SIGKILL
console.log('');
return;
}
if (!!result.force && ['y', 'Y', 'yes', 'YES', 'true', 'TRUE', 't', 'T'].indexOf(result.force) > -1) {
// carry on
return next();
}
console.error('Project could not be created :('.red);
});
prompt.start();
},
// create Project in directory
function copyBaseProjectAndRender(next) {
debug('copySync BASE_TEMPLATE: %s -> projectDir', BASE_TEMPLATE);
fs.copySync(BASE_TEMPLATE, projectDir);
const EJSData = {
serviceName,
serviceId,
author: 'Muhammad Dadu',
};
debug('EJSData: %s', JSON.stringify(EJSData));
utils.getGlobbedFiles(`${projectDir}/*.ejs`).forEach((filePath) => {
let file = fs.readFileSync(filePath, 'ascii'),
rendered = ejs.render(file, EJSData),
newFilePath = filePath.replace('.ejs', '');
fs.writeFileSync(newFilePath, rendered);
fs.removeSync(filePath);
});
next();
},
function copyTemplate(next) {
const templateDir = path.join(TEMPLATES_DIR, template);
if (!fs.existsSync(templateDir)) {
return next(`Template \`${template}\` not found`);
}
fs.copySync(templateDir, projectDir);
next();
},
function (next) {
const cloutConf_json = {
nodes: 1,
};
fs.writeFileSync(path.join(projectDir, 'clout.json'), JSON.stringify(cloutConf_json, null, '\t'));
next();
},
function (next) {
// run npm install
console.log('Installing project dependencies');
exec(`cd "${projectDir}" && npm install`, (error, stdout, stderr) => {
next();
});
},
], (err) => {
if (err) {
return console.error(err.red);
}
console.error('Project Created');
});
},
};

@@ -9,2 +9,3 @@ /*!

*/
const debug = require('debug')('clout:config');
const path = require('path');

@@ -14,3 +15,2 @@ const fs = require('fs-extra');

const utils = require('./utils');
const debug = require('debug')('clout:config');

@@ -22,57 +22,56 @@ /**

class Config {
/**
* @constructor
* @param {object} defaultConf default configuration object
*/
constructor(defaultConf) {
debug('initializing config');
this.env = process.env.NODE_ENV || 'development';
/**
* @constructor
* @param {object} defaultConf default configuration object
*/
constructor(defaultConf) {
debug('initializing config');
this.env = process.env.NODE_ENV || 'development';
if (defaultConf) {
_.merge(this, defaultConf);
}
}
if (defaultConf) {
_.merge(this, defaultConf);
}
}
/**
* Loads configuration
* @param {object} dir directory
*/
loadFromDir(dir) {
debug('loading config from dir %s', dir);
if (!fs.existsSync(dir)) {
debug('dir does not exist');
return;
}
/**
* Loads configuration
* @param {object} dir directory
*/
loadFromDir(dir) {
debug('loading config from dir %s', dir);
if (!fs.existsSync(dir)) {
debug('dir does not exist');
return;
}
// load configurations
const globDefault = '*default.js';
const globEnv = `*${this.env}.js`;
// load configurations
var globDefault = '*default.js',
globEnv = '*' + this.env + '.js';
// 1) load default configuration
utils.getGlobbedFiles(path.join(dir, globDefault)).forEach((file) => {
debug('loading config from: %s', file);
_.merge(this, require(file));
});
// 1) load default configuration
utils.getGlobbedFiles(path.join(dir, globDefault)).forEach((file) => {
debug('loading config from: %s', file);
_.merge(this, require(file));
});
// 2) load env specific configuration
utils.getGlobbedFiles(path.join(dir, globEnv)).forEach((file) => {
debug('loading config from: %s', file);
_.merge(this, require(file));
});
}
// 2) load env specific configuration
utils.getGlobbedFiles(path.join(dir, globEnv)).forEach((file) => {
debug('loading config from: %s', file);
_.merge(this, require(file));
});
}
/**
* Add config
* @param {array} config configuration object
*/
merge(...opts) {
_.merge(...[this, ...opts]);
}
/**
* Add config
* @param {array} config configuration object
*/
merge(...opts) {
_.merge(...[this, ...opts]);
}
toString() {
return `[clout config] ${JSON.stringify(this, null, ' ')}`;
}
toString() {
return `[clout config] ${JSON.stringify(this, null, ' ')}`;
}
}
module.exports = Config;

@@ -9,3 +9,2 @@ /*!

*/
const util = require('util');
const path = require('path');

@@ -24,76 +23,75 @@

class Logger extends winston.Logger {
/**
* @constructor
* @param {object} clout clout-js instance
*/
constructor(clout) {
debug('initialize logger');
super();
/**
* @constructor
* @param {object} clout clout-js instance
*/
constructor(clout) {
debug('initialize logger');
super();
this.clout = clout;
this.transports = [];
this.clout = clout;
this.transports = [];
this.clout.registerHook('start', this.appendToMiddleware, this.clout.CORE_PRIORITY.MIDDLEWARE);
this.clout.registerHook('start', this.appendToMiddleware, this.clout.CORE_PRIORITY.MIDDLEWARE);
if (this.clout.config.logToDir === true) {
this.logToDir();
}
if (this.clout.config.logToDir === true) {
this.logToDir();
}
// dont log to console in production
if (clout.config.env !== 'production') {
this.logToConsole();
}
// dont log to console in production
if (clout.config.env !== 'production') {
this.logToConsole();
}
this.saveConfiguration();
}
this.saveConfiguration();
}
/**
* Appends logger to middleware
*/
appendToMiddleware(next) {
this.app.use((req, resp, done) => {
req.logger = this.logger;
done();
});
next();
}
/**
* Appends logger to middleware
*/
appendToMiddleware(next) {
this.app.use((req, resp, next) => {
req.logger = this.logger;
next();
});
next();
}
/**
* Enables logging to console
*/
logToConsole() {
this.transports.push(new (winston.transports.Console)());
}
/**
* Enables logging to console
*/
logToConsole() {
this.transports.push(new (winston.transports.Console)());
}
/**
* Enables logging to application directory
*/
logToDir() {
const logDirectory = path.join(this.clout.rootDirectory, 'logs');
/**
* Enables logging to application directory
*/
logToDir() {
let logDirectory = path.join(this.clout.rootDirectory, 'logs');
fs.ensureDirSync(logDirectory);
debug('logDirectory: %s', logDirectory);
debug('add transport', 'DailyRotateFile');
fs.ensureDirSync(logDirectory);
debug('logDirectory: %s', logDirectory);
debug('add transport', 'DailyRotateFile');
const dailyRotateFile = new DailyRotateFile({
filename: path.join(logDirectory, 'clout_'),
datePattern: 'yyyy-MM-dd.log',
});
let dailyRotateFile = new DailyRotateFile({
filename: path.join(logDirectory, 'clout_'),
datePattern: 'yyyy-MM-dd.log'
});
this.transports.push(dailyRotateFile);
}
this.transports.push(dailyRotateFile);
}
/**
* Save configuration and update log level
* @param {String} level log level
*/
saveConfiguration(level) {
this.configure({
level: level || process.env.LOG_LEVEL || 'verbose',
transports: this.transports
});
}
/**
* Save configuration and update log level
* @param {String} level log level
*/
saveConfiguration(level) {
this.configure({
level: level || process.env.LOG_LEVEL || 'verbose',
transports: this.transports,
});
}
}
module.exports = Logger;

@@ -15,20 +15,20 @@ /*!

const utils = module.exports = {};
const utils = {};
utils.expandGlobPatterns = (globPatterns) => {
if (!isInTranspiler) {
return globPatterns;
}
if (!isInTranspiler) {
return globPatterns;
}
return globPatterns.reduce((acc, globPattern) => {
acc.push(globPattern);
return globPatterns.reduce((acc, globPattern) => {
acc.push(globPattern);
if (globPattern.includes('.js')) {
const tsPattern = globPattern.replace('.js', '.ts');
acc.push(tsPattern);
}
if (globPattern.includes('.js')) {
const tsPattern = globPattern.replace('.js', '.ts');
acc.push(tsPattern);
}
return acc;
}, [])
}
return acc;
}, []);
};

@@ -38,9 +38,12 @@ /**

* @param {string|array} globPatterns glob pattern
* @return {array} files array of files matching glob
* @return {array} files array of files matching glob
*/
utils.getGlobbedFiles = function getGlobbedFiles(globPatterns) {
const patterns = _.isArray(globPatterns) ? globPatterns : [globPatterns];
const expandedPatterns = utils.expandGlobPatterns(patterns);
const patterns = _.isArray(globPatterns) ? globPatterns : [globPatterns];
const expandedPatterns = utils.expandGlobPatterns(patterns);
return expandedPatterns.reduce((acc, globPattern) => [...acc, ...glob(globPattern, {sync: true})], [])
return expandedPatterns.reduce(
(acc, globPattern) => [...acc, ...glob(globPattern, { sync: true })],
[],
);
};

@@ -59,32 +62,32 @@

utils.getValue = function getValue(keyString, obj) {
let nodes = keyString.split('.');
let key = obj;
const nodes = keyString.split('.');
let key = obj;
do {
let node = nodes.shift(); // whos the lucky node?
let hasNode = key && key.hasOwnProperty(node);
do {
const node = nodes.shift(); // whos the lucky node?
const hasNode = key && key[node];
if (hasNode) {
key = key[node]; // traval
continue;
}
if (hasNode) {
return undefined;
}
nodes = []; // derefference nodes
key = undefined; // nothing found :(
} while (nodes.length > 0);
key = key[node]; // traval
} while (nodes.length > 0);
return key;
return key;
};
utils.safePromisifyCallFn = (fn, context, [req, resp, done, ...args]) => {
return new Promise((resolve, reject) => {
const done = (err, data) => err ? reject(err) : resolve(data);
const maybePromise = fn.apply(context, [req, resp, done, ...args])
utils.safePromisifyCallFn = function safePromisifyCallFn(fn, context, [req, resp, , ...args]) {
return new Promise((resolve, reject) => {
const next = (err, data) => (err ? reject(err) : resolve(data));
const maybePromise = fn.apply(context, [req, resp, next, ...args]);
if (maybePromise && maybePromise.then) {
maybePromise
.then(data => resolve(data))
.catch(err => reject(err));
}
})
}
if (maybePromise && maybePromise.then) {
maybePromise
.then(data => resolve(data))
.catch(err => reject(err));
}
});
};
module.exports = utils;
{
"name": "clout-js",
"version": "2.1.0-beta.2",
"version": "2.1.0-beta.3",
"description": "Clean, simplistic, enterprise grade full-stack NodeJS framework",

@@ -9,2 +9,4 @@ "main": "index.js",

"grunt": "grunt",
"lint": "eslint .",
"pretest": "npm run lint",
"test": "mocha test/*_test.js",

@@ -32,2 +34,7 @@ "test:watch": "mocha test/*_test.js --watch",

},
"husky": {
"hooks": {
"pre-push": "npm audit; npm run test"
}
},
"author": "Muhammad Dadu",

@@ -42,3 +49,3 @@ "license": "MIT",

"debug": "^3.1.0",
"ejs": "^2.5.7",
"ejs": "^3.0.1",
"express": "^4.16.2",

@@ -48,5 +55,4 @@ "express-session": "^1.15.6",

"glob": "^7.1.2",
"grunt": "^1.0.2",
"hbs": "^4.0.1",
"lodash": "^4.17.5",
"hbs": "^4.1.0",
"lodash": "^4.17.15",
"prompt": "^1.0.0",

@@ -58,11 +64,16 @@ "q": "^1.5.1",

"devDependencies": {
"@types/express": "^4.16.1",
"eslint": "^4.17.0",
"grunt": "^1.0.2",
"grunt-jsdoc": "^2.2.1",
"@types/express": "^4.17.2",
"eslint": "^4.19.1",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.12.4",
"grunt": "^1.0.4",
"grunt-jsdoc": "^2.4.1",
"grunt-mocha-test": "^0.13.3",
"jsdoc": "^3.5.5",
"husky": "^1.3.1",
"jsdoc": "^3.6.3",
"minami": "^1.2.3",
"mocha": "^5.0.0",
"request": "^2.83.0",
"request": "^2.88.0",
"should": "^13.2.1",

@@ -69,0 +80,0 @@ "sinon": "^4.2.2"

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc