src/recink.js
'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;