1import { 2 AssignmentExpression, BinaryExpression, Bundle, chainBundle, getNonAssignmentOperatorForCompoundAssignment, 3 isAccessExpression, isExpression, isLeftHandSideExpression, isLogicalOrCoalescingAssignmentExpression, 4 isPropertyAccessExpression, isSimpleCopiableExpression, LogicalOrCoalescingAssignmentOperator, Node, 5 skipParentheses, SourceFile, SyntaxKind, Token, TransformationContext, TransformFlags, visitEachChild, visitNode, 6 VisitResult, 7} from "../_namespaces/ts"; 8 9/** @internal */ 10export function transformES2021(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { 11 const { 12 hoistVariableDeclaration, 13 factory 14 } = context; 15 return chainBundle(context, transformSourceFile); 16 17 function transformSourceFile(node: SourceFile) { 18 if (node.isDeclarationFile) { 19 return node; 20 } 21 22 return visitEachChild(node, visitor, context); 23 } 24 25 function visitor(node: Node): VisitResult<Node> { 26 if ((node.transformFlags & TransformFlags.ContainsES2021) === 0) { 27 return node; 28 } 29 switch (node.kind) { 30 case SyntaxKind.BinaryExpression: 31 const binaryExpression = node as BinaryExpression; 32 if (isLogicalOrCoalescingAssignmentExpression(binaryExpression)) { 33 return transformLogicalAssignment(binaryExpression); 34 } 35 // falls through 36 default: 37 return visitEachChild(node, visitor, context); 38 } 39 } 40 41 function transformLogicalAssignment(binaryExpression: AssignmentExpression<Token<LogicalOrCoalescingAssignmentOperator>>): VisitResult<Node> { 42 const operator = binaryExpression.operatorToken; 43 const nonAssignmentOperator = getNonAssignmentOperatorForCompoundAssignment(operator.kind); 44 let left = skipParentheses(visitNode(binaryExpression.left, visitor, isLeftHandSideExpression)); 45 let assignmentTarget = left; 46 const right = skipParentheses(visitNode(binaryExpression.right, visitor, isExpression)); 47 48 if (isAccessExpression(left)) { 49 const propertyAccessTargetSimpleCopiable = isSimpleCopiableExpression(left.expression); 50 const propertyAccessTarget = propertyAccessTargetSimpleCopiable ? left.expression : 51 factory.createTempVariable(hoistVariableDeclaration); 52 const propertyAccessTargetAssignment = propertyAccessTargetSimpleCopiable ? left.expression : factory.createAssignment( 53 propertyAccessTarget, 54 left.expression 55 ); 56 57 if (isPropertyAccessExpression(left)) { 58 assignmentTarget = factory.createPropertyAccessExpression( 59 propertyAccessTarget, 60 left.name 61 ); 62 left = factory.createPropertyAccessExpression( 63 propertyAccessTargetAssignment, 64 left.name 65 ); 66 } 67 else { 68 const elementAccessArgumentSimpleCopiable = isSimpleCopiableExpression(left.argumentExpression); 69 const elementAccessArgument = elementAccessArgumentSimpleCopiable ? left.argumentExpression : 70 factory.createTempVariable(hoistVariableDeclaration); 71 72 assignmentTarget = factory.createElementAccessExpression( 73 propertyAccessTarget, 74 elementAccessArgument 75 ); 76 left = factory.createElementAccessExpression( 77 propertyAccessTargetAssignment, 78 elementAccessArgumentSimpleCopiable ? left.argumentExpression : factory.createAssignment( 79 elementAccessArgument, 80 left.argumentExpression 81 ) 82 ); 83 } 84 } 85 86 return factory.createBinaryExpression( 87 left, 88 nonAssignmentOperator, 89 factory.createParenthesizedExpression( 90 factory.createAssignment( 91 assignmentTarget, 92 right 93 ) 94 ) 95 ); 96 } 97} 98