UNPKG

dino-core

Version:

A dependency injection framework for NodeJS applications

130 lines 6.33 kB
"use strict"; // 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