@egodigital/nef
Version:
Managed Extensibility Framework like library written for Node.js
369 lines • 12.4 kB
JavaScript
;
/**
* 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