CommentsTransformer.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import { inject, injectable, } from 'inversify';
  2. import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
  3. import * as ESTree from 'estree';
  4. import * as estraverse from 'estraverse';
  5. import { IOptions } from '../../interfaces/options/IOptions';
  6. import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
  7. import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
  8. import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
  9. import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
  10. import { ConditionalCommentObfuscatingGuard } from '../preparing-transformers/obfuscating-guards/ConditionalCommentObfuscatingGuard';
  11. import { NodeGuards } from '../../node/NodeGuards';
  12. @injectable()
  13. export class CommentsTransformer extends AbstractNodeTransformer {
  14. /**
  15. * @type {string[]}
  16. */
  17. private static readonly preservedWords: string[] = [
  18. '@license',
  19. '@preserve'
  20. ];
  21. /**
  22. * @param {IRandomGenerator} randomGenerator
  23. * @param {IOptions} options
  24. */
  25. public constructor (
  26. @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
  27. @inject(ServiceIdentifiers.IOptions) options: IOptions
  28. ) {
  29. super(randomGenerator, options);
  30. }
  31. /**
  32. * @param {TransformationStage} transformationStage
  33. * @returns {IVisitor | null}
  34. */
  35. public getVisitor (transformationStage: TransformationStage): IVisitor | null {
  36. switch (transformationStage) {
  37. case TransformationStage.Initializing:
  38. return {
  39. leave: (node: ESTree.Node): ESTree.Node | undefined => {
  40. if (NodeGuards.isProgramNode(node)) {
  41. return this.transformNode(node);
  42. }
  43. }
  44. };
  45. default:
  46. return null;
  47. }
  48. }
  49. /**
  50. * Removes all comments from node except comments that contain
  51. * `@license`, `@preserve` or `javascript-obfuscator` words
  52. * Move comments to their nodes
  53. *
  54. * @param {Node} rootNode
  55. * @returns {NodeGuards}
  56. */
  57. public transformNode (rootNode: ESTree.Program): ESTree.Node {
  58. if (!rootNode.comments || !rootNode.comments.length) {
  59. return rootNode;
  60. }
  61. const comments: ESTree.Comment[] = this.transformComments(rootNode.comments);
  62. if (comments.length === 0) {
  63. return rootNode;
  64. }
  65. if (!rootNode.body.length) {
  66. rootNode.leadingComments = comments;
  67. return rootNode;
  68. }
  69. estraverse.traverse(rootNode, {
  70. enter: (node: ESTree.Node): void => {
  71. if (node === rootNode) {
  72. return;
  73. }
  74. const commentIdx: number = comments.findIndex((comment: ESTree.Comment) =>
  75. comment.range && node.range && comment.range[0] < node.range[0]
  76. );
  77. if (commentIdx === -1) {
  78. return;
  79. }
  80. node.leadingComments = comments.splice(commentIdx, comments.length - commentIdx).reverse();
  81. }
  82. });
  83. if (comments.length > 0) {
  84. rootNode.trailingComments = comments.reverse();
  85. }
  86. return rootNode;
  87. }
  88. /**
  89. * @param {Comment[]} comments
  90. * @returns {Comment[]}
  91. */
  92. private transformComments (comments: ESTree.Comment[]): ESTree.Comment[] {
  93. return comments.filter((comment: ESTree.Comment) =>
  94. CommentsTransformer.preservedWords
  95. .some((preservedWord: string) => comment.value.includes(preservedWord)) ||
  96. ConditionalCommentObfuscatingGuard.isConditionalComment(comment)
  97. ).reverse();
  98. }
  99. }