@hapiness/core
Version:
Project to have a HapiJS (https://hapijs.com/) based framework to create easier NodeJS back-end with some awesome features
244 lines • 8.35 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const rxjs_1 = require("rxjs");
const logger_1 = require("./logger");
const metadata_1 = require("./metadata");
const decorators_1 = require("./decorators");
const di_1 = require("./di");
const enums_1 = require("./enums");
class ModuleManager {
/**
* Resolve into a tree of CoreModule
*
* @param {any} module
* @returns Observable
*/
static resolve(module) {
this.logger.debug(`Resolving module '${module.name}'`);
return this.resolution(module);
}
static instantiate(module, providers) {
this.logger.debug(`Instantiation of module '${module.name}'`);
return this.instantiation(module, providers);
}
/**
* Get all the tree modules
*
* @param {CoreModule} module
* @returns CoreModule
*/
static getModules(module) {
const lookup = (_module) => {
return []
.concat(_module)
.concat([]
.concat(_module.modules)
.filter(_ => !!_)
.map(m => lookup(m))
.reduce((a, c) => a.concat(c), []));
};
return lookup(module);
}
/**
* Helper to convert provider
* to a CoreProvide type
*
* @param {any} provider
* @returns CoreProvide
*/
static toCoreProvider(provider) {
return (!!provider.provide ?
provider :
{ provide: provider, useClass: provider });
}
/**
* ===========================================================================
*
* MODULE RESOLUTION
*
* ===========================================================================
*/
/**
* Process module to CoreModule type
* from metadata and the children
*
* @param {any} module
* @param {CoreModule} parent?
* @returns Observable
*/
static resolution(module, parent) {
return rxjs_1.Observable
.of(module)
.map(_ => this.toCoreModuleWithProviders(_))
.flatMap(cmwp => this
.extractMetadata(cmwp.module)
.map(_ => ({ metadata: _, moduleWithProviders: cmwp })))
.flatMap(mcmwp => this
.metadataToCoreModule(mcmwp.metadata, mcmwp.moduleWithProviders, parent)
.map(_ => this.coreModuleParentConfigProviders(_))
.map(_ => Object.assign({ module: _ }, mcmwp)))
.flatMap(data => rxjs_1.Observable
.from(data.metadata.imports || [])
.flatMap(_ => this.resolution(_, data.module))
.toArray()
.do(_ => this.logger.debug(`'${data.module.name}' got ${_.length} children`))
.map(_ => Object.assign({ modules: _ }, data.module)));
}
/**
* FIX for exported providers
* that need internal config
*
* @todo find a better solution
* @param {CoreModule} module
* @returns CoreModule
*/
static coreModuleParentConfigProviders(module) {
module.providers = []
.concat(module.providers)
.concat((module.parent && module.parent.providers) ?
module.parent.providers.filter(_ => (_.provide instanceof decorators_1.InjectionToken)) :
[])
.filter(_ => !!_);
return module;
}
/**
* Convert metadata to CoreModule type
*
* @param {HapinessModule} metadata
* @param {CoreModuleWithProviders} moduleWithProviders
* @param {CoreModule} parent?
* @returns Observable
*/
static metadataToCoreModule(metadata, moduleWithProviders, parent) {
return rxjs_1.Observable
.of({
parent,
token: moduleWithProviders.module,
name: moduleWithProviders.module.name,
version: metadata.version,
exports: metadata.exports || [],
declarations: metadata.declarations || [],
providers: (metadata.providers || [])
.concat(moduleWithProviders.providers)
.map(_ => this.toCoreProvider(_)),
level: !!parent ?
parent.level === enums_1.ModuleLevel.ROOT ?
enums_1.ModuleLevel.PRIMARY :
enums_1.ModuleLevel.SECONDARY :
enums_1.ModuleLevel.ROOT
})
.do(_ => this.logger.debug(`Build CoreModule for '${_.name}'`));
}
/**
* Get HapinessModule metadata type
* if does not exist, throw an error
*
* @param {Type<any>} module
* @returns Observable
*/
static extractMetadata(module) {
return rxjs_1.Observable
.of(metadata_1.extractMetadataByDecorator(module, this.decoratorName))
.flatMap(_ => !!_ ?
rxjs_1.Observable.of(_) :
rxjs_1.Observable.throw(new Error(`Module '${module ? module.name : null}' resolution failed: No metadata`)));
}
/**
* Make sure to convert module into
* a CoreModuleWithProviders type
*
* @param {CoreModuleWithProviders|Type<any>} module
* @returns CoreModuleWithProviders
*/
static toCoreModuleWithProviders(module) {
return ((module && module['module']) ?
module :
{
module,
providers: []
});
}
/**
* ===========================================================================
*
* MODULE INSTANTIATION
*
* ===========================================================================
*/
/**
* Create the module's DI
* and instantiate the module
*
* @param {CoreModule} module
* @param {CoreProvide[]} providers?
* @param {CoreModule} parent?
* @returns Observable
*/
static instantiation(module, providers, parent) {
return rxjs_1.Observable
.of(module)
.flatMap(_ => rxjs_1.Observable
.from(_.modules)
.flatMap(child => this.instantiation(child, providers, parent))
.toArray()
.map(children => Object.assign({}, _, { modules: children })))
.flatMap(_ => di_1.DependencyInjection
.createAndResolve(this.collectProviders(_, providers))
.map(di => Object.assign({ di }, _)))
.flatMap(_ => di_1.DependencyInjection
.instantiateComponent(_.token, _.di)
.map(instance => Object.assign({ instance }, _)))
.flatMap(_ => this.instantiateLibs(_));
}
/**
* Instantiate and return array of libs
*
* @param {CoreModule} module
* @returns Type
*/
static instantiateLibs(module) {
return rxjs_1.Observable
.from(module.declarations)
.filter(_ => !!_ && !!metadata_1.extractMetadataByDecorator(_, 'Lib'))
.flatMap(_ => di_1.DependencyInjection.instantiateComponent(_, module.di))
.toArray()
.map(_ => module);
}
/**
* Collect all providers to
* inject into the DI
*
* @param {HapinessModule} module
*/
static collectProviders(module, providers) {
this.logger.debug(`Collect providers for '${module.name}'`);
return []
.concat(module.providers)
.concat(providers)
.filter(_ => !!_)
.concat(this.extractExportedProviders(module));
}
/**
* Extract exported children providers
*
* @param {CoreModule} module
* @returns CoreProvide[]
*/
static extractExportedProviders(module) {
this.logger.debug(`Extract exported children providers for '${module.name}'`);
return []
.concat(module.modules)
.filter(_ => (!!_.exports && _.exports.length > 0))
.map(_ => []
.concat(_.exports)
.concat(_.providers
.filter(__ => (__.provide instanceof decorators_1.InjectionToken))))
.reduce((a, c) => a.concat(c), [])
.filter(_ => !!_)
.map(_ => this.toCoreProvider(_));
}
}
ModuleManager.decoratorName = 'HapinessModule';
ModuleManager.logger = new logger_1.InternalLogger('module');
exports.ModuleManager = ModuleManager;
//# sourceMappingURL=module.js.map