'use strict';
/* eslint-disable security/detect-object-injection */
/* eslint-disable security/detect-non-literal-require */
/**
* Module dependencies, required for ALL Twyr' modules
* @ignore
*/
/**
* Module dependencies, required for this module
* @ignore
*/
const TwyrBaseClass = require('./twyr-base-class').TwyrBaseClass;
const TwyrBaseError = require('./twyr-base-error').TwyrBaseError;
const TwyrBaseService = require('./twyr-base-service').TwyrBaseService;
/**
* @class TwyrModuleLoader
* @extends {TwyrBaseClass}
* @classdesc The Twyr Server Base Class for all Module Loaders.
*
* @param {TwyrBaseModule} [twyrModule] - The parent module, if any.
*
* @description
* Serves as the "base class" for all other loaders in the Twyr Web Application Server, including {@link TwyrComponentLoader},
* {@link TwyrMiddlewareLoader}, {@link TwyrServiceLoader}, and {@link TwyrTemplateLoader}.
*
* Responsible for invoking the standard "lifecycle" hooks on sub-modules of this module, if any - see {@link TwyrBaseModule#load},
* {@link TwyrBaseModule#initialize}, {@link TwyrBaseModule#start}, {@link TwyrBaseModule#stop}, {@link TwyrBaseModule#uninitialize},
* and {@link TwyrBaseModule#unload}.
*
*/
class TwyrModuleLoader extends TwyrBaseClass {
// #region Constructor
constructor(twyrModule) {
super();
Object.defineProperties(this, {
'$twyrModule': {
'get': () => { return twyrModule; }
}
});
}
// #endregion
// #region Lifecycle hooks
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name load
*
* @param {ConfigurationService} configSrvc - Instance of the {@link ConfigurationService} that supplies configuration.
*
* @returns {Object} - The load status of $twyrModule's sub-modules.
*
* @summary Loads sub-modules.
*/
async load(configSrvc) {
try {
const allStatuses = [];
let lifecycleStatuses = null;
lifecycleStatuses = await this._loadUtilities(configSrvc);
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._loadServices(configSrvc);
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
// Special case.... if the server is loading, configSrvc will be null
// so re-look at the loaded services and set the configSrvc to the loaded one
if(!this.$twyrModule.$parent && !configSrvc) configSrvc = this.$twyrModule.$services['ConfigurationService'];
lifecycleStatuses = await this._loadMiddlewares(configSrvc);
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._loadComponents(configSrvc);
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
// Templates are loaded only for servers, and not for features...
if(!this.$twyrModule.$parent) {
lifecycleStatuses = await this._loadTemplates(configSrvc);
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
// Features are loaded only for servers or templates, and not for services, middlewares, etc....
if(!this.$twyrModule.$parent || (Object.getPrototypeOf(Object.getPrototypeOf(this.$twyrModule)).name === 'TwyrBaseFeature')) {
lifecycleStatuses = await this._loadFeatures(configSrvc);
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
return this._filterStatus(allStatuses);
}
catch(err) {
throw new TwyrBaseError(`${this.$twyrModule.name}::loader::load error`, err);
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name initialize
*
* @returns {Object} - The initialization status of $twyrModule's sub-modules.
*
* @summary Initializes sub-modules.
*/
async initialize() {
try {
const allStatuses = [];
let lifecycleStatuses = null;
lifecycleStatuses = await this._initializeServices();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._initializeMiddlewares();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._initializeComponents();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
if(!this.$twyrModule.$parent) {
lifecycleStatuses = await this._initializeTemplates();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
if(!this.$twyrModule.$parent || (Object.getPrototypeOf(Object.getPrototypeOf(this.$twyrModule)).name === 'TwyrBaseFeature')) {
lifecycleStatuses = await this._initializeFeatures();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
return this._filterStatus(allStatuses);
}
catch(err) {
throw new TwyrBaseError(`${this.$twyrModule.name}::loader::initialize error`, err);
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name start
*
* @returns {Object} - The start status of $twyrModule's sub-modules.
*
* @summary Starts sub-modules.
*/
async start() {
try {
const allStatuses = [];
let lifecycleStatuses = null;
lifecycleStatuses = await this._startServices();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._startMiddlewares();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._startComponents();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
if(!this.$twyrModule.$parent) {
lifecycleStatuses = await this._startTemplates();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
if(!this.$twyrModule.$parent || (Object.getPrototypeOf(Object.getPrototypeOf(this.$twyrModule)).name === 'TwyrBaseFeature')) {
lifecycleStatuses = await this._startFeatures();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
return this._filterStatus(allStatuses);
}
catch(err) {
throw new TwyrBaseError(`${this.$twyrModule.name}::loader::start error`, err);
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name stop
*
* @returns {Object} - The stop status of $twyrModule's sub-modules.
*
* @summary Stops sub-modules.
*/
async stop() {
try {
const allStatuses = [];
let lifecycleStatuses = null;
if(!this.$twyrModule.$parent || (Object.getPrototypeOf(Object.getPrototypeOf(this.$twyrModule)).name === 'TwyrBaseFeature')) {
lifecycleStatuses = await this._stopFeatures();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
if(!this.$twyrModule.$parent) {
lifecycleStatuses = await this._stopTemplates();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
lifecycleStatuses = await this._stopComponents();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._stopMiddlewares();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._stopServices();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
return this._filterStatus(allStatuses);
}
catch(err) {
throw new TwyrBaseError(`${this.$twyrModule.name}::loader::stop error`, err);
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name uninitialize
*
* @returns {Object} - The uninitialization status of $twyrModule's sub-modules.
*
* @summary Un-initializes sub-modules.
*/
async uninitialize() {
try {
const allStatuses = [];
let lifecycleStatuses = null;
if(!this.$twyrModule.$parent || (Object.getPrototypeOf(Object.getPrototypeOf(this.$twyrModule)).name === 'TwyrBaseFeature')) {
lifecycleStatuses = await this._uninitializeFeatures();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
if(!this.$twyrModule.$parent) {
lifecycleStatuses = await this._uninitializeTemplates();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
lifecycleStatuses = await this._uninitializeComponents();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._uninitializeMiddlewares();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._uninitializeServices();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
return this._filterStatus(allStatuses);
}
catch(err) {
throw new TwyrBaseError(`${this.$twyrModule.name}::loader::uninitialize error`, err);
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name unload
*
* @returns {Object} - The unloading status of $twyrModule's sub-modules.
*
* @summary Unloads sub-modules.
*/
async unload() {
try {
const allStatuses = [];
let lifecycleStatuses = null;
if(!this.$twyrModule.$parent || (Object.getPrototypeOf(Object.getPrototypeOf(this.$twyrModule)).name === 'TwyrBaseFeature')) {
lifecycleStatuses = await this._unloadFeatures();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
if(!this.$twyrModule.$parent) {
lifecycleStatuses = await this._unloadTemplates();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
}
lifecycleStatuses = await this._unloadComponents();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._unloadMiddlewares();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._unloadServices();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
lifecycleStatuses = await this._unloadUtilities();
if(lifecycleStatuses && lifecycleStatuses.status && (lifecycleStatuses.status instanceof Error))
throw lifecycleStatuses.status;
allStatuses.push(lifecycleStatuses);
return this._filterStatus(allStatuses);
}
catch(err) {
throw new TwyrBaseError(`${this.$twyrModule.name}::loader::unload error`, err);
}
}
// #endregion
// #region Utilities Lifecycle hooks
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _loadUtilities
*
* @param {ConfigurationService} configSrvc - Instance of the {@link ConfigurationService} that supplies configuration.
*
* @returns {Object} Object containing the load status of each of the $twyrModule utilities.
*
* @summary Load utilities defined as part of this {@link TwyrBaseModule}.
*/
async _loadUtilities(configSrvc) { // eslint-disable-line no-unused-vars
const path = require('path');
const promises = require('bluebird');
try {
if(!this.$twyrModule.$utilities) this.$twyrModule.$utilities = {};
const definedUtilities = await this._findFiles(path.join(this.$twyrModule.basePath, 'utilities'), 'utility.js');
for(const definedUtility of definedUtilities) {
const utility = require(definedUtility).utility;
if(!utility) continue;
if(!utility.name || !utility.method)
continue;
this.$twyrModule.$utilities[utility.name] = utility.method.bind(this.$twyrModule);
if(utility.isAsync) this.$twyrModule.$utilities[`${utility.name}Async`] = promises.promisify(utility.method.bind(this.$twyrModule));
}
return {
'type': 'utilities',
'status': Object.keys(this.$twyrModule.$utilities).length ? Object.keys(this.$twyrModule.$utilities) : null
};
}
catch(err) {
return {
'type': 'utilities',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_loadUtilities error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _unloadUtilities
*
* @returns {Object} Object containing the unload status of each of the $twyrModule utilities.
*
* @summary Unload utilities defined as part of this {@link TwyrBaseModule}.
*/
async _unloadUtilities() {
try {
const utilityNames = Object.keys(this.$twyrModule.$utilities || {});
utilityNames.forEach((utilityName) => {
delete this.$twyrModule.$utilities[utilityName];
delete this.$twyrModule.$utilities[`${utilityName}Async`];
});
delete this.$twyrModule.$utilities;
return {
'type': 'utilities',
'status': utilityNames.length ? utilityNames : null
};
}
catch(err) {
return {
'type': 'utilities',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_unloadServices error`, err)
};
}
}
// #endregion
// #region Services Lifecycle hooks
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _loadServices
*
* @param {ConfigurationService} configSrvc - Instance of the {@link ConfigurationService} that supplies configuration.
*
* @returns {Object} Object containing the load status of each of the $twyrModule services.
*
* @summary Load services defined as part of this {@link TwyrBaseModule}.
*
* @description
* Special processing:
* If the configSrvc parameter is undefined, load the {@link ConfigurationService} first, if any found.
*
* Since the {@link ConfigurationServiceLoader} calls load / initialize / start in one shot, the other sub-modules get, basically,
* a fully functional configuration service by the time they load, allowing them to use their configurations from the get-go.
*/
async _loadServices(configSrvc) {
const path = require('path');
try {
if(!this.$twyrModule.$services) this.$twyrModule.$services = {};
const definedServices = await this._findFiles(path.join(this.$twyrModule.basePath, 'services'), 'service.js');
let configSrvcLoadStatus = null,
instConfigSrvc = null;
if(!configSrvc) { // eslint-disable-line curly
for(const definedService of definedServices) {
const Service = require(definedService).service;
if(!Service) continue;
// Construct the Service...
instConfigSrvc = new Service(this.$twyrModule);
instConfigSrvc.$dependants = [];
if(instConfigSrvc.name !== 'ConfigurationService') {
instConfigSrvc = undefined;
continue;
}
// Check to see valid typeof
// eslint-disable-next-line curly
if(!(instConfigSrvc instanceof TwyrBaseService)) {
throw new TwyrBaseError(`${definedService} does not contain a valid TwyrBaseService definition`);
}
configSrvcLoadStatus = await instConfigSrvc.load(null);
break;
}
}
for(const definedService of definedServices) {
// Check validity of the definition...
const Service = require(definedService).service;
if(!Service) continue;
// Construct the service
const serviceInstance = new Service(this.$twyrModule);
serviceInstance.$dependants = [];
if(serviceInstance.name === 'ConfigurationService')
continue;
// Check to see valid typeof
// eslint-disable-next-line curly
if(!(serviceInstance instanceof TwyrBaseService)) {
throw new TwyrBaseError(`${definedService} does not contain a valid TwyrBaseService definition`);
}
this.$twyrModule.$services[serviceInstance.name] = serviceInstance;
}
const nameStatusPairs = await this._doLifecycleAction('services', 'load', [configSrvc || instConfigSrvc]);
if(instConfigSrvc) {
this.$twyrModule.$services['ConfigurationService'] = instConfigSrvc;
nameStatusPairs['ConfigurationService'] = configSrvcLoadStatus;
}
return {
'type': 'services',
'status': nameStatusPairs
};
}
catch(err) {
return {
'type': 'services',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_loadServices error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _initializeServices
*
* @returns {Object} Object containing the initialization status of each of the $twyrModule services.
*
* @summary Initialize Services defined as part of this {@link TwyrBaseModule}.
*/
async _initializeServices() {
try {
const nameStatusPairs = await this._doLifecycleAction('services', 'initialize');
return { 'type': 'services', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'services',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_initializeServices error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _startServices
*
* @returns {Object} Object containing the start status of each of the $twyrModule services.
*
* @summary Start Services defined as part of this {@link TwyrBaseModule}.
*/
async _startServices() {
try {
const DepGraph = require('dependency-graph').DepGraph;
const initOrder = new DepGraph();
const serviceNames = Object.keys(this.$twyrModule.$services || {});
serviceNames.forEach((serviceName) => {
initOrder.addNode(serviceName);
});
serviceNames.forEach((serviceName) => {
const thisService = this.$twyrModule.$services[serviceName];
if(!thisService.dependencies.length) return;
thisService.dependencies.forEach((thisServiceDependency) => {
if(serviceNames.indexOf(thisServiceDependency) < 0) return;
initOrder.addDependency(thisService.name, thisServiceDependency);
});
});
const initOrderList = initOrder.overallOrder();
const nameStatusPairs = {};
for(const serviceName of initOrderList) {
let lifecycleStatus = null;
try {
const moduleInstance = this.$twyrModule.$services[serviceName];
const dependencies = this._getDependencies(moduleInstance);
lifecycleStatus = await moduleInstance.start(dependencies);
}
catch(err) {
lifecycleStatus = new TwyrBaseError(`${this.$twyrModule.name}::loader::_startServices::${serviceName} error`, err);
}
nameStatusPairs[serviceName] = lifecycleStatus;
}
return { 'type': 'services', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'services',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_startServices error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _stopServices
*
* @returns {Object} Object containing the stop status of each of the $twyrModule services.
*
* @summary Stop Services defined as part of this {@link TwyrBaseModule}.
*/
async _stopServices() {
try {
const DepGraph = require('dependency-graph').DepGraph;
const uninitOrder = new DepGraph();
const serviceNames = Object.keys(this.$twyrModule.$services || {});
serviceNames.forEach((serviceName) => {
uninitOrder.addNode(serviceName);
});
serviceNames.forEach((serviceName) => {
const thisService = this.$twyrModule.$services[serviceName];
if(!thisService.dependencies.length) return;
thisService.dependencies.forEach((thisServiceDependency) => {
if(serviceNames.indexOf(thisServiceDependency) < 0) return;
uninitOrder.addDependency(thisService.name, thisServiceDependency);
});
});
const uninitOrderList = uninitOrder.overallOrder().reverse();
const nameStatusPairs = {};
for(const serviceName of uninitOrderList) {
let lifecycleStatus = null;
try {
lifecycleStatus = await this.$twyrModule.$services[serviceName].stop();
}
catch(err) {
lifecycleStatus = new TwyrBaseError(`${this.$twyrModule.name}::loader::_stopServices error`, err);
}
nameStatusPairs[serviceName] = lifecycleStatus;
}
return {
'type': 'services',
'status': nameStatusPairs
};
}
catch(err) {
return {
'type': 'services',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_stopServices error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _uninitializeServices
*
* @returns {Object} Object containing the uninit status of each of the $twyrModule services.
*
* @summary Uninitialize Services defined as part of this {@link TwyrBaseModule}.
*/
async _uninitializeServices() {
try {
const nameStatusPairs = await this._doLifecycleAction('services', 'uninitialize');
return { 'type': 'services', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'services',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_uninitializeServices error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _unloadServices
*
* @returns {Object} Object containing the unload status of each of the $twyrModule services.
*
* @summary Unload Services defined as part of this {@link TwyrBaseModule}.
*/
async _unloadServices() {
try {
const nameStatusPairs = await this._doLifecycleAction('services', 'unload');
return { 'type': 'services', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'services',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_uninitializeServices error`, err)
};
}
}
// #endregion
// #region Middlewares Lifecycle hooks
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _loadMiddlewares
*
* @param {ConfigurationService} configSrvc - Instance of the {@link ConfigurationService} that supplies configuration.
*
* @returns {Object} Object containing the load status of each of the $twyrModule middlewares.
*
* @summary Load middlewares defined as part of this {@link TwyrBaseModule}.
*/
async _loadMiddlewares(configSrvc) {
const path = require('path');
try {
if(!this.$twyrModule.$middlewares) this.$twyrModule.$middlewares = {};
const definedMiddlewares = await this._findFiles(path.join(this.$twyrModule.basePath, 'middlewares'), 'middleware.js');
for(const definedMiddleware of definedMiddlewares) {
// Check validity of the definition...
const Middleware = require(definedMiddleware).middleware;
if(!Middleware) continue;
// Construct the service
const middlewareInstance = new Middleware(this.$twyrModule);
// Check to see valid typeof
// if(!(middlewareInstance instanceof TwyrBaseMiddleware))
// throw new TwyrBaseError(`${definedMiddleware} does not contain a valid TwyrBaseMiddleware definition`);
this.$twyrModule.$middlewares[middlewareInstance.name] = middlewareInstance;
}
const nameStatusPairs = await this._doLifecycleAction('middlewares', 'load', [configSrvc]);
return { 'type': 'middlewares', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'middlewares',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_loadMiddlewares error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _initializeMiddlewares
*
* @returns {Object} Object containing the initialization status of each of the $twyrModule middlewares.
*
* @summary Initialize Middlewares defined as part of this {@link TwyrBaseModule}.
*/
async _initializeMiddlewares() {
try {
const nameStatusPairs = await this._doLifecycleAction('middlewares', 'initialize');
return { 'type': 'middlewares', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'middlwares',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_initializeMiddlewares error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _startMiddlewares
*
* @returns {Object} Object containing the start status of each of the $twyrModule middlewares.
*
* @summary Start Middlewares defined as part of this {@link TwyrBaseModule}.
*/
async _startMiddlewares() {
try {
const middlewareNames = Object.keys(this.$twyrModule.$middlewares || {}),
nameStatusPairs = {};
for(const middlewareName of middlewareNames) {
let lifecycleStatus = null;
try {
const moduleInstance = this.$twyrModule.$middlewares[middlewareName];
const dependencies = this._getDependencies(moduleInstance);
lifecycleStatus = await moduleInstance.start(dependencies);
}
catch(err) {
lifecycleStatus = new TwyrBaseError(`${this.$twyrModule.name}::loader::_doLifecycleAction (${middlewareName} / start) error`, err);
}
nameStatusPairs[middlewareName] = lifecycleStatus;
}
return {
'type': 'middlewares',
'status': nameStatusPairs
};
}
catch(err) {
return {
'type': 'middlwares',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_startMiddlewares error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _stopMiddlewares
*
* @returns {Object} Object containing the stop status of each of the $twyrModule middlewares.
*
* @summary Stop Middlewares defined as part of this {@link TwyrBaseModule}.
*/
async _stopMiddlewares() {
try {
const nameStatusPairs = await this._doLifecycleAction('middlewares', 'stop');
return { 'type': 'middlewares', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'middlewares',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_stopMiddlewares error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _uninitializeMiddlewares
*
* @returns {Object} Object containing the uninit status of each of the $twyrModule middlewares.
*
* @summary Uninitialize Middlewares defined as part of this {@link TwyrBaseModule}.
*/
async _uninitializeMiddlewares() {
try {
const nameStatusPairs = await this._doLifecycleAction('middlewares', 'uninitialize');
return { 'type': 'middlewares', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'middlewares',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_uninitializeMiddlewares error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _unloadMiddlewares
*
* @returns {Object} Object containing the unload status of each of the $twyrModule middlewares.
*
* @summary Unload Middlewares defined as part of this {@link TwyrBaseModule}.
*/
async _unloadMiddlewares() {
try {
const nameStatusPairs = await this._doLifecycleAction('middlewares', 'unload');
return { 'type': 'middlewares', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'middlewares',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_unloadMiddlewares error`, err)
};
}
}
// #endregion
// #region Components Lifecycle hooks
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _loadComponents
*
* @param {ConfigurationService} configSrvc - Instance of the {@link ConfigurationService} that supplies configuration.
*
* @returns {Object} Object containing the load status of each of the $twyrModule components.
*
* @summary Load components defined as part of this {@link TwyrBaseModule}.
*/
async _loadComponents(configSrvc) {
const path = require('path');
try {
if(!this.$twyrModule.$components) this.$twyrModule.$components = {};
const definedComponents = await this._findFiles(path.join(this.$twyrModule.basePath, 'components'), 'component.js');
for(const definedComponent of definedComponents) {
// Check validity of the definition...
const Component = require(definedComponent).component;
if(!Component) continue;
// Construct the service
const componentInstance = new Component(this.$twyrModule);
// Check to see valid typeof
// if(!(serviceInstance instanceof TwyrBaseService))
// throw new TwyrBaseError(`${definedService} does not contain a valid TwyrBaseService definition`);
this.$twyrModule.$components[componentInstance.name] = componentInstance;
}
const nameStatusPairs = await this._doLifecycleAction('components', 'load', [configSrvc]);
return { 'type': 'components', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'components',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_loadComponents error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _initializeComponents
*
* @returns {Object} Object containing the initialization status of each of the $twyrModule components.
*
* @summary Initialize Components defined as part of this {@link TwyrBaseModule}.
*/
async _initializeComponents() {
try {
const nameStatusPairs = await this._doLifecycleAction('components', 'initialize');
return { 'type': 'components', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'components',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_initializeComponents error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _startComponents
*
* @returns {Object} Object containing the start status of each of the $twyrModule components.
*
* @summary Start Components defined as part of this {@link TwyrBaseModule}.
*/
async _startComponents() {
try {
const componentNames = Object.keys(this.$twyrModule.$components || {}),
nameStatusPairs = {};
for(const componentName of componentNames) {
let lifecycleStatus = null;
try {
const moduleInstance = this.$twyrModule.$components[componentName];
const dependencies = this._getDependencies(moduleInstance);
lifecycleStatus = await moduleInstance.start(dependencies);
}
catch(err) {
lifecycleStatus = new TwyrBaseError(`${this.$twyrModule.name}::loader::_doLifecycleAction (${componentName} / start) error`, err);
}
nameStatusPairs[componentName] = lifecycleStatus;
}
return {
'type': 'components',
'status': nameStatusPairs
};
}
catch(err) {
return {
'type': 'components',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_startComponents error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _stopComponents
*
* @returns {Object} Object containing the stop status of each of the $twyrModule components.
*
* @summary Stop Components defined as part of this {@link TwyrBaseModule}.
*/
async _stopComponents() {
try {
const nameStatusPairs = await this._doLifecycleAction('components', 'stop');
return { 'type': 'components', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'components',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_stopComponents error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _uninitializeComponents
*
* @returns {Object} Object containing the uninit status of each of the $twyrModule components.
*
* @summary Uninitialize Components defined as part of this {@link TwyrBaseModule}.
*/
async _uninitializeComponents() {
try {
const nameStatusPairs = await this._doLifecycleAction('components', 'uninitialize');
return { 'type': 'components', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'components',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_uninitializeComponents error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _unloadComponents
*
* @returns {Object} Object containing the unload status of each of the $twyrModule components.
*
* @summary Unload Components defined as part of this {@link TwyrBaseModule}.
*/
async _unloadComponents() {
try {
const nameStatusPairs = await this._doLifecycleAction('components', 'unload');
return { 'type': 'components', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'components',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_unloadComponents error`, err)
};
}
}
// #endregion
// #region Templates Lifecycle hooks
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _loadTemplates
*
* @param {ConfigurationService} configSrvc - Instance of the {@link ConfigurationService} that supplies configuration.
*
* @returns {Object} Object containing the load status of each of the $twyrModule templates.
*
* @summary Load templates defined as part of this {@link TwyrBaseModule}.
*/
async _loadTemplates(configSrvc) {
const path = require('path');
try {
if(!this.$twyrModule.$templates) this.$twyrModule.$templates = {};
const definedTemplates = await this._findFiles(path.join(this.$twyrModule.basePath, 'templates'), 'template.js');
for(const definedTemplate of definedTemplates) {
// Check validity of the definition...
const Template = require(definedTemplate).template;
if(!Template) continue;
// Construct the template
const templateInstance = new Template(this.$twyrModule);
// Check to see valid typeof
// if(!(serviceInstance instanceof TwyrBaseService))
// throw new TwyrBaseError(`${definedService} does not contain a valid TwyrBaseService definition`);
this.$twyrModule.$templates[templateInstance.name] = templateInstance;
}
const nameStatusPairs = await this._doLifecycleAction('templates', 'load', [configSrvc]);
return { 'type': 'templates', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'templates',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_loadTemplates error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _initializeTemplates
*
* @returns {Object} Object containing the initialization status of each of the $twyrModule templates.
*
* @summary Initialize Templates defined as part of this {@link TwyrBaseModule}.
*/
async _initializeTemplates() {
try {
const nameStatusPairs = await this._doLifecycleAction('templates', 'initialize');
return { 'type': 'templates', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'templates',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_initializeTemplates error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _startTemplates
*
* @returns {Object} Object containing the start status of each of the $twyrModule templates.
*
* @summary Start Templates defined as part of this {@link TwyrBaseModule}.
*/
async _startTemplates() {
try {
const nameStatusPairs = {},
templateNames = Object.keys(this.$twyrModule.$templates || {});
for(const templateName of templateNames) {
let lifecycleStatus = null;
try {
const moduleInstance = this.$twyrModule.$templates[templateName];
const dependencies = this._getDependencies(moduleInstance);
lifecycleStatus = await moduleInstance.start(dependencies);
}
catch(err) {
lifecycleStatus = new TwyrBaseError(`${this.$twyrModule.name}::loader::_doLifecycleAction (${templateName} / start) error`, err);
}
nameStatusPairs[templateName] = lifecycleStatus;
}
return {
'type': 'templates',
'status': nameStatusPairs
};
}
catch(err) {
return {
'type': 'templates',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_startTemplates error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _stopTemplates
*
* @returns {Object} Object containing the stop status of each of the $twyrModule templates.
*
* @summary Stop Templates defined as part of this {@link TwyrBaseModule}.
*/
async _stopTemplates() {
try {
const nameStatusPairs = await this._doLifecycleAction('templates', 'stop');
return { 'type': 'templates', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'templates',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_stopTemplates error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _uninitializeTemplates
*
* @returns {Object} Object containing the uninit status of each of the $twyrModule templates.
*
* @summary Uninitialize Templates defined as part of this {@link TwyrBaseModule}.
*/
async _uninitializeTemplates() {
try {
const nameStatusPairs = await this._doLifecycleAction('templates', 'uninitialize');
return { 'type': 'templates', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'templates',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_uninitializeTemplates error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _unloadTemplates
*
* @returns {Object} Object containing the unload status of each of the $twyrModule templates.
*
* @summary Unload Templates defined as part of this {@link TwyrBaseModule}.
*/
async _unloadTemplates() {
try {
const nameStatusPairs = await this._doLifecycleAction('templates', 'unload');
return { 'type': 'templates', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'templates',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_unloadTemplates error`, err)
};
}
}
// #endregion
// #region Features Lifecycle hooks
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _loadFeatures
*
* @param {ConfigurationService} configSrvc - Instance of the {@link ConfigurationService} that supplies configuration.
*
* @returns {Object} Object containing the load status of each of the $twyrModule features.
*
* @summary Load features defined as part of this {@link TwyrBaseModule}.
*/
async _loadFeatures(configSrvc) {
const path = require('path');
try {
if(!this.$twyrModule.$features) this.$twyrModule.$features = {};
const definedFeatures = await this._findFiles(path.join(this.$twyrModule.basePath, 'features'), 'feature.js');
for(const definedFeature of definedFeatures) {
// Check validity of the definition...
const Feature = require(definedFeature).feature;
if(!Feature) continue;
// Construct the service
const featureInstance = new Feature(this.$twyrModule);
// Check to see valid typeof
// if(!(serviceInstance instanceof TwyrBaseService))
// throw new TwyrBaseError(`${definedService} does not contain a valid TwyrBaseService definition`);
this.$twyrModule.$features[featureInstance.name] = featureInstance;
}
const nameStatusPairs = await this._doLifecycleAction('features', 'load', [configSrvc]);
return { 'type': 'features', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'features',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_loadFeatures error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _initializeFeatures
*
* @returns {Object} Object containing the initialization status of each of the $twyrModule features.
*
* @summary Initialize Features defined as part of this {@link TwyrBaseModule}.
*/
async _initializeFeatures() {
try {
const nameStatusPairs = await this._doLifecycleAction('features', 'initialize');
return { 'type': 'features', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'features',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_initializeFeatures error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _startFeatures
*
* @returns {Object} Object containing the start status of each of the $twyrModule features.
*
* @summary Start Features defined as part of this {@link TwyrBaseModule}.
*/
async _startFeatures() {
try {
const featureNames = Object.keys(this.$twyrModule.$features || {}),
nameStatusPairs = {};
for(const featureName of featureNames) {
let lifecycleStatus = null;
try {
const moduleInstance = this.$twyrModule.$features[featureName];
const dependencies = this._getDependencies(moduleInstance);
lifecycleStatus = await moduleInstance.start(dependencies);
}
catch(err) {
lifecycleStatus = new TwyrBaseError(`${this.$twyrModule.name}::loader::_doLifecycleAction (${featureName} / start) error`, err);
}
nameStatusPairs[featureName] = lifecycleStatus;
}
return {
'type': 'features',
'status': nameStatusPairs
};
}
catch(err) {
return {
'type': 'features',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_startFeatures error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _stopFeatures
*
* @returns {Object} Object containing the stop status of each of the $twyrModule features.
*
* @summary Stop Features defined as part of this {@link TwyrBaseModule}.
*/
async _stopFeatures() {
try {
const nameStatusPairs = await this._doLifecycleAction('features', 'stop');
return { 'type': 'features', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'features',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_stopFeatures error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _uninitializeFeatures
*
* @returns {Object} Object containing the uninit status of each of the $twyrModule features.
*
* @summary Uninitialize Features defined as part of this {@link TwyrBaseModule}.
*/
async _uninitializeFeatures() {
try {
const nameStatusPairs = await this._doLifecycleAction('features', 'uninitialize');
return { 'type': 'features', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'features',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_uninitializeFeatures error`, err)
};
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _unloadFeatures
*
* @returns {Object} Object containing the unload status of each of the $twyrModule features.
*
* @summary Unload Features defined as part of this {@link TwyrBaseModule}.
*/
async _unloadFeatures() {
try {
const nameStatusPairs = await this._doLifecycleAction('features', 'unload');
return { 'type': 'features', 'status': nameStatusPairs };
}
catch(err) {
return {
'type': 'features',
'status': new TwyrBaseError(`${this.$twyrModule.name}::loader::_unloadFeatures error`, err)
};
}
}
// #endregion
// #region Utilities
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _findFiles
*
* @param {string} rootDir - Path of the folder to recursively search for.
* @param {string} filename - Name of the file to search for.
*
* @returns {Array} List of files in the folder matching the name passed in.
*
* @summary Finds files in folders, and recursively, sub-folders, as well.
*/
async _findFiles(rootDir, filename) {
try {
const fs = require('fs-extra'),
path = require('path'),
promises = require('bluebird');
const filesystem = promises.promisifyAll(fs);
let fileList = [];
const exists = await this._exists(path.join(rootDir, filename));
if(exists) {
fileList.push(path.join(rootDir, filename));
return fileList;
}
const rootDirObjects = await filesystem.readdirAsync(rootDir);
if(!rootDirObjects) return null;
const rootDirFolders = [];
for(const rootDirObject of rootDirObjects) {
const rootDirFolder = path.join(rootDir, rootDirObject);
const stat = await filesystem.statAsync(rootDirFolder);
if(!stat.isDirectory()) continue;
rootDirFolders.push(rootDirFolder);
}
for(const rootDirFolder of rootDirFolders) {
const subFolderFiles = await this._findFiles(rootDirFolder, filename);
if(subFolderFiles) fileList = fileList.concat(subFolderFiles);
}
return fileList;
}
catch(err) {
return [];
}
}
/**
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _getDependencies
*
* @param {TwyrBaseModule} moduleInstance - The module to find dependencies for.
*
* @returns {Object} The dependencies.
*
* @summary Walks up the $twyrModule.$parent chain, and at each level searches for a $service.
*/
_getDependencies(moduleInstance) {
try {
const moduleDependencies = {};
let requiredDependencies = moduleInstance.dependencies.slice(0);
if((requiredDependencies.length === 1) && (requiredDependencies[0] === '*')) {
requiredDependencies.length = 0;
let currentModule = this.$twyrModule;
while(!!currentModule) {
requiredDependencies = requiredDependencies.concat(...currentModule.dependencies, ...Object.keys(currentModule.$services || {}));
currentModule = currentModule.$parent;
}
const uniqueRequiredDependencies = new Set(requiredDependencies);
requiredDependencies = [...uniqueRequiredDependencies];
}
const uniqueDependencies = [];
requiredDependencies.forEach((thisDependency) => {
if(thisDependency === '*')
return;
if(uniqueDependencies.indexOf(thisDependency) >= 0)
return;
uniqueDependencies.push(thisDependency);
let currentDependency = null,
currentModule = this.$twyrModule;
while(!!currentModule && !currentDependency) {
currentDependency = currentModule.$services ? currentModule.$services[thisDependency] : null;
if(!currentDependency) currentModule = currentModule.$parent;
}
if(!currentDependency) throw new Error(`${moduleInstance.name}::dependency::${thisDependency} not found!`);
const interfaceMethod = function() {
if(!this.$enabled) return null;
return this.Interface ? this.Interface : this;
}.bind(currentDependency);
Object.defineProperty(moduleDependencies, thisDependency, {
'__proto__': null,
'configurable': true,
'enumerable': true,
'get': interfaceMethod
});
currentDependency.$dependants.push(moduleInstance);
});
return moduleDependencies;
}
catch(err) {
throw new TwyrBaseError(`${this.$twyrModule.name}::loader::_getDependencies error`, err);
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _doLifecycleAction
*
* @param {string} moduleType - The type of the module to iterate over (services, components, etc.).
* @param {string} action - The lifecycle action to execute (initialize, start, etc.).
* @param {Array} args - The arguments to pass to the lifecycle method.
*
* @returns {Object} Hash map of the lifecycle status for each of the modules found.
*
* @summary Executes a lifecycle action on the given hashmap.
*/
async _doLifecycleAction(moduleType, action, args) {
try {
args = args || [];
const modules = this.$twyrModule[`$${moduleType}`];
const moduleNames = Object.keys(modules || {});
const nameStatusPairs = {};
for(const moduleName of moduleNames) {
let lifecycleStatus = null;
try {
const moduleInstance = modules[moduleName];
lifecycleStatus = await moduleInstance[action](...args);
}
catch(err) {
lifecycleStatus = new TwyrBaseError(`${this.$twyrModule.name}::loader::_doLifecycleAction (${moduleName} / ${action}) error`, err);
}
nameStatusPairs[moduleName] = lifecycleStatus;
}
return nameStatusPairs;
}
catch(err) {
throw new TwyrBaseError(`${this.$twyrModule.name}::loader::_doLifecycleAction (${moduleType} / ${action}) error`, err);
}
}
/**
* @async
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _exists
*
* @param {string} filepath - Path of the filesystem entity.
* @param {number} mode - Permission to be checked for.
*
* @returns {boolean} True / False.
*
* @summary Checks to see if the path can be accessed by this process using the mode specified.
*/
async _exists(filepath, mode) {
const Promise = require('bluebird'),
filesystem = require('fs-extra');
return new Promise((resolve, reject) => {
try {
filesystem.access(filepath, (mode || filesystem.constants.F_OK), (exists) => {
resolve(!exists);
});
}
catch(err) {
const error = new TwyrBaseError(`${this.$twyrModule.name}::loader::_findFiles error`, err);
reject(error);
}
});
}
/**
* @function
* @instance
* @memberof TwyrModuleLoader
* @name _filterStatus
*
* @param {Array} status - The statuses to process and filter.
*
* @returns {Array} The stringified statuses.
*
* @summary Converts statuses to strings, depending on their type.
*/
_filterStatus(status) {
try {
const filteredStatus = status.map((thisStatus) => {
if(thisStatus.status === null)
return true;
if(thisStatus.status instanceof Error)
return thisStatus;
// eslint-disable-next-line curly
if(typeof thisStatus.status === 'object') {
Object.keys(thisStatus.status).forEach((key) => {
if(thisStatus.status[key] instanceof Error) {
thisStatus.status[key] = (thisStatus.status[key] instanceof TwyrBaseError) ? thisStatus.status[key].toString() : thisStatus.status[key]['stack'];
return;
}
if(Array.isArray(thisStatus.status[key])) {
thisStatus.status[key] = this._filterStatus(thisStatus.status[key]);
if(!thisStatus.status[key].length) thisStatus.status[key] = true;
}
});
}
return thisStatus;
})
.filter((thisStatus) => {
if(!thisStatus) return false;
if(!thisStatus.status) return false;
if(!Object.keys(thisStatus.status).length)
return false;
return true;
});
return filteredStatus.length ? filteredStatus : true;
}
catch(err) {
return [];
}
}
// #endregion
// #region Properties
/**
* @override
*/
get basePath() {
return __dirname;
}
// #endregion
}
exports.TwyrModuleLoader = TwyrModuleLoader;