NodeUtils.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import * as escodegen from 'escodegen';
  2. import * as estraverse from 'estraverse';
  3. import * as ESTree from 'estree';
  4. import { ecmaVersion } from '../constants/EcmaVersion';
  5. import { ASTParserFacade } from '../ASTParserFacade';
  6. import { NodeGuards } from './NodeGuards';
  7. import { NodeMetadata } from './NodeMetadata';
  8. export class NodeUtils {
  9. /**
  10. * @param {T} literalNode
  11. * @returns {T}
  12. */
  13. public static addXVerbatimPropertyTo (literalNode: ESTree.Literal): ESTree.Literal {
  14. literalNode['x-verbatim-property'] = {
  15. content: literalNode.raw,
  16. precedence: escodegen.Precedence.Primary
  17. };
  18. return literalNode;
  19. }
  20. /**
  21. * @param {T} astTree
  22. * @returns {T}
  23. */
  24. public static clone <T extends ESTree.Node = ESTree.Node> (astTree: T): T {
  25. return NodeUtils.parentizeAst(NodeUtils.cloneRecursive(astTree));
  26. }
  27. /**
  28. * @param {string} code
  29. * @returns {Statement[]}
  30. */
  31. public static convertCodeToStructure (code: string): ESTree.Statement[] {
  32. const structure: ESTree.Program = ASTParserFacade.parse(code, {
  33. ecmaVersion,
  34. sourceType: 'script'
  35. });
  36. estraverse.replace(structure, {
  37. enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node => {
  38. NodeUtils.parentizeNode(node, parentNode);
  39. if (NodeGuards.isLiteralNode(node)) {
  40. NodeUtils.addXVerbatimPropertyTo(node);
  41. }
  42. NodeMetadata.set(node, { ignoredNode: false });
  43. return node;
  44. }
  45. });
  46. return <ESTree.Statement[]>structure.body;
  47. }
  48. /**
  49. * @param {NodeGuards[]} structure
  50. * @returns {string}
  51. */
  52. public static convertStructureToCode (structure: ESTree.Node[]): string {
  53. return structure.reduce((code: string, node: ESTree.Node) => {
  54. return code + escodegen.generate(node, {
  55. sourceMapWithCode: true
  56. }).code;
  57. }, '');
  58. }
  59. /**
  60. * @param {UnaryExpression} unaryExpressionNode
  61. * @returns {NodeGuards}
  62. */
  63. public static getUnaryExpressionArgumentNode (unaryExpressionNode: ESTree.UnaryExpression): ESTree.Node {
  64. if (NodeGuards.isUnaryExpressionNode(unaryExpressionNode.argument)) {
  65. return NodeUtils.getUnaryExpressionArgumentNode(unaryExpressionNode.argument);
  66. }
  67. return unaryExpressionNode.argument;
  68. }
  69. /**
  70. * @param {T} astTree
  71. * @returns {T}
  72. */
  73. public static parentizeAst <T extends ESTree.Node = ESTree.Node> (astTree: T): T {
  74. estraverse.replace(astTree, {
  75. enter: NodeUtils.parentizeNode
  76. });
  77. return astTree;
  78. }
  79. /**
  80. * @param {T} node
  81. * @param {Node} parentNode
  82. * @returns {T}
  83. */
  84. public static parentizeNode <T extends ESTree.Node = ESTree.Node> (node: T, parentNode: ESTree.Node | null): T {
  85. node.parentNode = parentNode || node;
  86. return node;
  87. }
  88. /**
  89. * @param {T} node
  90. * @returns {T}
  91. */
  92. private static cloneRecursive <T> (node: T): T {
  93. if (node === null) {
  94. return node;
  95. }
  96. const copy: Partial<T> = {};
  97. const nodeKeys: (keyof T)[] = <(keyof T)[]>Object.keys(node);
  98. nodeKeys
  99. .forEach((property: keyof T) => {
  100. if (property === 'parentNode') {
  101. return;
  102. }
  103. const value: T[keyof T] = node[property];
  104. let clonedValue: T[keyof T] | T[keyof T][] | null;
  105. if (value === null || value instanceof RegExp) {
  106. clonedValue = value;
  107. } else if (Array.isArray(value)) {
  108. clonedValue = value.map(NodeUtils.cloneRecursive);
  109. } else if (typeof value === 'object') {
  110. clonedValue = NodeUtils.cloneRecursive(value);
  111. } else {
  112. clonedValue = value;
  113. }
  114. copy[property] = <T[keyof T]>clonedValue;
  115. });
  116. return <T>copy;
  117. }
  118. }