UNPKG

@egodigital/nef

Version:

Managed Extensibility Framework like library written for Node.js

369 lines 12.4 kB
"use strict"; /** * This file is part of the @egodigital/nef distribution. * Copyright (c) e.GO Digital GmbH, Aachen, Germany (https://www.e-go-digital.com/) * * @egodigital/nef is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, version 3. * * @egodigital/nef is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CompositionContainer = exports.ImportMany = exports.Import = exports.Export = void 0; const _ = require("lodash"); const ApplicationCatalog_1 = require("./catalogs/ApplicationCatalog"); const ClassCatalog_1 = require("./catalogs/ClassCatalog"); const DirectoryCatalog_1 = require("./catalogs/DirectoryCatalog"); const FileCatalog_1 = require("./catalogs/FileCatalog"); const ModuleCatalog_1 = require("./catalogs/ModuleCatalog"); const util_1 = require("./util"); const EXPORTS = Symbol('EXPORTS'); // key for string @Export() definitions const IMPORTS = Symbol('IMPORTS'); // key for string @Import() definitions const IMPORT_MANYS = Symbol('IMPORT_MANYS'); // key for string @ImportMany() definitions const VALUE_NOT_CREATED = Symbol('VALUE_NOT_CREATED'); function Export(...args) { return (target) => { let keyToUse; if (args.length < 1) { keyToUse = target; } else { keyToUse = args[0]; } if (_.isNil(target[EXPORTS])) { target[EXPORTS] = []; } const EXPORT_DEF = { key: keyToUse, }; target[EXPORTS].push(EXPORT_DEF); }; } exports.Export = Export; function Import(...args) { return function (target, key) { if (_.isNil(target.constructor.prototype[IMPORTS])) { target.constructor.prototype[IMPORTS] = []; } const IMPORT_DEF = { property: key, service: args[0], }; target.constructor.prototype[IMPORTS].push(IMPORT_DEF); }; } exports.Import = Import; function ImportMany(...args) { return function (target, key) { if (_.isNil(target.constructor.prototype[IMPORT_MANYS])) { target.constructor.prototype[IMPORT_MANYS] = []; } const IMPORT_DEF = { property: key, service: args[0], }; target.constructor.prototype[IMPORT_MANYS].push(IMPORT_DEF); }; } exports.ImportMany = ImportMany; /** * A container, that composes instances. */ class CompositionContainer { constructor() { this._CATALOGS = []; this._instances = VALUE_NOT_CREATED; } /** * Adds one or more processes. If you process is defined, the current one is taken. * * @param {NodeJS.Process[]} [processes] One or more processes to add. * * @return {this} */ addApplications(...processes) { if (!processes.length) { processes = [process]; } return this.addCatalogs(...processes.filter(p => !_.isNil(p)) .map(p => new ApplicationCatalog_1.ApplicationCatalog({ application: p, }))); } /** * Adds one or more catalogs. * * @param {ComposablePartCatalog[]} [catalogs] One or more catalogs. * * @return {this} */ addCatalogs(...catalogs) { catalogs.filter(c => !_.isNil(c)).forEach(c => { this._CATALOGS.push(c); }); return this; } /** * Adds one or more class (catalogs). * * @param {any[]} [classes] The classes to add. * * @return {this} */ addClasses(...classes) { return this.addCatalogs(...classes.filter(c => !_.isNil(c)) .map(c => new ClassCatalog_1.ClassCatalog(c))); } /** * Adds one or more directories. * * @param {string[]} [dirs] The directories (paths) to add. * * @return {this} */ addDirectories(...dirs) { return this.addCatalogs(...dirs.map(d => util_1.toStringSafe(d)) .filter(d => '' !== d.trim()) .map(d => new DirectoryCatalog_1.DirectoryCatalog(d))); } /** * Adds one or more files. * * @param {string[]} [files] The files (paths) to add. * * @return {this} */ addFiles(...files) { return this.addCatalogs(...files.map(f => util_1.toStringSafe(f)) .filter(f => '' !== f.trim()) .map(f => new FileCatalog_1.FileCatalog(f))); } /** * Adds one or more module (catalogs). * * @param {any[]} [mods] The modules to add. * * @return {this} */ addModules(...mods) { return this.addCatalogs(...mods.filter(m => !_.isNil(m)) .map(m => new ModuleCatalog_1.ModuleCatalog(m))); } /** * Composes all @Export() instances. * * @param {any[]} [objs] One or more object, where to write the instances to. */ async compose(...objs) { // keep sure we already have an // initialized value in 'this._instances' await this.getGlobalExportInstances(); this.handleDecoratorsOfObjectList(objs); } /** * Composes all @Export() instances. * * @param {any[]} [objs] One or more object, where to write the instances to. * * @return {this} */ composeSync(...objs) { // keep sure we already have an // initialized value in 'this._instances' this.getGlobalExportInstancesSync(); this.handleDecoratorsOfObjectList(objs); return this; } /** @inheritdoc */ dispose() { // dispose all instances const INSTANCES = this._instances; if (Array.isArray(INSTANCES)) { while (INSTANCES.length) { const I = INSTANCES.pop(); if (!_.isNil(I.instance)) { if (_.isFunction(I.instance.dispose)) { I.instance.dispose(); } } } } this._instances = null; } fillInstances(instances, classes) { for (const CLS of classes) { const EXPORT_DEFS = util_1.asArray(CLS[EXPORTS]); EXPORT_DEFS.forEach(e => { const NEW_INSTANCE = new (Function.prototype.bind.apply(CLS, [CLS].concat( // constructor params ))); instances.push({ instance: NEW_INSTANCE, service: e.key, }); }); } } fillMatchingExports(importDef, matchingExports) { this._instances.filter(ei => { return ei.service === importDef.service; }).forEach(ei => { matchingExports.push(ei); }); } /** * Returns all services by key. * * @param {ServiceKey} service The service key. * * @return {Promise<TService[]>} The promise with the services. */ async getAllServices(service) { await this.getGlobalExportInstances(); return this.getAllServicesInner(service); } /** * Returns all services by key. * * @param {ServiceKey} service The service key. * * @return {TService[]} The services. */ getAllServicesSync(service) { this.getGlobalExportInstancesSync(); return this.getAllServicesInner(service); } getAllServicesInner(service) { const MATCHING_EXPORTS = []; this.fillMatchingExports({ property: null, service: service, }, MATCHING_EXPORTS); return MATCHING_EXPORTS.map(me => me.instance); } async getGlobalExportInstances() { let instances; if (_.isSymbol(this._instances)) { // load classes from catalogs const LOADED_CLASSES = []; for (const CAT of this._CATALOGS) { const CLASSES = util_1.asArray(await CAT.getClasses()); CLASSES.forEach(cls => { if (_.isFunction(cls.constructor)) { LOADED_CLASSES.push(cls); } }); } instances = []; this.fillInstances(instances, LOADED_CLASSES); this._instances = instances; this.handleDecoratorsOfObjectList(instances.map(ei => ei.instance)); } else { instances = this._instances; } return instances; } getGlobalExportInstancesSync() { let instances; if (_.isSymbol(this._instances)) { // load classes from catalogs const LOADED_CLASSES = []; for (const CAT of this._CATALOGS) { const CLASSES = util_1.asArray(CAT.getClassesSync()); CLASSES.forEach(cls => { if (_.isFunction(cls.constructor)) { LOADED_CLASSES.push(cls); } }); } instances = []; this.fillInstances(instances, LOADED_CLASSES); this._instances = instances; this.handleDecoratorsOfObjectList(instances.map(ei => ei.instance)); } else { instances = this._instances; } return instances; } /** * Returns a single service by key. * * @param {ServiceKey} service The service key. * * @return {Promise<TService>} The promise with the single service. */ async getService(service) { await this.getGlobalExportInstances(); return this.getServiceInner(service); } /** * Returns a single service by key. * * @param {ServiceKey} service The service key. * * @return {TService} The single service. */ getServiceSync(service) { this.getGlobalExportInstancesSync(); return this.getServiceInner(service); } getServiceInner(service) { const MATCHING_EXPORTS = []; this.fillMatchingExports({ property: null, service: service, }, MATCHING_EXPORTS); if (MATCHING_EXPORTS.length < 1) { throw new Error(`No exported instance found for '${util_1.toStringSafe(service)}'`); } if (MATCHING_EXPORTS.length > 1) { throw new Error(`More than one exported instance found for '${util_1.toStringSafe(service)}'`); } return MATCHING_EXPORTS[0].instance; } handleDecoratorsOfObjectList(objects) { objects.forEach(o => { this.handleImports(o); this.handleImportManys(o); }); } handleImportManys(obj) { const IMPORT_DEFS = util_1.asArray(obj[IMPORT_MANYS]); IMPORT_DEFS.forEach(id => { obj[id.property] = this.getAllServicesInner(id.service); }); } handleImports(obj) { const IMPORT_DEFS = util_1.asArray(obj[IMPORTS]); IMPORT_DEFS.forEach(id => { obj[id.property] = this.getServiceInner(id.service); }); } } exports.CompositionContainer = CompositionContainer; __exportStar(require("./catalogs/ApplicationCatalog"), exports); __exportStar(require("./catalogs/ClassCatalog"), exports); __exportStar(require("./catalogs/DirectoryCatalog"), exports); __exportStar(require("./catalogs/FileCatalog"), exports); __exportStar(require("./catalogs/FilteredCatalog"), exports); __exportStar(require("./catalogs/ModuleCatalog"), exports); //# sourceMappingURL=index.js.map