UNPKG

javascript-obfuscator

Version:
134 lines (109 loc) 4.71 kB
import { inject, injectable, } from 'inversify'; import { ServiceIdentifiers } from '../../container/ServiceIdentifiers'; import * as ESTree from 'estree'; import { IOptions } from '../../interfaces/options/IOptions'; import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator'; import { IVisitor } from '../../interfaces/node-transformers/IVisitor'; import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage'; import { AbstractNodeTransformer } from '../AbstractNodeTransformer'; import { NodeFactory } from '../../node/NodeFactory'; import { NodeGuards } from '../../node/NodeGuards'; import { NodeUtils } from '../../node/NodeUtils'; /** * Transform ES2015 template literals to ES5 * Thanks to Babel for algorithm */ @injectable() export class TemplateLiteralTransformer extends AbstractNodeTransformer { /** * @param {IRandomGenerator} randomGenerator * @param {IOptions} options */ public constructor ( @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator, @inject(ServiceIdentifiers.IOptions) options: IOptions ) { super(randomGenerator, options); } /** * @param {NodeGuards} node * @returns {boolean} */ private static isLiteralNodeWithStringValue (node: ESTree.Node | undefined): boolean { return !!node && NodeGuards.isLiteralNode(node) && typeof node.value === 'string'; } /** * @param {NodeTransformationStage} nodeTransformationStage * @returns {IVisitor | null} */ public getVisitor (nodeTransformationStage: NodeTransformationStage): IVisitor | null { switch (nodeTransformationStage) { case NodeTransformationStage.Converting: return { enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => { if (parentNode && NodeGuards.isTemplateLiteralNode(node)) { return this.transformNode(node, parentNode); } } }; default: return null; } } /** * @param {ESTree.TemplateLiteral} templateLiteralNode * @param {ESTree.Node} parentNode * @returns {ESTree.Node} */ public transformNode (templateLiteralNode: ESTree.TemplateLiteral, parentNode: ESTree.Node): ESTree.Node { if (NodeGuards.isTaggedTemplateExpressionNode(parentNode)) { return templateLiteralNode; } return this.transformTemplateLiteralNode(templateLiteralNode, parentNode); } /** * @param {ESTree.TemplateLiteral} templateLiteralNode * @param {ESTree.Node} parentNode * @returns {ESTree.Expression} */ private transformTemplateLiteralNode (templateLiteralNode: ESTree.TemplateLiteral, parentNode: ESTree.Node): ESTree.Expression { const templateLiteralExpressions: ESTree.Expression[] = templateLiteralNode.expressions; let nodes: ESTree.Expression[] = []; templateLiteralNode.quasis.forEach((templateElement: ESTree.TemplateElement) => { nodes.push(NodeFactory.literalNode(templateElement.value.cooked)); const expression: ESTree.Expression | undefined = templateLiteralExpressions.shift(); if (!expression) { return; } nodes.push(expression); }); nodes = nodes.filter((node: ESTree.Literal | ESTree.Expression) => { return !(NodeGuards.isLiteralNode(node) && node.value === ''); }); // since `+` is left-to-right associative // ensure the first node is a string if first/second isn't if ( !TemplateLiteralTransformer.isLiteralNodeWithStringValue(nodes[0]) && !TemplateLiteralTransformer.isLiteralNodeWithStringValue(nodes[1]) ) { nodes.unshift(NodeFactory.literalNode('')); } let transformedNode: ESTree.Node; if (nodes.length > 1) { let root: ESTree.BinaryExpression = NodeFactory.binaryExpressionNode( '+', <ESTree.Literal>nodes.shift(), <ESTree.Expression>nodes.shift() ); nodes.forEach((node: ESTree.Literal | ESTree.Expression) => { root = NodeFactory.binaryExpressionNode('+', root, node); }); transformedNode = root; } else { transformedNode = nodes[0]; } NodeUtils.parentizeAst(transformedNode); NodeUtils.parentizeNode(transformedNode, parentNode); return transformedNode; } }