UNPKG

grind-framework

Version:

An opinionated Node web framework built on Express

203 lines (170 loc) 4.99 kB
import './Config' import './Paths' import './ProviderCollection' import { lazy } from 'grind-support' const EventEmitter = require('events') /** * Main Application class for Grind */ export class Application extends EventEmitter { _env = null config = null paths = null booted = false booting = false providers = null /** * Create an instance of the Grind Application * * @param {string} options.env Override env name * By default, env is populated by the NODE_ENV * environment variable. This value takes * precedence over all other values. * @param {integer} options.port Override port to listen on * By default, port is populated by the app.port * config value, with an optional override via the * NODE_PORT environment variable. This value * takes precedence over all other values. * @param {Class} options.routerClass Override for Router class * @param {Class} options.configClass Override for Config class * @param {Class} options.errorHandlerClass Override for ErrorHandler class * @param {Class} options.urlGeneratorClass Override for UrlGenerator class * @param {Class} options.pathsClass Override for Paths class */ constructor( kernelClass, { env, port, routerClass, configClass, errorHandlerClass, urlGeneratorClass, pathsClass, ...extra } = {}, ) { super() this._env = env configClass = configClass || Config pathsClass = pathsClass || Paths this.paths = new pathsClass() this.config = new configClass(this) this.providers = new ProviderCollection(this) this.debug = this.config.get('app.debug', this.env() === 'local') this.on('error', err => Log.error('EventEmitter error', err)) this.kernel = new kernelClass(this, { port, routerClass, errorHandlerClass, urlGeneratorClass, pathsClass, ...extra, }) if (!this.kernel.as.isNil) { this[this.kernel.as] = this.kernel } for (const provider of this.kernel.providers) { this.providers.add(provider) } } /** * @return {string} Current environment */ env() { return this._env || process.env.NODE_ENV || 'local' } /** * Boots the current application, if not already booted. * This will notify all registered providers and start them. * * @return {Promise} */ async boot() { if (this.booted) { return } this.booting = true this.providers.sort((a, b) => (a.priority > b.priority ? -1 : 1)) for (const provider of this.providers) { await provider(this) } this.emit('boot', this) this.booted = true this.booting = false } /** * Loads a Kernel Provider * * Kernel Providers are a special type of * provider that get loaded immediately upon * being added rather than delayed until boot. * * @param function provider */ loadKernelProvider(provider) { if (typeof provider.shutdown === 'function') { const shutdown = provider.shutdown.bind(provider, this) this.once('shutdown', () => { const result = shutdown() if (result.isNil || typeof result.then !== 'function') { return } Log.warn('WARNING: Kernel Providers do not support async shutdown functions.') Log.warn('--> App shutdown is not waiting for this provider to finish.') Log.warn(`--> Offending provider: ${provider.name}`) }) } const result = provider(this) if (result.isNil || typeof result.then !== 'function') { return } throw new Error('Kernel Providers can not be async.') } /** * Starts the Kernel */ async start(...args) { await this.boot() return this.kernel.start(...args) } /** * Shuts down the current application, if booted. * This will notify all registered providers with a shutdown handler. * * @return {Promise} */ async shutdown() { if (!this.booted) { return } for (const provider of this.providers) { if (typeof provider.shutdown !== 'function') { continue } try { await provider.shutdown(this) } catch (e) { Log.error(`Error while shutting ${provider.name} down`, e) } } this.emit('shutdown', this) this.booted = false } /** * Register a property on the app instance that will be * populated with the value of `callback` after the first * time it’s called. * * @param {string} name Name of the property to registe * @param {Function} callback Callback handler that should return * the value of the property */ lazy(name, callback) { lazy(this, name, callback) } // Pass through properties for the http kernel get errorHandler() { return this.http.errorHandler } }