TemplateLiteralTransformer.ts 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import { injectable, inject } from 'inversify';
  2. import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
  3. import * as ESTree from 'estree';
  4. import { IOptions } from '../../interfaces/options/IOptions';
  5. import { IObfuscationReplacer } from '../../interfaces/node-transformers/IObfuscationReplacer';
  6. import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscationReplacers';
  7. import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
  8. import { Node } from '../../node/Node';
  9. import { Nodes } from '../../node/Nodes';
  10. /**
  11. * Transform ES2015 template literals to ES5
  12. * Thanks to Babel for algorithm
  13. */
  14. @injectable()
  15. export class TemplateLiteralTransformer extends AbstractNodeTransformer {
  16. /**
  17. * @type {(replacer: NodeObfuscatorsReplacers) => IObfuscationReplacer}
  18. */
  19. private readonly replacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscationReplacer;
  20. /**
  21. * @param replacersFactory
  22. * @param options
  23. */
  24. constructor(
  25. @inject(ServiceIdentifiers.Factory__IObfuscatorReplacer) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscationReplacer,
  26. @inject(ServiceIdentifiers.IOptions) options: IOptions
  27. ) {
  28. super(options);
  29. this.replacersFactory = replacersFactory;
  30. }
  31. /**
  32. * @param node
  33. * @return {boolean}
  34. */
  35. private static isLiteralNodeWithStringValue (node: ESTree.Node): boolean {
  36. return node && Node.isLiteralNode(node) && typeof node.value === 'string';
  37. }
  38. /**
  39. * @param templateLiteralNode
  40. * @param parentNode
  41. * @returns {ESTree.Node}
  42. */
  43. public transformNode (templateLiteralNode: ESTree.TemplateLiteral, parentNode: ESTree.Node): ESTree.Node {
  44. const templateLiteralExpressions: ESTree.Expression[] = templateLiteralNode.expressions;
  45. let nodes: (ESTree.Literal | ESTree.Expression)[] = [];
  46. for (const templateElement of templateLiteralNode.quasis) {
  47. nodes.push(Nodes.getLiteralNode(templateElement.value.cooked));
  48. const expression: ESTree.Expression | undefined = templateLiteralExpressions.shift();
  49. if (!expression) {
  50. continue;
  51. }
  52. nodes.push(expression);
  53. }
  54. nodes = nodes.filter((node: ESTree.Literal | ESTree.Expression) => {
  55. return !(Node.isLiteralNode(node) && node.value === '');
  56. });
  57. // since `+` is left-to-right associative
  58. // ensure the first node is a string if first/second isn't
  59. if (
  60. !TemplateLiteralTransformer.isLiteralNodeWithStringValue(nodes[0]) &&
  61. !TemplateLiteralTransformer.isLiteralNodeWithStringValue(nodes[1])
  62. ) {
  63. nodes.unshift(Nodes.getLiteralNode(''));
  64. }
  65. if (nodes.length > 1) {
  66. let root: ESTree.BinaryExpression = Nodes.getBinaryExpressionNode(
  67. '+',
  68. <ESTree.Literal>nodes.shift(),
  69. <ESTree.Expression>nodes.shift()
  70. );
  71. for (const node of nodes) {
  72. root = Nodes.getBinaryExpressionNode('+', root, <ESTree.Literal | ESTree.Expression>node);
  73. }
  74. return root;
  75. }
  76. return nodes[0];
  77. }
  78. }