NodeUtils.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import * as escodegen from 'escodegen';
  2. import * as espree from 'espree';
  3. import * as estraverse from 'estraverse';
  4. import * as ESTree from 'estree';
  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 = espree.parse(code, { sourceType: 'script' });
  32. estraverse.replace(structure, {
  33. enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node => {
  34. NodeUtils.parentizeNode(node, parentNode);
  35. if (NodeGuards.isLiteralNode(node)) {
  36. NodeUtils.addXVerbatimPropertyTo(node);
  37. }
  38. NodeMetadata.set(node, { ignoredNode: false });
  39. return node;
  40. }
  41. });
  42. return <ESTree.Statement[]>structure.body;
  43. }
  44. /**
  45. * @param {NodeGuards[]} structure
  46. * @returns {string}
  47. */
  48. public static convertStructureToCode (structure: ESTree.Node[]): string {
  49. return structure.reduce((code: string, node: ESTree.Node) => {
  50. return code + escodegen.generate(node, {
  51. sourceMapWithCode: true
  52. }).code;
  53. }, '');
  54. }
  55. /**
  56. * @param {UnaryExpression} unaryExpressionNode
  57. * @returns {NodeGuards}
  58. */
  59. public static getUnaryExpressionArgumentNode (unaryExpressionNode: ESTree.UnaryExpression): ESTree.Node {
  60. if (NodeGuards.isUnaryExpressionNode(unaryExpressionNode.argument)) {
  61. return NodeUtils.getUnaryExpressionArgumentNode(unaryExpressionNode.argument);
  62. }
  63. return unaryExpressionNode.argument;
  64. }
  65. /**
  66. * @param {T} astTree
  67. * @returns {T}
  68. */
  69. public static parentizeAst <T extends ESTree.Node = ESTree.Node> (astTree: T): T {
  70. estraverse.replace(astTree, {
  71. enter: NodeUtils.parentizeNode
  72. });
  73. return astTree;
  74. }
  75. /**
  76. * @param {T} node
  77. * @param {Node} parentNode
  78. * @returns {T}
  79. */
  80. public static parentizeNode <T extends ESTree.Node = ESTree.Node> (node: T, parentNode: ESTree.Node | null): T {
  81. node.parentNode = parentNode || node;
  82. return node;
  83. }
  84. /**
  85. * @param {T} node
  86. * @returns {T}
  87. */
  88. private static cloneRecursive <T> (node: T): T {
  89. if (node === null) {
  90. return node;
  91. }
  92. const copy: Partial<T> = {};
  93. const nodeKeys: (keyof T)[] = <(keyof T)[]>Object.keys(node);
  94. nodeKeys
  95. .forEach((property: keyof T) => {
  96. if (property === 'parentNode') {
  97. return;
  98. }
  99. const value: T[keyof T] = node[property];
  100. let clonedValue: T[keyof T] | T[keyof T][] | null;
  101. if (value === null || value instanceof RegExp) {
  102. clonedValue = value;
  103. } else if (Array.isArray(value)) {
  104. clonedValue = value.map(NodeUtils.cloneRecursive);
  105. } else if (typeof value === 'object') {
  106. clonedValue = NodeUtils.cloneRecursive(value);
  107. } else {
  108. clonedValue = value;
  109. }
  110. copy[property] = <T[keyof T]>clonedValue;
  111. });
  112. return <T>copy;
  113. }
  114. }