From 6d0f0c3b6e023774229a283385abc9b779a3581f Mon Sep 17 00:00:00 2001 From: Robert Haritonov Date: Thu, 13 Aug 2015 18:31:41 +0200 Subject: [PATCH 1/5] utils.extendOptions --- core/lib/utils.js | 16 ++++++++++++++ core/loadOptions.js | 16 +++----------- test/specs/lib/utils.js | 48 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 test/specs/lib/utils.js diff --git a/core/lib/utils.js b/core/lib/utils.js index 79bbfcc..8535b31 100644 --- a/core/lib/utils.js +++ b/core/lib/utils.js @@ -1,6 +1,22 @@ 'use strict'; +var _ = require('lodash'); + module.exports.requireUncached = function (module) { delete require.cache[require.resolve(module)]; return require(module); +}; + +module.exports.extendOptions = function () { + var args = Array.prototype.slice.call(arguments); + var cb = function (a, b) { + if (_.isArray(a)) { + return b; + } + }; + + args.push(cb); + + // Don't merge arrays + return _.merge.apply(this, args); }; \ No newline at end of file diff --git a/core/loadOptions.js b/core/loadOptions.js index f6ff0e3..91eeb38 100644 --- a/core/loadOptions.js +++ b/core/loadOptions.js @@ -1,7 +1,6 @@ 'use strict'; var fs = require('fs'); -var _ = require('lodash'); var path = require('path'); var configUtils = require('./lib/configUtils'); var utils = require('./lib/utils'); @@ -9,15 +8,6 @@ var colors = require('colors'); // jshint ignore:line var silent; -var extendOptions = function(to, from){ - // Don't merge arrays - _.merge(to, from, function (a, b) { - if (_.isArray(a)) { - return b; - } - }); -}; - var legacyOptionsWarn = function(oldStruct, newStruct, fileName){ var _fileName = fileName || 'options.js'; @@ -95,16 +85,16 @@ module.exports = function(basePath, _silent){ var userLocalSettingsFile = path.join(pathToUser, 'local-options.js'); // Adding assets npm plugin list to options - extendOptions(mergedOptions, configUtils.prepareClientNpmPlugins(pathToUser)); + utils.extendOptions(mergedOptions, configUtils.prepareClientNpmPlugins(pathToUser)); // If user settings file is present, override core settings if(fs.existsSync(userSettingsFile)) { - extendOptions(mergedOptions, legacyOptionsChecker(utils.requireUncached(userSettingsFile)), 'options.js'); + utils.extendOptions(mergedOptions, legacyOptionsChecker(utils.requireUncached(userSettingsFile)), 'options.js'); } // If local settings file is present, override core settings if(fs.existsSync(userLocalSettingsFile)) { - extendOptions(mergedOptions, legacyOptionsChecker(utils.requireUncached(userLocalSettingsFile)), 'local-options.js'); + utils.extendOptions(mergedOptions, legacyOptionsChecker(utils.requireUncached(userLocalSettingsFile)), 'local-options.js'); } return mergedOptions; diff --git a/test/specs/lib/utils.js b/test/specs/lib/utils.js new file mode 100644 index 0000000..11cd4bc --- /dev/null +++ b/test/specs/lib/utils.js @@ -0,0 +1,48 @@ +var should = require('should'); +var assert = require('assert'); +var path = require('path'); + +var pathToMasterApp = path.resolve('./'); +var utils = require(path.join(pathToMasterApp, 'core/lib/utils')); + +describe('utils: extendOptions', function () { + it('it should extend objects skipping arrays merge', function (done) { + var input = { + a: { + foo: "bar" + }, + b: ['foo', 'bar'] + }; + + utils.extendOptions(input, { + a: { + bar: "foo" + }, + b: ['bar'], + c: 'val' + }); + + input.should.have.property('c', 'val'); + + input.a.should.have.property('foo', 'bar'); + input.a.should.have.property('bar', 'foo'); + + input.should.have.property('b').with.lengthOf(1); + input.should.have.property('b', ['bar']); + + done(); + }); + + it('it should accept multiple objects', function (done) { + var input = {}; + + utils.extendOptions(input, {a: 'val', arr: ['foo', 'bar', 'foo']}, {'b': 'val'}, {c: 'val', arr: ['bar', 'foo']}); + + input.should.have.property('a', 'val'); + input.should.have.property('b', 'val'); + input.should.have.property('c', 'val'); + input.should.have.property('arr', ['bar', 'foo']); + + done(); + }); +}); \ No newline at end of file From 50251b1d7c298e1a11f807e27cfa62131131fa5f Mon Sep 17 00:00:00 2001 From: Robert Haritonov Date: Thu, 13 Aug 2015 18:33:03 +0200 Subject: [PATCH 2/5] middleware loader #117 --- app.js | 33 +---- core/middleware/userMiddleware.js | 26 ---- core/{middleware => middlewares}/clarify.js | 0 core/middlewares/loader.js | 153 ++++++++++++++++++++ core/{middleware => middlewares}/md.js | 2 + core/{middleware => middlewares}/mdTag.js | 0 core/{middleware => middlewares}/read.js | 0 core/{middleware => middlewares}/send.js | 0 core/{middleware => middlewares}/wrap.js | 0 9 files changed, 161 insertions(+), 53 deletions(-) delete mode 100644 core/middleware/userMiddleware.js rename core/{middleware => middlewares}/clarify.js (100%) create mode 100644 core/middlewares/loader.js rename core/{middleware => middlewares}/md.js (95%) rename core/{middleware => middlewares}/mdTag.js (100%) rename core/{middleware => middlewares}/read.js (100%) rename core/{middleware => middlewares}/send.js (100%) rename core/{middleware => middlewares}/wrap.js (100%) diff --git a/app.js b/app.js index 73ea810..6b73b51 100644 --- a/app.js +++ b/app.js @@ -99,14 +99,15 @@ app.use(bodyParser.json()); -/* Middlewares */ +/* Includes */ + +// Middlewares +require('./core/middlewares/loader').process(app, global.opts); // Auth initializing var auth = require('./core/auth')(app); app.use(auth.everyauth.middleware()); -// Clarify -app.use(require('./core/middleware/clarify')); // File tree module var fileTree = require('./core/file-tree'); @@ -135,29 +136,6 @@ app.use('/api/updateFileTree', function(req, res){ }); -// Middleware that loads spec content -var read = require("./core/middleware/read"); -app.use(read.process); - -// Markdown -app.use(require("./core/middleware/md").process); -app.use(require("./core/middleware/mdTag").process); - -// Load user defined middleware, that processes spec content -require("./core/middleware/userMiddleware"); - -// Middleware that wraps spec with Source template -app.use(require("./core/middleware/wrap").process); - -// Middleware that sends final spec response -app.use(require("./core/middleware/send").process); - -/* /Middlewares */ - - - -/* Includes */ - // Routes require('./core/routes'); @@ -233,7 +211,7 @@ app.use(logErrors); -// Server start +/* Server start */ if (!module.parent) { var serverOpts = global.opts.core.server; var port = serverOpts.port; @@ -256,3 +234,4 @@ if (!module.parent) { }); } } +/* Server start */ diff --git a/core/middleware/userMiddleware.js b/core/middleware/userMiddleware.js deleted file mode 100644 index b09952f..0000000 --- a/core/middleware/userMiddleware.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -/** - * - * Loading user middleware - * - * */ - -var fs = require('fs'); -var path = require('path'); -var pathToUserMiddleware = path.join(global.app.get('user'), 'node_modules'); - -// Loading all sourcejs-*/core/index.js files from npm plugins section -if (fs.existsSync(pathToUserMiddleware)) { - var userMiddlewareFiles = fs.readdirSync(pathToUserMiddleware); - - userMiddlewareFiles.map(function (file) { - if ((/^sourcejs-/).test(file)) { - var pluginIndexPath = path.join(pathToUserMiddleware, file, 'core/middleware', 'index.js'); - if (fs.existsSync(pluginIndexPath)) { - var middleware = require(pluginIndexPath); - global.app.use(middleware.process); - } - } - }); -} \ No newline at end of file diff --git a/core/middleware/clarify.js b/core/middlewares/clarify.js similarity index 100% rename from core/middleware/clarify.js rename to core/middlewares/clarify.js diff --git a/core/middlewares/loader.js b/core/middlewares/loader.js new file mode 100644 index 0000000..4d2703e --- /dev/null +++ b/core/middlewares/loader.js @@ -0,0 +1,153 @@ +'use strict'; + +var _ = require('lodash'); +var fs = require('fs'); +var path = require('path'); + +var appRoot = path.resolve('./'); +var utils = require(path.join(appRoot, 'core/lib/utils')); +var log = require(path.join(appRoot, 'core/logger')).log; + +var gatherMiddlewares = function(dest, filterRegExp, mainJS){ + var output = {}; + + if (fs.existsSync(dest)) { + var userMiddlewareFiles = fs.readdirSync(dest); + + userMiddlewareFiles.map(function (dir) { + if (!filterRegExp || filterRegExp.test(dir)) { + var middlewareName = dir; + var _mainJS = mainJS || 'index.js'; + + var pluginIndexPath = path.join(dest, dir, _mainJS); + if (fs.existsSync(pluginIndexPath)) { + output[middlewareName] = { + order: 0, + group: 'default', + process: require(pluginIndexPath).process + }; + + // Load middleware options + var configPath = path.join(dest, dir, 'options.js'); + if (fs.existsSync(configPath)) { + var middlewareConfig = require(configPath); + + _.forOwn(output[middlewareName], function (value, key) { + var overVal = middlewareConfig[key]; + + if (overVal) { + if (key === 'order') { + if (overVal >= 0) { + output[middlewareName][key] = overVal; + } else { + log.warn('Middlewares are restricted to define order with value lower than 0 (zero). Please modify '+ middlewareName+ ' middleware options.'); + } + } else { + output[middlewareName][key] = overVal; + } + } + }); + } + } + } + }); + } + + return output; +}; + +var sortMiddlewares = function(groupsOrder, list){ + var output = []; + + if (!_.isArray(groupsOrder) && !list) return output; + + var groupedList = {}; + + // Sort by groups + _.forOwn(list, function (value, key) { + var group = value.group || 'default'; + var middleware = value; + + middleware.name = middleware.name || key; + + groupedList[group] = groupedList[group] || []; + groupedList[group].push(value); + }); + + // Sort each group + _.forOwn(groupedList, function (value, key) { + groupedList[key] = _.sortByOrder(value, ['order'], ['asc']); + }); + + // Concat groups by order + groupsOrder.forEach(function(item){ + output = output.concat(groupedList[item]); + }); + + return output; +}; + +var loadMiddlewares = function(listArr, app){ + if (!_.isArray(listArr) && !app) return; + + log.debug('loading', listArr); + + listArr.forEach(function(item){ + if (typeof item.process === 'function') { + app.use(item.process); + } + }); +}; + +module.exports.process = function(app, globalOptions){ + var config = { + loadGroupsOrder: [ + 'request', + 'pre-html', + 'default', + 'html', + 'response' + ], + list: { + md: { + order: -1, + group: 'pre-html', + process: require(path.join(appRoot, 'core/middlewares/md')).process + }, + mdTag: { + order: 0, + group: 'html', + process: require(path.join(appRoot, 'core/middlewares/mdTag')).process + }, + clarify: { + order: -2, + group: 'request', + process: require(path.join(appRoot, 'core/middlewares/clarify')) + }, + read: { + order: -1, + group: 'request', + process: require(path.join(appRoot, 'core/middlewares/read')).process + }, + send: { + order: -1, + group: 'response', + process: require(path.join(appRoot, 'core/middlewares/send')).process + }, + wrap: { + order: -1, + group: 'html', + process: require(path.join(appRoot, 'core/middlewares/wrap')).process + } + } + }; + utils.extendOptions( + config, + { + list: gatherMiddlewares(path.join(app.get('user'), 'node_modules'), new RegExp(/^sourcejs-/), 'core/middleware/index.js') + }, + globalOptions.core.middlewares + ); + + loadMiddlewares(sortMiddlewares(config.loadGroupsOrder, config.list), app); +}; \ No newline at end of file diff --git a/core/middleware/md.js b/core/middlewares/md.js similarity index 95% rename from core/middleware/md.js rename to core/middlewares/md.js index 9653b0b..971aa41 100644 --- a/core/middleware/md.js +++ b/core/middlewares/md.js @@ -21,6 +21,8 @@ exports.process = function (req, res, next) { var end = process.hrtime(start); global.log.debug('Markdown processing took: ', prettyHrtime(end)); + req.specData.isMd = false; + next(); } else { next(); diff --git a/core/middleware/mdTag.js b/core/middlewares/mdTag.js similarity index 100% rename from core/middleware/mdTag.js rename to core/middlewares/mdTag.js diff --git a/core/middleware/read.js b/core/middlewares/read.js similarity index 100% rename from core/middleware/read.js rename to core/middlewares/read.js diff --git a/core/middleware/send.js b/core/middlewares/send.js similarity index 100% rename from core/middleware/send.js rename to core/middlewares/send.js diff --git a/core/middleware/wrap.js b/core/middlewares/wrap.js similarity index 100% rename from core/middleware/wrap.js rename to core/middlewares/wrap.js From 7bf551ebdc268877a39293b561c63377db248a2a Mon Sep 17 00:00:00 2001 From: Robert Haritonov Date: Thu, 13 Aug 2015 18:52:12 +0200 Subject: [PATCH 3/5] allow middleware disabling && error handling --- core/middlewares/loader.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/middlewares/loader.js b/core/middlewares/loader.js index 4d2703e..09df85f 100644 --- a/core/middlewares/loader.js +++ b/core/middlewares/loader.js @@ -68,6 +68,8 @@ var sortMiddlewares = function(groupsOrder, list){ var group = value.group || 'default'; var middleware = value; + if (!middleware.enabled) return; + middleware.name = middleware.name || key; groupedList[group] = groupedList[group] || []; @@ -81,7 +83,7 @@ var sortMiddlewares = function(groupsOrder, list){ // Concat groups by order groupsOrder.forEach(function(item){ - output = output.concat(groupedList[item]); + if (groupedList[item]) output = output.concat(groupedList[item]); }); return output; @@ -93,7 +95,7 @@ var loadMiddlewares = function(listArr, app){ log.debug('loading', listArr); listArr.forEach(function(item){ - if (typeof item.process === 'function') { + if (item && typeof item.process === 'function') { app.use(item.process); } }); @@ -110,31 +112,37 @@ module.exports.process = function(app, globalOptions){ ], list: { md: { + enabled: true, order: -1, group: 'pre-html', process: require(path.join(appRoot, 'core/middlewares/md')).process }, mdTag: { + enabled: true, order: 0, group: 'html', process: require(path.join(appRoot, 'core/middlewares/mdTag')).process }, clarify: { + enabled: true, order: -2, group: 'request', process: require(path.join(appRoot, 'core/middlewares/clarify')) }, read: { + enabled: true, order: -1, group: 'request', process: require(path.join(appRoot, 'core/middlewares/read')).process }, send: { + enabled: true, order: -1, group: 'response', process: require(path.join(appRoot, 'core/middlewares/send')).process }, wrap: { + enabled: true, order: -1, group: 'html', process: require(path.join(appRoot, 'core/middlewares/wrap')).process From 97f8cec3425f9bf0da0640ee1dc975b3a4c91904 Mon Sep 17 00:00:00 2001 From: Robert Haritonov Date: Fri, 14 Aug 2015 11:57:27 +0200 Subject: [PATCH 4/5] don't load middleware index file if it's disabled --- core/middlewares/clarify.js | 2 +- core/middlewares/loader.js | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/core/middlewares/clarify.js b/core/middlewares/clarify.js index 7549b10..08c7361 100644 --- a/core/middlewares/clarify.js +++ b/core/middlewares/clarify.js @@ -228,7 +228,7 @@ var getSectionsIDList = function(sections) { return output; }; -module.exports = function(req, res, next) { +module.exports.process = function(req, res, next) { var parsedUrl = url.parse(req.url, true); // Query params diff --git a/core/middlewares/loader.js b/core/middlewares/loader.js index 09df85f..28b4dfb 100644 --- a/core/middlewares/loader.js +++ b/core/middlewares/loader.js @@ -22,9 +22,10 @@ var gatherMiddlewares = function(dest, filterRegExp, mainJS){ var pluginIndexPath = path.join(dest, dir, _mainJS); if (fs.existsSync(pluginIndexPath)) { output[middlewareName] = { + enabled: true, order: 0, group: 'default', - process: require(pluginIndexPath).process + indexPath: pluginIndexPath }; // Load middleware options @@ -95,8 +96,8 @@ var loadMiddlewares = function(listArr, app){ log.debug('loading', listArr); listArr.forEach(function(item){ - if (item && typeof item.process === 'function') { - app.use(item.process); + if (item && fs.existsSync(item.indexPath)) { + app.use(require(item.indexPath).process); } }); }; @@ -115,37 +116,37 @@ module.exports.process = function(app, globalOptions){ enabled: true, order: -1, group: 'pre-html', - process: require(path.join(appRoot, 'core/middlewares/md')).process + indexPath: path.join(appRoot, 'core/middlewares/md.js') }, mdTag: { enabled: true, order: 0, group: 'html', - process: require(path.join(appRoot, 'core/middlewares/mdTag')).process + indexPath: path.join(appRoot, 'core/middlewares/mdTag.js') }, clarify: { enabled: true, order: -2, group: 'request', - process: require(path.join(appRoot, 'core/middlewares/clarify')) + indexPath: path.join(appRoot, 'core/middlewares/clarify.js') }, read: { enabled: true, order: -1, group: 'request', - process: require(path.join(appRoot, 'core/middlewares/read')).process + indexPath: path.join(appRoot, 'core/middlewares/read.js') }, send: { enabled: true, order: -1, group: 'response', - process: require(path.join(appRoot, 'core/middlewares/send')).process + indexPath: path.join(appRoot, 'core/middlewares/send.js') }, wrap: { enabled: true, order: -1, group: 'html', - process: require(path.join(appRoot, 'core/middlewares/wrap')).process + indexPath: path.join(appRoot, 'core/middlewares/wrap.js') } } }; From d78ae3e992ee2c7cb16c2653035292e1bb62688c Mon Sep 17 00:00:00 2001 From: Robert Haritonov Date: Fri, 14 Aug 2015 16:02:58 +0200 Subject: [PATCH 5/5] improving error handling --- core/middlewares/loader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/middlewares/loader.js b/core/middlewares/loader.js index 28b4dfb..35de9e7 100644 --- a/core/middlewares/loader.js +++ b/core/middlewares/loader.js @@ -60,7 +60,7 @@ var gatherMiddlewares = function(dest, filterRegExp, mainJS){ var sortMiddlewares = function(groupsOrder, list){ var output = []; - if (!_.isArray(groupsOrder) && !list) return output; + if (!(groupsOrder && _.isArray(groupsOrder) && list)) return output; var groupedList = {}; @@ -96,7 +96,7 @@ var loadMiddlewares = function(listArr, app){ log.debug('loading', listArr); listArr.forEach(function(item){ - if (item && fs.existsSync(item.indexPath)) { + if (item && item.indexPath && fs.existsSync(item.indexPath)) { app.use(require(item.indexPath).process); } });