UNPKG

recink

Version:

Rethink CI for JavaScript applications

245 lines (204 loc) 5.97 kB
'use strict'; const path = require('path'); const merge = require('merge'); const Emitter = require('./emitter'); const events = require('./events'); const logger = require('./logger'); const Container = require('./container'); const configFactory = require('./config/factory'); const AbstractComponent = require('./component/abstract-component'); /** * Recink entry point */ class Recink extends Emitter { constructor() { super(); this._config = {}; this._components = []; this._container = new Container(); this._registerDebugers(); } /** * @private */ _registerDebugers() { this.on(events.config.load, (container, configFile) => { logger.info(logger.emoji.smiley, `Load config from ${ configFile }`); logger.debug(container.dump()); }); this.on(events.components.load, (...components) => { logger.debug( 'Loading components -', components.map(c => c.name).join(', ') || 'None' ); }); this.on(events.component.load, component => { logger.debug(`Load component ${ component.name }`); }); this.on(events.component.subscribe, component => { logger.debug(`Component ${ component.name } is subscribed`); }); this.on(events.component.ready, component => { logger.debug(`Component ${ component.name } is ready`); }); this.on(events.components.run, (...components) => { components.map(component => { logger.info( component.isActive ? logger.emoji.check : logger.emoji.cross, `${ component.name.toUpperCase() } component` ); }); }); } /** * @returns {Promise} */ run() { this.emit(events.components.run, ...this._components); const activeComponents = this._components.filter(component => component.isActive); if (activeComponents.length <= 0) { return Promise.resolve(); } return Promise.all(activeComponents.map(component => { this.emit(events.component.init, component); return component.init(this); })) .then(() => { return Promise.all(activeComponents.map(component => { this.emit(events.component.run, component); return component.run(this); })); }) .then(() => { return Promise.all(this._components.map(component => { this.emit(events.component.teardown, component); return component.teardown(this); })); }); } /** * @param {Function} targetClass * @returns {string|null} * @private */ _getBaseClass(targetClass) { if (targetClass instanceof Function) { let baseClass = targetClass; while (baseClass) { const newBaseClass = Object.getPrototypeOf(baseClass); if (newBaseClass && newBaseClass !== Object && newBaseClass.name) { baseClass = newBaseClass; } else { break; } } return baseClass.name; } return null; } /** * @param {AbstractComponent} components * @returns {Promise} */ components(...components) { this.emit(events.components.load, ...components); if (components.length <= 0) { return Promise.resolve(); } return Promise.all(components.map(component => { if (!(component instanceof AbstractComponent) && [ 'ConfigBasedComponent', 'AbstractComponent' ] .indexOf(this._getBaseClass(component.constructor)) === -1) { return Promise.reject(new Error( `Component ${ component.constructor.name } should be an instance of AbstractComponent` )); } component.setLogger(logger); this._components.push(component); this.emit(events.component.load, component); return component.subscribe(this) .then(() => { return this.emit(events.component.subscribe, component, this); }) .then(() => component.ready()) .then(() => { this.emit(events.component.ready, component); return Promise.resolve(component); }); })); } /** * @param {string} configFile * @param {*} extendConfigs * @returns {Promise} */ configureExtend(configFile, ...extendConfigs) { if (extendConfigs.length <= 0) { return this.configure(configFile); } const promises = extendConfigs .concat([ configFile ]) .map(cfgFile => configFactory.guess(cfgFile).load()); return Promise.all(promises).then(configVectors => { return Promise.resolve(merge.recursive(true, ...configVectors)); }); } /** * @param {string} configFile * @returns {Promise} */ configure(configFile = Recink.CONFIG_FILE) { return configFactory.guess(configFile).load().then(config => Promise.resolve(config)); } /** * @param {Object} config * @param {String} configFile * @returns {Promise} */ configLoad(config, configFile) { return this.emitBlocking(events.config.preprocess, config).then(() => { this._config = config; this._container.reload(this._config); this.emit(events.config.load, this.container, configFile); return Promise.resolve(this._config); }); } /** * @param {string} name * * @returns {AbstractComponent} */ component(name) { return this._components.filter(c => c.name === name)[0] || null; } /** * @returns {AbstractComponent[]} */ listComponents() { return this._components; } /** * @returns {Container} */ get container() { return this._container; } /** * @returns {AbstractConfig} */ get config() { return this._config; } /** * @returns {string} */ static get CONFIG_FILE() { return path.resolve(process.cwd(), this.CONFIG_FILE_NAME); } /** * @returns {string} */ static get CONFIG_FILE_NAME() { return '.recink.yml'; } } module.exports = Recink;