UNPKG

sails

Version:

API-driven framework for building realtime apps, using MVC conventions (based on Express and Socket.io)

295 lines (247 loc) 14.4 kB
/** * Module dependencies */ var util = require('util'); var _ = require('@sailshq/lodash'); var async = require('async'); var flaverr = require('flaverr'); var __Configuration = require('./configuration'); var __initializeHooks = require('./private/loadHooks'); var __loadActionModules = require('./private/controller/load-action-modules'); var __checkGruntConfig = require('./private/checkGruntConfig'); /** * @param {SailsApp} sails * @returns {Function} */ module.exports = function(sails) { var Configuration = __Configuration(sails); var initializeHooks = __initializeHooks(sails); var checkGruntConfig = __checkGruntConfig(sails); /** * Expose loader start point. * (idempotent) * * @api public */ return function load(configOverride, cb) { if (sails._exiting) { return cb(new Error('\n*********\nCannot load or lift an app after it has already been lowered. \nYou can make a new app instance with:\nvar SailsApp = require(\'sails\').Sails;\nvar sails = new SailsApp();\n\nAnd then you can do:\nsails.load([opts,] cb)\n\n')); } // Log a verbose log message about the fact that EVEN MORE verbosity // is available in `silly` mode. sails.log.verbose('• • • • • • • • • • • • • • • • • • • • • • • • • • • • • •'); sails.log.verbose('• Loading Sails with "verbose" logging enabled... •'); sails.log.verbose('• (For even more details, try "silly".) •'); sails.log.silly ('• Actually, looks like you\'re already using "silly"! •'); sails.log.verbose('• •'); sails.log.verbose('• http://sailsjs.com/config/log •'); sails.log.verbose('• • • • • • • • • • • • • • • • • • • • • • • • • • • • • •'); // configOverride is optional if (_.isFunction(configOverride)) { cb = configOverride; configOverride = {}; } // Ensure override is an object and clone it (or make an empty object if it's not). // The shallow clone protects against the caller accidentally adding/removing props // to the config after Sails has loaded (but they could still mess with nested config). configOverride = configOverride || {}; sails.config = _.clone(configOverride); // If host is explicitly specified, set `explicitHost` // (otherwise when host is omitted, Express will accept all connections via INADDR_ANY) if (configOverride.host) { configOverride.explicitHost = configOverride.host; } // Optionally expose services, models, sails, _, async, etc. as globals as soon as the // user config loads. sails.on('hook:userconfig:loaded', sails.exposeGlobals); async.auto({ // Apply core defaults and hook-agnostic configuration, // esp. overrides including command-line options, environment variables, // and options that were passed in programmatically. config: [Configuration.load], // Verify that the combination of Sails environment and NODE_ENV is valid // as early as possible -- that is, as soon as we know for sure what the // Sails environment is. verifyEnv: ['config', function(results, cb) { // If the userconfig hook is active, wait until it's finished to // verify the environment, since it might be set in a config file. if (_.isUndefined(sails.config.hooks) || (sails.config.hooks !== false && sails.config.hooks.userconfig !== false)) { sails.on('hook:userconfig:loaded', verifyEnvironment); } // Otherwise verify it right now. The Sails environment will be // whatever was set on the command line, or via the sails_environment // env var, or defaulted to "development". else { verifyEnvironment(); } return cb(); }], // Check if the current app needs the Grunt hook installed but doesn't have it. grunt: ['config', checkGruntConfig], // Load hooks into memory, with their middleware and routes hooks: ['verifyEnv', 'config', helpLoadHooks], // Load actions from disk and config overrides controller: ['hooks', function(results, cb) { __loadActionModules(sails, cb); }], // Populate the "registry" // Houses "middleware-esque" functions bound by various hooks and/or Sails core itself. // (i.e. `function (req, res [,next]) {}`) // // (Basically, that means we grab an exposed `middleware` object, // full of functions, from each hook, then make it available as // `sails.middleware.[HOOK_ID]`.) // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // FUTURE: finish refactoring to change "middleware" nomenclature // to avoid confusion with the more specific (and more common) // usage of the term. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - registry: ['hooks', function populateRegistry(results, cb) { // Iterate through hooks and absorb the middleware therein // Save a reference to registry and expose it on // the Sails instance. sails.middleware = sails.registry = // Namespace functions by their source hook's identity _.reduce(sails.hooks, function(registry, hook, identity) { registry[identity] = hook.middleware; return registry; }, {}); sails.emit('middleware:registered'); cb(); } ], // Load the router and bind routes in `sails.config.routes` router: ['registry', sails.router.load] }, ready__(cb)); // Makes `app.load()` chainable return sails; }; // ============================================================================== // < inline function declarations > // ██╗ ██╗███╗ ██╗██╗ ██╗███╗ ██╗███████╗ ███████╗███╗ ██╗ ██████╗ ███████╗███████╗███████╗ ██╗ // ██╔╝ ██║████╗ ██║██║ ██║████╗ ██║██╔════╝ ██╔════╝████╗ ██║ ██╔══██╗██╔════╝██╔════╝██╔════╝ ╚██╗ // ██╔╝ ██║██╔██╗ ██║██║ ██║██╔██╗ ██║█████╗ █████╗ ██╔██╗ ██║ ██║ ██║█████╗ █████╗ ███████╗ ╚██╗ // ╚██╗ ██║██║╚██╗██║██║ ██║██║╚██╗██║██╔══╝ ██╔══╝ ██║╚██╗██║ ██║ ██║██╔══╝ ██╔══╝ ╚════██║ ██╔╝ // ╚██╗ ██║██║ ╚████║███████╗██║██║ ╚████║███████╗ ██║ ██║ ╚████║ ██████╔╝███████╗██║ ███████║ ██╔╝ // ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚══════╝ ╚═╝ // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // FUTURE: extrapolate the following inline function definitions into // separate files -- or if appropriate (and if there's no tangible impact // on lift performance) then pull them inline above. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** * Load hooks in parallel * let them work out dependencies themselves, * taking advantage of events fired from the sails object * * @api private */ function helpLoadHooks(results, cb) { sails.hooks = { }; // If config.hooks is disabled, skip hook loading altogether if (sails.config.hooks === false) { return cb(); } async.series([ function(cb) { loadHookDefinitions(sails.hooks, cb); }, function(cb) { initializeHooks(sails.hooks, cb); } ], function(err) { if (err) { return cb(err); } // Inform any listeners that the initial, built-in hooks // are finished loading sails.emit('hooks:builtIn:ready'); sails.log.silly('Built-in hooks are ready.'); return cb(); }); } /** * Load built-in hook definitions from `sails.config.hooks` * and put them back into `hooks` (probably `sails.hooks`) * * @api private */ function loadHookDefinitions(hooks, cb) { // Mix in user-configured hook definitions _.extend(hooks, sails.config.hooks); // Make sure these changes to the hooks object get applied // to sails.config.hooks to keep logic consistent // (I think we can get away w/o this, but leaving as a stub) // sails.config.hooks = hooks; // If user configured `loadHooks`, only include those. if (sails.config.loadHooks) { if (!_.isArray(sails.config.loadHooks)) { return cb('Invalid `loadHooks` config. ' + 'Please specify an array of string hook names.\n' + 'You specified ::' + util.inspect(sails.config.loadHooks)); } _.each(hooks, function(def, hookName) { if (!_.contains(sails.config.loadHooks, hookName)) { hooks[hookName] = false; } }); sails.log.verbose('Deliberate partial load-- will only initialize hooks ::', sails.config.loadHooks); } return cb(); } function verifyEnvironment() { // At this point, the Sails environment is set to its final value, // whether it came from the command line or a config file. So we // can now compare it to the NODE_ENV environment variable and // act accordingly. This may involve changing NODE_ENV to "production", // which we want to do as early as possible since dependencies might // be relying on that value. // If the Sails environment is production, but NODE_ENV is undefined, // log a warning and change NODE_ENV to "production". if (sails.config.environment === 'production' && process.env.NODE_ENV !== 'production' ) { if (_.isUndefined(process.env.NODE_ENV)) { sails.log.debug('Detected Sails environment is "production", but NODE_ENV is `undefined`.'); sails.log.debug('Automatically setting the NODE_ENV environment variable to "production".'); sails.log.debug(); process.env.NODE_ENV = 'production'; } else { throw flaverr({ name: 'userError', code: 'E_INVALID_NODE_ENV' }, new Error('When the Sails environment is set to "production", NODE_ENV must also be set to "production" (but it was set to "' + process.env.NODE_ENV + '" instead).')); } } } /** * Returns function which is fired when Sails is ready to go * * @api private */ function ready__(cb) { return function(err) { if (err) { return cb && cb(err); } sails.log.silly('The router & all hooks were loaded successfully.'); // If userconfig hook is turned off, still load globals. if (sails.config.hooks && sails.config.hooks.userconfig === false || (sails.config.loadHooks && sails.config.loadHooks.indexOf('userconfig') === -1)) { sails.exposeGlobals(); } // If the Sails environment is set to "production" but the Node environment isn't, // log a warning. if (sails.config.environment === 'production' && process.env.NODE_ENV !== 'production') { sails.log.warn('Detected Sails environment of `production`, but Node environment is `' + process.env.NODE_ENV + '`.\n' + 'It is recommended that in production mode, both the Sails and Node environments be set to `production`.'); } cb && cb(null, sails); }; } // ██╗ ██╗ ██╗███╗ ██╗██╗ ██╗███╗ ██╗███████╗ ███████╗███╗ ██╗ ██████╗ ███████╗███████╗███████╗ ██╗ // ██╔╝ ██╔╝ ██║████╗ ██║██║ ██║████╗ ██║██╔════╝ ██╔════╝████╗ ██║ ██╔══██╗██╔════╝██╔════╝██╔════╝ ╚██╗ // ██╔╝ ██╔╝ ██║██╔██╗ ██║██║ ██║██╔██╗ ██║█████╗ █████╗ ██╔██╗ ██║ ██║ ██║█████╗ █████╗ ███████╗ ╚██╗ // ╚██╗ ██╔╝ ██║██║╚██╗██║██║ ██║██║╚██╗██║██╔══╝ ██╔══╝ ██║╚██╗██║ ██║ ██║██╔══╝ ██╔══╝ ╚════██║ ██╔╝ // ╚██╗██╔╝ ██║██║ ╚████║███████╗██║██║ ╚████║███████╗ ██║ ██║ ╚████║ ██████╔╝███████╗██║ ███████║ ██╔╝ // ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚══════╝ ╚═╝ // // </ inline function declarations (see note above) > // ============================================================================== };