NodeTransformersRunner.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { inject, injectable } from 'inversify';
  2. import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
  3. import * as estraverse from 'estraverse';
  4. import * as ESTree from 'estree';
  5. import { TNodeTransformerFactory } from '../types/container/node-transformers/TNodeTransformerFactory';
  6. import { TDictionary } from '../types/TDictionary';
  7. import { TVisitorDirection } from '../types/node-transformers/TVisitorDirection';
  8. import { TVisitorFunction } from '../types/node-transformers/TVisitorFunction';
  9. import { TVisitorResult } from '../types/node-transformers/TVisitorResult';
  10. import { INodeTransformer } from '../interfaces/node-transformers/INodeTransformer';
  11. import { INodeTransformersRunner } from '../interfaces/node-transformers/INodeTransformersRunner';
  12. import { ITransformerNamesGroupsBuilder } from '../interfaces/utils/ITransformerNamesGroupsBuilder';
  13. import { IVisitor } from '../interfaces/node-transformers/IVisitor';
  14. import { NodeTransformer } from '../enums/node-transformers/NodeTransformer';
  15. import { NodeTransformationStage } from '../enums/node-transformers/NodeTransformationStage';
  16. import { VisitorDirection } from '../enums/node-transformers/VisitorDirection';
  17. import { NodeGuards } from '../node/NodeGuards';
  18. import { NodeMetadata } from '../node/NodeMetadata';
  19. @injectable()
  20. export class NodeTransformersRunner implements INodeTransformersRunner {
  21. /**
  22. * @type {TNodeTransformerFactory}
  23. */
  24. private readonly nodeTransformerFactory: TNodeTransformerFactory;
  25. /**
  26. * @type {ITransformerNamesGroupsBuilder}
  27. */
  28. private readonly nodeTransformerNamesGroupsBuilder: ITransformerNamesGroupsBuilder<
  29. NodeTransformer,
  30. INodeTransformer
  31. >;
  32. /**
  33. * @param {TNodeTransformerFactory} nodeTransformerFactory
  34. * @param {ITransformerNamesGroupsBuilder} nodeTransformerNamesGroupsBuilder
  35. */
  36. public constructor (
  37. @inject(ServiceIdentifiers.Factory__INodeTransformer)
  38. nodeTransformerFactory: TNodeTransformerFactory,
  39. @inject(ServiceIdentifiers.INodeTransformerNamesGroupsBuilder)
  40. nodeTransformerNamesGroupsBuilder: ITransformerNamesGroupsBuilder<
  41. NodeTransformer,
  42. INodeTransformer
  43. >,
  44. ) {
  45. this.nodeTransformerFactory = nodeTransformerFactory;
  46. this.nodeTransformerNamesGroupsBuilder = nodeTransformerNamesGroupsBuilder;
  47. }
  48. /**
  49. * @param {T} astTree
  50. * @param {NodeTransformer[]} nodeTransformerNames
  51. * @param {NodeTransformationStage} nodeTransformationStage
  52. * @returns {T}
  53. */
  54. public transform <T extends ESTree.Node = ESTree.Program> (
  55. astTree: T,
  56. nodeTransformerNames: NodeTransformer[],
  57. nodeTransformationStage: NodeTransformationStage
  58. ): T {
  59. if (!nodeTransformerNames.length) {
  60. return astTree;
  61. }
  62. const normalizedNodeTransformers: TDictionary<INodeTransformer> =
  63. this.buildNormalizedNodeTransformers(nodeTransformerNames, nodeTransformationStage);
  64. const nodeTransformerNamesGroups: NodeTransformer[][] =
  65. this.nodeTransformerNamesGroupsBuilder.build(normalizedNodeTransformers);
  66. for (const nodeTransformerNamesGroup of nodeTransformerNamesGroups) {
  67. const enterVisitors: IVisitor[] = [];
  68. const leaveVisitors: IVisitor[] = [];
  69. for (const nodeTransformerName of nodeTransformerNamesGroup) {
  70. const nodeTransformer: INodeTransformer = normalizedNodeTransformers[nodeTransformerName];
  71. const visitor: IVisitor | null = nodeTransformer.getVisitor(nodeTransformationStage);
  72. if (!visitor) {
  73. continue;
  74. }
  75. if (visitor.enter) {
  76. enterVisitors.push({ enter: visitor.enter });
  77. }
  78. if (visitor.leave) {
  79. leaveVisitors.push({ leave: visitor.leave });
  80. }
  81. }
  82. if (!enterVisitors.length && !leaveVisitors.length) {
  83. continue;
  84. }
  85. estraverse.replace(astTree, {
  86. enter: this.mergeVisitorsForDirection(enterVisitors, VisitorDirection.Enter),
  87. leave: this.mergeVisitorsForDirection(leaveVisitors, VisitorDirection.Leave)
  88. });
  89. }
  90. return astTree;
  91. }
  92. /**
  93. * @param {NodeTransformer[]} nodeTransformerNames
  94. * @param {NodeTransformationStage} nodeTransformationStage
  95. * @returns {TDictionary<INodeTransformer>}
  96. */
  97. private buildNormalizedNodeTransformers (
  98. nodeTransformerNames: NodeTransformer[],
  99. nodeTransformationStage: NodeTransformationStage
  100. ): TDictionary<INodeTransformer> {
  101. return nodeTransformerNames
  102. .reduce<TDictionary<INodeTransformer>>(
  103. (acc: TDictionary<INodeTransformer>, nodeTransformerName: NodeTransformer) => {
  104. const nodeTransformer: INodeTransformer = this.nodeTransformerFactory(nodeTransformerName);
  105. if (!nodeTransformer.getVisitor(nodeTransformationStage)) {
  106. return acc;
  107. }
  108. return {
  109. ...acc,
  110. [nodeTransformerName]: nodeTransformer
  111. };
  112. },
  113. {}
  114. );
  115. }
  116. /**
  117. * @param {IVisitor[]} visitors
  118. * @param {TVisitorDirection} direction
  119. * @returns {TVisitorFunction}
  120. */
  121. private mergeVisitorsForDirection (visitors: IVisitor[], direction: TVisitorDirection): TVisitorFunction {
  122. const visitorsLength: number = visitors.length;
  123. if (!visitorsLength) {
  124. return (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node => node;
  125. }
  126. return (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | estraverse.VisitorOption => {
  127. if (NodeMetadata.isIgnoredNode(node)) {
  128. return estraverse.VisitorOption.Skip;
  129. }
  130. for (let i: number = 0; i < visitorsLength; i++) {
  131. const visitorFunction: TVisitorFunction | undefined = visitors[i][direction];
  132. if (!visitorFunction) {
  133. continue;
  134. }
  135. const visitorResult: TVisitorResult = visitorFunction(node, parentNode);
  136. if (!visitorResult || !NodeGuards.isNode(visitorResult)) {
  137. continue;
  138. }
  139. node = visitorResult;
  140. }
  141. return node;
  142. };
  143. }
  144. }