NodeUtils.ts 4.0 KB

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