import { inject, injectable } from 'inversify'; import { ServiceIdentifiers } from '../container/ServiceIdentifiers'; import { TDictionary } from '../types/TDictionary'; import { TTransformersRelationEdge } from '../types/utils/TTransformersRelationEdge'; import { ILevelledTopologicalSorter } from '../interfaces/utils/ILevelledTopologicalSorter'; import { ITransformer } from '../interfaces/ITransformer'; import { ITransformerNamesGroupsBuilder } from '../interfaces/utils/ITransformerNamesGroupsBuilder'; @injectable() export abstract class AbstractTransformerNamesGroupsBuilder < TTransformerName extends string, TTransformer extends ITransformer > implements ITransformerNamesGroupsBuilder < TTransformerName, TTransformer > { /** * @type {ILevelledTopologicalSorter} */ private readonly levelledTopologicalSorter: ILevelledTopologicalSorter; public constructor ( @inject(ServiceIdentifiers.ILevelledTopologicalSorter) levelledTopologicalSorter: ILevelledTopologicalSorter ) { this.levelledTopologicalSorter = levelledTopologicalSorter; } /** * Builds sorted transformer names by topological sort with levels * * For example, if SplitString transformer has following dependencies inside `runAfter` property: * - NodeTransformer.ObjectExpressionKeysTransformer, * - NodeTransformer.TemplateLiteralTransformer * * Than result node transformer names groups will be like: * [ * [ * SomeTransformerA, * ObjectExpressionKeysTransformer, * TemplateLiteralTransformer, * SomeTransformerB * ], * [ * SplitStringTransformer * ] * ] * * @param {TDictionary} normalizedTransformers * @returns {TTransformerName[][]} */ public build (normalizedTransformers: TDictionary): TTransformerName[][] { const transformerNames: TTransformerName[] = Object.keys(normalizedTransformers); const relationEdges: TTransformersRelationEdge[] = this.buildTransformersRelationEdges( transformerNames, normalizedTransformers ); for (const [precedent, consequent] of relationEdges) { this.levelledTopologicalSorter.add(precedent, consequent); } return this.levelledTopologicalSorter.sortByGroups(); } /** * @param {TTransformerName[]} transformerNames * @param {TDictionary} normalizedTransformers * @returns {TTransformersRelationEdge[]} */ private buildTransformersRelationEdges ( transformerNames: TTransformerName[], normalizedTransformers: TDictionary ): TTransformersRelationEdge[] { const relationEdges: TTransformersRelationEdge[] = []; for (const transformerName of transformerNames) { const transformer: TTransformer = normalizedTransformers[transformerName]; const runAfterRelations: TTransformerName[] | undefined = transformer.runAfter; const areRunAfterRelationsEmpty = !runAfterRelations || !runAfterRelations.length; if (areRunAfterRelationsEmpty) { relationEdges.push([transformerName, null]); continue; } for (const runAfterRelation of runAfterRelations) { const isUnknownRelation: boolean = normalizedTransformers[runAfterRelation] === undefined; if (isUnknownRelation) { relationEdges.push([transformerName, null]); continue; } relationEdges.push([runAfterRelation, transformerName]); } } return relationEdges; } }