UNPKG

choreo

Version:

A Robotics Framework

216 lines (187 loc) 6.72 kB
var _ = require('lodash'); var async = require('async'); var __hooks = require('../../hooks'); var defaultsDeep = require('merge-defaults'); module.exports = function (choreo) { var Hook = __hooks(choreo); /** * Resolve the hook definitions and then finish loading them * * @api private */ return function initializeHooks(hooks, cb) { function prepareHook(id) { var hookPrototype = hooks[id]; // Allow disabling of hooks by setting them to "false" if (hookPrototype === false || hooks[id.split('.')[0]] === false) { delete hooks[id]; return; } // Check for invalid hook config if (hooks.userconfig && !hooks.moduleloader) { return cb( 'Invalid configuration:: Cannot use the `userconfig` hook ' + 'w/o the `moduleloader` hook enabled!'); } // Handle folder-defined modules (default to index.js) // Since a hook definition must be a function if (_.isObject(hookPrototype) && !_.isArray(hookPrototype) && !_.isFunction(hookPrototype)) { hookPrototype = hookPrototype.index; } if (!_.isFunction(hookPrototype)) { choreo.log.error('Malformed hook! (' + id + ')'); choreo.log.error('Hooks should be a function with one argument ' + '(`choreo`)'); process.exit(1); } // Instantiate the hook var def = hookPrototype(choreo); // Mix in an `identity` property to hook definition def.identity = id.toDropCase(); // If a config key was defined for this hook when it was loaded, // (probably because a user is overridding the default config key) // set it on the hook definition def.configKey = hookPrototype.configKey || def.identity; // New up an actual Hook instance hooks[id] = new Hook(def); } // Function to apply a hook's "defaults" obj or function function applyDefaults(hook) { // Get the hook defaults var defaults = (_.isFunction(hook.defaults) ? hook.defaults(choreo.config) : hook.defaults) || {}; // Replace the special __configKey__ key with the actual config key if (hook.defaults.__configKey__ && hook.configKey) { hook.defaults[hook.configKey] = hook.defaults.__configKey__; delete hook.defaults.__configKey__; } defaultsDeep(choreo.config, defaults); } // Load a hook (bind its routes, load any modules and initialize it) function loadHook(id, cb) { var timeoutInterval = (choreo.config[hooks[id].configKey || id] && choreo.config[hooks[id].configKey || id]._hookTimeout) || choreo.config.hookTimeout || 20000; var hookTimeout; if (id != 'userhooks') { hookTimeout = setTimeout(function tooLong() { var hooksTookTooLongErr = 'The hook `' + id + '` is taking too long ' + 'to load.\n' + 'Make sure it is triggering its `initialize()` callback, ' + 'or else set `choreo.config.' + (hooks[id].configKey || id) + '._hookTimeout to a higher value (currently ' + timeoutInterval + ')'; var err = new Error(hooksTookTooLongErr); err.code = 'E_HOOK_TIMEOUT'; cb(err); }, timeoutInterval); } hooks[id].load(function (err) { if (id != 'userhooks') { clearTimeout(hookTimeout); } if (err) { if (id != 'userhooks') { choreo.log.error('A hook (`' + id + '`) failed to load!'); } choreo.emit('hook:' + id + ':error'); return cb(err); } choreo.log.verbose(id, 'hook loaded successfully.'); choreo.emit('hook:' + id + ':loaded'); // Defer to next tick to allow other stuff to happen process.nextTick(cb); }); } async.series({ // First load the moduleloader (if any) moduleloader: function (cb) { if (!hooks.moduleloader) { return cb(); } prepareHook('moduleloader'); applyDefaults(hooks['moduleloader']); hooks['moduleloader'].configure(); loadHook('moduleloader', cb); }, // Next load the user config (if any) userconfig: function (cb) { if (!hooks.userconfig) { return cb(); } prepareHook('userconfig'); applyDefaults(hooks['userconfig']); hooks['userconfig'].configure(); loadHook('userconfig', cb); }, // Next get the user hooks (if any), which will be // added to the list of hooks to load userhooks: function (cb) { if (!hooks.userhooks) { return cb(); } prepareHook('userhooks'); applyDefaults(hooks['userhooks']); hooks['userhooks'].configure(); loadHook('userhooks', cb); }, // Prepare all other hooks prepare: function (cb) { async.each(_.without(_.keys(hooks), 'userconfig', 'moduleloader', 'userhooks'), function (id, cb) { prepareHook(id); // Defer to next tick to allow other stuff to happen process.nextTick(cb); }, cb); }, // Apply the default config for all other hooks defaults: function (cb) { async.each(_.without(_.keys(hooks), 'userconfig', 'moduleloader', 'userhooks'), function (id, cb) { var hook = hooks[id]; applyDefaults(hook); // Defer to next tick to allow other stuff to happen process.nextTick(cb); }, cb); }, // Run configuration method for all other hooks configure: function (cb) { async.each(_.without(_.keys(hooks), 'userconfig', 'moduleloader', 'userhooks'), function (id, cb) { var hook = hooks[id]; hook.configure(); // Defer to next tick to allow other stuff to happen process.nextTick(cb); }, cb); }, // Load all other hooks load: function (cb) { async.each(_.without(_.keys(hooks), 'userconfig', 'moduleloader', 'userhooks'), function (id, cb) { choreo.log.silly('Loading hook: ' + id); loadHook(id, cb); }, cb); } }, function hooksReady(err) { return cb(err); }); }; };