@inversifyjs/core
Version:
InversifyJs core package
99 lines • 3.78 kB
JavaScript
import { stringifyServiceIdentifier, } from '@inversifyjs/common';
import { bindingTypeValues } from '../../binding/models/BindingType.js';
import { isStackOverflowError } from '../../error/calculations/isStackOverflowError.js';
import { InversifyCoreError } from '../../error/models/InversifyCoreError.js';
import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind.js';
import { isPlanServiceRedirectionBindingNode } from './isPlanServiceRedirectionBindingNode.js';
const INDEX_NOT_FOUND = -1;
export function handleResolveError(params, error) {
if (isStackOverflowError(error) ||
InversifyCoreError.isErrorOfKind(error, InversifyCoreErrorKind.planningMaxDepthExceeded)) {
const stringifiedCircularDependencies = stringifyServiceIdentifierTrace(extractLikelyCircularDependency(params));
throw new InversifyCoreError(InversifyCoreErrorKind.planning, `Circular dependency found: ${stringifiedCircularDependencies}`, { cause: error });
}
throw error;
}
function extractLikelyCircularDependency(params) {
const root = params.planResult.tree.root;
const stack = [];
function depthFirstSearch(node) {
const existingIndex = stack.indexOf(node);
if (existingIndex !== INDEX_NOT_FOUND) {
const cycleNodes = [
...stack.slice(existingIndex),
node,
];
return cycleNodes.map((n) => n.serviceIdentifier);
}
stack.push(node);
try {
for (const child of getChildServiceNodes(node)) {
const result = depthFirstSearch(child);
if (result !== undefined) {
return result;
}
}
}
finally {
stack.pop();
}
return undefined;
}
const result = depthFirstSearch(root);
return result ?? [];
}
function getChildServiceNodes(serviceNode) {
const children = [];
const bindings = serviceNode.bindings;
if (bindings === undefined) {
return children;
}
const processBindingNode = (bindingNode) => {
if (isPlanServiceRedirectionBindingNode(bindingNode)) {
for (const redirection of bindingNode.redirections) {
processBindingNode(redirection);
}
return;
}
switch (bindingNode.binding.type) {
case bindingTypeValues.Instance: {
const instanceNode = bindingNode;
for (const ctorParam of instanceNode.constructorParams) {
if (ctorParam !== undefined) {
children.push(ctorParam);
}
}
for (const propParam of instanceNode.propertyParams.values()) {
children.push(propParam);
}
break;
}
case bindingTypeValues.ResolvedValue: {
const resolvedValueNode = bindingNode;
for (const param of resolvedValueNode.params) {
children.push(param);
}
break;
}
default:
break;
}
};
if (Array.isArray(bindings)) {
for (const bindingNode of bindings) {
processBindingNode(bindingNode);
}
}
else {
processBindingNode(bindings);
}
return children;
}
function stringifyServiceIdentifierTrace(serviceIdentifiers) {
const serviceIdentifiersArray = [...serviceIdentifiers];
if (serviceIdentifiersArray.length === 0) {
return '(No dependency trace)';
}
return serviceIdentifiersArray.map(stringifyServiceIdentifier).join(' -> ');
}
//# sourceMappingURL=handleResolveError.js.map