diff --git a/src/Esprima/Ast/AssignmentExpression.cs b/src/Esprima/Ast/AssignmentExpression.cs index 826cf62a..6c8c3414 100644 --- a/src/Esprima/Ast/AssignmentExpression.cs +++ b/src/Esprima/Ast/AssignmentExpression.cs @@ -42,7 +42,6 @@ public AssignmentExpression( Right = right; } - public static AssignmentOperator ParseAssignmentOperator(string op) { return op switch @@ -67,6 +66,30 @@ public static AssignmentOperator ParseAssignmentOperator(string op) }; } + public static string ConvertAssignmentOperator(AssignmentOperator op) + { + return op switch + { + AssignmentOperator.Assign => "=", + AssignmentOperator.PlusAssign => "+=", + AssignmentOperator.MinusAssign => "-=", + AssignmentOperator.TimesAssign => "*=", + AssignmentOperator.DivideAssign => "/=", + AssignmentOperator.ModuloAssign => "%=", + AssignmentOperator.BitwiseAndAssign => "&=", + AssignmentOperator.BitwiseOrAssign => "|=", + AssignmentOperator.BitwiseXOrAssign => "^=", + AssignmentOperator.ExponentiationAssign => "**=", + AssignmentOperator.LeftShiftAssign => "<<=", + AssignmentOperator.RightShiftAssign => ">>=", + AssignmentOperator.UnsignedRightShiftAssign => ">>>=", + AssignmentOperator.NullishAssign => "??=", + AssignmentOperator.AndAssign => "&&=", + AssignmentOperator.OrAssign => "||=", + _ => ThrowArgumentOutOfRangeException(nameof(op), "Invalid assignment operator: " + op) + }; + } + public override NodeCollection ChildNodes => new(Left, Right); protected internal override void Accept(AstVisitor visitor) diff --git a/src/Esprima/Ast/BinaryExpression.cs b/src/Esprima/Ast/BinaryExpression.cs index d210fb23..b0b27c3d 100644 --- a/src/Esprima/Ast/BinaryExpression.cs +++ b/src/Esprima/Ast/BinaryExpression.cs @@ -84,6 +84,39 @@ public static BinaryOperator ParseBinaryOperator(string op) }; } + public static string ConvertBinaryOperator(BinaryOperator op) + { + return op switch + { + BinaryOperator.Plus => "+", + BinaryOperator.Minus => "-", + BinaryOperator.Times => "*", + BinaryOperator.Divide => "/", + BinaryOperator.Modulo => "%", + BinaryOperator.Equal => "==", + BinaryOperator.NotEqual => "!=", + BinaryOperator.Greater => ">", + BinaryOperator.GreaterOrEqual => ">=", + BinaryOperator.Less => "<", + BinaryOperator.LessOrEqual => "<=", + BinaryOperator.StrictlyEqual => "===", + BinaryOperator.StricltyNotEqual => "!==", + BinaryOperator.BitwiseAnd => "&", + BinaryOperator.BitwiseOr => "|", + BinaryOperator.BitwiseXOr => "^", + BinaryOperator.LeftShift => "<<", + BinaryOperator.RightShift => ">>", + BinaryOperator.UnsignedRightShift => ">>>", + BinaryOperator.InstanceOf => "instanceof", + BinaryOperator.In => "in", + BinaryOperator.LogicalAnd => "&&", + BinaryOperator.LogicalOr => "||", + BinaryOperator.Exponentiation => "**", + BinaryOperator.NullishCoalescing => "??", + _ => ThrowArgumentOutOfRangeException(nameof(op), "Invalid binary operator: " + op) + }; + } + public override NodeCollection ChildNodes => new(Left, Right); protected internal override void Accept(AstVisitor visitor) diff --git a/src/Esprima/Ast/UnaryExpression.cs b/src/Esprima/Ast/UnaryExpression.cs index be1794b1..6232ac6f 100644 --- a/src/Esprima/Ast/UnaryExpression.cs +++ b/src/Esprima/Ast/UnaryExpression.cs @@ -33,7 +33,7 @@ protected UnaryExpression(Nodes type, string? op, Expression arg) : base(type) Prefix = true; } - private static UnaryOperator ParseUnaryOperator(string? op) + public static UnaryOperator ParseUnaryOperator(string? op) { return op switch { @@ -50,6 +50,23 @@ private static UnaryOperator ParseUnaryOperator(string? op) }; } + public static string ConvertUnaryOperator(UnaryOperator op) + { + return op switch + { + UnaryOperator.Plus => "+", + UnaryOperator.Minus => "-", + UnaryOperator.Increment => "++", + UnaryOperator.Decrement => "--", + UnaryOperator.BitwiseNot => "~", + UnaryOperator.LogicalNot => "!", + UnaryOperator.Delete => "delete", + UnaryOperator.Void => "void", + UnaryOperator.TypeOf => "typeof", + _ => ThrowArgumentOutOfRangeException(nameof(op), "Invalid unary operator: " + op) + }; + } + public override NodeCollection ChildNodes => new(Argument); protected internal override void Accept(AstVisitor visitor) diff --git a/src/Esprima/JavascriptParser.cs b/src/Esprima/JavascriptParser.cs index 81529518..78d9eea7 100644 --- a/src/Esprima/JavascriptParser.cs +++ b/src/Esprima/JavascriptParser.cs @@ -2281,7 +2281,7 @@ private void CheckPatternParam(ParsedParameters options, Node param) }; } - private const int MaxAssignmentDepth = 100; + public int MaxAssignmentDepth { get; set; } = 1000; private int _assignmentDepth = 0; private Expression ParseAssignmentExpression() diff --git a/src/Esprima/Utils/ToJavascriptConverter.cs b/src/Esprima/Utils/ToJavascriptConverter.cs new file mode 100644 index 00000000..3a06d283 --- /dev/null +++ b/src/Esprima/Utils/ToJavascriptConverter.cs @@ -0,0 +1,1585 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using Esprima.Ast; + +namespace Esprima.Utils +{ + public static class ToJavascriptConverterExtension + { + public static string ToJavascript(this Node node, bool beautify = false) + { + return ToJavascriptConverter.ToJavascript(node, beautify); + } + } + + public class ToJavascriptConverter + { + public static string ToJavascript(Node node, bool beautify = false) + { + var visitor = new ToJavascriptConverter() { Beautify = beautify }; + visitor.Visit(node); + return visitor.ToString(); + } + + public bool Beautify { get; set; } + + public int IndentionSize { get; set; } = 4; + + public string NewlineFormat { get; set; } = Environment.NewLine; + + protected StringBuilder _sb = new StringBuilder(); + private int _indentionLevel = 0; + + private readonly List _parentStack = new List(); + protected IReadOnlyList ParentStack => _parentStack; + + private void Append(string text) + { + _sb.Append(text); + } + + private void AppendBeautificationSpace() + { + if (Beautify) + { + _sb.Append(" "); + } + } + + private void AppendBeautificationIndent() + { + if (Beautify) + { + _sb.Append("".PadLeft(_indentionLevel * IndentionSize, ' ')); + } + } + + private void AppendBeautificationNewline() + { + if (Beautify) + { + _sb.Append(NewlineFormat); + } + } + + private void IncreaseIndent() + { + _indentionLevel++; + } + + private void DecreaseIndent() + { + _indentionLevel--; + } + + /// + /// Returns parent node at specified position. + /// + /// Zero index value returns current node; one corresponds to direct + /// parent of current node. + protected Node? TryGetParentAt(int offset) + { + if (_parentStack.Count < offset + 1) + { + return null; + } + + return _parentStack[_parentStack.Count - 1 - offset]; + } + + public virtual void Visit(Node node) + { + _parentStack.Add(node); + + switch (node.Type) + { + case Nodes.AssignmentExpression: + VisitAssignmentExpression(node.As()); + break; + case Nodes.ArrayExpression: + VisitArrayExpression(node.As()); + break; + case Nodes.BlockStatement: + VisitBlockStatement(node.As()); + break; + case Nodes.BinaryExpression: + VisitBinaryExpression(node.As()); + break; + case Nodes.BreakStatement: + VisitBreakStatement(node.As()); + break; + case Nodes.CallExpression: + VisitCallExpression(node.As()); + break; + case Nodes.CatchClause: + VisitCatchClause(node.As()); + break; + case Nodes.ConditionalExpression: + VisitConditionalExpression(node.As()); + break; + case Nodes.ContinueStatement: + VisitContinueStatement(node.As()); + break; + case Nodes.DoWhileStatement: + VisitDoWhileStatement(node.As()); + break; + case Nodes.DebuggerStatement: + VisitDebuggerStatement(node.As()); + break; + case Nodes.EmptyStatement: + VisitEmptyStatement(node.As()); + break; + case Nodes.ExpressionStatement: + VisitExpressionStatement(node.As()); + break; + case Nodes.ForStatement: + VisitForStatement(node.As()); + break; + case Nodes.ForInStatement: + VisitForInStatement(node.As()); + break; + case Nodes.FunctionDeclaration: + VisitFunctionDeclaration(node.As()); + break; + case Nodes.FunctionExpression: + VisitFunctionExpression(node.As()); + break; + case Nodes.Identifier: + VisitIdentifier(node.As()); + break; + case Nodes.IfStatement: + VisitIfStatement(node.As()); + break; + case Nodes.Import: + VisitImport(node.As()); + break; + case Nodes.Literal: + VisitLiteral(node.As()); + break; + case Nodes.LabeledStatement: + VisitLabeledStatement(node.As()); + break; + case Nodes.LogicalExpression: + VisitBinaryExpression(node.As()); + break; + case Nodes.MemberExpression: + VisitMemberExpression(node.As()); + break; + case Nodes.NewExpression: + VisitNewExpression(node.As()); + break; + case Nodes.ObjectExpression: + VisitObjectExpression(node.As()); + break; + case Nodes.Program: + VisitProgram(node.As()); + break; + case Nodes.Property: + VisitProperty(node.As()); + break; + case Nodes.PropertyDefinition: + VisitPropertyDefinition(node.As()); + break; + case Nodes.RestElement: + VisitRestElement(node.As()); + break; + case Nodes.ReturnStatement: + VisitReturnStatement(node.As()); + break; + case Nodes.SequenceExpression: + VisitSequenceExpression(node.As()); + break; + case Nodes.SwitchStatement: + VisitSwitchStatement(node.As()); + break; + case Nodes.SwitchCase: + VisitSwitchCase(node.As()); + break; + case Nodes.TemplateElement: + VisitTemplateElement(node.As()); + break; + case Nodes.TemplateLiteral: + VisitTemplateLiteral(node.As()); + break; + case Nodes.ThisExpression: + VisitThisExpression(node.As()); + break; + case Nodes.ThrowStatement: + VisitThrowStatement(node.As()); + break; + case Nodes.TryStatement: + VisitTryStatement(node.As()); + break; + case Nodes.UnaryExpression: + VisitUnaryExpression(node.As()); + break; + case Nodes.UpdateExpression: + VisitUpdateExpression(node.As()); + break; + case Nodes.VariableDeclaration: + VisitVariableDeclaration(node.As()); + break; + case Nodes.VariableDeclarator: + VisitVariableDeclarator(node.As()); + break; + case Nodes.WhileStatement: + VisitWhileStatement(node.As()); + break; + case Nodes.WithStatement: + VisitWithStatement(node.As()); + break; + case Nodes.ArrayPattern: + VisitArrayPattern(node.As()); + break; + case Nodes.AssignmentPattern: + VisitAssignmentPattern(node.As()); + break; + case Nodes.SpreadElement: + VisitSpreadElement(node.As()); + break; + case Nodes.ObjectPattern: + VisitObjectPattern(node.As()); + break; + case Nodes.ArrowParameterPlaceHolder: + VisitArrowParameterPlaceHolder(node.As()); + break; + case Nodes.MetaProperty: + VisitMetaProperty(node.As()); + break; + case Nodes.Super: + VisitSuper(node.As()); + break; + case Nodes.TaggedTemplateExpression: + VisitTaggedTemplateExpression(node.As()); + break; + case Nodes.YieldExpression: + VisitYieldExpression(node.As()); + break; + case Nodes.ArrowFunctionExpression: + VisitArrowFunctionExpression(node.As()); + break; + case Nodes.AwaitExpression: + VisitAwaitExpression(node.As()); + break; + case Nodes.ClassBody: + VisitClassBody(node.As()); + break; + case Nodes.ClassDeclaration: + VisitClassDeclaration(node.As()); + break; + case Nodes.ForOfStatement: + VisitForOfStatement(node.As()); + break; + case Nodes.MethodDefinition: + VisitMethodDefinition(node.As()); + break; + case Nodes.ImportSpecifier: + VisitImportSpecifier(node.As()); + break; + case Nodes.ImportDefaultSpecifier: + VisitImportDefaultSpecifier(node.As()); + break; + case Nodes.ImportNamespaceSpecifier: + VisitImportNamespaceSpecifier(node.As()); + break; + case Nodes.ImportDeclaration: + VisitImportDeclaration(node.As()); + break; + case Nodes.ExportSpecifier: + VisitExportSpecifier(node.As()); + break; + case Nodes.ExportNamedDeclaration: + VisitExportNamedDeclaration(node.As()); + break; + case Nodes.ExportAllDeclaration: + VisitExportAllDeclaration(node.As()); + break; + case Nodes.ExportDefaultDeclaration: + VisitExportDefaultDeclaration(node.As()); + break; + case Nodes.ClassExpression: + VisitClassExpression(node.As()); + break; + case Nodes.ChainExpression: + VisitChainExpression(node.As()); + break; + default: + VisitUnknownNode(node); + break; + } + _parentStack.RemoveAt(_parentStack.Count - 1); + } + + protected virtual void VisitProgram(Program program) + { + VisitNodeList(program.Body, appendAtEnd: ";", addLineBreaks: true); + } + + protected virtual void VisitUnknownNode(Node node) + { + throw new NotImplementedException($"AST visitor doesn't support nodes of type {node.Type}, you can override VisitUnknownNode to handle this case."); + } + + protected virtual void VisitChainExpression(ChainExpression chainExpression) + { + Visit(chainExpression.Expression); + } + + protected virtual void VisitCatchClause(CatchClause catchClause) + { + Append("("); + if (catchClause.Param is not null) + { + Visit(catchClause.Param); + } + Append(")"); + Visit(catchClause.Body); + } + + protected virtual void VisitFunctionDeclaration(FunctionDeclaration functionDeclaration) + { + if (functionDeclaration.Async) + { + Append("async "); + } + Append("function"); + if (functionDeclaration.Generator) + { + Append("*"); + } + if (functionDeclaration.Id != null) + { + Append(" "); + Visit(functionDeclaration.Id); + } + Append("("); + VisitNodeList(functionDeclaration.Params, appendSeperatorString: ","); + Append(")"); + AppendBeautificationSpace(); + Visit(functionDeclaration.Body); + } + + protected virtual void VisitWithStatement(WithStatement withStatement) + { + Append("with("); + Visit(withStatement.Object); + Append(")"); + Visit(withStatement.Body); + } + + protected virtual void VisitWhileStatement(WhileStatement whileStatement) + { + Append("while("); + Visit(whileStatement.Test); + Append(")"); + Visit(whileStatement.Body); + } + + protected virtual void VisitVariableDeclaration(VariableDeclaration variableDeclaration) + { + Append(variableDeclaration.Kind.ToString().ToLower() + " "); + VisitNodeList(variableDeclaration.Declarations, appendSeperatorString: ","); + } + + protected virtual void VisitTryStatement(TryStatement tryStatement) + { + Append("try "); + Visit(tryStatement.Block); + if (tryStatement.Handler != null) + { + Append(" catch"); + Visit(tryStatement.Handler); + } + if (tryStatement.Finalizer != null) + { + Append(" finally"); + Visit(tryStatement.Finalizer); + } + } + + protected virtual void VisitThrowStatement(ThrowStatement throwStatement) + { + Append("throw "); + Visit(throwStatement.Argument); + Append(";"); + } + + protected virtual void VisitSwitchStatement(SwitchStatement switchStatement) + { + Append("switch("); + Visit(switchStatement.Discriminant); + Append(")"); + AppendBeautificationSpace(); + Append("{"); + + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + + VisitNodeList(switchStatement.Cases, addLineBreaks: true); + + AppendBeautificationNewline(); + DecreaseIndent(); + AppendBeautificationIndent(); + + Append("}"); + } + + protected virtual void VisitSwitchCase(SwitchCase switchCase) + { + if (switchCase.Test != null) + { + Append("case "); + Visit(switchCase.Test); + } + else + { + Append("default"); + } + Append(":"); + + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + + VisitNodeList(switchCase.Consequent, appendAtEnd: ";", addLineBreaks: true); + + DecreaseIndent(); + } + + protected virtual void VisitReturnStatement(ReturnStatement returnStatement) + { + Append("return"); + if (returnStatement.Argument != null) + { + Append(" "); + Visit(returnStatement.Argument); + } + Append(";"); + } + + protected virtual void VisitLabeledStatement(LabeledStatement labeledStatement) + { + Visit(labeledStatement.Label); + Append(":"); + Visit(labeledStatement.Body); + } + + protected virtual void VisitIfStatement(IfStatement ifStatement) + { + Append("if"); + AppendBeautificationSpace(); + Append("("); + Visit(ifStatement.Test); + Append(")"); + AppendBeautificationSpace(); + + if (ifStatement.Consequent is not BlockStatement) + { + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + } + Visit(ifStatement.Consequent); + if (NodeNeedsSemicolon(ifStatement.Consequent)) + { + Append(";"); + } + if (ifStatement.Consequent is not BlockStatement) + { + DecreaseIndent(); + if (ifStatement.Alternate != null) + { + AppendBeautificationNewline(); + AppendBeautificationIndent(); + } + } + if (ifStatement.Alternate != null) + { + Append(" else "); + if (ifStatement.Alternate is not BlockStatement && ifStatement.Alternate is not IfStatement) + { + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + } + Visit(ifStatement.Alternate); + if (NodeNeedsSemicolon(ifStatement.Alternate)) + { + Append(";"); + } + if (ifStatement.Alternate is not BlockStatement && ifStatement.Alternate is not IfStatement) + { + DecreaseIndent(); + } + } + } + + protected virtual void VisitEmptyStatement(EmptyStatement emptyStatement) + { + Append(";"); + } + + protected virtual void VisitDebuggerStatement(DebuggerStatement debuggerStatement) + { + Append("debugger"); + } + + protected virtual void VisitExpressionStatement(ExpressionStatement expressionStatement) + { + if (expressionStatement.Expression is CallExpression callExpression && !(callExpression.Callee is Identifier)) + { + if (ExpressionNeedsBrackets(callExpression.Callee)) + { + Append("("); + } + Visit(callExpression.Callee); + if (ExpressionNeedsBrackets(callExpression.Callee)) + { + Append(")"); + } + Append("("); + VisitNodeList(callExpression.Arguments, appendSeperatorString: ","); + Append(")"); + } + else if (expressionStatement.Expression is ClassExpression) + { + Append("("); + Visit(expressionStatement.Expression); + Append(")"); + } + else + { + if (expressionStatement.Expression is FunctionExpression) + { + Append("("); + } + Visit(expressionStatement.Expression); + if (expressionStatement.Expression is FunctionExpression) + { + Append(")"); + } + } + } + + protected virtual void VisitForStatement(ForStatement forStatement) + { + Append("for("); + if (forStatement.Init != null) + { + Visit(forStatement.Init); + } + Append(";"); + AppendBeautificationSpace(); + if (forStatement.Test != null) + { + Visit(forStatement.Test); + } + Append(";"); + AppendBeautificationSpace(); + if (forStatement.Update != null) + { + Visit(forStatement.Update); + } + Append(")"); + AppendBeautificationSpace(); + + if (forStatement.Body is not BlockStatement) + { + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + } + Visit(forStatement.Body); + if (NodeNeedsSemicolon(forStatement.Body)) + { + Append(";"); + } + if (forStatement.Body is not BlockStatement) + { + DecreaseIndent(); + } + } + + protected virtual void VisitForInStatement(ForInStatement forInStatement) + { + Append("for("); + Visit(forInStatement.Left); + Append(" in "); + Visit(forInStatement.Right); + Append(")"); + AppendBeautificationSpace(); + + if (forInStatement.Body is not BlockStatement) + { + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + } + Visit(forInStatement.Body); + if (NodeNeedsSemicolon(forInStatement.Body)) + { + Append(";"); + } + if (forInStatement.Body is not BlockStatement) + { + DecreaseIndent(); + } + } + + protected virtual void VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + Append("do "); + Visit(doWhileStatement.Body); + if (NodeNeedsSemicolon(doWhileStatement.Body)) + { + Append(";"); + } + Append("while("); + Visit(doWhileStatement.Test); + Append(")"); + } + + protected virtual void VisitArrowFunctionExpression(ArrowFunctionExpression arrowFunctionExpression) + { + if (arrowFunctionExpression.Async) + { + Append("async "); + } + + if (arrowFunctionExpression.Id != null) + { + Visit(arrowFunctionExpression.Id); + } + + if (arrowFunctionExpression.Params.Count == 1) + { + if (arrowFunctionExpression.Params[0] is RestElement || ExpressionNeedsBrackets(arrowFunctionExpression.Params[0])) + { + Append("("); + } + Visit(arrowFunctionExpression.Params[0]); + if (arrowFunctionExpression.Params[0] is RestElement || ExpressionNeedsBrackets(arrowFunctionExpression.Params[0])) + { + Append(")"); + } + } + else + { + Append("("); + VisitNodeList(arrowFunctionExpression.Params, appendSeperatorString: ",", appendBracketsIfNeeded: true); ; + Append(")"); + } + Append("=>"); + if (arrowFunctionExpression.Body is ObjectExpression || arrowFunctionExpression.Body is SequenceExpression) + { + Append("("); + } + Visit(arrowFunctionExpression.Body); + if (arrowFunctionExpression.Body is ObjectExpression || arrowFunctionExpression.Body is SequenceExpression) + { + Append(")"); + } + } + + protected virtual void VisitUnaryExpression(UnaryExpression unaryExpression) + { + var op = UnaryExpression.ConvertUnaryOperator(unaryExpression.Operator); + if (unaryExpression.Prefix) + { + Append(op); + if (char.IsLetter(op[0])) + Append(" "); + } + if (!(unaryExpression.Argument is Literal) && !(unaryExpression.Argument is UnaryExpression)) + { + Append("("); + } + Visit(unaryExpression.Argument); + if (!(unaryExpression.Argument is Literal) && !(unaryExpression.Argument is UnaryExpression)) + { + Append(")"); + } + if (!unaryExpression.Prefix) + { + Append(op); + } + } + + protected virtual void VisitUpdateExpression(UpdateExpression updateExpression) + { + if (updateExpression.Prefix) + { + Append(UnaryExpression.ConvertUnaryOperator(updateExpression.Operator)); + } + Visit(updateExpression.Argument); + if (!updateExpression.Prefix) + { + Append(UnaryExpression.ConvertUnaryOperator(updateExpression.Operator)); + } + } + + protected virtual void VisitThisExpression(ThisExpression thisExpression) + { + Append("this"); + } + + protected virtual void VisitSequenceExpression(SequenceExpression sequenceExpression) + { + VisitNodeList(sequenceExpression.Expressions, appendSeperatorString: Beautify ? ", " : ","); + } + + protected virtual void VisitObjectExpression(ObjectExpression objectExpression) + { + Append("{"); + if (objectExpression.Properties.Count > 0) + { + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + } + VisitNodeList(objectExpression.Properties, appendSeperatorString: ",", addLineBreaks: true); + if (objectExpression.Properties.Count > 0) + { + AppendBeautificationNewline(); + DecreaseIndent(); + AppendBeautificationIndent(); + } + Append("}"); + } + + protected virtual void VisitNewExpression(NewExpression newExpression) + { + Append("new"); + if (ExpressionNeedsBrackets(newExpression.Callee)) + { + Append("("); + } + else + { + Append(" "); + } + Visit(newExpression.Callee); + if (ExpressionNeedsBrackets(newExpression.Callee)) + { + Append(")"); + } + if (newExpression.Arguments.Count > 0) + { + Append("("); + VisitNodeList(newExpression.Arguments, appendSeperatorString: ","); + Append(")"); + } + } + + protected virtual void VisitMemberExpression(MemberExpression memberExpression) + { + if (ExpressionNeedsBrackets(memberExpression.Object) || (memberExpression.Object is Literal l && l.TokenType != TokenType.StringLiteral)) + { + Append("("); + } + Visit(memberExpression.Object); + if (ExpressionNeedsBrackets(memberExpression.Object) || (memberExpression.Object is Literal l2 && l2.TokenType != TokenType.StringLiteral)) + { + Append(")"); + } + if (memberExpression.Computed) + { + Append("["); + } + else + { + if (TryGetParentAt(0) is ChainExpression) + Append("?"); + Append("."); + } + Visit(memberExpression.Property); + if (memberExpression.Computed) + { + Append("]"); + } + } + + protected virtual void VisitLiteral(Literal literal) + { + Append(literal.Raw); + } + + protected virtual void VisitIdentifier(Identifier identifier) + { + Append(identifier.Name); + } + + protected virtual void VisitFunctionExpression(IFunction function) + { + var isParentMethod = TryGetParentAt(1) is MethodDefinition; + if (!isParentMethod) + { + if (function.Async) + { + Append("async "); + } + if (!(TryGetParentAt(1) is MethodDefinition)) + { + Append("function"); + } + if (function.Generator) + { + Append("*"); + } + } + if (function.Id != null) + { + Append(" "); + Visit(function.Id); + } + Append("("); + VisitNodeList(function.Params, appendSeperatorString: ","); + Append(")"); + AppendBeautificationSpace(); + Visit(function.Body); + } + + protected virtual void VisitClassExpression(ClassExpression classExpression) + { + Append("class "); + if (classExpression.Id != null) + { + Visit(classExpression.Id); + } + if (classExpression.SuperClass != null) + { + Append(" extends "); + Visit(classExpression.SuperClass); + } + + AppendBeautificationSpace(); + Append("{"); + + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + + Visit(classExpression.Body); + + AppendBeautificationNewline(); + DecreaseIndent(); + AppendBeautificationIndent(); + + Append("}"); + } + + protected virtual void VisitExportDefaultDeclaration(ExportDefaultDeclaration exportDefaultDeclaration) + { + Append("export default "); + if (exportDefaultDeclaration.Declaration != null) + { + Visit(exportDefaultDeclaration.Declaration); + } + } + + protected virtual void VisitExportAllDeclaration(ExportAllDeclaration exportAllDeclaration) + { + Append("export*from"); + Visit(exportAllDeclaration.Source); + } + + protected virtual void VisitExportNamedDeclaration(ExportNamedDeclaration exportNamedDeclaration) + { + Append("export"); + if (exportNamedDeclaration.Declaration != null) + { + Append(" "); + Visit(exportNamedDeclaration.Declaration); + } + if (exportNamedDeclaration.Specifiers.Count > 0) + { + Append("{"); + VisitNodeList(exportNamedDeclaration.Specifiers, appendSeperatorString: ","); + Append("}"); + } + if (exportNamedDeclaration.Source != null) + { + Append("from"); + Visit(exportNamedDeclaration.Source); + } + if (exportNamedDeclaration.Declaration == null && exportNamedDeclaration.Specifiers.Count == 0 && exportNamedDeclaration.Source == null) + { + Append("{}"); + } + + } + + protected virtual void VisitExportSpecifier(ExportSpecifier exportSpecifier) + { + Visit(exportSpecifier.Local); + if (exportSpecifier.Local != exportSpecifier.Exported) + { + Append(" as "); + Visit(exportSpecifier.Exported); + } + } + + protected virtual void VisitImport(Import import) + { + Append("import("); + Visit(import.Source); + Append(")"); + } + + protected virtual void VisitImportDeclaration(ImportDeclaration importDeclaration) + { + Append("import "); + var firstSpecifier = importDeclaration.Specifiers.FirstOrDefault(); + if (firstSpecifier is ImportDefaultSpecifier) + { + Visit(firstSpecifier); + if (importDeclaration.Specifiers.Count > 1) + { + Append(","); + AppendBeautificationSpace(); + if (importDeclaration.Specifiers[1] is ImportNamespaceSpecifier) + { + VisitNodeList(importDeclaration.Specifiers.Skip(1), appendSeperatorString: Beautify ? ", " : ","); + } + else + { + Append("{"); + AppendBeautificationSpace(); + VisitNodeList(importDeclaration.Specifiers.Skip(1), appendSeperatorString: Beautify ? ", " : ","); + AppendBeautificationSpace(); + Append("}"); + } + } + } + else if (importDeclaration.Specifiers.Any()) + { + if (importDeclaration.Specifiers[0] is ImportNamespaceSpecifier) + { + VisitNodeList(importDeclaration.Specifiers, appendSeperatorString: Beautify ? ", " : ","); + } + else + { + Append("{"); + AppendBeautificationSpace(); + VisitNodeList(importDeclaration.Specifiers, appendSeperatorString: Beautify ? ", " : ","); + AppendBeautificationSpace(); + Append("}"); + } + } + if (importDeclaration.Specifiers.Count > 0) + { + Append(" from "); + } + Visit(importDeclaration.Source); + } + + protected virtual void VisitImportNamespaceSpecifier(ImportNamespaceSpecifier importNamespaceSpecifier) + { + Append("* as "); + Visit(importNamespaceSpecifier.Local); + } + + protected virtual void VisitImportDefaultSpecifier(ImportDefaultSpecifier importDefaultSpecifier) + { + Visit(importDefaultSpecifier.Local); + } + + protected virtual void VisitImportSpecifier(ImportSpecifier importSpecifier) + { + Visit(importSpecifier.Imported); + if (importSpecifier.Local != importSpecifier.Imported) + { + Append(" as "); + Visit(importSpecifier.Local); + } + } + + protected virtual void VisitMethodDefinition(MethodDefinition methodDefinition) + { + if (methodDefinition.Static) + { + Append("static "); + } + if (IsAsync(methodDefinition.Value)) + { + Append("async "); + } + if (methodDefinition.Value is FunctionExpression f && f.Generator) + { + Append("*"); + } + if (methodDefinition.Kind == PropertyKind.Get) + { + Append("get "); + } + else if (methodDefinition.Kind == PropertyKind.Set) + { + Append("set "); + } + if (methodDefinition.Key is MemberExpression || ExpressionNeedsBrackets(methodDefinition.Key)) + { + Append("["); + } + if (ExpressionNeedsBrackets(methodDefinition.Key)) + { + Append("("); + } + Visit(methodDefinition.Key); + if (ExpressionNeedsBrackets(methodDefinition.Key)) + { + Append(")"); + } + if (methodDefinition.Key is MemberExpression || ExpressionNeedsBrackets(methodDefinition.Key)) + { + Append("]"); + } + Visit(methodDefinition.Value); + } + + protected virtual void VisitForOfStatement(ForOfStatement forOfStatement) + { + Append("for("); + Visit(forOfStatement.Left); + Append(" of "); + Visit(forOfStatement.Right); + Append(")"); + AppendBeautificationSpace(); + + if (forOfStatement.Body is not BlockStatement) + { + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + } + Visit(forOfStatement.Body); + if (NodeNeedsSemicolon(forOfStatement.Body)) + { + Append(";"); + } + if (forOfStatement.Body is not BlockStatement) + { + DecreaseIndent(); + } + } + + protected virtual void VisitClassDeclaration(ClassDeclaration classDeclaration) + { + Append("class "); + if (classDeclaration.Id != null) + { + Visit(classDeclaration.Id); + } + + if (classDeclaration.SuperClass != null) + { + Append(" extends "); + Visit(classDeclaration.SuperClass); + } + + AppendBeautificationSpace(); + Append("{"); + + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + + Visit(classDeclaration.Body); + + AppendBeautificationNewline(); + DecreaseIndent(); + AppendBeautificationIndent(); + + Append("}"); + } + + protected virtual void VisitClassBody(ClassBody classBody) + { + VisitNodeList(classBody.Body, addLineBreaks: true); + } + + protected virtual void VisitYieldExpression(YieldExpression yieldExpression) + { + Append("yield "); + if (yieldExpression.Argument != null) + { + Visit(yieldExpression.Argument); + } + } + + protected virtual void VisitTaggedTemplateExpression(TaggedTemplateExpression taggedTemplateExpression) + { + Visit(taggedTemplateExpression.Tag); + Visit(taggedTemplateExpression.Quasi); + } + + protected virtual void VisitSuper(Super super) + { + Append("super"); + } + + protected virtual void VisitMetaProperty(MetaProperty metaProperty) + { + Visit(metaProperty.Meta); + Append("."); + Visit(metaProperty.Property); + } + + protected virtual void VisitArrowParameterPlaceHolder(ArrowParameterPlaceHolder arrowParameterPlaceHolder) + { + VisitNodeList(arrowParameterPlaceHolder.Params); + } + + protected virtual void VisitObjectPattern(ObjectPattern objectPattern) + { + Append("{"); + VisitNodeList(objectPattern.Properties, appendSeperatorString: ","); + Append("}"); + } + + protected virtual void VisitSpreadElement(SpreadElement spreadElement) + { + Append("..."); + Visit(spreadElement.Argument); + } + + protected virtual void VisitAssignmentPattern(AssignmentPattern assignmentPattern) + { + Visit(assignmentPattern.Left); + Append("="); + Visit(assignmentPattern.Right); + } + + protected virtual void VisitArrayPattern(ArrayPattern arrayPattern) + { + Append("["); + VisitNodeList(arrayPattern.Elements, appendSeperatorString: ","); + Append("]"); + } + + protected virtual void VisitVariableDeclarator(VariableDeclarator variableDeclarator) + { + Visit(variableDeclarator.Id); + if (variableDeclarator.Init != null) + { + AppendBeautificationSpace(); + Append("="); + AppendBeautificationSpace(); + if (ExpressionNeedsBrackets(variableDeclarator.Init)) + { + Append("("); + } + Visit(variableDeclarator.Init); + if (ExpressionNeedsBrackets(variableDeclarator.Init)) + { + Append(")"); + } + } + } + + protected virtual void VisitTemplateLiteral(TemplateLiteral templateLiteral) + { + Append("`"); + for (int n = 0; n < templateLiteral.Quasis.Count; n++) + { + Visit(templateLiteral.Quasis[n]); + if (templateLiteral.Expressions.Count > n) + { + Append("${"); + Visit(templateLiteral.Expressions[n]); + Append("}"); + } + } + Append("`"); + } + + protected virtual void VisitTemplateElement(TemplateElement templateElement) + { + Append(templateElement.Value.Raw); + } + + protected virtual void VisitRestElement(RestElement restElement) + { + Append("..."); + Visit(restElement.Argument); + } + + protected virtual void VisitProperty(Property property) + { + if (property.Key is MemberExpression || ExpressionNeedsBrackets(property.Key)) + { + Append("["); + } + if (ExpressionNeedsBrackets(property.Key)) + { + Append("("); + } + Visit(property.Key); + if (ExpressionNeedsBrackets(property.Key)) + { + Append(")"); + } + if (property.Key is MemberExpression || ExpressionNeedsBrackets(property.Key)) + { + Append("]"); + } + if (property.Key is Identifier keyI && property.Value is Identifier valueI && keyI.Name == valueI.Name) + { } + else + { + AppendBeautificationSpace(); + Append(":"); + AppendBeautificationSpace(); + if (property.Value is not ObjectPattern && ExpressionNeedsBrackets(property.Value)) + { + Append("("); + } + Visit(property.Value); + if (property.Value is not ObjectPattern && ExpressionNeedsBrackets(property.Value)) + { + Append(")"); + } + } + } + + protected virtual void VisitPropertyDefinition(PropertyDefinition propertyDefinition) + { + if (propertyDefinition.Static) + { + Append("static "); + } + if (propertyDefinition.Key is MemberExpression || ExpressionNeedsBrackets(propertyDefinition.Key)) + { + Append("["); + } + if (ExpressionNeedsBrackets(propertyDefinition.Key)) + { + Append("("); + } + Visit(propertyDefinition.Key); + if (ExpressionNeedsBrackets(propertyDefinition.Key)) + { + Append(")"); + } + if (propertyDefinition.Key is MemberExpression || ExpressionNeedsBrackets(propertyDefinition.Key)) + { + Append("]"); + } + if (propertyDefinition.Value != null) + { + Append("="); + Visit(propertyDefinition.Value); + } + Append(";"); + } + + protected virtual void VisitAwaitExpression(AwaitExpression awaitExpression) + { + Append("await "); + Visit(awaitExpression.Argument); + } + + protected virtual void VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + if (conditionalExpression.Test is AssignmentExpression) + { + Append("("); + } + Visit(conditionalExpression.Test); + if (conditionalExpression.Test is AssignmentExpression) + { + Append(")"); + } + AppendBeautificationSpace(); + Append("?"); + AppendBeautificationSpace(); + if (ExpressionNeedsBrackets(conditionalExpression.Consequent)) + { + Append("("); + } + Visit(conditionalExpression.Consequent); + if (ExpressionNeedsBrackets(conditionalExpression.Consequent)) + { + Append(")"); + } + AppendBeautificationSpace(); + Append(":"); + AppendBeautificationSpace(); + if (ExpressionNeedsBrackets(conditionalExpression.Alternate)) + { + Append("("); + } + Visit(conditionalExpression.Alternate); + if (ExpressionNeedsBrackets(conditionalExpression.Alternate)) + { + Append(")"); + } + } + + protected virtual void VisitCallExpression(CallExpression callExpression) + { + if (ExpressionNeedsBrackets(callExpression.Callee)) + { + Append("("); + } + Visit(callExpression.Callee); + if (ExpressionNeedsBrackets(callExpression.Callee)) + { + Append(")"); + } + Append("("); + VisitNodeList(callExpression.Arguments, appendSeperatorString: ",", appendBracketsIfNeeded: true); + Append(")"); + } + + protected virtual void VisitBinaryExpression(BinaryExpression binaryExpression) + { + if (ExpressionNeedsBrackets(binaryExpression.Left)) + { + Append("("); + } + Visit(binaryExpression.Left); + if (ExpressionNeedsBrackets(binaryExpression.Left)) + { + Append(")"); + } + var op = BinaryExpression.ConvertBinaryOperator(binaryExpression.Operator); + if (char.IsLetter(op[0])) + { + Append(" "); + } + else + { + AppendBeautificationSpace(); + } + Append(op); + if (char.IsLetter(op[0])) + { + Append(" "); + } + else + { + AppendBeautificationSpace(); + } + if (ExpressionNeedsBrackets(binaryExpression.Right)) + { + Append("("); + } + Visit(binaryExpression.Right); + if (ExpressionNeedsBrackets(binaryExpression.Right)) + { + Append(")"); + } + } + + protected virtual void VisitArrayExpression(ArrayExpression arrayExpression) + { + Append("["); + VisitNodeList(arrayExpression.Elements, appendSeperatorString: ","); + Append("]"); + } + + protected virtual void VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + if (assignmentExpression.Left is ObjectPattern) + { + Append("("); + } + var op = AssignmentExpression.ConvertAssignmentOperator(assignmentExpression.Operator); + Visit(assignmentExpression.Left); + AppendBeautificationSpace(); + Append(op); + AppendBeautificationSpace(); + if (ExpressionNeedsBrackets(assignmentExpression.Right) && !(assignmentExpression.Right is AssignmentExpression)) + { + Append("("); + } + Visit(assignmentExpression.Right); + if (ExpressionNeedsBrackets(assignmentExpression.Right) && !(assignmentExpression.Right is AssignmentExpression)) + { + Append(")"); + } + if (assignmentExpression.Left is ObjectPattern) + { + Append(")"); + } + } + + protected virtual void VisitContinueStatement(ContinueStatement continueStatement) + { + Append("continue "); + if (continueStatement.Label != null) + { + Visit(continueStatement.Label); + } + } + + protected virtual void VisitBreakStatement(BreakStatement breakStatement) + { + if (breakStatement.Label != null) + { + Visit(breakStatement.Label); + } + Append("break"); + } + + protected virtual void VisitBlockStatement(BlockStatement blockStatement) + { + Append("{"); + + AppendBeautificationNewline(); + IncreaseIndent(); + AppendBeautificationIndent(); + + VisitNodeList(blockStatement.Body, appendAtEnd: ";", addLineBreaks: true); + + AppendBeautificationNewline(); + DecreaseIndent(); + AppendBeautificationIndent(); + + Append("}"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void VisitNodeList(IEnumerable nodeList, string appendAtEnd = null, string appendSeperatorString = null, bool appendBracketsIfNeeded = false, bool addLineBreaks = false) + where TNode : Node + { + var notfirst = false; + foreach (var node in nodeList) + { + if (node != null) + { + if (notfirst && appendSeperatorString != null) + { + Append(appendSeperatorString); + } + if (notfirst && addLineBreaks) + { + AppendBeautificationNewline(); + AppendBeautificationIndent(); + } + if (appendBracketsIfNeeded && ExpressionNeedsBrackets(node)) + { + Append("("); + } + Visit(node); + if (appendBracketsIfNeeded && ExpressionNeedsBrackets(node)) + { + Append(")"); + } + notfirst = true; + if (appendAtEnd != null && NodeNeedsSemicolon(node)) + { + Append(appendAtEnd); + } + } + } + } + + public override string ToString() + { + return _sb.ToString(); + } + + public bool IsAsync(Node node) + { + if (node is ArrowFunctionExpression afe) + { + return afe.Async; + } + if (node is ArrowParameterPlaceHolder apph) + { + return apph.Async; + } + if (node is FunctionDeclaration fd) + { + return fd.Async; + } + if (node is FunctionExpression fe) + { + return fe.Async; + } + return false; + } + + public bool NodeNeedsSemicolon(Node? node) + { + if (node is BlockStatement || + node is IfStatement || + node is SwitchStatement || + node is ForInStatement || + node is ForOfStatement || + node is ForStatement || + node is FunctionDeclaration || + node is ReturnStatement || + node is ThrowStatement || + node is TryStatement || + node is EmptyStatement || + node is ClassDeclaration) + { + return false; + } + if (node is ExportNamedDeclaration end) + { + return NodeNeedsSemicolon(end.Declaration); + } + return true; + } + + public bool ExpressionNeedsBrackets(Node? node) + { + if (node is FunctionExpression) + { + return true; + } + if (node is ArrowFunctionExpression) + { + return true; + } + if (node is AssignmentExpression) + { + return true; + } + if (node is SequenceExpression) + { + return true; + } + if (node is ConditionalExpression) + { + return true; + } + if (node is BinaryExpression) + { + return true; + } + if (node is UnaryExpression) + { + return true; + } + if (node is CallExpression) + { + return true; + } + if (node is NewExpression) + { + return true; + } + if (node is ObjectPattern) + { + return true; + } + if (node is ArrayPattern) + { + return true; + } + if (node is YieldExpression) + { + return true; + } + return false; + } + } +} diff --git a/test/Esprima.Tests/JavascriptTest.cs b/test/Esprima.Tests/JavascriptTest.cs new file mode 100644 index 00000000..fbf64f9b --- /dev/null +++ b/test/Esprima.Tests/JavascriptTest.cs @@ -0,0 +1,585 @@ +using System; +using System.Text.RegularExpressions; +using Esprima.Utils; +using Xunit; + +namespace Esprima.Tests +{ + public class JavascriptTest + { + [Fact] + public void ToJavascriptTest1() + { + var parser = new JavaScriptParser(@"if (true) { p(); } +switch(foo) { + case 'A': + p(); + break; +} +switch(foo) { + default: + p(); + break; +} +for (var a = []; ; ) { } +for (var elem of list) { } +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + + Assert.Equal("if(true){p();}switch(foo){case 'A':p();break;}switch(foo){default:p();break;}for(var a=[];;){}for(var elem of list){}", code); + } + + [Fact] + public void ToJavascriptTest2() + { + var parser = new JavaScriptParser(@"let tips = [ + ""Click on any AST node with a '+' to expand it"", + + ""Hovering over a node highlights the \ + corresponding location in the source code"", + + ""Shift click on an AST node to expand the whole subtree"" +]; + + function printTips() + { + tips.forEach((tip, i) => console.log(`Tip ${ i}:` +tip)); + }"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("let tips=[\"Click on any AST node with a '+' to expand it\",\"Hovering over a node highlights the \\\r\n corresponding location in the source code\",\"Shift click on an AST node to expand the whole subtree\"];function printTips(){tips.forEach((tip,i)=>console.log((`Tip ${i}:`+tip)));}", code); + } + + [Fact] + public void ToJavascriptTest3() + { + var parser = new JavaScriptParser(@"export class aa extends HTMLElement{ + constructor(a, b) + { + super(a); + this._div = document.createElement('div'); + } + static get is() { + return 'aa'; + } +}"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("export class aa extends HTMLElement{constructor(a,b){super(a);this._div=(document.createElement('div'));}static get is(){return 'aa';}}", code); + } + + [Fact] + public void ToJavascriptTest4() + { + var source = @"import { MccDialog } from '../mccDialogHandler'; +import { commonClient, bb as f } from '../commonClient/commonClient'; +import ii, { hh, jj } from '../commonClient/commonClient'; +import '../commonClient/commonClient'; +import aa from 'module-name'; +import zz, * as ff from 'module-name'; +import * as name from 'module-name'; +import('qq'); +a++; +--a; +export function checkSecurityAnswerCodeDirect(result) { + if (!result) { + MccDialog.warning({ + title: 'SecurityClientErrorOccured', + message: '

internal error, check console

', + }); + return false; + } + switch (result.SecurityAnswerCode) { + case 'Allowed': + return true; + case 'Exception': + MccDialog.warning({ + title: 'SecurityClientInfoTitle', + message: '

SecurityClientExceptionOccured

Exception: ' + result.Message + '

' + result.StackTrace, + }); + return false; + case 'Error': + MccDialog.warning({ + title: 'SecurityClientErrorOccured', + message: '

' + + commonClient.getTranslation('SecurityClientMessage') + + ': ' + + commonClient.getTranslation(result.Message) + + '

' + + (result.MessageDetails ? '

SecurityClientDetails: ' + result.MessageDetails + '

' : ' '), + }); + return false; + default: { + let messagesnippet = '

SecurityClient_' + result.SecurityAnswerCode + '

'; + if (result.Message !== undefined && result.SecurityAnswerCode === 'LoginFailed') { + messagesnippet += '\n\nSecurityClient_InternalServerErrorMessage\n' + result.Message + ''; + } + if (result.Role) { + messagesnippet += '

SecurityClient_CheckedRole' + ' [' + result.Role + ']' + '

'; + } + MccDialog.warning({ + title: 'SecurityClientInfoTitle', + message: messagesnippet, + }); + return false; + } + } +}"; + source = Regex.Replace(source, @"\r\n|\n\r|\n|\r", Environment.NewLine); + var parser = new JavaScriptParser(source); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program, true); + + var expected = @"import { MccDialog } from '../mccDialogHandler'; +import { commonClient, bb as f } from '../commonClient/commonClient'; +import ii, { hh, jj } from '../commonClient/commonClient'; +import '../commonClient/commonClient'; +import aa from 'module-name'; +import zz, * as ff from 'module-name'; +import * as name from 'module-name'; +import('qq'); +a++; +--a; +export function checkSecurityAnswerCodeDirect(result) { + if (!(result)) { + MccDialog.warning({ + title : 'SecurityClientErrorOccured', + message : '

internal error, check console

' + }); + return false; + } + switch(result.SecurityAnswerCode) { + case 'Allowed': + return true; + case 'Exception': + MccDialog.warning({ + title : 'SecurityClientInfoTitle', + message : ((('

SecurityClientExceptionOccured

Exception: ' + result.Message) + '

') + result.StackTrace) + }); + return false; + case 'Error': + MccDialog.warning({ + title : 'SecurityClientErrorOccured', + message : ((((('

' + (commonClient.getTranslation('SecurityClientMessage'))) + ': ') + (commonClient.getTranslation(result.Message))) + '

') + (result.MessageDetails ? (('

SecurityClientDetails: ' + result.MessageDetails) + '

') : ' ')) + }); + return false; + default: + { + let messagesnippet = (('

SecurityClient_' + result.SecurityAnswerCode) + '

'); + if ((result.Message !== undefined) && (result.SecurityAnswerCode === 'LoginFailed')) { + messagesnippet += (('\n\nSecurityClient_InternalServerErrorMessage\n' + result.Message) + ''); + } + if (result.Role) { + messagesnippet += (((('

SecurityClient_CheckedRole' + ' [') + result.Role) + ']') + '

'); + } + MccDialog.warning({ + title : 'SecurityClientInfoTitle', + message : messagesnippet + }); + return false; + } + } +}"; + expected = Regex.Replace(expected, @"\r\n|\n\r|\n|\r", Environment.NewLine); + Assert.Equal(expected, code); + } + + [Fact] + public void ToJavascriptTest5() + { + var source = @"(function () { + 'use strict'; +})(); + +(class ApplyShimInterface { + constructor() { + this.customStyleInterface = null; + applyShim['invalidCallback'] = ApplyShimUtils.invalidate; + } +}); + +( + a +)(); + + +aa({}); + +(function aa(){});"; + source = Regex.Replace(source, @"\r\n|\n\r|\n|\r", Environment.NewLine); + var parser = new JavaScriptParser(source); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program, true); + + var expected = @"(function() { + 'use strict'; +})(); +(class ApplyShimInterface { + constructor() { + this.customStyleInterface = null; + applyShim['invalidCallback'] = ApplyShimUtils.invalidate; + } +}); +a(); +aa({}); +(function aa() { + +});"; + expected = Regex.Replace(expected, @"\r\n|\n\r|\n|\r", Environment.NewLine); + Assert.Equal(expected, code); + } + + [Fact] + public void ToJavascriptTest6() + { + var source = @"function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + }"; + source = Regex.Replace(source, @"\r\n|\n\r|\n|\r", Environment.NewLine); + var parser = new JavaScriptParser(source); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("function _createClass(Constructor,protoProps,staticProps){if(protoProps)_defineProperties(Constructor.prototype,protoProps);if(staticProps)_defineProperties(Constructor,staticProps);return Constructor;}", code); + } + + [Fact] + public void ToJavascriptTest7() + { + var parser = new JavaScriptParser(@"if ((x ? a.nodeName.toLowerCase() === f : 1 === a.nodeType) && ++d && (p && ((i = (o = a[S] || (a[S] = {}))[a.uniqueID] || (o[a.uniqueID] = {}))[h] = [k, d]), a === e)) +{ +}"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("if(((x?((a.nodeName.toLowerCase())===f):(1===a.nodeType))&&(++d))&&(p&&((i=((o=(a[S]||(a[S]={})))[a.uniqueID]||(o[a.uniqueID]={})))[h]=[k,d]),a===e)){}", code); + } + + [Fact] + public void ToJavascriptTest8() + { + var parser = new JavaScriptParser(@" +class a extends b { + constructor() { + super(); + this.g=1; + } + + q=1; + r='cc'; +} +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("class a extends b{constructor(){super();this.g=1;}q=1;r='cc';}", code); + } + + [Fact] + public void ToJavascriptTest9() + { + var parser = new JavaScriptParser(@" +d = (s = (r = (i = (o = (a = c)[S] || (a[S] = {}))[a.uniqueID] || (o[a.uniqueID] = {}))[h] || [])[0] === k && r[1]) && r[2], a = s && c.childNodes[s]; +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("d=((s=(((r=((i=((o=((a=c)[S]||(a[S]={})))[a.uniqueID]||(o[a.uniqueID]={})))[h]||[]))[0]===k)&&r[1]))&&r[2]),a=(s&&c.childNodes[s]);", code); + } + + [Fact] + public void ToJavascriptTest10() + { + var parser = new JavaScriptParser(@" +m = (z.document, !!v.documentElement && !!v.head && 'function' == typeof v.addEventListener && v.createElement, ~a.indexOf('MSIE') || a.indexOf('Trident/'), '___FONT_AWESOME___') +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("m=(z.document,(((!!(v.documentElement))&&(!!(v.head)))&&('function'==(typeof (v.addEventListener))))&&v.createElement,(~(a.indexOf('MSIE')))||(a.indexOf('Trident/')),'___FONT_AWESOME___');", code); + } + + [Fact] + public void ToJavascriptTest11() + { + var parser = new JavaScriptParser(@" + var h = (c.navigator || {}).userAgent, + a = void 0 === h ? '' : h, + z = c, + v = l, + m = (z.document, !!v.documentElement && !!v.head && 'function' == typeof v.addEventListener && v.createElement, ~a.indexOf('MSIE') || a.indexOf('Trident/'), '___FONT_AWESOME___'), + e = function() { + try { + return !0 + } catch (c) { + return !1 + } + }(); +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("var h=(c.navigator||{}).userAgent,a=((void 0)===h?'':h),z=c,v=l,m=(z.document,(((!!(v.documentElement))&&(!!(v.head)))&&('function'==(typeof (v.addEventListener))))&&v.createElement,(~(a.indexOf('MSIE')))||(a.indexOf('Trident/')),'___FONT_AWESOME___'),e=((function(){try {return !0;} catch(c){return !1;}})());", code); + } + + [Fact] + public void ToJavascriptTest12() + { + var parser = new JavaScriptParser(@" +var a = { +children: (b = O, 'g' === b.tag ? b.children : [b]) +} +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("var a={children:(b=O,'g'===b.tag?b.children:[b])};", code); + } + + [Fact] + public void ToJavascriptTest13() + { + var parser = new JavaScriptParser(@" +if (e.IsWebService) + if (h = e.HttpRequest.responseXML, 'undefined' == typeof h) Trace.Write('Error: ' + e.UniqueId + ' data has no properties!'), m = !0; + else try { + h.setProperty('SelectionLanguage', 'XPath') + } catch (l) { + Trace.Write('Error: data.setProperty('SelectionLanguage', 'XPath') because ' + l.message) + } else h = e.HttpRequest.responseText; +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("if(e.IsWebService)if(h=e.HttpRequest.responseXML,'undefined'==(typeof (h)))Trace.Write((('Error: '+e.UniqueId)+' data has no properties!')),m=(!0); else try {h.setProperty('SelectionLanguage','XPath');} catch(l){Trace.Write('Error: data.setProperty(',SelectionLanguage,', ',XPath,') because '+l.message);} else h=e.HttpRequest.responseText;", code); + } + + [Fact] + public void ToJavascriptTest14() + { + var source = @"function tt(t, r) { + var n, e, i = b(t), + s = b(r); + if (s && (e = ft(r)), i); + else if (s) return D(t, e) ? void $(t, e) : (n = l(e, t), G(t, n), void ht(t)); + var g, o, f; + for (f = t.length < r.length ? t.length : r.length, o = 0, g = 0; f > g; g++) o += t[g] + r[g], t[g] = o & _t, o >>= at; + for (g = f; o && g < t.length; g++) o += t[g], t[g] = o & _t, o >>= at +} +"; + source = Regex.Replace(source, @"\r\n|\n\r|\n|\r", Environment.NewLine); + var parser = new JavaScriptParser(source); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program, true); + + var expected = @"function tt(t,r) { + var n,e,i = (b(t)),s = (b(r)); + if (s && (e = (ft(r))), i) + ; + else if (s) + return D(t,e) ? (void ($(t,e))) : (n = (l(e,t)), G(t,n), void (ht(t))); + var g,o,f; + for(f = (t.length < r.length ? t.length : r.length), o = 0, g = 0; f > g; g++) + o += (t[g] + r[g]), t[g] = (o & _t), o >>= at; + for(g = f; o && (g < t.length); g++) + o += t[g], t[g] = (o & _t), o >>= at; +}"; + expected = Regex.Replace(expected, @"\r\n|\n\r|\n|\r", Environment.NewLine); + Assert.Equal(expected, code); + } + + [Fact] + public void ToJavascriptTest15() + { + var parser = new JavaScriptParser(@" +h='M'+(+new Date).toString(36) +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("h=('M'+((+(new Date)).toString(36)));", code); + } + + [Fact] + public void ToJavascriptTest16() + { + var parser = new JavaScriptParser(@" +input.onchange = async (e) => { + const files = await readFiles(input.files, readMode); + document.body.removeChild(input); + resolve(files); + }; +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("input.onchange=(async e=>{const files=await readFiles(input.files,readMode);document.body.removeChild(input);resolve(files);});", code); + } + + [Fact] + public void ToJavascriptTest17() + { + var parser = new JavaScriptParser(@" +export const Base = LegacyElementMixin(HTMLElement).prototype; +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("export const Base=(LegacyElementMixin(HTMLElement)).prototype;", code); + } + + [Fact] + public void ToJavascriptTest18() + { + var parser = new JavaScriptParser(@" +let {is} = getIsExtends(element); +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("let {is}=(getIsExtends(element));", code); + } + + [Fact] + public void ToJavascriptTest19() + { + var parser = new JavaScriptParser(@" +export const wrap = + (window['ShadyDOM'] && window['ShadyDOM']['wrap']) || (node => node); +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("export const wrap=((window['ShadyDOM']&&window['ShadyDOM']['wrap'])||(node=>node));", code); + } + + [Fact] + public void ToJavascriptTest20() + { + var parser = new JavaScriptParser(@" +export {}"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("export{};", code); + } + + [Fact] + public void ToJavascriptTest21() + { + var parser = new JavaScriptParser(@" +(() => { + mutablePropertyChange = MutableData._mutablePropertyChange; +})(); +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("(()=>{mutablePropertyChange=MutableData._mutablePropertyChange;})();", code); + } + + [Fact] + public void ToJavascriptTest22() + { + var parser = new JavaScriptParser(@" +var Ol, jl = new (function() { + var l, h, z; + return l = c + }()) +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("var Ol,jl=(new((function(){var l,h,z;return l=c;})()));", code); + } + + [Fact] + public void ToJavascriptTest23() + { + var parser = new JavaScriptParser(@" + +[y, { + [Symbol.iterator]() { + return b + },a:5 + }] + +"); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program); + Assert.Equal("[y,{[Symbol.iterator]:(function(){return b;}),a:5}];", code); + } + + [Fact] + public void ToJavascriptTest24() + { + var source = @" + +class A { +*[Symbol.iterator]() { + let L = this._first; + for (; L !== _.Undefined; ) + yield L.element, + L = L.next + } +} + +"; + source = Regex.Replace(source, @"\r\n|\n\r|\n|\r", Environment.NewLine); + var parser = new JavaScriptParser(source); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program, true); + + var expected = @"class A { + *[Symbol.iterator]() { + let L = this._first; + for(; L !== _.Undefined; ) + yield L.element, L = L.next; + } +}"; + expected = Regex.Replace(expected, @"\r\n|\n\r|\n|\r", Environment.NewLine); + Assert.Equal(expected, code); + } + + [Fact] + public void ToJavascriptTest25() + { + var source = @"var i = function e(i) { + var r = n[i]; + if (void 0 !== r) + return r.exports; + var a = n[i] = { + exports: {} + }; + return t[i](a, a.exports, e), + a.exports + }(15); +"; + source = Regex.Replace(source, @"\r\n|\n\r|\n|\r", Environment.NewLine); + var parser = new JavaScriptParser(source); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program, true); + + var expected = @"var i = ((function e(i) { + var r = n[i]; + if ((void 0) !== r) + return r.exports; + var a = (n[i] = { + exports : {} + }); + return t[i](a,a.exports,e), a.exports; +})(15));"; + expected = Regex.Replace(expected, @"\r\n|\n\r|\n|\r", Environment.NewLine); + Assert.Equal(expected, code); + } + + [Fact] + public void ToJavascriptTest26() + { + var source = @"class A { + aa() { + let a = 1; + } +} +var b = 1; +var c; +if (b == 2) { + c = 1; +} else { + c = 3; +}"; + source = Regex.Replace(source, @"\r\n|\n\r|\n|\r", Environment.NewLine); + var parser = new JavaScriptParser(source); + var program = parser.ParseScript(); + var code = ToJavascriptConverter.ToJavascript(program, true); + Assert.Equal(source, code); + } + } +}