dino-core
Version:
A dependency injection framework for NodeJS applications
130 lines • 6.33 kB
JavaScript
;
// Copyright 2021 Quirino Brizi [quirino.brizi@gmail.com]
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApplicationContextHelper = void 0;
const Logger_1 = require("../Logger");
class ApplicationContextHelper {
constructor() {
this.expandedDependencies = new Map();
}
/**
* Build a dependency tree for the specified set of component descriptors based on the dependencies defined.
*
* @param {Array<ComponentDescriptor>} componentDescriptors the array of component descriptors
*
* @public
*/
buildDependencyTree(componentDescriptors, computationRequired = true) {
const dependencyTree = new Map();
this.expandedDependencies = new Map();
if (!computationRequired) {
Logger_1.Logger.info('dependency tree computation has been disabled, leaving descriptors untouched');
dependencyTree.set(Symbol.for('0'), componentDescriptors);
return dependencyTree;
}
Logger_1.Logger.info('dependency tree computation started');
const start = Date.now();
const waitingList = [];
for (let index = 0; index < componentDescriptors.length; index++) {
const componentDescriptor = componentDescriptors[index];
this.doBuildDependencyTree(componentDescriptor, dependencyTree, waitingList);
}
const circularDependencies = [];
while (waitingList.length > 0) {
const componentDescriptor = waitingList.pop();
if (componentDescriptor !== undefined) {
this.doBuildDependencyTree(componentDescriptor, dependencyTree, circularDependencies, false);
}
}
if (circularDependencies.length > 0) {
circularDependencies.forEach((cd) => {
this.addAtLevel(dependencyTree, Symbol.for('1000'), cd);
});
}
Logger_1.Logger.debug(`dependency tree computation completed in ${Date.now() - start} ms`);
return dependencyTree;
}
/**
* Effectively build the dependency tree
* @param {ComponentDescriptor} componentDescriptor the component descriptor to analyse
* @param {Map<Symbol, ComponentDescriptor>} dependencyTree the dependency tree
* @param {Array<ComponentDescriptor>} waitingList a waiting list for components that have not been placed
*
* @private
*/
doBuildDependencyTree(componentDescriptor, dependencyTree, waitingList, firstPass = true) {
const level = this.computeLevel(dependencyTree, waitingList, componentDescriptor, firstPass);
if (level !== undefined) {
this.addAtLevel(dependencyTree, level, componentDescriptor);
}
}
/**
* Compute the level this component descriptor should be placed
*
* @param {Map<Symbol, ComponentDescriptor>} dependencyTree the dependency tree map
* @param {Array<ComponentDescriptor>} waitingList an array containing components that cannot still be placed
* @param {ComponentDescriptor} componentDescriptor the component descriptor to compute the level for
*
* @private
*/
computeLevel(dependencyTree, waitingList, componentDescriptor, firstPass = true) {
let _level = 0;
const dependencies = componentDescriptor.getDependencies();
const numOfDependencies = dependencies.length;
Logger_1.Logger.debug(`Computing component ${componentDescriptor.getName()} with ${numOfDependencies} dependencies`);
for (let index = 0; index < numOfDependencies; index++) {
const dependency = dependencies[index];
for (let level = dependencyTree.size - 1; level >= 0; level--) {
Logger_1.Logger.debug(`Computing level ${level} of ${dependencyTree.size} for component ${componentDescriptor.getName()} with ${numOfDependencies} dependencies`);
const values = this.expandedDependencies.get(Symbol.for(`${level}`));
const dependencyIsPresentAtLevel = values.includes(dependency);
Logger_1.Logger.debug(`>>>> checking ${dependency} in ${values} present? ${dependencyIsPresentAtLevel}`);
if (dependencyIsPresentAtLevel) {
_level = level + 1;
break;
}
}
}
if (this.isForWaitingList(_level, componentDescriptor, firstPass)) {
waitingList.push(componentDescriptor);
return undefined;
}
Logger_1.Logger.debug(`Component ${componentDescriptor.getName()} with ${dependencies.length} dependencies has been placed at level ${_level}`);
return Symbol.for(`${_level}`);
}
/**
* Add the requested component descriptor to the requested level
*
* @param {Map} dependencyTree the dependency tree map
* @param {Symbol} level the level that the component should be added to
* @param {ComponentDescriptor} componentDescriptor the component descriptor to add
*
* @private
*/
addAtLevel(dependencyTree, level, componentDescriptor) {
if (!dependencyTree.has(level)) {
dependencyTree.set(level, []);
this.expandedDependencies.set(level, []);
}
const currentLevel = dependencyTree.get(level);
currentLevel?.push(componentDescriptor);
this.expandedDependencies.get(level).push(componentDescriptor.getName());
}
isForWaitingList(level, componentDescriptor, firstPass) {
return level === 0 && !componentDescriptor.hasNotDependenciesOrDependsOnlyOnBase() && firstPass;
}
}
exports.ApplicationContextHelper = ApplicationContextHelper;
//# sourceMappingURL=ApplicationContextHelper.js.map