diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d9e1a52 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..216e7df --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +sudo: false +language: elixir +elixir: + - 1.8.1 +otp_release: + - 21.3 diff --git a/CHANGELOG.md b/CHANGELOG.md index d0fab5f..063b4da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,46 +1,95 @@ -# v2.2.0-dev -* Enhancements: - * Added structs for JSX AST +# v2.7.0 + +- Enhancements: + - [Allow for options to be customized (https://github.com/elixirscript/elixir-estree/pull/27) + +# v2.6.1 + +- Enhancements: + - Add async to arrow function expressions + +# v2.6.0 + +- Enhancements: + - [Add parenthesis around await expressions in await expressions or in call expressions](https://github.com/elixirscript/elixir-estree/pull/20) + - [Add ESTree.Tools.ESTreeJSONTransformer to translate ESTree JSON data into structs](https://github.com/bryanjos/elixir-estree/pull/21) + +# v2.5.1 + +- Enhancements: + - [Correct If statement identation](https://github.com/bryanjos/elixir-estree/pull/18) + +# v2.5.0 + +- Enhancements: + - [Greatly improved formatting of generated JavaScript](https://github.com/bryanjos/elixir-estree/pull/15) + +# v2.4.2 + +- Fixes + - [Expressions in variable declarations](https://github.com/bryanjos/elixir-estree/pull/13) + +# v2.4.1 + +- Fixes + - Handle edge case when string literal is a JSX child + +# v2.4.0 + +- Enhancements: + - Added exponential operators from ES2016 + +# v2.3.0 + +- Enhancements: + - Added AssignmentProperty for use with ObjectPattern + - Updated some typespecs to reflect updates in ESTree spec + +# v2.2.0 + +- Enhancements: + - Added structs for JSX AST # v2.1.2 -* Bug fixes - * ESTree.Tools.Generator: updated UnaryExpression to put space in when operator is :typeof +- Bug fixes + - ESTree.Tools.Generator: updated UnaryExpression to put space in when operator is :typeof # v2.1.1 -* Enhancements - * ESTree.Tools.Generator: Updated yield to account for delegate field +- Enhancements + - ESTree.Tools.Generator: Updated yield to account for delegate field # v2.1.0 -* Enhancements - * Added async field to FunctionDeclaration and FunctionExpression - * Added AwaitExpression +- Enhancements + - Added async field to FunctionDeclaration and FunctionExpression + - Added AwaitExpression # v2.0.1 -* Enhancements - * Changed TemplateElement.value.value to TemplateElement.value.raw - * Began adding indentation - * Added empty string for when nil is given to generate +- Enhancements + - Changed TemplateElement.value.value to TemplateElement.value.raw + - Began adding indentation + - Added empty string for when nil is given to generate # v2.0.0 -* Enhancements - * Updated to latest ESTree Spec - * Added ESTree.Tools.Generator to turn JavaScript AST into code +- Enhancements + + - Updated to latest ESTree Spec + - Added ESTree.Tools.Generator to turn JavaScript AST into code -* Breaking - * ESTree.Builder is now ESTree.Tools.Builder +- Breaking + - ESTree.Builder is now ESTree.Tools.Builder # v1.0.1 -* Enhancements - * Add new regex property to literal +- Enhancements + - Add new regex property to literal # v1.0.0 -* Enhancements - * Has the Node definitions from the [ESTree Spec](https://github.com/estree/estree) - * Fills in ES6 Node definitions from [ast-types](https://github.com/benjamn/ast-types) and some from testing with [acorn](https://github.com/marijnh/acorn) +- Enhancements + - Has the Node definitions from the [ESTree Spec](https://github.com/estree/estree) + - Fills in ES6 Node definitions from [ast-types](https://github.com/benjamn/ast-types) and some from testing with [acorn](https://github.com/marijnh/acorn) diff --git a/README.md b/README.md index d374cc6..634393b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -## Elixir-ESTree [![Documentation](https://img.shields.io/badge/docs-hexpm-blue.svg)](http://hexdocs.pm/estree/) [![Downloads](https://img.shields.io/hexpm/dt/estree.svg)](https://hex.pm/packages/estree) +## Elixir-ESTree [![Documentation](https://img.shields.io/badge/docs-hexpm-blue.svg)](http://hexdocs.pm/estree/) [![Downloads](https://img.shields.io/hexpm/dt/estree.svg)](https://hex.pm/packages/estree) [![Build Status](https://travis-ci.org/elixirscript/elixir-estree.svg?branch=master)](https://travis-ci.org/elixirscript/elixir-estree) + Defines structs that represent the JavaScript AST nodes from the ESTree spec. diff --git a/lib/es_tree.ex b/lib/es_tree.ex index 6e86ec8..aaf89db 100644 --- a/lib/es_tree.ex +++ b/lib/es_tree.ex @@ -1,6 +1,6 @@ defmodule ESTree do @moduledoc """ - Defines structs that represent the JavaScript AST nodes from the ESTree spec. + Defines structs that represent the JavaScript AST nodes from the ESTree spec. [ESTree Spec](https://github.com/estree/estree) @@ -20,15 +20,15 @@ defmodule ESTree do @type unary_operator :: :- | :+ | :! | :"~" | :typeof | :void | :delete - @type binary_operator :: :== | :!= | :=== | :!== | :< | :<= | :> | :>= | - :"<<" | :">>" | :>>> | :+ | :- | :* | :/ | :% | :| | - :^ | :& | :in | :instanceof + @type binary_operator :: :== | :!= | :=== | :!== | :< | :<= | :> | :>= | + :"<<" | :">>" | :>>> | :+ | :- | :* | :/ | :% | :| | + :^ | :& | :in | :instanceof | :"**" @type logical_operator :: :|| | :&& - @type assignment_operator :: := | :"+=" | :"-=" | :"*=" | :"/=" | :"%=" | - :"<<=" | :">>=" | :">>>=" | - :"|=" | :"^=" | :"&=" + @type assignment_operator :: := | :"+=" | :"-=" | :"*=" | :"/=" | :"%=" | + :"<<=" | :">>=" | :">>>=" | + :"|=" | :"^=" | :"&=" | :"**=" @type update_operator :: :++ | :-- diff --git a/lib/es_tree/arrow_function_expression.ex b/lib/es_tree/arrow_function_expression.ex index a860bb0..0027062 100644 --- a/lib/es_tree/arrow_function_expression.ex +++ b/lib/es_tree/arrow_function_expression.ex @@ -1,20 +1,22 @@ defmodule ESTree.ArrowFunctionExpression do - @type t :: %ESTree.ArrowFunctionExpression{ - type: binary, + @type t :: %ESTree.ArrowFunctionExpression{ + type: binary, loc: ESTree.SourceLocation.t | nil, params: [ESTree.Pattern.t], defaults: [ ESTree.Expression.t ], rest: ESTree.Identifier.t | nil, body: ESTree.BlockStatement.t | ESTree.Expression.t, generator: boolean, - expression: boolean + expression: boolean, + async: boolean } - defstruct type: "ArrowFunctionExpression", - loc: nil, + defstruct type: "ArrowFunctionExpression", + loc: nil, params: [], defaults: [], rest: nil, body: %ESTree.BlockStatement{}, generator: false, - expression: false -end \ No newline at end of file + expression: false, + async: false +end diff --git a/lib/es_tree/assignment_expression.ex b/lib/es_tree/assignment_expression.ex index a68bc07..5a8a12a 100644 --- a/lib/es_tree/assignment_expression.ex +++ b/lib/es_tree/assignment_expression.ex @@ -1,14 +1,14 @@ defmodule ESTree.AssignmentExpression do - @type t :: %ESTree.AssignmentExpression{ - type: binary, + @type t :: %ESTree.AssignmentExpression{ + type: binary, loc: ESTree.SourceLocation.t | nil, operator: ESTree.assignment_operator, - left: ESTree.Pattern.t | ESTree.Expression.t, + left: ESTree.Pattern.t, right: ESTree.Expression.t } - defstruct type: "AssignmentExpression", - loc: nil, + defstruct type: "AssignmentExpression", + loc: nil, operator: nil, left: %ESTree.EmptyExpression{}, - right: %ESTree.EmptyExpression{} -end \ No newline at end of file + right: %ESTree.EmptyExpression{} +end diff --git a/lib/es_tree/assignment_property.ex b/lib/es_tree/assignment_property.ex new file mode 100644 index 0000000..6fdf9df --- /dev/null +++ b/lib/es_tree/assignment_property.ex @@ -0,0 +1,10 @@ +defmodule ESTree.AssignmentProperty do + @type t :: %ESTree.AssignmentProperty{ + type: binary, + loc: ESTree.SourceLocation.t | nil, + value: ESTree.Pattern.t + } + defstruct type: "Property", + loc: nil, + value: %ESTree.EmptyExpression{} +end diff --git a/lib/es_tree/for_in_statement.ex b/lib/es_tree/for_in_statement.ex index 7840a22..9a81902 100644 --- a/lib/es_tree/for_in_statement.ex +++ b/lib/es_tree/for_in_statement.ex @@ -1,8 +1,8 @@ defmodule ESTree.ForInStatement do - @type t :: %ESTree.ForInStatement{ - type: binary, + @type t :: %ESTree.ForInStatement{ + type: binary, loc: ESTree.SourceLocation.t | nil, - left: ESTree.VariableDeclaration.t | ESTree.Expression.t , + left: ESTree.VariableDeclaration.t | ESTree.Pattern.t , right: ESTree.Expression.t , body: ESTree.Statement.t } @@ -11,4 +11,4 @@ defmodule ESTree.ForInStatement do left: %ESTree.EmptyExpression{}, right: %ESTree.EmptyExpression{}, body: %ESTree.EmptyStatement{} -end \ No newline at end of file +end diff --git a/lib/es_tree/for_of_statement.ex b/lib/es_tree/for_of_statement.ex index d3ceef9..7649a54 100644 --- a/lib/es_tree/for_of_statement.ex +++ b/lib/es_tree/for_of_statement.ex @@ -1,14 +1,14 @@ defmodule ESTree.ForOfStatement do - @type t :: %ESTree.ForOfStatement{ - type: binary, + @type t :: %ESTree.ForOfStatement{ + type: binary, loc: ESTree.SourceLocation.t | nil, - left: ESTree.VariableDeclaration.t | ESTree.Expression.t , + left: ESTree.VariableDeclaration.t | ESTree.Pattern.t , right: ESTree.Expression.t , - body: ESTree.Statement.t + body: ESTree.Statement.t } - defstruct type: "ForOfStatement", - loc: nil, + defstruct type: "ForOfStatement", + loc: nil, left: %ESTree.EmptyExpression{}, right: %ESTree.EmptyExpression{}, body: %ESTree.EmptyStatement{} -end \ No newline at end of file +end diff --git a/lib/es_tree/node.ex b/lib/es_tree/node.ex index 5578027..46f3da3 100644 --- a/lib/es_tree/node.ex +++ b/lib/es_tree/node.ex @@ -8,8 +8,6 @@ defmodule ESTree.Node do ESTree.Pattern.t | ESTree.SwitchCase.t | ESTree.CatchClause.t | - ESTree.Identifier.t | - ESTree.Literal.t | ESTree.MethodDefinition.t | ESTree.Class.t | ESTree.ClassBody.t | diff --git a/lib/es_tree/object_pattern.ex b/lib/es_tree/object_pattern.ex index 619edeb..196d422 100644 --- a/lib/es_tree/object_pattern.ex +++ b/lib/es_tree/object_pattern.ex @@ -1,10 +1,10 @@ defmodule ESTree.ObjectPattern do - @type t :: %ESTree.ObjectPattern{ + @type t :: %ESTree.ObjectPattern{ type: binary, loc: ESTree.SourceLocation.t | nil, - properties: [ESTree.Property.t] + properties: [ESTree.AssignmentProperty.t] } defstruct type: "ObjectPattern", loc: nil, properties: [] -end +end diff --git a/lib/es_tree/tools/builder.ex b/lib/es_tree/tools/builder.ex index 7363003..5f69dbe 100644 --- a/lib/es_tree/tools/builder.ex +++ b/lib/es_tree/tools/builder.ex @@ -4,67 +4,79 @@ defmodule ESTree.Tools.Builder do """ @spec array_expression( - [ESTree.Expression.t | nil], + [ESTree.Expression.t | nil], ESTree.SourceLocation.t | nil ) :: ESTree.ArrayExpression.t def array_expression(elements, loc \\ nil) do %ESTree.ArrayExpression{ elements: elements, loc: loc } - end + end @spec array_pattern( - [ESTree.Pattern.t | nil], + [ESTree.Pattern.t | nil], ESTree.SourceLocation.t | nil ) :: ESTree.ArrayPattern.t def array_pattern(elements, loc \\ nil) do %ESTree.ArrayPattern{ elements: elements, loc: loc } - end + end @spec arrow_function_expression( - [ESTree.Pattern.t], - [ESTree.Expression.t], + [ESTree.Pattern.t], + [ESTree.Expression.t], ESTree.BlockStatement.t | ESTree.Expression.t, boolean, boolean, ESTree.SourceLocation.t | nil ) :: ESTree.ArrowFunctionExpression.t - def arrow_function_expression(params, defaults, body, generator \\ false, expression \\ false, loc \\ nil) do - %ESTree.ArrowFunctionExpression{ - params: params, defaults: defaults, - body: body, generator: generator, expression: expression, loc: loc + def arrow_function_expression(params, defaults, body, generator \\ false, expression \\ false, async \\ false, loc \\ nil) do + %ESTree.ArrowFunctionExpression{ + params: params, defaults: defaults, + body: body, generator: generator, expression: expression, loc: loc, + async: async } - end + end @spec assignment_expression( - ESTree.assignment_operator, - ESTree.Pattern.t | ESTree.Expression.t, + ESTree.assignment_operator, + ESTree.Pattern.t, ESTree.Expression.t, ESTree.SourceLocation.t | nil ) :: ESTree.AssignmentExpression.t def assignment_expression(operator, left, right, loc \\ nil) do - %ESTree.AssignmentExpression{ + %ESTree.AssignmentExpression{ operator: operator, left: left, right: right, loc: loc } end + @spec assignment_property( + ESTree.Pattern.t, + ESTree.SourceLocation.t | nil + ) :: ESTree.AssignmentProperty.t + def assignment_property(value, loc \\ nil) do + %ESTree.AssignmentProperty{ + value: value, loc: loc + } + end + + @spec await_expression( - ESTree.Expression.t | nil, + ESTree.Expression.t | nil, boolean, ESTree.SourceLocation.t | nil ) :: ESTree.AwaitExpression.t def await_expression(argument, all \\ false, loc \\ nil) do - %ESTree.AwaitExpression{ + %ESTree.AwaitExpression{ argument: argument, all: all, loc: loc } end @spec binary_expression( - ESTree.binary_operator, - ESTree.Expression.t, + ESTree.binary_operator, + ESTree.Expression.t, ESTree.Expression.t, ESTree.SourceLocation.t | nil ) :: ESTree.BinaryExpression.t def binary_expression(operator, left, right, loc \\ nil) do - %ESTree.BinaryExpression{ + %ESTree.BinaryExpression{ operator: operator, left: left, right: right, loc: loc } end @@ -74,7 +86,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.BlockStatement.t def block_statement(body, loc \\ nil) do - %ESTree.BlockStatement{ + %ESTree.BlockStatement{ body: body, loc: loc } end @@ -84,10 +96,10 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.BreakStatement.t def break_statement(label \\ nil, loc \\ nil) do - %ESTree.BreakStatement{ + %ESTree.BreakStatement{ label: label, loc: loc } - end + end @spec call_expression( ESTree.Expression.t, @@ -95,10 +107,10 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.CallExpression.t def call_expression(callee, arguments, loc \\ nil) do - %ESTree.CallExpression{ + %ESTree.CallExpression{ callee: callee, arguments: arguments, loc: loc } - end + end @spec catch_clause( ESTree.Pattern.t, @@ -106,20 +118,20 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.CatchClause.t def catch_clause(param, body, loc \\ nil) do - %ESTree.CatchClause{ + %ESTree.CatchClause{ param: param, body: body, loc: loc } - end + end @spec class_body( [ESTree.MethodDefinition.t], ESTree.SourceLocation.t | nil ) :: ESTree.ClassBody.t def class_body(body, loc \\ nil) do - %ESTree.ClassBody{ + %ESTree.ClassBody{ body: body, loc: loc } - end + end @spec class_declaration( ESTree.Identifier.t, @@ -128,10 +140,10 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ClassDeclaration.t def class_declaration(id, body, superClass \\ nil, loc \\ nil) do - %ESTree.ClassDeclaration{ + %ESTree.ClassDeclaration{ id: id, body: body, loc: loc, superClass: superClass } - end + end @spec class_expression( ESTree.ClassBody.t, @@ -139,10 +151,10 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ClassExpression.t def class_expression(body, superClass \\ nil, loc \\ nil) do - %ESTree.ClassExpression{ + %ESTree.ClassExpression{ body: body, loc: loc, superClass: superClass } - end + end @spec conditional_statement( ESTree.Expression.t, @@ -151,7 +163,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ConditionalStatement.t def conditional_statement(test, alternate, consequent, loc \\ nil) do - %ESTree.ConditionalStatement{ + %ESTree.ConditionalStatement{ test: test, alternate: alternate, consequent: consequent, loc: loc } end @@ -161,16 +173,16 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ContinueStatement.t def continue_statement(label, loc \\ nil) do - %ESTree.ContinueStatement{ + %ESTree.ContinueStatement{ label: label, loc: loc } - end + end @spec debugger_statement( ESTree.SourceLocation.t | nil ) :: ESTree.DebuggerStatement.t def debugger_statement(loc \\ nil) do - %ESTree.DebuggerStatement{ + %ESTree.DebuggerStatement{ loc: loc } end @@ -181,7 +193,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.DoWhileStatement.t def do_while_statement(body, test, loc \\ nil) do - %ESTree.DoWhileStatement{ + %ESTree.DoWhileStatement{ body: body, test: test, loc: loc } end @@ -190,36 +202,36 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.EmptyExpression.t def empty_expression(loc \\ nil) do - %ESTree.EmptyExpression{ + %ESTree.EmptyExpression{ loc: loc } - end + end @spec empty_statement( ESTree.SourceLocation.t | nil ) :: ESTree.EmptyStatement.t def empty_statement(loc \\ nil) do - %ESTree.EmptyStatement{ + %ESTree.EmptyStatement{ loc: loc } end @spec export_all_declaration( - ESTree.Identifier.t | nil, + ESTree.Identifier.t | nil, ESTree.SourceLocation.t | nil ) :: ESTree.ExportAllDeclaration.t def export_all_declaration(source \\ nil, loc \\ nil) do - %ESTree.ExportAllDeclaration{ + %ESTree.ExportAllDeclaration{ loc: loc, source: source } end @spec export_default_declaration( - ESTree.Declaration.t | ESTree.Expression.t | nil, + ESTree.Declaration.t | ESTree.Expression.t | nil, ESTree.SourceLocation.t | nil ) :: ESTree.ExportDefaultDeclaration.t def export_default_declaration(declaration \\ nil, loc \\ nil) do - %ESTree.ExportDefaultDeclaration{ + %ESTree.ExportDefaultDeclaration{ loc: loc, declaration: declaration } end @@ -231,7 +243,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ExportNamedDeclaration.t def export_named_declaration(declaration, specifiers \\ [], source \\ nil, loc \\ nil) do - %ESTree.ExportNamedDeclaration{ + %ESTree.ExportNamedDeclaration{ declaration: declaration, specifiers: specifiers, source: source, loc: loc } @@ -239,11 +251,11 @@ defmodule ESTree.Tools.Builder do @spec export_specifier( ESTree.Identifier.t, - ESTree.Identifier.t | nil, + ESTree.Identifier.t | nil, ESTree.SourceLocation.t | nil ) :: ESTree.ExportSpecifier.t def export_specifier(exported, local \\ nil, loc \\ nil) do - %ESTree.ExportSpecifier{ + %ESTree.ExportSpecifier{ local: local, exported: exported, loc: loc } end @@ -253,31 +265,31 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ExpressionStatement.t def expression_statement(expression, loc \\ nil) do - %ESTree.ExpressionStatement{ + %ESTree.ExpressionStatement{ expression: expression, loc: loc } end @spec for_in_statement( - ESTree.VariableDeclaration.t | ESTree.Expression.t, + ESTree.VariableDeclaration.t | ESTree.Pattern.t, ESTree.Expression.t, ESTree.Statement.t, ESTree.SourceLocation.t | nil ) :: ESTree.ForInStatement.t def for_in_statement(left, right, body, loc \\ nil) do - %ESTree.ForInStatement{ + %ESTree.ForInStatement{ left: left, right: right, body: body, loc: loc } end @spec for_of_statement( - ESTree.VariableDeclaration.t | ESTree.Expression.t, + ESTree.VariableDeclaration.t | ESTree.Pattern.t, ESTree.Expression.t, ESTree.Statement.t, ESTree.SourceLocation.t | nil ) :: ESTree.ForOfStatement.t def for_of_statement(left, right, body, loc \\ nil) do - %ESTree.ForOfStatement{ + %ESTree.ForOfStatement{ left: left, right: right, body: body, loc: loc } end @@ -290,15 +302,15 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ForStatement.t def for_statement(init, test, update, body, loc \\ nil) do - %ESTree.ForStatement{ + %ESTree.ForStatement{ init: init, test: test, update: update, body: body, loc: loc } end @spec function_declaration( ESTree.Identifier.t, - [ESTree.Pattern.t], - [ESTree.Expression.t], + [ESTree.Pattern.t], + [ESTree.Expression.t], ESTree.BlockStatement.t, boolean, boolean, @@ -306,15 +318,15 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.FunctionDeclaration.t def function_declaration(id, params, defaults, body, generator \\ false, expression \\ false, async \\ false, loc \\ nil) do - %ESTree.FunctionDeclaration{ - id: id, params: params, defaults: defaults, + %ESTree.FunctionDeclaration{ + id: id, params: params, defaults: defaults, body: body, generator: generator, async: async, - expression: expression, loc: loc + expression: expression, loc: loc } - end + end @spec function_expression( - [ESTree.Pattern.t], + [ESTree.Pattern.t], [ ESTree.Expression.t ], ESTree.BlockStatement.t, boolean, @@ -323,19 +335,19 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.FunctionExpression.t def function_expression(params, defaults, body, generator \\ false, expression \\ false, async \\ false, loc \\ nil) do - %ESTree.FunctionExpression{ + %ESTree.FunctionExpression{ params: params, defaults: defaults, body: body, generator: generator, async: async, - expression: expression, loc: loc + expression: expression, loc: loc } - end + end @spec identifier( binary, ESTree.SourceLocation.t | nil ) :: ESTree.Identifier.t def identifier(name, loc \\ nil) do - %ESTree.Identifier{ + %ESTree.Identifier{ name: name, loc: loc } end @@ -347,7 +359,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.IfStatement.t def if_statement(test, consequent, alternate \\ nil, loc \\ nil) do - %ESTree.IfStatement{ + %ESTree.IfStatement{ test: test, consequent: consequent, alternate: alternate, loc: loc } end @@ -358,7 +370,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ImportDeclaration.t def import_declaration(specifiers, source \\ nil, loc \\ nil) do - %ESTree.ImportDeclaration{ + %ESTree.ImportDeclaration{ specifiers: specifiers, source: source, loc: loc } end @@ -368,7 +380,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ImportDefaultSpecifier.t def import_default_specifier(local, loc \\ nil) do - %ESTree.ImportDefaultSpecifier{ + %ESTree.ImportDefaultSpecifier{ local: local, loc: loc } end @@ -378,7 +390,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ImportNamespaceSpecifier.t def import_namespace_specifier(local, loc \\ nil) do - %ESTree.ImportNamespaceSpecifier{ + %ESTree.ImportNamespaceSpecifier{ local: local, loc: loc } end @@ -389,7 +401,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ImportSpecifier.t def import_specifier(imported, local \\ nil, loc \\ nil) do - %ESTree.ImportSpecifier{ + %ESTree.ImportSpecifier{ local: local, imported: imported, loc: loc } end @@ -400,7 +412,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.LabeledStatement.t def labeled_statement(label, body, loc \\ nil) do - %ESTree.LabeledStatement{ + %ESTree.LabeledStatement{ label: label, body: body, loc: loc } end @@ -411,48 +423,48 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.Literal.t def literal(value, regex \\ nil, loc \\ nil) do - %ESTree.Literal{ + %ESTree.Literal{ value: value, regex: regex, loc: loc } end @spec logical_expression( - ESTree.logical_operator, - ESTree.Expression.t, + ESTree.logical_operator, + ESTree.Expression.t, ESTree.Expression.t, ESTree.SourceLocation.t | nil ) :: ESTree.LogicalExpression.t def logical_expression(operator, left, right, loc \\ nil) do - %ESTree.LogicalExpression{ + %ESTree.LogicalExpression{ operator: operator, left: left, right: right, loc: loc } end @spec member_expression( - ESTree.Expression.t | ESTree.Super.t, + ESTree.Expression.t | ESTree.Super.t, ESTree.Identifier.t | ESTree.Expression.t, boolean, ESTree.SourceLocation.t | nil ) :: ESTree.MemberExpression.t def member_expression(object, property, computed \\ false, loc \\ nil) do - %ESTree.MemberExpression{ + %ESTree.MemberExpression{ object: object, property: property, computed: computed, loc: loc } end @spec meta_property( - ESTree.Identifier.t, + ESTree.Identifier.t, ESTree.Identifier.t, ESTree.SourceLocation.t | nil ) :: ESTree.MetaProperty.t def meta_property(meta, property, loc \\ nil) do - %ESTree.MetaProperty{ + %ESTree.MetaProperty{ meta: meta, property: property, loc: loc } end @spec method_definition( - ESTree.Identifier.t, + ESTree.Identifier.t, ESTree.FunctionExpression.t, :constructor | :method | :get | :set, boolean, @@ -460,7 +472,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.MethodDefinition.t def method_definition(key, value, kind \\ :method, computed \\ false, static \\ false, loc \\ nil) do - %ESTree.MethodDefinition{ + %ESTree.MethodDefinition{ key: key, value: value, kind: kind, computed: computed, static: static, loc: loc } end @@ -471,7 +483,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.NewExpression.t def new_expression(callee, arguments, loc \\ nil) do - %ESTree.NewExpression{ + %ESTree.NewExpression{ callee: callee, arguments: arguments, loc: loc } end @@ -481,27 +493,27 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ObjectExpression.t def object_expression(properties, loc \\ nil) do - %ESTree.ObjectExpression{ + %ESTree.ObjectExpression{ properties: properties, loc: loc } - end + end @spec object_pattern( [ESTree.Property.t], ESTree.SourceLocation.t | nil ) :: ESTree.ObjectPattern.t def object_pattern(properties, loc \\ nil) do - %ESTree.ObjectPattern{ + %ESTree.ObjectPattern{ properties: properties, loc: loc } - end + end @spec position( pos_integer, non_neg_integer ) :: ESTree.Position.t def position(line, column) do - %ESTree.Position{ + %ESTree.Position{ line: line, column: column } end @@ -512,13 +524,13 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.Program.t def program(body, sourceType \\ :script, loc \\ nil) do - %ESTree.Program{ + %ESTree.Program{ body: body, loc: loc, sourceType: sourceType } end @spec property( - ESTree.Expression.t, + ESTree.Expression.t, ESTree.Expression.t , :init | :get | :set, boolean, @@ -527,7 +539,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.Property.t def property(key, value, kind \\ :init, shorthand \\ false, method \\ false, computed \\false, loc \\ nil) do - %ESTree.Property{ + %ESTree.Property{ key: key, value: value, kind: kind, shorthand: shorthand, method: method, loc: loc, computed: computed @@ -539,7 +551,7 @@ defmodule ESTree.Tools.Builder do binary ) :: ESTree.Regex.t def regex(pattern, flags) do - %ESTree.Regex{ + %ESTree.Regex{ pattern: pattern, flags: flags } end @@ -549,7 +561,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.RestElement.t def rest_element(argument, loc \\ nil) do - %ESTree.RestElement{ + %ESTree.RestElement{ argument: argument, loc: loc } end @@ -559,7 +571,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ReturnStatement.t def return_statement(argument, loc \\ nil) do - %ESTree.ReturnStatement{ + %ESTree.ReturnStatement{ argument: argument, loc: loc } end @@ -569,7 +581,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.SequenceExpression.t def sequence_expression(expressions, loc \\ nil) do - %ESTree.SequenceExpression{ + %ESTree.SequenceExpression{ expressions: expressions, loc: loc } end @@ -580,7 +592,7 @@ defmodule ESTree.Tools.Builder do ESTree.Position.t ) :: ESTree.SourceLocation.t def source_location(source, start, the_end) do - %ESTree.SourceLocation{ + %ESTree.SourceLocation{ source: source, start: start, end: the_end } end @@ -590,7 +602,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.SpreadElement.t def spread_element(argument, loc \\ nil) do - %ESTree.SpreadElement{ + %ESTree.SpreadElement{ argument: argument, loc: loc } end @@ -599,7 +611,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.Super.t def super(loc \\ nil) do - %ESTree.Super{ + %ESTree.Super{ loc: loc } end @@ -610,7 +622,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.SwitchCase.t def switch_case(test, consequent, loc \\ nil) do - %ESTree.SwitchCase{ + %ESTree.SwitchCase{ test: test, consequent: consequent, loc: loc } end @@ -621,7 +633,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.SwitchStatement.t def switch_statement(discriminant, cases, loc \\ nil) do - %ESTree.SwitchStatement{ + %ESTree.SwitchStatement{ discriminant: discriminant, cases: cases, loc: loc } end @@ -632,7 +644,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.TaggedTemplateExpression.t def tagged_template_expression(tag, quasi, loc \\ nil) do - %ESTree.TaggedTemplateExpression{ + %ESTree.TaggedTemplateExpression{ tag: tag, quasi: quasi, loc: loc } end @@ -644,7 +656,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.TemplateElement.t def template_element(raw, cooked_value, tail, loc \\ nil) do - %ESTree.TemplateElement{ + %ESTree.TemplateElement{ value: %{cooked: cooked_value, raw: raw}, tail: tail, loc: loc } end @@ -655,7 +667,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.TemplateLiteral.t def template_literal(quasis, expressions, loc \\ nil) do - %ESTree.TemplateLiteral{ + %ESTree.TemplateLiteral{ quasis: quasis, expressions: expressions, loc: loc } end @@ -664,7 +676,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ThisExpression.t def this_expression(loc \\ nil) do - %ESTree.ThisExpression{ + %ESTree.ThisExpression{ loc: loc } end @@ -674,7 +686,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.ThrowStatement.t def throw_statement(argument, loc \\ nil) do - %ESTree.ThrowStatement{ + %ESTree.ThrowStatement{ argument: argument, loc: loc } end @@ -686,76 +698,76 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.TryStatement.t def try_statement(block, handler, finalizer \\ nil, loc \\ nil) do - %ESTree.TryStatement{ + %ESTree.TryStatement{ block: block, handler: handler, finalizer: finalizer, loc: loc } end @spec unary_expression( - ESTree.unary_operator, - boolean, + ESTree.unary_operator, + boolean, ESTree.Expression.t, ESTree.SourceLocation.t | nil ) :: ESTree.UnaryExpression.t def unary_expression(operator, prefix, argument, loc \\ nil) do - %ESTree.UnaryExpression{ + %ESTree.UnaryExpression{ operator: operator, prefix: prefix, argument: argument, loc: loc } end @spec update_expression( - ESTree.update_operator, + ESTree.update_operator, ESTree.Expression.t, - boolean, + boolean, ESTree.SourceLocation.t | nil ) :: ESTree.UpdateExpression.t def update_expression(operator, argument, prefix, loc \\ nil) do - %ESTree.UpdateExpression{ + %ESTree.UpdateExpression{ operator: operator, prefix: prefix, argument: argument, loc: loc } end @spec variable_declaration( - [ESTree.VariableDeclarator.t], + [ESTree.VariableDeclarator.t], :var | :let | :const, ESTree.SourceLocation.t | nil ) :: ESTree.VariableDeclaration.t def variable_declaration(declarations, kind \\ :var, loc \\ nil) do - %ESTree.VariableDeclaration{ + %ESTree.VariableDeclaration{ declarations: declarations, kind: kind, loc: loc } end @spec variable_declarator( - ESTree.Pattern.t, + ESTree.Pattern.t, ESTree.Expression.t | nil, ESTree.SourceLocation.t | nil ) :: ESTree.VariableDeclarator.t def variable_declarator(id, init \\ nil, loc \\ nil) do - %ESTree.VariableDeclarator{ + %ESTree.VariableDeclarator{ id: id, init: init, loc: loc } end @spec while_statement( - ESTree.Expression.t, + ESTree.Expression.t, ESTree.Statement.t, ESTree.SourceLocation.t | nil ) :: ESTree.WhileStatement.t def while_statement(test, body, loc \\ nil) do - %ESTree.WhileStatement{ + %ESTree.WhileStatement{ test: test, body: body, loc: loc } end @spec with_statement( - ESTree.Expression.t, + ESTree.Expression.t, ESTree.Statement.t, ESTree.SourceLocation.t | nil ) :: ESTree.WithStatement.t def with_statement(object, body, loc \\ nil) do - %ESTree.WithStatement{ + %ESTree.WithStatement{ object: object, body: body, loc: loc } end @@ -766,7 +778,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.YieldExpression.t def yield_expression(argument \\ nil, delegate \\ false, loc \\ nil) do - %ESTree.YieldExpression{ + %ESTree.YieldExpression{ argument: argument, loc: loc, delegate: delegate } end @@ -777,7 +789,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.JSXIdentifier.t def jsx_identifier(name, loc \\ nil) do - %ESTree.JSXIdentifier{ + %ESTree.JSXIdentifier{ name: name, loc: loc } end @@ -789,7 +801,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.MemberExpression.t def jsx_member_expression(object, property, loc \\ nil) do - %ESTree.JSXMemberExpression{ + %ESTree.JSXMemberExpression{ object: object, property: property, loc: loc } end @@ -800,7 +812,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.JSXNamespacedName.t def jsx_namespaced_name(namespace, name, loc \\ nil) do - %ESTree.JSXNamespacedName{ + %ESTree.JSXNamespacedName{ namespace: namespace, name: name, loc: loc } end @@ -809,7 +821,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.JSXEmptyExpression.t def jsx_empty_expression(loc \\ nil) do - %ESTree.JSXEmptyExpression{ + %ESTree.JSXEmptyExpression{ loc: loc } end @@ -819,7 +831,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.JSXExpressionContainer.t def jsx_expression_container(expression, loc \\ nil) do - %ESTree.JSXExpressionContainer{ + %ESTree.JSXExpressionContainer{ expression: expression, loc: loc } end @@ -832,7 +844,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.JSXOpeningElement.t def jsx_opening_element(name, attributes \\ [], selfClosing \\ false, loc \\ nil) do - %ESTree.JSXOpeningElement{ + %ESTree.JSXOpeningElement{ name: name, attributes: attributes, selfClosing: selfClosing, loc: loc } end @@ -843,7 +855,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.JSXClosingElement.t def jsx_closing_element(name, loc \\ nil) do - %ESTree.JSXClosingElement{ + %ESTree.JSXClosingElement{ name: name, loc: loc } end @@ -855,7 +867,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.JSXAttribute.t def jsx_attribute(name, value \\ nil, loc \\ nil) do - %ESTree.JSXAttribute{ + %ESTree.JSXAttribute{ name: name, value: value, loc: loc } end @@ -865,7 +877,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.SpreadElement.t def jsx_spread_attribute(argument, loc \\ nil) do - %ESTree.JSXSpreadAttribute{ + %ESTree.JSXSpreadAttribute{ argument: argument, loc: loc } end @@ -877,7 +889,7 @@ defmodule ESTree.Tools.Builder do ESTree.SourceLocation.t | nil ) :: ESTree.JSXElement.t def jsx_element(openingElement, children \\ [], closingElement \\ nil, loc \\ nil) do - %ESTree.JSXElement{ + %ESTree.JSXElement{ openingElement: openingElement, children: children, closingElement: closingElement, loc: loc } end diff --git a/lib/es_tree/tools/estree_json_transformer.ex b/lib/es_tree/tools/estree_json_transformer.ex new file mode 100644 index 0000000..9ba23da --- /dev/null +++ b/lib/es_tree/tools/estree_json_transformer.ex @@ -0,0 +1,569 @@ +defmodule ESTree.Tools.ESTreeJSONTransformer do + @moduledoc """ + Converts ESTree JSON into the structs in the ESTree library + """ + + + @spec convert(%{}) :: ESTree.Node.t + def convert(json) + + def convert(%{"type" => "ArrayExpression"} = json) do + %ESTree.ArrayExpression{ + elements: convert_property(json, "elements", []) + } + end + + def convert(%{"type" => "ArrayPattern"} = json) do + %ESTree.ArrayPattern{ + elements: convert_property(json, "elements", []) + } + end + + def convert(%{"type" => "ArrowFunctionExpression"} = json) do + %ESTree.ArrowFunctionExpression{ + generator: convert_property(json, "generator", false), + expression: convert_property(json, "expression", false), + params: convert_property(json, "params", []), + body: convert_property(json, "body", []) + } + end + + def convert(%{"type" => "AssignmentExpression"} = json) do + %ESTree.AssignmentExpression{ + operator: convert_property_to_atom(json, "operator", "="), + left: convert_property(json, "left"), + right: convert_property(json, "right") + } + end + + def convert(%{"type" => "AssignmentPattern"} = json) do + %ESTree.AssignmentPattern{ + left: convert_property(json, "left"), + right: convert_property(json, "right") + } + end + + def convert(%{"type" => "AwaitExpression"} = json) do + %ESTree.AwaitExpression{ + argument: convert_property(json, "argument") + } + end + + def convert(%{"type" => "BinaryExpression"} = json) do + %ESTree.BinaryExpression{ + operator: convert_property_to_atom(json, "operator", "="), + left: convert_property(json, "left"), + right: convert_property(json, "right") + } + end + + def convert(%{"type" => "BlockStatement"} = json) do + %ESTree.BlockStatement{ + body: convert_property(json, "body", []) + } + end + + def convert(%{"type" => "BreakStatement"} = json) do + %ESTree.BreakStatement{ + label: convert_property(json, "label") + } + end + + def convert(%{"type" => "CallExpression"} = json) do + %ESTree.CallExpression{ + callee: convert_property(json, "callee", false), + arguments: convert_property(json, "arguments", []) + } + end + + def convert(%{"type" => "CatchClause"} = json) do + %ESTree.CatchClause{ + param: convert_property(json, "param"), + body: convert_property(json, "body") + } + end + + def convert(%{"type" => "ClassBody"} = json) do + %ESTree.ClassBody{ + body: convert_property(json, "body", []) + } + end + + def convert(%{"type" => "ClassDeclaration"} = json) do + %ESTree.ClassDeclaration{ + id: convert_property(json, "id"), + body: convert_property(json, "body"), + superClass: convert_property(json, "superClass") + } + end + + def convert(%{"type" => "ClassExpression"} = json) do + %ESTree.ClassExpression{ + body: convert_property(json, "body"), + superClass: convert_property(json, "superClass") + } + end + + def convert(%{"type" => "ConditionalStatement"} = json) do + %ESTree.ConditionalStatement{ + test: convert_property(json, "test"), + alternate: convert_property(json, "alternate"), + consequent: convert_property(json, "consequent") + } + end + + def convert(%{"type" => "ContinueStatement"} = json) do + %ESTree.ContinueStatement{ + label: convert_property(json, "label") + } + end + + def convert(%{"type" => "DebuggerStatement"}) do + %ESTree.DebuggerStatement{} + end + + def convert(%{"type" => "DoWhileStatement"} = json) do + %ESTree.DoWhileStatement{ + test: convert_property(json, "test"), + body: convert_property(json, "body") + } + end + + def convert(%{"type" => "EmptyExpression"}) do + %ESTree.EmptyExpression{} + end + + def convert(%{"type" => "EmptyStatement"}) do + %ESTree.EmptyStatement{} + end + + def convert(%{"type" => "ExportAllDeclaration"} = json) do + %ESTree.ExportAllDeclaration{ + source: convert_property(json, "source") + } + end + + def convert(%{"type" => "ExportDefaultDeclaration"} = json) do + %ESTree.ExportDefaultDeclaration{ + declaration: convert_property(json, "declaration") + } + end + + def convert(%{"type" => "ExportNamedDeclaration"} = json) do + %ESTree.ExportNamedDeclaration{ + declaration: convert_property(json, "declaration"), + specifiers: convert_property(json, "specifiers", []), + source: convert_property(json, "source") + } + end + + def convert(%{"type" => "ExportSpecifier"} = json) do + %ESTree.ExportSpecifier{ + local: convert_property(json, "local"), + exported: convert_property(json, "exported") + } + end + + def convert(%{"type" => "ExpressionStatement"} = json) do + %ESTree.ExpressionStatement{ + expression: convert_property(json, "expression") + } + end + + def convert(%{"type" => "ForInStatement"} = json) do + %ESTree.ForInStatement{ + left: convert_property(json, "left"), + right: convert_property(json, "right"), + body: convert_property(json, "body") + } + end + + def convert(%{"type" => "ForOfStatement"} = json) do + %ESTree.ForOfStatement{ + left: convert_property(json, "left"), + right: convert_property(json, "right"), + body: convert_property(json, "body") + } + end + + def convert(%{"type" => "ForStatement"} = json) do + %ESTree.ForStatement{ + init: convert_property(json, "init"), + test: convert_property(json, "test"), + update: convert_property(json, "update"), + body: convert_property(json, "body") + } + end + + def convert(%{"type" => "FunctionDeclaration"} = json) do + %ESTree.FunctionDeclaration{ + id: convert_property(json, "id"), + params: convert_property(json, "params", []), + defaults: convert_property(json, "defaults", []), + body: convert_property(json, "body"), + generator: convert_property(json, "generator", false), + expression: convert_property(json, "expression", false), + async: convert_property(json, "async", false) + } + end + + def convert(%{"type" => "FunctionExpression"} = json) do + %ESTree.FunctionExpression{ + params: convert_property(json, "params", []), + defaults: convert_property(json, "defaults", []), + body: convert_property(json, "body"), + generator: convert_property(json, "generator", false), + expression: convert_property(json, "expression", false), + async: convert_property(json, "async", false) + } + end + + def convert(%{"type" => "Identifier"} = json) do + %ESTree.Identifier{ + name: convert_property(json, "name") + } + end + + def convert(%{"type" => "IfStatement"} = json) do + %ESTree.IfStatement{ + test: convert_property(json, "test"), + consequent: convert_property(json, "consequent"), + alternate: convert_property(json, "alternate") + } + end + + def convert(%{"type" => "ImportDeclaration"} = json) do + %ESTree.ImportDeclaration{ + specifiers: convert_property(json, "specifiers", []), + source: convert_property(json, "source") + } + end + + def convert(%{"type" => "ImportDefaultSpecifier"} = json) do + %ESTree.ImportDefaultSpecifier{ + local: convert_property(json, "local") + } + end + + def convert(%{"type" => "ImportNamespaceSpecifier"} = json) do + %ESTree.ImportNamespaceSpecifier{ + local: convert_property(json, "local") + } + end + + def convert(%{"type" => "ImportSpecifier"} = json) do + %ESTree.ImportSpecifier{ + local: convert_property(json, "local"), + imported: convert_property(json, "imported") + } + end + + def convert(%{"type" => "JSXAttribute"} = json) do + %ESTree.JSXAttribute{ + name: convert_property(json, "name"), + value: convert_property(json, "value") + } + end + + def convert(%{"type" => "JSXClosingElement"} = json) do + %ESTree.JSXClosingElement{ + name: convert_property(json, "name") + } + end + + def convert(%{"type" => "JSXElement"} = json) do + %ESTree.JSXElement{ + openingElement: convert_property(json, "openingElement"), + children: convert_property(json, "children", []), + closingElement: convert_property(json, "closingElement") + } + end + + def convert(%{"type" => "JSXEmptyExpression"}) do + %ESTree.JSXEmptyExpression{} + end + + def convert(%{"type" => "JSXExpressionContainer"} = json) do + %ESTree.JSXExpressionContainer{ + expression: convert_property(json, "expression") + } + end + + def convert(%{"type" => "JSXIdentifier"} = json) do + %ESTree.JSXIdentifier{ + name: convert_property(json, "name") + } + end + + def convert(%{"type" => "JSXMemberExpression"} = json) do + %ESTree.JSXMemberExpression{ + object: convert_property(json, "object"), + property: convert_property(json, "property") + } + end + + def convert(%{"type" => "JSXNamespacedName"} = json) do + %ESTree.JSXNamespacedName{ + namespace: convert_property(json, "namespace"), + name: convert_property(json, "name") + } + end + + def convert(%{"type" => "JSXOpeningElement"} = json) do + %ESTree.JSXOpeningElement{ + name: convert_property(json, "name"), + attributes: convert_property(json, "attributes", []), + selfClosing: convert_property(json, "selfClosing", false) + } + end + + def convert(%{"type" => "JSXSpreadAttribute"} = json) do + %ESTree.JSXSpreadAttribute{ + argument: convert_property(json, "argument") + } + end + + def convert(%{"type" => "LabeledStatement"} = json) do + %ESTree.LabeledStatement{ + label: convert_property(json, "label"), + body: convert_property(json, "body") + } + end + + def convert(%{"type" => "Literal"} = json) do + %ESTree.Literal{ + value: convert_property(json, "value") + } + end + + def convert(%{"type" => "LogicalExpression"} = json) do + %ESTree.LogicalExpression{ + operator: convert_property_to_atom(json, "operator", ""), + left: convert_property(json, "left"), + right: convert_property(json, "right") + } + end + + def convert(%{"type" => "MemberExpression"} = json) do + %ESTree.MemberExpression{ + object: convert_property(json, "object"), + property: convert_property(json, "property"), + computed: convert_property(json, "computed", false) + } + end + + def convert(%{"type" => "MetaProperty"} = json) do + %ESTree.MetaProperty{ + meta: convert_property(json, "meta"), + property: convert_property(json, "property") + } + end + + def convert(%{"type" => "MethodDefinition"} = json) do + %ESTree.MethodDefinition{ + key: convert_property(json, "key"), + value: convert_property(json, "value"), + kind: convert_property_to_atom(json, "kind", ""), + computed: convert_property(json, "computed", false), + static: convert_property(json, "static", false) + } + end + + def convert(%{"type" => "NewExpression"} = json) do + %ESTree.NewExpression{ + callee: convert_property(json, "callee", false), + arguments: convert_property(json, "arguments", []) + } + end + + def convert(%{"type" => "ObjectExpression"} = json) do + %ESTree.ObjectExpression{ + properties: convert_property(json, "properties", []) + } + end + + def convert(%{"type" => "ObjectPattern"} = json) do + %ESTree.ObjectPattern{ + properties: convert_property(json, "properties", []) + } + end + + def convert(%{"type" => "Program"} = json) do + %ESTree.Program{ + body: convert_property(json, "body", []), + sourceType: convert_property(json, "sourceType", "module") + } + end + + def convert(%{"type" => "Property"} = json) do + %ESTree.Property{ + key: convert_property(json, "key"), + value: convert_property(json, "value"), + kind: convert_property_to_atom(json, "kind", ""), + method: convert_property(json, "method", false), + shorthand: convert_property(json, "shorthand", false), + computed: convert_property(json, "computed", false) + } + end + + def convert(%{"type" => "RestElement"} = json) do + %ESTree.RestElement{ + argument: convert_property(json, "argument") + } + end + + def convert(%{"type" => "ReturnStatement"} = json) do + %ESTree.ReturnStatement{ + argument: convert_property(json, "argument") + } + end + + def convert(%{"type" => "SequenceExpression"} = json) do + %ESTree.SequenceExpression{ + expressions: convert_property(json, "expressions", []) + } + end + + def convert(%{"type" => "SpreadElement"} = json) do + %ESTree.SpreadElement{ + argument: convert_property(json, "argument") + } + end + + def convert(%{"type" => "Super"}) do + %ESTree.Super{} + end + + def convert(%{"type" => "SwitchCase"} = json) do + %ESTree.SwitchCase{ + test: convert_property(json, "test"), + consequent: convert_property(json, "consequent") + } + end + + def convert(%{"type" => "SwitchStatement"} = json) do + %ESTree.SwitchStatement{ + discriminant: convert_property(json, "discriminant"), + cases: convert_property(json, "cases", []) + } + end + + def convert(%{"type" => "TaggedTemplateExpression"} = json) do + %ESTree.TaggedTemplateExpression{ + tag: convert_property(json, "tag"), + quasi: convert_property(json, "quasi") + } + end + + def convert(%{"type" => "TemplateElement"} = json) do + value = case Map.get(json, "value") do + map when is_map(map) -> + %{ + cooked: convert_property(map, "cooked"), + raw: convert_property(map, "raw") + } + other -> + other + end + + %ESTree.TemplateElement{ + value: value, + tail: convert_property(json, "tail", false) + } + end + + def convert(%{"type" => "TemplateLiteral"} = json) do + %ESTree.TemplateLiteral{ + quasis: convert_property(json, "quasis", []), + expressions: convert_property(json, "expressions", []) + } + end + + def convert(%{"type" => "ThisExpression"}) do + %ESTree.ThisExpression{} + end + + def convert(%{"type" => "ThrowStatement"} = json) do + %ESTree.ThrowStatement{ + argument: convert_property(json, "argument") + } + end + + def convert(%{"type" => "TryStatement"} = json) do + %ESTree.TryStatement{ + block: convert_property(json, "block"), + handler: convert_property(json, "handler"), + finalizer: convert_property(json, "finalizer") + } + end + + def convert(%{"type" => "UnaryExpression"} = json) do + %ESTree.UnaryExpression{ + operator: convert_property_to_atom(json, "operator", ""), + prefix: convert_property(json, "prefix", false), + argument: convert_property(json, "argument") + } + end + + def convert(%{"type" => "UpdateExpression"} = json) do + %ESTree.UpdateExpression{ + operator: convert_property_to_atom(json, "operator", ""), + prefix: convert_property(json, "prefix", false), + argument: convert_property(json, "argument") + } + end + + def convert(%{"type" => "VariableDeclaration"} = json) do + %ESTree.VariableDeclaration{ + kind: convert_property_to_atom(json, "kind", "var"), + declarations: convert_property(json, "declarations", []) + } + end + + def convert(%{"type" => "VariableDeclarator"} = json) do + %ESTree.VariableDeclarator{ + id: convert_property(json, "id"), + init: convert_property(json, "init") + } + end + + def convert(%{"type" => "WhileStatement"} = json) do + %ESTree.WhileStatement{ + test: convert_property(json, "test"), + body: convert_property(json, "body") + } + end + + def convert(%{"type" => "WithStatement"} = json) do + %ESTree.WithStatement{ + object: convert_property(json, "object"), + body: convert_property(json, "body") + } + end + + def convert(%{"type" => "YieldExpression"} = json) do + %ESTree.YieldExpression{ + argument: convert_property(json, "argument"), + delegate: convert_property(json, "delegate", false) + } + end + + defp convert_property_to_atom(json, property, default) do + json + |> Map.get(property, default) + |> String.to_atom() + end + + defp convert_property(json, property, default \\ nil) do + case Map.get(json, property, default) do + nil -> + nil + list when is_list(list) -> + Enum.map(list, &convert/1) + map when is_map(map) -> + convert(map) + val -> + val + end + end +end \ No newline at end of file diff --git a/lib/es_tree/tools/generator.ex b/lib/es_tree/tools/generator.ex index efa9b48..3d44623 100644 --- a/lib/es_tree/tools/generator.ex +++ b/lib/es_tree/tools/generator.ex @@ -4,763 +4,1531 @@ defmodule ESTree.Tools.Generator do it to JavaScript code """ + alias ESTree.ArrayExpression + alias ESTree.ArrayPattern + alias ESTree.ArrowFunctionExpression + alias ESTree.AssignmentExpression + alias ESTree.AssignmentPattern + alias ESTree.AssignmentProperty + alias ESTree.AwaitExpression + alias ESTree.BinaryExpression + alias ESTree.BlockStatement + alias ESTree.BreakStatement + alias ESTree.CallExpression + alias ESTree.CatchClause + alias ESTree.ClassBody + alias ESTree.ClassDeclaration + alias ESTree.ClassExpression + alias ESTree.ConditionalStatement + alias ESTree.ContinueStatement + alias ESTree.DebuggerStatement + alias ESTree.DoWhileStatement + alias ESTree.EmptyStatement + alias ESTree.ExportAllDeclaration + alias ESTree.ExportDefaultDeclaration + alias ESTree.ExportNamedDeclaration + alias ESTree.ExportSpecifier + alias ESTree.ExpressionStatement + alias ESTree.ForInStatement + alias ESTree.ForOfStatement + alias ESTree.ForStatement + alias ESTree.FunctionDeclaration + alias ESTree.FunctionExpression + alias ESTree.Identifier + alias ESTree.IfStatement + alias ESTree.ImportDeclaration + alias ESTree.ImportDefaultSpecifier + alias ESTree.ImportNamespaceSpecifier + alias ESTree.ImportSpecifier + alias ESTree.JSXAttribute + alias ESTree.JSXClosingElement + alias ESTree.JSXElement + alias ESTree.JSXEmptyExpression + alias ESTree.JSXExpressionContainer + alias ESTree.JSXIdentifier + alias ESTree.JSXMemberExpression + alias ESTree.JSXNamespacedName + alias ESTree.JSXOpeningElement + alias ESTree.JSXSpreadAttribute + alias ESTree.LabeledStatement + alias ESTree.Literal + alias ESTree.LogicalExpression + alias ESTree.MemberExpression + alias ESTree.MetaProperty + alias ESTree.MethodDefinition + alias ESTree.NewExpression + alias ESTree.ObjectExpression + alias ESTree.ObjectPattern + alias ESTree.Program + alias ESTree.Property + alias ESTree.RestElement + alias ESTree.ReturnStatement + alias ESTree.SequenceExpression + alias ESTree.SpreadElement + alias ESTree.Super + alias ESTree.SwitchCase + alias ESTree.SwitchStatement + alias ESTree.TaggedTemplateExpression + alias ESTree.TemplateElement + alias ESTree.TemplateLiteral + alias ESTree.ThisExpression + alias ESTree.ThrowStatement + alias ESTree.TryStatement + alias ESTree.UnaryExpression + alias ESTree.UpdateExpression + alias ESTree.VariableDeclaration + alias ESTree.VariableDeclarator + alias ESTree.WhileStatement + alias ESTree.WithStatement + alias ESTree.YieldExpression + + @operator_precedence %{ + ||: 3, + &&: 4, + |: 5, + ^: 6, + &: 7, + ==: 8, + !=: 8, + ===: 8, + !==: 8, + <: 9, + >: 9, + <=: 9, + >=: 9, + in: 9, + instanceof: 9, + "<<": 10, + ">>": 10, + >>>: 10, + +: 11, + -: 11, + *: 12, + %: 12, + /: 12, + "**": 13 + } + + @expressions_precedence %{ + ArrayExpression => 20, + TaggedTemplateExpression => 20, + ThisExpression => 20, + Identifier => 20, + Literal => 18, + TemplateLiteral => 20, + Super => 20, + SequenceExpression => 20, + MemberExpression => 19, + CallExpression => 19, + NewExpression => 19, + ArrowFunctionExpression => 18, + ClassExpression => 17, + FunctionExpression => 17, + ObjectExpression => 17, + UpdateExpression => 16, + UnaryExpression => 15, + BinaryExpression => 14, + LogicalExpression => 13, + ConditionalStatement => 4, + AssignmentExpression => 3, + YieldExpression => 2, + AwaitExpression => 2, + RestElement => 1 + } + @operators [ :-, :+ , :! , :"~" , :typeof , :void , :delete, - :== , :!= , :=== , :!== , :< , :<= , :> , :>= , - :"<<" , :">>" , :>>> , :+ , :- , :* , :/ , :% , :| , - :^ , :& , :in , :instanceof, :|| , :&&, := , :"+=" , - :"-=" , :"*=" , :"/=" , :"%=" , :"<<=" , :">>=" , + :== , :!= , :=== , :!== , :< , :<= , :> , :>= , + :"<<" , :">>" , :>>> , :+ , :- , :* , :/ , :% , :| , + :^ , :& , :in , :instanceof, :|| , :&&, := , :"+=" , + :"-=" , :"*=" , :"/=" , :"%=" , :"<<=" , :">>=" , :">>>=" , :"|=" , :"^=" , :"&=", :++, :-- ] - @indent " " + @spec generate(ESTree.operator | ESTree.Node.t, boolean | map) :: binary + def generate(value, beauty_or_opts \\ true) do + opts = if beauty_or_opts do + %{ + beauty: true, + wh_sep: " ", + comma_sep: ", ", + colon_sep: ": " + } + else + %{ + beauty: false, + wh_sep: "", + comma_sep: ",", + colon_sep: ":" + } + end + |> Map.merge( + %{indent: 0, + indent_start: 0, + indent_level: 4, + no_trailing_semicolon: false + } + ) + + opts = if is_map(beauty_or_opts), do: Map.merge(opts, beauty_or_opts), else: opts - @spec generate(ESTree.operator | ESTree.Node.t, integer) :: binary - def generate(value, level \\ 0) do - "#{indent(level)}#{do_generate(value, level + 1)}" + value + |> do_generate(opts) + |> :erlang.iolist_to_binary() end - def do_generate(nil, level)do - "" + defp do_generate(nil, _opts) do + [] end - def do_generate(operator, level) when operator in @operators do + defp do_generate(operator, _opts) when operator in @operators do to_string(operator) end - - def do_generate(%ESTree.Identifier{name: name}, level) do - to_string(name) + + # ArrayExpression + + defp do_generate(%ArrayExpression{elements: nil}, _opts) do + "[]" end - def do_generate(%ESTree.Literal{value: nil}, level) do - "null" + defp do_generate(%ArrayExpression{elements: []}, _opts) do + "[]" end - - def do_generate(%ESTree.Literal{value: %{}, regex: regex}, level) do - "/#{regex.pattern}/#{regex.flags}" + + defp do_generate(%ArrayExpression{elements: elements}, opts) do + elements = elements + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) + + ["[", elements, "]"] end - def do_generate(%ESTree.Literal{value: value}, level) when is_boolean(value) do - "#{to_string(value)}" + # ArrayPattern + + defp do_generate(%ArrayPattern{elements: nil}, _opts) do + "[]" end - - def do_generate(%ESTree.Literal{value: value}, level) when is_atom(value) do - "'#{to_string(value)}'" + + defp do_generate(%ArrayPattern{elements: []}, _opts) do + "[]" end - - def do_generate(%ESTree.Literal{value: value}, level) when is_binary(value) do - value = convert_string_characters(value) - - "'#{value}'" + + defp do_generate(%ArrayPattern{elements: elements}, opts) do + elements = elements + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) + + ["[", elements, "]"] end - def do_generate(%ESTree.Literal{value: value}, level) do - to_string(value) + # ArrowFunctionExpression + + defp do_generate(%ArrowFunctionExpression{params: params, defaults: defaults, body: body, generator: generator, async: async} = ast, %{wh_sep: wh_sep} = opts) do + generator = if generator, do: "*", else: "" + async = if async, do: "async ", else: "" + params = params_and_defaults(params, defaults, opts) + body = if body.__struct__ == ObjectExpression do + ["(", do_generate(body, opts), ")"] + else + do_generate(body, opts) + end + + if not opts.beauty and length(ast.params) == 1 and hd(ast.params).__struct__ != ObjectPattern do + [async, params, generator, wh_sep, "=>", wh_sep, body] + else + [async, "(", params, ")", generator, wh_sep, "=>", wh_sep, body] + end end - def do_generate(%ESTree.Program{body: []}, level) do - "" + # AssignmentExpression + + defp do_generate(%AssignmentExpression{operator: operator, left: left, right: right}, %{wh_sep: wh_sep} = opts) do + operator = do_generate(operator, opts) + left = do_generate(left, opts) + right = do_generate(right, opts) + + [left, wh_sep, operator, wh_sep, right] end - - def do_generate(%ESTree.Program{body: body}, level) do - Enum.map_join(body, "\n", &generate(&1, level + 1)) + + # AssignmentPattern + + defp do_generate(%AssignmentPattern{left: left, right: right}, %{wh_sep: wh_sep} = opts) do + left = do_generate(left, opts) + right = do_generate(right, opts) + + [left, wh_sep, "=", wh_sep, right] end - def do_generate(%ESTree.FunctionDeclaration{} = ast, level) do - generator = if ast.generator, do: "*", else: "" - async = if ast.async, do: "async ", else: "" - - params = params_and_defaults(ast.params, ast.defaults) - id = generate(ast.id) - - "#{async}function#{generator} #{id}(#{params})#{generate(ast.body, level + 1)}" + # AssignmentProperty + + defp do_generate(%AssignmentProperty{value: value}, opts) do + do_generate(value, opts) end - def do_generate(%ESTree.FunctionExpression{} = ast, level) do - generator = if ast.generator, do: "*", else: "" - async = if ast.async, do: "async ", else: "" - params = params_and_defaults(ast.params, ast.defaults) - - "#{async}function#{generator}(#{params})#{generate(ast.body, level + 1)}" + # AwaitExpression + + defp do_generate(%AwaitExpression{argument: %AwaitExpression{} = argument, all: _all}, opts) do + ["await ", "(", do_generate(argument, opts), ")"] end - - def do_generate(%ESTree.EmptyStatement{}, level) do - ";" + + defp do_generate(%AwaitExpression{argument: argument, all: _all}, opts) do + ["await ", do_generate(argument, opts)] end - - def do_generate(%ESTree.BlockStatement{body: body}, level) do - "{\n#{indent(level + 1)}" <> Enum.map_join(body, "\n#{indent(level + 1)}", &generate(&1)) <> "\n#{indent(level)}}" + + # BinaryExpression + + defp do_generate(%BinaryExpression{operator: operator, left: left, right: right} = node, opts) when operator in [:in, :instanceof] do + operator = [" ", do_generate(operator, opts), " "] + left = format_binary_expression(left, node, false, opts) + right = format_binary_expression(right, node, true, opts) + + if node.operator == :in do + ["(", left, operator, right, ")"] + else + [left, operator, right] + end end - - def do_generate(%ESTree.ExpressionStatement{expression: expression}, level) do - "#{generate(expression)};" + + defp do_generate(%BinaryExpression{operator: operator, left: left, right: right} = node, %{wh_sep: wh_sep} = opts) do + operator = do_generate(operator, opts) + left = format_binary_expression(left, node, false, opts) + right = format_binary_expression(right, node, true, opts) + + [left, wh_sep, operator, wh_sep, right] end - def do_generate(%ESTree.IfStatement{test: test, consequent: consequent, alternate: alternate}, level) do - test = generate(test) - consequent = generate(consequent, level + 1) - result = "if(#{test}) #{consequent}" + # BlockStatement - if alternate do - result = result <> " else #{generate(alternate, level + 1)}" + defp do_generate(%BlockStatement{body: []}, _opts) do + "{}" + end + + defp do_generate(%BlockStatement{body: body}, opts) do + close_bracket = if opts.beauty do + ["\n", indent(opts), "}"] + else + "}" end - result - end + opts = next_indent(opts) + i = indent(opts) + + sep = if opts.beauty do + ["\n\n", i] + else + "" + end + + open_bracket = if opts.beauty do + ["{\n", i] + else + "{" + end + + statements = body + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(sep) - def do_generate(%ESTree.LabeledStatement{label: label, body: body}, level) do - """ - #{generate(label, level)}: - #{generate(body, level + 1)} - """ + [open_bracket, statements, close_bracket] end - def do_generate(%ESTree.BreakStatement{label: nil}, level) do + # BreakStatement + + defp do_generate(%BreakStatement{label: nil}, _opts) do "break;" end - - def do_generate(%ESTree.BreakStatement{label: label}, level) do - "break #{generate(label)};" - end - def do_generate(%ESTree.ContinueStatement{label: nil}, level) do - "continue;" - end - - def do_generate(%ESTree.ContinueStatement{label: label}, level) do - "continue #{generate(label)};" + defp do_generate(%BreakStatement{label: label}, opts) do + ["break ", do_generate(label, opts), ";"] end - def do_generate(%ESTree.WithStatement{object: object, body: body}, level) do - "with(#{generate(object)})#{generate(body, level + 1)}" - end + # CallExpression + + defp do_generate(%CallExpression{callee: callee, arguments: arguments}, opts) do + precedence = Map.get(@expressions_precedence, callee.__struct__) + callee = do_generate(callee, opts) - def do_generate(%ESTree.SwitchStatement{discriminant: discriminant, cases: cases}, level) do - cases = Enum.map_join(cases, "\n", &generate(&1, level + 1)) - "switch(#{generate(discriminant)}){ #{cases} }" + callee = if precedence < @expressions_precedence[CallExpression] do + ["(", callee, ")"] + else + callee + end + + arguments = arguments + |> Enum.map(fn + %s{} = x when s in [AwaitExpression, YieldExpression] -> + ["("] ++ do_generate(x, opts) ++ [")"] + x -> + do_generate(x, opts) + end) + |> Enum.intersperse(opts.comma_sep) + + [callee, "(", arguments, ")"] end - def do_generate(%ESTree.ReturnStatement{argument: argument}, level) do - "return #{generate(argument, level + 1)};" + # CatchClause + + defp do_generate(%CatchClause{param: param, body: body}, %{wh_sep: wh_sep} = opts) do + param = do_generate(param, opts) + body = do_generate(body, opts) + + ["catch", wh_sep, "(", param, ")", wh_sep, body] end - def do_generate(%ESTree.ThrowStatement{argument: argument}, level) do - "throw #{generate(argument, level + 1)};" + # ClassBody + + defp do_generate(%ClassBody{body: []}, _opts) do + "{}" end - def do_generate(%ESTree.TryStatement{block: block, handler: handler, finalizer: nil}, level) do - "try#{generate(block, level + 1)}#{generate(handler, level + 1)}" + defp do_generate(%ClassBody{body: body}, opts) do + close_bracket = if opts.beauty do + ["\n", indent(opts), "}"] + else + "}" + end + + opts = next_indent(opts) + i = indent(opts) + + sep = if opts.beauty do + [";\n", i] + else + ";" + end + + open_bracket = if opts.beauty do + ["{\n", i] + else + "{" + end + + statements = body + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(sep) + + [open_bracket, statements, close_bracket] end - def do_generate(%ESTree.TryStatement{block: block, handler: nil, finalizer: finalizer}, level) do - "try#{generate(block, level + 1)}finally#{generate(finalizer, level + 1)}" + # ClassDeclaration + + defp do_generate(%ClassDeclaration{id: id, body: body, superClass: nil}, %{wh_sep: wh_sep} = opts) do + id = do_generate(id, opts) + body = do_generate(body, opts) + + ["class ", id, wh_sep, body] end - - def do_generate(%ESTree.TryStatement{block: block, handler: handler, finalizer: finalizer}, level) do - "try#{generate(block, level + 1)}#{generate(handler, level + 1)}finally#{generate(finalizer, level + 1)}" + + defp do_generate(%ClassDeclaration{id: id, body: body, superClass: super_class}, %{wh_sep: wh_sep} = opts) do + id = do_generate(id, opts) + body = do_generate(body, opts) + super_class = do_generate(super_class, opts) + + ["class ", id, " extends ", super_class, wh_sep, body] end - - def do_generate(%ESTree.WhileStatement{test: test, body: body}, level) do - "while(#{generate(test)}) #{generate(body, level + 1)}" + + # ClassExpression + + defp do_generate(%ClassExpression{body: body, superClass: nil}, %{wh_sep: wh_sep} = opts) do + ["class", wh_sep, do_generate(body, opts)] end - def do_generate(%ESTree.DoWhileStatement{test: test, body: body}, level) do - "do #{generate(body, level + 1)} while(#{generate(test, level)});" + defp do_generate(%ClassExpression{body: body, superClass: super_class}, %{wh_sep: wh_sep} = opts) do + super_class = do_generate(super_class, opts) + body = do_generate(body, opts) + + ["class extends ", super_class, wh_sep, body] end - def do_generate(%ESTree.ForStatement{init: init, test: test, update: update, body: body}, level) do - init = generate(init) - test = generate(test) - update = generate(update) - body = generate(body, level + 1) + # ConditionalStatement + + defp do_generate(%ConditionalStatement{test: test, alternate: alternate, consequent: consequent}, %{wh_sep: wh_sep} = opts) do + precedence = Map.get(@expressions_precedence, test.__struct__) + test = do_generate(test, opts) - "for(#{init}; #{test}; #{update}) #{body}" + test = if precedence < @expressions_precedence[ConditionalStatement] do + ["(", test, ")"] + else + test + end + + consequent = do_generate(consequent, opts) + alternate = do_generate(alternate, opts) + + [test, wh_sep, "?", wh_sep, consequent, wh_sep, ":", wh_sep, alternate] end - def do_generate(%ESTree.ForInStatement{left: left, right: right, body: body}, level) do - left = generate(left) |> String.replace(";","") - right = generate(right) - body = generate(body, level + 1) + # ContinueStatement + + defp do_generate(%ContinueStatement{label: nil}, _opts) do + "continue;" + end - "for(#{left} in #{right}) #{body}" + defp do_generate(%ContinueStatement{label: label}, opts) do + ["continue ", do_generate(label, opts), ";"] end - def do_generate(%ESTree.DebuggerStatement{}, level) do + # DebuggerStatement + + defp do_generate(%DebuggerStatement{}, _opts) do "debugger;" end - def do_generate(%ESTree.VariableDeclaration{kind: kind, declarations: [declaration]}, level) when kind in [:var, :let, :const] do - declaration = generate(declaration) + # DoWhileStatement - "#{to_string(kind)} #{declaration};" + defp do_generate(%DoWhileStatement{test: test, body: body}, %{wh_sep: wh_sep} = opts) do + test = do_generate(test, opts) + body = do_generate(body, opts) + + ["do", wh_sep, body, wh_sep, "while", wh_sep, "(", test, ");"] end - - def do_generate(%ESTree.VariableDeclaration{kind: kind, declarations: declarations}, level) when kind in [:var, :let, :const] do - ids = Enum.map_join(declarations, ",", fn(x) -> generate(x.id) end) - inits = Enum.map_join(declarations, ",", fn(x) -> generate(x.init) end) + # EmptyStatement - "#{to_string(kind)} #{ids} = #{inits};" + defp do_generate(%EmptyStatement{}, _opts) do + ";" end - def do_generate(%ESTree.VariableDeclarator{id: id, init: nil}, level) do - generate(id) + # ExportAllDeclaration + + defp do_generate(%ExportAllDeclaration{source: source}, %{wh_sep: wh_sep} = opts) do + source = do_generate(source, opts) + + ["export", wh_sep, "*", wh_sep, "from", wh_sep, source, ";"] end - - def do_generate(%ESTree.VariableDeclarator{id: id, init: init}, level) do - "#{generate(id)} = #{generate(init)}" + + # ExportDefaultDeclaration + + defp do_generate(%ExportDefaultDeclaration{declaration: %ClassDeclaration{} = declaration}, opts) do + ["export default ", do_generate(declaration, opts)] end - def do_generate(%ESTree.ThisExpression{}, level) do - "this" + defp do_generate(%ExportDefaultDeclaration{declaration: %FunctionDeclaration{} = declaration}, opts) do + ["export default ", do_generate(declaration, opts)] end - def do_generate(%ESTree.ArrayExpression{elements: nil}, level) do - "[]" + defp do_generate(%ExportDefaultDeclaration{declaration: declaration}, opts) do + ["export default ", do_generate(declaration, opts), ";"] end - - def do_generate(%ESTree.ArrayExpression{elements: elements}, level) do - "[" <> Enum.map_join(elements, ", ", &generate(&1)) <> "]" + + # ExportNamedDeclaration + + defp do_generate(%ExportNamedDeclaration{declaration: declaration, specifiers: [], source: nil}, opts) do + ["export ", do_generate(declaration, opts)] end - def do_generate(%ESTree.ObjectExpression{properties: nil}, level) do - "{}" + defp do_generate(%ExportNamedDeclaration{declaration: nil, specifiers: specifiers, source: nil}, %{wh_sep: wh_sep} = opts) do + specifiers = specifiers + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) + + ["export", wh_sep, "{", wh_sep, specifiers, wh_sep, "};"] end - def do_generate(%ESTree.ObjectExpression{properties: []}, level) do - "{}" + defp do_generate(%ExportNamedDeclaration{declaration: nil, specifiers: specifiers, source: source}, %{wh_sep: wh_sep} = opts) do + specifiers = specifiers + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) + + ["export", wh_sep, "{", wh_sep, specifiers, wh_sep, "}", wh_sep, "from", wh_sep, do_generate(source, opts), ";"] end - - def do_generate(%ESTree.ObjectExpression{properties: properties}, level) do - "{\n#{indent(level + 1)}" <> Enum.map_join(properties, ", ", &generate(&1, level + 1)) <> "\n#{indent(level)}}" + + # ExportSpecifier + + defp do_generate(%ExportSpecifier{local: local, exported: exported}, opts) do + if local == exported do + do_generate(local, opts) + else + [do_generate(exported, opts), " as ", do_generate(local, opts)] + end end - def do_generate(%ESTree.Property{key: key, value: value, kind: :init, shorthand: false, method: false, computed: false}, level) do - key = generate(key) - value = generate(value) + # ExpressionStatement + + defp do_generate(%ExpressionStatement{expression: expression}, opts) do + precedence = Map.get(@expressions_precedence, expression.__struct__) - "#{key}: #{value}" + if precedence == 17 or (precedence == 3 and expression.left.__struct__ == ObjectPattern) do + ["(", do_generate(expression, opts), ");"] + else + [do_generate(expression, opts), ";"] + end end - def do_generate(%ESTree.Property{key: key, value: _, kind: :init, shorthand: true, method: false, computed: false}, level) do - key = generate(key) + # ForInStatement + + defp do_generate(%ForInStatement{left: left, right: right, body: body}, %{wh_sep: wh_sep} = opts) do + left = do_generate(left, %{opts | no_trailing_semicolon: true}) + right = do_generate(right, opts) + body = do_generate(body, opts) - "#{key}" + ["for", wh_sep, "(", left, " in ", right, ")", wh_sep, body] end - def do_generate(%ESTree.Property{key: key, value: value, kind: :init, shorthand: false, method: false, computed: true}, level) do - key = generate(key) - value = generate(value) + # ForOfStatement - "[#{key}]: #{value}" + defp do_generate(%ForOfStatement{left: left, right: right, body: body}, %{wh_sep: wh_sep} = opts) do + left = do_generate(left, %{opts | no_trailing_semicolon: true}) + right = do_generate(right, opts) + body = do_generate(body, opts) + + ["for", wh_sep, "(", left, " of ", right, ")", wh_sep, body] end - def do_generate(%ESTree.Property{key: key, value: %ESTree.FunctionExpression{body: body}, kind: :get, shorthand: false, method: true, computed: false}, level) do - key = generate(key) - value = generate(body) + # ForStatement + + defp do_generate(%ForStatement{init: init, test: test, update: update, body: body}, %{wh_sep: wh_sep} = opts) do + init = do_generate(init, %{opts | no_trailing_semicolon: true}) + test = do_generate(test, opts) + update = do_generate(update, opts) + body = do_generate(body, opts) - "#{key}() #{value}" + ["for", wh_sep, "(", init, ";", wh_sep, test, ";", wh_sep, update, ")", wh_sep, body] end - - def do_generate(%ESTree.Property{key: key, value: %ESTree.FunctionExpression{body: body}, kind: :get}, level) do - key = generate(key) - value = generate(body) + # FunctionDeclaration + + defp do_generate(%FunctionDeclaration{generator: generator, async: async, id: id, params: params, defaults: defaults, body: body}, %{wh_sep: wh_sep} = opts) do + generator = if generator, do: "*", else: "" + async = if async, do: "async ", else: "" + params = params_and_defaults(params, defaults, opts) + id = do_generate(id, opts) + body = do_generate(body, opts) + + sep = if generator == "" do + " " + else + wh_sep + end - "get #{key}() #{value}" + [async, "function", generator, sep, id, "(", params, ")", wh_sep, body] end - def do_generate(%ESTree.Property{key: key, value: %ESTree.FunctionExpression{params: params, body: body}, kind: :set, shorthand: false, method: true, computed: false}, level) do - key = generate(key) - param = generate(hd(params)) - value = generate(body) + # FunctionExpression - "#{key}(#{param}) #{value}" + defp do_generate(%FunctionExpression{generator: generator, async: async, params: params, defaults: defaults, body: body}, %{wh_sep: wh_sep} = opts) do + generator = if generator, do: "*", else: "" + async = if async, do: "async ", else: "" + params = params_and_defaults(params, defaults, opts) + body = do_generate(body, opts) + + [async, "function", generator, "(", params, ")", wh_sep, body] end - - def do_generate(%ESTree.Property{key: key, value: %ESTree.FunctionExpression{params: params, body: body}, kind: :set}, level) do - key = generate(key) - param = generate(hd(params)) - value = generate(body) + # Identifier - "set #{key}(#{param}) #{value}" + defp do_generate(%Identifier{name: name}, _opts) do + to_string(name) end - def do_generate(%ESTree.SequenceExpression{expressions: expressions}, level) do - Enum.map_join(expressions, ",", &generate(&1)) + # IfStatement + + defp do_generate(%IfStatement{test: test, consequent: consequent, alternate: nil}, %{wh_sep: wh_sep} = opts) do + test = do_generate(test, opts) + + sep_c = if opts.beauty do + ["\n", indent(next_indent(opts))] + else + "" + end + + consequent = + case consequent do + %BlockStatement{} -> + [wh_sep, do_generate(consequent, opts)] + + _ -> + [sep_c, do_generate(consequent, opts)] + end + + ["if", wh_sep, "(", test, ")", consequent] end - def do_generate(%ESTree.UnaryExpression{operator: :typeof, prefix: true, argument: argument}, level) do - "#{generate(:typeof)} #{generate(argument)}" + defp do_generate(%IfStatement{test: test, consequent: consequent, alternate: alternate}, %{wh_sep: wh_sep} = opts) do + is_cons_block = consequent.__struct__ == BlockStatement + + i_1 = indent(opts) + i_2 = indent(next_indent(opts)) + + consequent = + case consequent do + %BlockStatement{} -> + [wh_sep, do_generate(consequent, opts)] + + _ -> + indent = if opts.beauty do + ["\n", i_2] + else + "" + end + + [indent, do_generate(consequent, opts)] + end + + alternate_indent = if opts.beauty do + ["\n", i_2] + else + " " + end + + alternate = if is_cons_block do + case alternate do + %BlockStatement{} -> + [wh_sep, "else", wh_sep, do_generate(alternate, opts)] + + %IfStatement{} -> + [wh_sep, "else ", do_generate(alternate, opts)] + + _ -> + [wh_sep, "else", alternate_indent, do_generate(alternate, opts)] + end + else + indent = if opts.beauty do + ["\n", i_1] + else + "" + end + + case alternate do + %BlockStatement{} -> + [indent, "else", wh_sep, do_generate(alternate, opts)] + + %IfStatement{} -> + [indent, "else ", do_generate(alternate, opts)] + + _ -> + [indent, "else", alternate_indent, do_generate(alternate, opts)] + end + end + + test = do_generate(test, opts) + + ["if", wh_sep, "(", test, ")", consequent, alternate] end - def do_generate(%ESTree.UnaryExpression{operator: operator, prefix: true, argument: argument}, level) do - "#{generate(operator)}#{generate(argument)}" + # ImportDeclaration + + defp do_generate(%ImportDeclaration{specifiers: [%ImportDefaultSpecifier{}] = specifiers, source: source}, %{wh_sep: wh_sep} = opts) do + specifiers = specifiers + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) + + source = do_generate(source, opts) + + ["import ", specifiers, " from", wh_sep, source, ";"] end - def do_generate(%ESTree.UnaryExpression{operator: operator, prefix: false, argument: argument}, level) do - "#{generate(argument)}#{generate(operator)}" + defp do_generate(%ImportDeclaration{specifiers: [%ImportNamespaceSpecifier{}] = specifiers, source: source}, %{wh_sep: wh_sep} = opts) do + specifiers = specifiers + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) + + ["import", specifiers, " from", wh_sep, do_generate(source, opts), ";"] end - - def do_generate(%ESTree.BinaryExpression{operator: operator, left: %ESTree.BinaryExpression{} = left, right: %ESTree.BinaryExpression{} = right}, level) do - operator = generate(operator) - left = generate(left) - right = generate(right) - "(#{left}) #{operator} (#{right})" + defp do_generate(%ImportDeclaration{specifiers: specifiers, source: source}, %{wh_sep: wh_sep} = opts) do + specifiers = specifiers + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) + + ["import", wh_sep, "{", wh_sep, specifiers, wh_sep, "}", wh_sep, "from", wh_sep, do_generate(source, opts), ";"] end - - def do_generate(%ESTree.BinaryExpression{operator: operator, left: left, right: %ESTree.BinaryExpression{} = right}, level) do - operator = generate(operator) - left = generate(left) - right = generate(right) - "#{left} #{operator} (#{right})" + # ImportDefaultSpecifier + + defp do_generate(%ImportDefaultSpecifier{local: local}, opts) do + do_generate(local, opts) end - - def do_generate(%ESTree.BinaryExpression{operator: operator, left: left, right: right}, level) do - operator = generate(operator) - left = generate(left) - right = generate(right) - "#{left} #{operator} #{right}" + # ImportNamespaceSpecifier + + defp do_generate(%ImportNamespaceSpecifier{local: local}, %{wh_sep: wh_sep} = opts) do + [wh_sep, "*", wh_sep, "as ", do_generate(local, opts)] end - def do_generate(%ESTree.AssignmentExpression{operator: operator, left: left, right: right}, level) do - operator = generate(operator) - left = generate(left) - right = generate(right) + # ImportSpecifier - "#{left} #{operator} #{right}" + defp do_generate(%ImportSpecifier{local: local, imported: imported}, opts) do + if local == imported do + do_generate(local, opts) + else + [do_generate(imported, opts), " as ", do_generate(local, opts)] + end end - def do_generate(%ESTree.UpdateExpression{operator: operator, prefix: true, argument: argument}, level) do - "#{generate(operator)}#{generate(argument)}" + # JSXAttribute + + defp do_generate(%JSXAttribute{name: name, value: value}, opts) do + [do_generate(name, opts), "=", do_generate(value, opts)] end - def do_generate(%ESTree.UpdateExpression{operator: operator, prefix: false, argument: argument}, level) do - "#{generate(argument)}#{generate(operator)}" + # JSXClosingElement + + defp do_generate(%JSXClosingElement{name: name}, opts) do + [""] end - def do_generate(%ESTree.LogicalExpression{operator: operator, left: left, right: right}, level) do - operator = generate(operator) - left = generate(left) - right = generate(right) + # JSXElement + + defp do_generate(%JSXElement{openingElement: opening_element, children: children, closingElement: closing_element}, opts) do + opening_element = do_generate(opening_element, opts) + children = generate_jsx_children(children, opts) + closing_element = do_generate(closing_element, opts) - "#{left} #{operator} #{right}" + [opening_element, children, closing_element] end - def do_generate(%ESTree.ConditionalStatement{test: test, alternate: alternate, consequent: consequent}, level) do - test = generate(test) - alternate = generate(alternate) - consequent = generate(consequent) + # JSXEmptyExpression - "#{test} ? #{consequent} : #{alternate}" + defp do_generate(%JSXEmptyExpression{}, _opts) do + "" end - def do_generate(%ESTree.CallExpression{callee: %ESTree.MemberExpression{ object: %ESTree.FunctionExpression{} } = callee, arguments: arguments}, level) do - callee = generate(callee) - arguments = Enum.map_join(arguments, ",", &generate(&1)) + # JSXExpressionContainer - "(#{callee}(#{arguments}))" + defp do_generate(%JSXExpressionContainer{expression: expression}, opts) do + ["{", do_generate(expression, opts), "}"] end - def do_generate(%ESTree.CallExpression{callee: %ESTree.Super{}, arguments: arguments}, level) do - arguments = Enum.map_join(arguments, ",", &generate(&1)) + # JSXIdentifier - "super(#{arguments})" + defp do_generate(%JSXIdentifier{name: name}, _opts) when is_binary(name) do + name end - - def do_generate(%ESTree.CallExpression{callee: callee, arguments: arguments}, level) do - callee = generate(callee) - arguments = Enum.map_join(arguments, ",", &generate(&1)) - "#{callee}(#{arguments})" + defp do_generate(%JSXIdentifier{name: name}, _opts) when is_atom(name) do + to_string(name) end - def do_generate(%ESTree.NewExpression{callee: callee, arguments: arguments}, level) do - callee = generate(callee) - arguments = Enum.map_join(arguments, ",", &generate(&1)) + # JSXMemberExpression - "new #{callee}(#{arguments})" + defp do_generate(%JSXMemberExpression{object: object, property: property}, opts) do + [do_generate(object, opts), ".", do_generate(property, opts)] end - def do_generate(%ESTree.MemberExpression{object: %ESTree.Super{}, property: property, computed: true}, level) do - property = generate(property) - "super[#{property}]" - end + # JSXNamespacedName - def do_generate(%ESTree.MemberExpression{object: object, property: property, computed: true}, level) do - object = generate(object) - property = generate(property) - "#{object}[#{property}]" + defp do_generate(%JSXNamespacedName{namespace: namespace, name: name}, opts) do + [do_generate(namespace, opts), ":", do_generate(name, opts)] end - def do_generate(%ESTree.MemberExpression{object: %ESTree.Super{}, property: property, computed: false}, level) do - property = generate(property) - "super.#{property}" + # JSXOpeningElement + + defp do_generate(%JSXOpeningElement{name: name, attributes: attributes, selfClosing: self_closing}, %{wh_sep: wh_sep} = opts) do + self_closing = if self_closing, do: [wh_sep, "/"], else: "" + attributes_value = cond do + Enum.empty?(attributes) -> "" + true -> + attributes = attributes + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(" ") + + [" " | attributes] + end + + ["<", do_generate(name, opts), attributes_value, self_closing, ">"] end - - def do_generate(%ESTree.MemberExpression{object: object, property: property, computed: false}, level) do - object = generate(object) - property = generate(property) - "#{object}.#{property}" + + # JSXSpreadAttribute + + defp do_generate(%JSXSpreadAttribute{argument: argument}, opts) do + ["{...", do_generate(argument, opts), "}"] end - def do_generate(%ESTree.SwitchCase{test: nil, consequent: consequent}, level) do - consequent = Enum.map_join(consequent, "\n", &generate(&1)) - """ - default: - #{consequent} - """ + # LabeledStatement + + defp do_generate(%LabeledStatement{label: label, body: body}, opts) do + label = do_generate(label, opts) + body = do_generate(body, opts) + + [label, ": ", body] end - - def do_generate(%ESTree.SwitchCase{test: test, consequent: consequent}, level) do - test = generate(test) - consequent = Enum.map_join(consequent, "\n", &generate(&1)) - """ - case #{test}: - #{consequent} - """ + # Literal + + defp do_generate(%Literal{value: nil}, _opts) do + "null" end - def do_generate(%ESTree.CatchClause{param: param, body: body}, level) do - param = generate(param) - body = generate(body) + defp do_generate(%Literal{value: %{}, regex: regex}, _opts) do + ["/", regex.pattern, "/", regex.flags] + end - "catch(#{param}) #{body}" + defp do_generate(%Literal{value: value}, _opts) when is_boolean(value) do + to_string(value) end - def do_generate(%ESTree.ForOfStatement{left: left, right: right, body: body}, level) do - left = generate(left) |> String.replace(";","") - right = generate(right) - body = generate(body) + defp do_generate(%Literal{value: value}, _opts) when is_atom(value) do + ["'", to_string(value), "'"] + end - "for(#{left} of #{right}) #{body}" + defp do_generate(%Literal{value: value}, _opts) when is_binary(value) do + ["'", escape_string(value), "'"] end - def do_generate(%ESTree.SpreadElement{argument: argument}, level) do - "...#{generate(argument)}" + defp do_generate(%Literal{value: value}, _opts) do + to_string(value) end - def do_generate(%ESTree.RestElement{argument: argument}, level) do - "...#{generate(argument)}" + # LogicalExpression + + defp do_generate(%LogicalExpression{operator: operator, left: left, right: right} = node, %{wh_sep: wh_sep} = opts) do + operator = do_generate(operator, opts) + left = format_binary_expression(left, node, false, opts) + right = format_binary_expression(right, node, true, opts) + + [left, wh_sep, operator, wh_sep, right] end - def do_generate(%ESTree.ArrowFunctionExpression{} = ast, level) do - generator = if ast.generator - do - "*" + # MemberExpression + + defp do_generate(%MemberExpression{object: object, property: property, computed: computed}, opts) do + precedence = Map.get(@expressions_precedence, object.__struct__) + object = do_generate(object, opts) + + object = if precedence < @expressions_precedence[MemberExpression] do + ["(", object, ")"] else - "" + object end - - params = params_and_defaults(ast.params, ast.defaults) - - "(#{params})#{generator} => #{generate(ast.body)}" + + property = do_generate(property, opts) + + property = if computed do + ["[", property, "]"] + else + [".", property] + end + + [object, property] + end + + # MetaProperty + + defp do_generate(%MetaProperty{meta: meta, property: property}, opts) do + [do_generate(meta, opts), ".", do_generate(property, opts)] end - def do_generate(%ESTree.YieldExpression{argument: argument, delegate: false}, level) do - "yield #{generate(argument)}" + # MethodDefinition + + defp do_generate(%MethodDefinition{key: _key, value: %FunctionExpression{params: params, defaults: defaults, body: body}, kind: :constructor}, opts) do + params = params_and_defaults(params, defaults, opts) + body = do_generate(body, opts) + + ["constructor(", params, ")", opts.wh_sep, body] end - def do_generate(%ESTree.YieldExpression{argument: argument, delegate: true}, level) do - "yield* #{generate(argument)}" + defp do_generate(%MethodDefinition{key: key, value: %FunctionExpression{params: params, defaults: defaults, body: body}, kind: :method, computed: false, static: false}, opts) do + key = do_generate(key, opts) + params = params_and_defaults(params, defaults, opts) + body = do_generate(body, opts) + + [key, "(", params, ")", opts.wh_sep, body] end - def do_generate(%ESTree.ObjectPattern{properties: properties}, level) do - "{" <> Enum.map_join(properties, ",", &generate(&1)) <> "}" + defp do_generate(%MethodDefinition{key: key, value: %FunctionExpression{params: params, defaults: defaults, body: body}, kind: :method, computed: false, static: true}, opts) do + key = do_generate(key, opts) + params = params_and_defaults(params, defaults, opts) + body = do_generate(body, opts) + + ["static ", key, "(", params, ")", opts.wh_sep, body] end - def do_generate(%ESTree.ArrayPattern{elements: elements}, level) do - "[" <> Enum.map_join(elements, ",", &generate(&1)) <> "]" + defp do_generate(%MethodDefinition{key: key, value: %FunctionExpression{params: params, defaults: defaults, body: body}, kind: :method, computed: true, static: false}, opts) do + key = do_generate(key, opts) + params = params_and_defaults(params, defaults, opts) + body = do_generate(body, opts) + + ["[", key, "](", params, ")", opts.wh_sep, body] end - def do_generate(%ESTree.AssignmentPattern{left: left, right: right}, level) do - left = generate(left) - right = generate(right) + defp do_generate(%MethodDefinition{key: key, value: %FunctionExpression{params: params, defaults: defaults, body: body}, kind: :method, computed: true, static: true}, opts) do + key = generate(key, opts) + params = params_and_defaults(params, defaults, opts) + body = do_generate(body, opts) - "#{left} = #{right}" + ["static [", key, "](", params, ")", opts.wh_sep, body] end - - def do_generate(%ESTree.ClassDeclaration{id: id, body: body, superClass: nil}, level) do - id = generate(id) - body = generate(body) + defp do_generate(%MethodDefinition{key: key, value: %FunctionExpression{body: body}, kind: :get, static: true}, opts) do + key = generate(key, opts) + body = do_generate(body, opts) - "class #{id} #{body}" + ["static get ", key, "()", opts.wh_sep, body] end - - def do_generate(%ESTree.ClassDeclaration{id: id, body: body, superClass: superClass}, level) do - id = generate(id) - body = generate(body) - superClass = generate(superClass) + defp do_generate(%MethodDefinition{key: key, value: %FunctionExpression{params: params, defaults: defaults, body: body}, kind: :set, static: true}, opts) do + key = generate(key, opts) + params = params_and_defaults(params, defaults, opts) + body = do_generate(body, opts) - "class #{id} extends #{superClass} #{body}" + ["static set ", key, "(", params, ")", opts.wh_sep, body] end + defp do_generate(%MethodDefinition{key: key, value: %FunctionExpression{body: body}, kind: :get}, opts) do + key = generate(key, opts) + body = do_generate(body, opts) - def do_generate(%ESTree.ClassExpression{body: body, superClass: nil}, level) do - body = generate(body) + ["get ", key, "()", opts.wh_sep, body] + end + + defp do_generate(%MethodDefinition{key: key, value: %FunctionExpression{params: params, defaults: defaults, body: body}, kind: :set}, opts) do + key = generate(key, opts) + params = params_and_defaults(params, defaults, opts) + body = do_generate(body, opts) - "class #{body}" + ["set ", key, "(", params, ")", opts.wh_sep, body] end - - def do_generate(%ESTree.ClassExpression{body: body, superClass: superClass}, level) do - body = generate(body) - superClass = generate(superClass) + # NewExpression + + defp do_generate(%NewExpression{callee: callee, arguments: arguments}, opts) do + precedence = Map.get(@expressions_precedence, callee.__struct__) + + callee = if precedence < @expressions_precedence[NewExpression] or has_call_expression(callee) do + ["(", do_generate(callee, opts), ")"] + else + do_generate(callee, opts) + end - "class extends #{superClass} #{body}" + arguments = arguments + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) + + ["new ", callee, "(", arguments, ")"] end - def do_generate(%ESTree.ClassBody{body: body}, level) do - body = Enum.map_join(body, "\n", &generate(&1)) + # ObjectExpression + + defp do_generate(%ObjectExpression{properties: nil}, _opts) do + "{}" + end - "{ #{body} }" + defp do_generate(%ObjectExpression{properties: []}, _opts) do + "{}" end - def do_generate(%ESTree.MethodDefinition{key: _key, value: %ESTree.FunctionExpression{ params: params, defaults: defaults, body: body}, kind: :constructor}, level) do - params = params_and_defaults(params, defaults) - body = generate(body) + defp do_generate(%ObjectExpression{properties: properties}, opts) do + close_bracket = if opts.beauty do + ["\n", indent(opts), "}"] + else + "}" + end + + opts = next_indent(opts) + i = indent(opts) + + open_bracket = if opts.beauty do + ["{\n", i] + else + "{" + end + + sep = if opts.beauty do + [",\n", i] + else + opts.comma_sep + end + + properties = properties + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(sep) - "constructor(#{params})#{body}" + [open_bracket, properties, close_bracket] end - - def do_generate(%ESTree.MethodDefinition{key: key, value: %ESTree.FunctionExpression{ params: params, defaults: defaults, body: body}, kind: :method, computed: false, static: false}, level) do - key = generate(key) - params = params_and_defaults(params, defaults) - body = generate(body) - "#{key}(#{params})#{body}" + # ObjectPattern + + defp do_generate(%ObjectPattern{properties: properties}, opts) do + properties = properties + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) + + ["{", properties, "}"] end - def do_generate(%ESTree.MethodDefinition{key: key, value: %ESTree.FunctionExpression{ params: params, defaults: defaults, body: body}, kind: :method, computed: false, static: true}, level) do - key = generate(key) - params = params_and_defaults(params, defaults) - body = generate(body) + # Program - "static #{key}(#{params})#{body}" + defp do_generate(%Program{body: []}, _opts) do + "" end - def do_generate(%ESTree.MethodDefinition{key: key, value: %ESTree.FunctionExpression{ params: params, defaults: defaults, body: body}, kind: :method, computed: true, static: false}, level) do - key = generate(key) - params = params_and_defaults(params, defaults) - body = generate(body) + defp do_generate(%Program{body: body}, opts) do + sep = if opts.beauty do + "\n\n" + else + "" + end - "[#{key}](#{params})#{body}" + body + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(sep) end - def do_generate(%ESTree.MethodDefinition{key: key, value: %ESTree.FunctionExpression{ params: params, defaults: defaults, body: body}, kind: :method, computed: true, static: true}, level) do - key = generate(key) - params = params_and_defaults(params, defaults) - body = generate(body) + # Property + + defp do_generate(%Property{key: key, value: value, kind: :init, shorthand: false, method: false, computed: false}, %{colon_sep: colon_sep} = opts) do + key = do_generate(key, opts) + value = do_generate(value, opts) - "static [#{key}](#{params})#{body}" + [key, colon_sep, value] end - def do_generate(%ESTree.MetaProperty{meta: meta, property: property}, level) do - meta = generate(meta) - property = generate(property) + defp do_generate(%Property{key: key, value: _, kind: :init, shorthand: true, method: false, computed: false}, opts) do + do_generate(key, opts) + end - "#{meta}.#{property}" + defp do_generate(%Property{key: key, value: value, kind: :init, shorthand: false, method: false, computed: true}, %{colon_sep: colon_sep} = opts) do + key = do_generate(key, opts) + value = do_generate(value, opts) + + ["[", key, "]", colon_sep, value] end - def do_generate(%ESTree.ImportDeclaration{specifiers: [%ESTree.ImportDefaultSpecifier{}] = specifiers, source: source}, level) do - specifiers = Enum.map_join(specifiers, ", ", &generate(&1)) - source = generate(source) + defp do_generate(%Property{key: key, value: %FunctionExpression{params: params, defaults: defaults, body: body}, kind: :init, shorthand: false, method: true, computed: true}, %{wh_sep: wh_sep} = opts) do + key = do_generate(key, opts) + params = params_and_defaults(params, defaults, opts) + body = do_generate(body, opts) - "import #{specifiers} from #{source};" + ["[", key, "]", "(", params, ")", wh_sep, body] end - def do_generate(%ESTree.ImportDeclaration{specifiers: [%ESTree.ImportNamespaceSpecifier{}] = specifiers, source: source}, level) do - specifiers = Enum.map_join(specifiers, ", ", &generate(&1)) - source = generate(source) + defp do_generate(%Property{key: key, value: %FunctionExpression{body: body}, kind: :get, shorthand: false, method: true, computed: false}, %{wh_sep: wh_sep} = opts) do + key = do_generate(key, opts) + body = do_generate(body, opts) - "import #{specifiers} from #{source};" + [key, "()", wh_sep, body] end - - def do_generate(%ESTree.ImportDeclaration{specifiers: specifiers, source: source}, level) do - specifiers = Enum.map_join(specifiers, ", ", &generate(&1)) - source = generate(source) - "import { #{specifiers} } from #{source};" + defp do_generate(%Property{key: key, value: %FunctionExpression{body: body}, kind: :get}, %{wh_sep: wh_sep} = opts) do + key = do_generate(key, opts) + body = do_generate(body, opts) + + ["get ", key, "()", wh_sep, body] end - - def do_generate(%ESTree.ImportSpecifier{local: local, imported: imported}, level) do - local = generate(local) - imported = generate(imported) + defp do_generate(%Property{key: key, value: %FunctionExpression{params: params, body: body}, kind: :set, shorthand: false, method: true, computed: false}, %{wh_sep: wh_sep} = opts) do + key = do_generate(key, opts) + params = do_generate(hd(params), opts) + body = do_generate(body, opts) - if local == imported do - "#{local}" - else - "#{imported} as #{local}" - end + [key, "(", params, ")", wh_sep, body] end - def do_generate(%ESTree.ImportDefaultSpecifier{local: local}, level) do - generate(local) + defp do_generate(%Property{key: key, value: %FunctionExpression{params: params, body: body}, kind: :set}, %{wh_sep: wh_sep} = opts) do + key = do_generate(key, opts) + params = do_generate(hd(params), opts) + body = do_generate(body, opts) + + ["set ", key, "(", params, ")", wh_sep, body] end - def do_generate(%ESTree.ImportNamespaceSpecifier{local: local}, level) do - local = generate(local) + # RestElement - "* as #{local}" + defp do_generate(%RestElement{argument: argument}, opts) do + ["...", do_generate(argument, opts)] end - def do_generate(%ESTree.ExportNamedDeclaration{declaration: %ESTree.FunctionDeclaration{} = declaration, specifiers: [], source: nil}, level) do - declaration = generate(declaration) - "export #{declaration}" + # ReturnStatement + defp do_generate(%ReturnStatement{argument: nil}, _opts) do + "return;" end - - def do_generate(%ESTree.ExportNamedDeclaration{declaration: declaration, specifiers: [], source: nil}, level) do - declaration = generate(declaration) - "export #{declaration};" + + defp do_generate(%ReturnStatement{argument: argument}, opts) do + ["return ", do_generate(argument, opts), ";"] end - - def do_generate(%ESTree.ExportNamedDeclaration{declaration: nil, specifiers: specifiers, source: nil} = ast, level) do - specifiers = Enum.map_join(specifiers, ", ", &generate(&1)) - "export { #{specifiers} };" + # SequenceExpression + + defp do_generate(%SequenceExpression{expressions: expressions}, opts) do + expressions = expressions + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) + + ["(", expressions, ")"] end - def do_generate(%ESTree.ExportNamedDeclaration{declaration: nil, specifiers: specifiers, source: source}, level) do - specifiers = Enum.map_join(specifiers, ", ", &generate(&1)) - source = generate(source) - - "export { #{specifiers} } from #{source};" + # SpreadElement + + defp do_generate(%SpreadElement{argument: argument}, opts) do + ["...", do_generate(argument, opts)] end - - def do_generate(%ESTree.ExportSpecifier{local: local, exported: exported}, level) do - local = generate(local) - exported = generate(exported) - if local == exported do - "#{local}" + # Super + + defp do_generate(%Super{}, _opts) do + "super" + end + + # SwitchCase + + defp do_generate(%SwitchCase{test: nil, consequent: consequent}, opts) do + start_sep = if opts.beauty do + indent(opts, 1) + else + "" + end + + sep = if opts.beauty do + ["\n", indent(opts)] else - "#{exported} as #{local}" + "" end + + consequent = consequent + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(sep) + + [start_sep, "default:", sep, consequent] end - def do_generate(%ESTree.ExportDefaultDeclaration{declaration: declaration}, level) do - declaration = generate(declaration) + defp do_generate(%SwitchCase{test: test, consequent: consequent}, opts) do + start_sep = if opts.beauty do + indent(opts, 1) + else + "" + end - "export default #{declaration};" + sep = if opts.beauty do + ["\n", indent(opts)] + else + "" + end + + test = do_generate(test, opts) + + consequent = consequent + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(sep) + + [start_sep, "case ", test, ":", sep, consequent] end - def do_generate(%ESTree.ExportAllDeclaration{source: source}, level) do - source = generate(source) - "export * from #{source};" + # SwitchStatement + + defp do_generate(%SwitchStatement{discriminant: discriminant, cases: cases}, %{wh_sep: wh_sep} = opts) do + open_bracket = if opts.beauty do + "{\n" + else + "{" + end + + close_bracket = if opts.beauty do + ["\n", indent(opts), "}"] + else + "}" + end + + sep = if opts.beauty do + "\n\n" + else + "" + end + + opts = next_indent(opts) + + cases = cases + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(sep) + + discriminant = do_generate(discriminant, opts) + + ["switch", wh_sep, "(", discriminant, ")", wh_sep, open_bracket, cases, close_bracket] end - def do_generate(%ESTree.TaggedTemplateExpression{tag: tag, quasi: quasi}, level) do - tag = generate(tag) - quasi = generate(quasi) + # TaggedTemplateExpression - "#{tag} #{quasi}" + defp do_generate(%TaggedTemplateExpression{tag: tag, quasi: quasi}, opts) do + [do_generate(tag, opts), " ", do_generate(quasi, opts)] end - def do_generate(%ESTree.TemplateLiteral{expressions: [], quasis: []}, level) do + # TemplateLiteral + + defp do_generate(%TemplateLiteral{expressions: [], quasis: []}, _opts) do "``" end - def do_generate(%ESTree.TemplateLiteral{expressions: [expression], quasis: []}, level) do - expression = generate(expression) - "`${#{expression}}`" + defp do_generate(%TemplateLiteral{expressions: [expression], quasis: []}, opts) do + ["`${", do_generate(expression, opts), "}`"] end - def do_generate(%ESTree.TemplateLiteral{expressions: [], quasis: [quasi]}, level) do - quasi = convert_string_characters(quasi.value.raw) - "`#{quasi}`" + defp do_generate(%TemplateLiteral{expressions: [], quasis: [quasi]}, _opts) do + ["`", escape_string(quasi.value.raw, false), "`"] end - def do_generate(%ESTree.TemplateLiteral{expressions: expressions, quasis: quasis}, level) do + defp do_generate(%TemplateLiteral{expressions: expressions, quasis: quasis}, opts) do elements = expressions ++ quasis - literal = Enum.sort(elements, fn(one, two) -> + literal = elements + |> Enum.sort(fn (one, two) -> one.loc.start.column < two.loc.start.column end) - |> Enum.reduce("", fn(el, str) -> + |> Enum.reduce("", fn (el, str) -> case el do - %ESTree.TemplateElement{} -> - str <> convert_string_characters(el.value.raw) + %TemplateElement{} -> + [str, escape_string(el.value.raw, false)] + _ -> - str <> "${#{generate(el)}}" + [str, "${", do_generate(el, opts), "}"] end end) + ["`", literal, "`"] + end + + # ThisExpression - "`#{literal}`" + defp do_generate(%ThisExpression{}, _opts) do + "this" end - def do_generate(%ESTree.AwaitExpression{ argument: argument, all: all }, level) do - "await #{generate(argument)}" + # ThrowStatement + + defp do_generate(%ThrowStatement{argument: argument}, opts) do + ["throw ", do_generate(argument, opts), ";"] end - def do_generate(%ESTree.JSXIdentifier{ name: name }, level) do - "#{name}" + # TryStatement + + defp do_generate(%TryStatement{block: block, handler: handler, finalizer: nil}, %{wh_sep: wh_sep} = opts) do + block = do_generate(block, opts) + handler = do_generate(handler, opts) + + ["try", wh_sep, block, wh_sep, handler] end - def do_generate(%ESTree.JSXMemberExpression{ object: object, property: property }, level) do - "#{ generate(object) }.#{ generate(property) }" + defp do_generate(%TryStatement{block: block, handler: nil, finalizer: finalizer}, %{wh_sep: wh_sep} = opts) do + block = do_generate(block, opts) + finalizer = do_generate(finalizer, opts) + + ["try", wh_sep, block, wh_sep, "finally", wh_sep, finalizer] end - def do_generate(%ESTree.JSXNamespacedName{ namespace: namespace, name: name }, level) do - "#{ generate(namespace) }:#{ generate(name) }" + defp do_generate(%TryStatement{block: block, handler: handler, finalizer: finalizer}, %{wh_sep: wh_sep} = opts) do + block = do_generate(block, opts) + handler = do_generate(handler, opts) + finalizer = do_generate(finalizer, opts) + + ["try", wh_sep, block, wh_sep, handler, wh_sep, "finally", wh_sep, finalizer] end - def do_generate(%ESTree.JSXEmptyExpression{}, level) do - "" + # UnaryExpression + + defp do_generate(%UnaryExpression{operator: operator, prefix: true, argument: argument}, opts) do + precedence = Map.get(@expressions_precedence, argument.__struct__) + + sep = if operator in [:delete, :typeof, :void], do: " ", else: "" + operator = do_generate(operator, opts) + argument = do_generate(argument, opts) + + if precedence < @expressions_precedence[UnaryExpression] do + [operator, sep, "(", argument, ")"] + else + [operator, sep, argument] + end + end + + defp do_generate(%UnaryExpression{operator: operator, prefix: false, argument: argument}, opts) do + [do_generate(argument, opts), do_generate(operator, opts)] + end + + # UpdateExpression + + defp do_generate(%UpdateExpression{operator: operator, prefix: true, argument: argument}, opts) do + [do_generate(operator, opts), do_generate(argument, opts)] + end + + defp do_generate(%UpdateExpression{operator: operator, prefix: false, argument: argument}, opts) do + [do_generate(argument, opts), do_generate(operator, opts)] + end + + # VariableDeclaration + + defp do_generate(%VariableDeclaration{kind: kind, declarations: declarations}, opts) when kind in [:var, :let, :const] do + i = indent(next_indent(opts)) + sep = if opts.beauty do + if kind == :const do + [",\n", i, " "] + else + [",\n", i] + end + else + opts.comma_sep + end + + declarators = declarations + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(sep) + + semicolon = if opts.no_trailing_semicolon do + "" + else + ";" + end + + [to_string(kind), " ", declarators, semicolon] + end + + # VariableDeclarator + + defp do_generate(%VariableDeclarator{id: id, init: nil}, opts) do + do_generate(id, opts) + end + + defp do_generate(%VariableDeclarator{id: id, init: %FunctionDeclaration{} = init}, %{wh_sep: wh_sep} = opts) do + opts = %{next_indent(opts) | no_trailing_semicolon: true} + + [do_generate(id, opts), wh_sep, "=", wh_sep, do_generate(init, opts)] + end + + defp do_generate(%VariableDeclarator{id: id, init: %FunctionExpression{} = init}, %{wh_sep: wh_sep} = opts) do + opts = %{next_indent(opts) | no_trailing_semicolon: true} + + [do_generate(id, opts), wh_sep, "=", wh_sep, do_generate(init, opts)] end - def do_generate(%ESTree.JSXExpressionContainer{ expression: expression }, level) do - "{#{ generate(expression) }}" + defp do_generate(%VariableDeclarator{id: id, init: init}, %{wh_sep: wh_sep} = opts) do + [do_generate(id, opts), wh_sep, "=", wh_sep, do_generate(init, opts)] end - def do_generate(%ESTree.JSXOpeningElement{ name: name, attributes: attributes, selfClosing: selfClosing }, level) do - selfClosing = if selfClosing, do: "/", else: "" + # WhileStatement + + defp do_generate(%WhileStatement{test: test, body: body}, %{wh_sep: wh_sep} = opts) do + test = do_generate(test, opts) + body = do_generate(body, opts) - "<#{generate(name)} #{ Enum.map(attributes, &generate(&1)) |> Enum.join(" ") }#{selfClosing}>" + ["while", wh_sep, "(", test, ")", wh_sep, body] end - def do_generate(%ESTree.JSXClosingElement{ name: name }, level) do - "" + # WithStatement + + defp do_generate(%WithStatement{object: object, body: body}, %{wh_sep: wh_sep} = opts) do + object = do_generate(object, opts) + body = do_generate(body, opts) + + ["with", wh_sep, "(", object, ")", wh_sep, body] + end + + # YieldExpression + + defp do_generate(%YieldExpression{argument: argument, delegate: false}, opts) do + ["yield ", do_generate(argument, opts)] + end + + defp do_generate(%YieldExpression{argument: argument, delegate: true}, %{wh_sep: wh_sep} = opts) do + ["yield*", wh_sep, do_generate(argument, opts)] + end + + # Helpers + + defp escape_string(string, escape_quotes \\ true) do + string = string + |> String.replace("\\", "\\\\") + |> String.replace("\n", "\\n") + |> String.replace("\r", "\\r") + |> String.replace("\t", "\\t") + |> String.replace("\b", "\\b") + |> String.replace("\f", "\\f") + |> String.replace("\v", "\\v") + |> String.replace("\u2028", "\\u2028") + |> String.replace("\u2029", "\\u2029") + |> String.replace("\ufeff", "\\ufeff") + + if escape_quotes do + string + |> String.replace("\x27", "\\'") + else + string + end end - def do_generate(%ESTree.JSXAttribute{ name: name, value: value }, level) do - "#{ generate(name) }=#{ generate(value) }" + defp params_and_defaults(params, [], opts) do + params + |> Enum.map(&do_generate(&1, opts)) + |> Enum.intersperse(opts.comma_sep) end - def do_generate(%ESTree.JSXSpreadAttribute{ argument: argument }, level) do - "{...#{ generate(argument) }}" + defp params_and_defaults(params, defaults, %{wh_sep: wh_sep} = opts) do + params + |> Enum.zip(defaults) + |> Enum.map(fn + {p, nil} -> do_generate(p, opts) + {p, d} -> [do_generate(p, opts), wh_sep, "=", wh_sep, do_generate(d, opts)] + end) + |> Enum.intersperse(opts.comma_sep) end - def do_generate(%ESTree.JSXElement{ openingElement: openingElement, children: children, closingElement: closingElement }, level) do - "#{ generate(openingElement) } #{ Enum.map(children, &generate(&1, level + 1)) |> Enum.join(" ") } #{ generate(closingElement) }" + defp format_binary_expression(node, node_parent, is_right_hand, opts) do + if parenthesis(node, node_parent, is_right_hand) do + ["(", do_generate(node, opts), ")"] + else + do_generate(node, opts) + end end - defp convert_string_characters(str) do - String.replace(str, "\n", "\\n") - |> String.replace("\t", "\\t") + defp parenthesis(node, parent_node, is_right_hand) do + node_p = @expressions_precedence[node.__struct__] + parent_node_p = @expressions_precedence[parent_node.__struct__] + + cond do + node_p != parent_node_p -> + node_p < parent_node_p + + node_p != 13 and node_p != 14 -> + false + + node.operator == :'**' and parent_node.operator == :'**' -> + not is_right_hand + + is_right_hand -> + @operator_precedence[node.operator] <= @operator_precedence[parent_node.operator] + + true -> + @operator_precedence[node.operator] < @operator_precedence[parent_node.operator] + end end - - defp params_and_defaults(params, []) do - Enum.map_join(params, ",", &generate(&1)) - end - - defp params_and_defaults(params, defaults) do - Enum.zip(params, defaults) - |> Enum.map_join(",", fn - {p, nil} -> "#{generate(p)}" - {p, d} -> "#{generate(p)} = #{generate(d)}" + + defp generate_jsx_children(children, opts) do + children + |> Enum.map(fn + %Literal{value: value} when is_binary(value) -> + convert_jsx_text(value) + + other -> + do_generate(other, opts) end) end - defp indent(level) do - String.duplicate(" ", level) + defp convert_jsx_text(string) do + string + |> String.replace("{", "{") + |> String.replace("}", "}") + |> String.replace("<", "<") + |> String.replace(">", ">") + end + + defp indent(opts, back \\ 0) do + String.duplicate(" ", opts.indent_start + opts.indent - back * opts.indent_level) + end + + defp next_indent(opts) do + %{opts | indent: opts.indent + opts.indent_level} + end + + defp has_call_expression(node) when not is_map(node) do + false + end + + defp has_call_expression(node) do + case node do + %CallExpression{} -> + true + + %MemberExpression{} -> + has_call_expression(node.object) + + _ -> + false + end end end diff --git a/mix.exs b/mix.exs index 88ddc0b..9a8190d 100644 --- a/mix.exs +++ b/mix.exs @@ -2,25 +2,27 @@ defmodule ESTree.Mixfile do use Mix.Project def project do - [app: :estree, - version: "2.2.0-dev", - elixir: "~> 1.0", - deps: deps, - description: description, - package: package, - source_url: "https://github.com/bryanjos/elixir-estree"] + [ + app: :estree, + version: "2.7.0", + elixir: "~> 1.0", + deps: deps(), + description: description(), + package: package(), + source_url: "https://github.com/bryanjos/elixir-estree" + ] end def application do - [applications: [:logger]] + [extra_applications: [:logger]] end defp deps do [ - {:earmark, "~> 0.2", only: :dev}, - {:ex_doc, "~> 0.11", only: :dev}, + {:ex_doc, "~> 0.21.1", only: :dev}, {:dialyze, "~> 0.2", only: :dev}, - {:shouldi, only: :test} + {:shouldi, "~> 0.3.2", only: :test}, + {:poison, "~> 4.0", only: :test} ] end @@ -32,11 +34,12 @@ defmodule ESTree.Mixfile do end defp package do - [ # These are the default files included in the package - files: ["lib", "mix.exs", "README*", "readme*", "LICENSE*", "license*", "CHANGELOG*"], + # These are the default files included in the package + [ + files: ["lib", "mix.exs", "README.md", "CHANGELOG.md"], maintainers: ["Bryan Joseph"], licenses: ["MIT"], - links: %{ "GitHub" => "https://github.com/bryanjos/elixir-estree" } + links: %{"GitHub" => "https://github.com/bryanjos/elixir-estree"} ] end end diff --git a/mix.lock b/mix.lock index f15309b..8eaf864 100644 --- a/mix.lock +++ b/mix.lock @@ -1,4 +1,10 @@ -%{"dialyze": {:hex, :dialyze, "0.2.0"}, - "earmark": {:hex, :earmark, "0.2.0"}, - "ex_doc": {:hex, :ex_doc, "0.11.3"}, - "shouldi": {:hex, :shouldi, "0.3.0"}} +%{ + "dialyze": {:hex, :dialyze, "0.2.1", "9fb71767f96649020d769db7cbd7290059daff23707d6e851e206b1fdfa92f9d", [:mix], []}, + "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.21.1", "5ac36660846967cd869255f4426467a11672fec3d8db602c429425ce5b613b90", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, + "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm"}, + "shouldi": {:hex, :shouldi, "0.3.2", "971f614669b5f37c03507ba643bb8e59aa165ac50ca66d726c9db53be255e440", [:mix], []}, +} diff --git a/test/support.ex b/test/support.ex new file mode 100644 index 0000000..ca40c54 --- /dev/null +++ b/test/support.ex @@ -0,0 +1,14 @@ +defmodule ESTree.Test.Support do + use ShouldI + + alias ESTree.Tools.Generator + + defmacro assert_gen(ast, str, opts \\ []) do + opts = Keyword.merge([beauty: true], opts) + beauty = Keyword.get(opts, :beauty) + + quote do + assert Generator.generate(unquote(ast), unquote(beauty)) == unquote(str) + end + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs index 948e26c..b253595 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1 +1,3 @@ -ExUnit.start(formatters: [ShouldI.CLIFormatter]) +ExUnit.start() + +Code.require_file "support.ex", __DIR__ diff --git a/test/tools/estree_json_transformer_test.exs b/test/tools/estree_json_transformer_test.exs new file mode 100644 index 0000000..57fdf9a --- /dev/null +++ b/test/tools/estree_json_transformer_test.exs @@ -0,0 +1,630 @@ +defmodule ESTree.Tools.ESTreeJSONTransformer.Test do + use ExUnit.Case, async: true + + alias ESTree.Tools.ESTreeJSONTransformer, as: EJT + + + test "convert json to structs" do + map = json() |> Poison.decode! + structs = EJT.convert(map) + + assert structs.type === "Program" + end + + + def json() do + """ + { + "type": "Program", + "start": 0, + "end": 215, + "range": [ + 0, + 215 + ], + "body": [ + { + "type": "FunctionDeclaration", + "start": 0, + "end": 215, + "range": [ + 0, + 215 + ], + "id": { + "type": "Identifier", + "start": 9, + "end": 17, + "range": [ + 9, + 17 + ], + "name": "fizzBuzz" + }, + "generator": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 19, + "end": 215, + "range": [ + 19, + 215 + ], + "body": [ + { + "type": "ForStatement", + "start": 22, + "end": 213, + "range": [ + 22, + 213 + ], + "init": { + "type": "VariableDeclaration", + "start": 26, + "end": 33, + "range": [ + 26, + 33 + ], + "declarations": [ + { + "type": "VariableDeclarator", + "start": 30, + "end": 33, + "range": [ + 30, + 33 + ], + "id": { + "type": "Identifier", + "start": 30, + "end": 31, + "range": [ + 30, + 31 + ], + "name": "i" + }, + "init": { + "type": "Literal", + "start": 32, + "end": 33, + "range": [ + 32, + 33 + ], + "value": 1, + "raw": "1" + } + } + ], + "kind": "var" + }, + "test": { + "type": "BinaryExpression", + "start": 34, + "end": 40, + "range": [ + 34, + 40 + ], + "left": { + "type": "Identifier", + "start": 34, + "end": 35, + "range": [ + 34, + 35 + ], + "name": "i" + }, + "operator": "<=", + "right": { + "type": "Literal", + "start": 37, + "end": 40, + "range": [ + 37, + 40 + ], + "value": 100, + "raw": "100" + } + }, + "update": { + "type": "UpdateExpression", + "start": 41, + "end": 44, + "range": [ + 41, + 44 + ], + "operator": "++", + "prefix": false, + "argument": { + "type": "Identifier", + "start": 41, + "end": 42, + "range": [ + 41, + 42 + ], + "name": "i" + } + }, + "body": { + "type": "BlockStatement", + "start": 45, + "end": 213, + "range": [ + 45, + 213 + ], + "body": [ + { + "type": "IfStatement", + "start": 49, + "end": 210, + "range": [ + 49, + 210 + ], + "test": { + "type": "LogicalExpression", + "start": 52, + "end": 74, + "range": [ + 52, + 74 + ], + "left": { + "type": "BinaryExpression", + "start": 52, + "end": 61, + "range": [ + 52, + 61 + ], + "left": { + "type": "BinaryExpression", + "start": 52, + "end": 55, + "range": [ + 52, + 55 + ], + "left": { + "type": "Identifier", + "start": 52, + "end": 53, + "range": [ + 52, + 53 + ], + "name": "i" + }, + "operator": "%", + "right": { + "type": "Literal", + "start": 54, + "end": 55, + "range": [ + 54, + 55 + ], + "value": 5, + "raw": "5" + } + }, + "operator": "===", + "right": { + "type": "Literal", + "start": 60, + "end": 61, + "range": [ + 60, + 61 + ], + "value": 0, + "raw": "0" + } + }, + "operator": "&&", + "right": { + "type": "BinaryExpression", + "start": 65, + "end": 74, + "range": [ + 65, + 74 + ], + "left": { + "type": "BinaryExpression", + "start": 65, + "end": 68, + "range": [ + 65, + 68 + ], + "left": { + "type": "Identifier", + "start": 65, + "end": 66, + "range": [ + 65, + 66 + ], + "name": "i" + }, + "operator": "%", + "right": { + "type": "Literal", + "start": 67, + "end": 68, + "range": [ + 67, + 68 + ], + "value": 3, + "raw": "3" + } + }, + "operator": "===", + "right": { + "type": "Literal", + "start": 73, + "end": 74, + "range": [ + 73, + 74 + ], + "value": 0, + "raw": "0" + } + } + }, + "consequent": { + "type": "BlockStatement", + "start": 75, + "end": 102, + "range": [ + 75, + 102 + ], + "body": [ + { + "type": "ExpressionStatement", + "start": 80, + "end": 98, + "range": [ + 80, + 98 + ], + "expression": { + "type": "CallExpression", + "start": 80, + "end": 97, + "range": [ + 80, + 97 + ], + "callee": { + "type": "Identifier", + "start": 80, + "end": 85, + "range": [ + 80, + 85 + ], + "name": "print" + }, + "arguments": [ + { + "type": "Literal", + "start": 86, + "end": 96, + "range": [ + 86, + 96 + ], + "value": "FizzBuzz", + "raw": "'FizzBuzz'" + } + ] + } + } + ] + }, + "alternate": { + "type": "IfStatement", + "start": 108, + "end": 210, + "range": [ + 108, + 210 + ], + "test": { + "type": "BinaryExpression", + "start": 111, + "end": 120, + "range": [ + 111, + 120 + ], + "left": { + "type": "BinaryExpression", + "start": 111, + "end": 114, + "range": [ + 111, + 114 + ], + "left": { + "type": "Identifier", + "start": 111, + "end": 112, + "range": [ + 111, + 112 + ], + "name": "i" + }, + "operator": "%", + "right": { + "type": "Literal", + "start": 113, + "end": 114, + "range": [ + 113, + 114 + ], + "value": 3, + "raw": "3" + } + }, + "operator": "===", + "right": { + "type": "Literal", + "start": 119, + "end": 120, + "range": [ + 119, + 120 + ], + "value": 0, + "raw": "0" + } + }, + "consequent": { + "type": "BlockStatement", + "start": 121, + "end": 144, + "range": [ + 121, + 144 + ], + "body": [ + { + "type": "ExpressionStatement", + "start": 126, + "end": 140, + "range": [ + 126, + 140 + ], + "expression": { + "type": "CallExpression", + "start": 126, + "end": 139, + "range": [ + 126, + 139 + ], + "callee": { + "type": "Identifier", + "start": 126, + "end": 131, + "range": [ + 126, + 131 + ], + "name": "print" + }, + "arguments": [ + { + "type": "Literal", + "start": 132, + "end": 138, + "range": [ + 132, + 138 + ], + "value": "Fizz", + "raw": "'Fizz'" + } + ] + } + } + ] + }, + "alternate": { + "type": "IfStatement", + "start": 150, + "end": 210, + "range": [ + 150, + 210 + ], + "test": { + "type": "BinaryExpression", + "start": 153, + "end": 162, + "range": [ + 153, + 162 + ], + "left": { + "type": "BinaryExpression", + "start": 153, + "end": 156, + "range": [ + 153, + 156 + ], + "left": { + "type": "Identifier", + "start": 153, + "end": 154, + "range": [ + 153, + 154 + ], + "name": "i" + }, + "operator": "%", + "right": { + "type": "Literal", + "start": 155, + "end": 156, + "range": [ + 155, + 156 + ], + "value": 5, + "raw": "5" + } + }, + "operator": "===", + "right": { + "type": "Literal", + "start": 161, + "end": 162, + "range": [ + 161, + 162 + ], + "value": 0, + "raw": "0" + } + }, + "consequent": { + "type": "BlockStatement", + "start": 163, + "end": 186, + "range": [ + 163, + 186 + ], + "body": [ + { + "type": "ExpressionStatement", + "start": 168, + "end": 182, + "range": [ + 168, + 182 + ], + "expression": { + "type": "CallExpression", + "start": 168, + "end": 181, + "range": [ + 168, + 181 + ], + "callee": { + "type": "Identifier", + "start": 168, + "end": 173, + "range": [ + 168, + 173 + ], + "name": "print" + }, + "arguments": [ + { + "type": "Literal", + "start": 174, + "end": 180, + "range": [ + 174, + 180 + ], + "value": "Buzz", + "raw": "'Buzz'" + } + ] + } + } + ] + }, + "alternate": { + "type": "BlockStatement", + "start": 192, + "end": 210, + "range": [ + 192, + 210 + ], + "body": [ + { + "type": "ExpressionStatement", + "start": 197, + "end": 206, + "range": [ + 197, + 206 + ], + "expression": { + "type": "CallExpression", + "start": 197, + "end": 205, + "range": [ + 197, + 205 + ], + "callee": { + "type": "Identifier", + "start": 197, + "end": 202, + "range": [ + 197, + 202 + ], + "name": "print" + }, + "arguments": [ + { + "type": "Identifier", + "start": 203, + "end": 204, + "range": [ + 203, + 204 + ], + "name": "i" + } + ] + } + } + ] + } + } + } + } + ] + } + } + ] + } + } + ], + "sourceType": "module" +} + """ + end +end \ No newline at end of file diff --git a/test/tools/generator/array_expression_test.exs b/test/tools/generator/array_expression_test.exs index 936d1aa..2e2a4c2 100644 --- a/test/tools/generator/array_expression_test.exs +++ b/test/tools/generator/array_expression_test.exs @@ -1,31 +1,31 @@ defmodule ESTree.Tools.Generator.ArrayExpression.Test do use ShouldI + alias ESTree.Tools.Builder - alias ESTree.Tools.Generator + import ESTree.Test.Support should "convert array when elements is nil" do - ast = Builder.array_expression(nil) - assert Generator.generate(ast) == "[]" + assert_gen Builder.array_expression(nil), "[]" end should "convert array when elements is empty" do - ast = Builder.array_expression([]) - assert Generator.generate(ast) == "[]" + assert_gen Builder.array_expression([]), "[]" end should "convert array when elements contains one element" do - ast = Builder.array_expression([ + assert_gen Builder.array_expression([ Builder.literal(1) - ]) - assert Generator.generate(ast) == "[1]" + ]), "[1]" end should "convert array when elements contains multiple elements" do ast = Builder.array_expression([ Builder.literal(1), - Builder.identifier(:a) + Builder.identifier(:a), + Builder.spread_element(Builder.identifier(:b)) ]) - assert Generator.generate(ast) == "[1, a]" - end + assert_gen ast, "[1, a, ...b]" + assert_gen ast, "[1,a,...b]", beauty: false + end end diff --git a/test/tools/generator/array_pattern_test.exs b/test/tools/generator/array_pattern_test.exs new file mode 100644 index 0000000..a360aa1 --- /dev/null +++ b/test/tools/generator/array_pattern_test.exs @@ -0,0 +1,31 @@ +defmodule ESTree.Tools.Generator.ArrayPattern.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert array pattern when elements is nil" do + assert_gen Builder.array_pattern(nil), "[]" + end + + should "convert array pattern when elements is empty" do + assert_gen Builder.array_pattern([]), "[]" + end + + should "convert array pattern when elements contains one element" do + assert_gen Builder.array_pattern([ + Builder.identifier(:a) + ]), "[a]" + end + + should "convert array pattern when elements contains multiple elements" do + ast = Builder.array_pattern([ + Builder.identifier(:a), + Builder.identifier(:b), + Builder.spread_element(Builder.identifier(:c)) + ]) + + assert_gen ast, "[a, b, ...c]" + assert_gen ast, "[a,b,...c]", beauty: false + end +end diff --git a/test/tools/generator/arrow_function_expression_test.exs b/test/tools/generator/arrow_function_expression_test.exs new file mode 100644 index 0000000..ce4dcea --- /dev/null +++ b/test/tools/generator/arrow_function_expression_test.exs @@ -0,0 +1,109 @@ +defmodule ESTree.Tools.Generator.ArrowFunctionExpression.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert basic arrow function" do + ast = Builder.arrow_function_expression( + [], + [], + Builder.block_statement([]), + false, + false + ) + + assert_gen ast, "() => {}" + assert_gen ast, "()=>{}", beauty: false + end + + should "convert basic arrow function returns object" do + ast = Builder.arrow_function_expression( + [], + [], + Builder.object_expression([]), + false, + false + ) + + assert_gen ast, "() => ({})" + assert_gen ast, "()=>({})", beauty: false + end + + should "convert basic arrow function object pattern has parens" do + ast = Builder.arrow_function_expression( + [Builder.object_pattern([Builder.identifier(:one)])], + [], + Builder.block_statement([]), + false, + false + ) + + assert_gen ast, "({one}) => {}" + assert_gen ast, "({one})=>{}", beauty: false + end + + should "convert basic arrow function expression generator" do + ast = Builder.arrow_function_expression( + [], + [], + Builder.block_statement([]), + true, + false + ) + + assert_gen ast, "()* => {}" + assert_gen ast, "()*=>{}", beauty: false + end + + should "convert arrow function with params" do + ast = Builder.arrow_function_expression( + [Builder.identifier(:one)], + [], + Builder.block_statement([]), + false, + false + ) + + assert_gen ast, "(one) => {}" + assert_gen ast, "one=>{}", beauty: false + + ast = Builder.arrow_function_expression( + [Builder.identifier(:one), Builder.identifier(:two)], + [], + Builder.block_statement([]), + false, + false + ) + + assert_gen ast, "(one, two) => {}" + assert_gen ast, "(one,two)=>{}", beauty: false + end + + should "convert arrow function with default values" do + ast = Builder.arrow_function_expression( + [Builder.identifier(:one), Builder.identifier(:two)], + [nil, Builder.literal(1)], + Builder.block_statement([]), + false, + false + ) + + assert_gen ast, "(one, two = 1) => {}" + assert_gen ast, "(one,two=1)=>{}", beauty: false + end + + should "convert basic arrow function expression async" do + ast = Builder.arrow_function_expression( + [], + [], + Builder.block_statement([]), + false, + false, + true + ) + + assert_gen ast, "async () => {}" + assert_gen ast, "async ()=>{}", beauty: false + end +end diff --git a/test/tools/generator/assignment_test.exs b/test/tools/generator/assignment_test.exs new file mode 100644 index 0000000..c4e101e --- /dev/null +++ b/test/tools/generator/assignment_test.exs @@ -0,0 +1,17 @@ +defmodule ESTree.Tools.Generator.Assignment.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert assignment expression" do + ast = Builder.assignment_expression( + :=, + Builder.identifier(:a), + Builder.literal(0) + ) + + assert_gen ast, "a = 0" + assert_gen ast, "a=0", beauty: false + end +end diff --git a/test/tools/generator/await_expression_test.exs b/test/tools/generator/await_expression_test.exs new file mode 100644 index 0000000..b91e13e --- /dev/null +++ b/test/tools/generator/await_expression_test.exs @@ -0,0 +1,24 @@ +defmodule ESTree.Tools.Generator.AwaitExpression.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert await expression" do + ast = Builder.await_expression( + Builder.literal(1) + ) + + assert_gen ast, "await 1" + end + + should "convert await expression that contains an await" do + ast = Builder.await_expression( + Builder.await_expression( + Builder.literal(1) + ) + ) + + assert_gen ast, "await (await 1)" + end +end \ No newline at end of file diff --git a/test/tools/generator/binary_expression_test.exs b/test/tools/generator/binary_expression_test.exs new file mode 100644 index 0000000..a69a1a7 --- /dev/null +++ b/test/tools/generator/binary_expression_test.exs @@ -0,0 +1,115 @@ +defmodule ESTree.Generator.BinaryExpression.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert binary expression with correct precedence" do + ast = Builder.binary_expression( + :+, + Builder.literal(1), + Builder.binary_expression( + :/, + Builder.binary_expression( + :*, + Builder.literal(2), + Builder.literal(3) + ), + Builder.literal(4) + ) + ) + + assert_gen ast, "1 + 2 * 3 / 4" + assert_gen ast, "1+2*3/4", beauty: false + + ast = Builder.binary_expression( + :+, + Builder.literal(1), + Builder.binary_expression( + :*, + Builder.literal(2), + Builder.binary_expression( + :/, + Builder.literal(3), + Builder.literal(4) + ) + ) + ) + + assert_gen ast, "1 + 2 * (3 / 4)" + assert_gen ast, "1+2*(3/4)", beauty: false + + ast = Builder.binary_expression( + :/, + Builder.binary_expression( + :*, + Builder.binary_expression( + :+, + Builder.literal(1), + Builder.literal(2) + ), + Builder.literal(3) + ), + Builder.literal(4) + ) + + assert_gen ast, "(1 + 2) * 3 / 4" + assert_gen ast, "(1+2)*3/4", beauty: false + + ast = Builder.binary_expression( + :*, + Builder.binary_expression( + :+, + Builder.literal(1), + Builder.literal(2) + ), + Builder.binary_expression( + :/, + Builder.literal(3), + Builder.literal(4) + ) + ) + + assert_gen ast, "(1 + 2) * (3 / 4)" + assert_gen ast, "(1+2)*(3/4)", beauty: false + end + + should "convert binary expression in" do + ast = Builder.binary_expression( + :in, + Builder.identifier(:a), + Builder.identifier(:b) + ) + + assert_gen ast, "(a in b)" + assert_gen ast, "(a in b)", beauty: false + end + + should "convert binary expression instanceof" do + ast = Builder.binary_expression( + :instanceof, + Builder.unary_expression( + :!, + true, + Builder.identifier(:x) + ), + Builder.identifier(:Number) + ) + + assert_gen ast, "!x instanceof Number" + assert_gen ast, "!x instanceof Number", beauty: false + + ast = Builder.unary_expression( + :!, + true, + Builder.binary_expression( + :instanceof, + Builder.identifier(:x), + Builder.identifier(:Number) + ) + ) + + assert_gen ast, "!(x instanceof Number)" + assert_gen ast, "!(x instanceof Number)", beauty: false + end +end diff --git a/test/tools/generator/block_statement_test.exs b/test/tools/generator/block_statement_test.exs new file mode 100644 index 0000000..a731c77 --- /dev/null +++ b/test/tools/generator/block_statement_test.exs @@ -0,0 +1,41 @@ +defmodule ESTree.Tools.Generator.BlockStatement.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert basic function declaration" do + ast = Builder.function_declaration( + Builder.identifier(:hello), + [], + [], + Builder.block_statement([ + Builder.variable_declaration([ + Builder.variable_declarator(Builder.identifier(:a), Builder.literal(1)), + Builder.variable_declarator(Builder.identifier(:b)), + Builder.variable_declarator( + Builder.identifier(:c), + Builder.binary_expression( + :+, + Builder.identifier(:a), + Builder.literal(2) + ) + ) + ]), + ]), + false, + false + ) + + str = """ +function hello() { + var a = 1, + b, + c = a + 2; +} +""" + + assert_gen ast, String.trim(str) + assert_gen ast, "function hello(){var a=1,b,c=a+2;}", beauty: false + end +end diff --git a/test/tools/generator/call_expression_test.exs b/test/tools/generator/call_expression_test.exs new file mode 100644 index 0000000..803e04f --- /dev/null +++ b/test/tools/generator/call_expression_test.exs @@ -0,0 +1,137 @@ +defmodule ESTree.Tools.Generator.CallExpression.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert call expression when arguments is empty" do + ast = Builder.call_expression(Builder.identifier(:x), []); + + assert_gen ast, "x()" + assert_gen ast, "x()", beauty: false + end + + should "convert call expression with one argument" do + ast = Builder.call_expression( + Builder.identifier(:x), + [ + Builder.identifier(:a) + ] + ); + + assert_gen ast, "x(a)" + assert_gen ast, "x(a)", beauty: false + end + + should "convert call expression with spread" do + ast = Builder.call_expression( + Builder.identifier(:x), + [ + Builder.spread_element(Builder.identifier(:a)) + ] + ); + + assert_gen ast, "x(...a)" + assert_gen ast, "x(...a)", beauty: false + end + + should "convert call expression with arguments" do + ast = Builder.call_expression( + Builder.identifier(:x), + [ + Builder.identifier(:a), + Builder.identifier(:b) + ] + ); + + assert_gen ast, "x(a, b)" + assert_gen ast, "x(a,b)", beauty: false + end + + should "convert call expression with arguments and spread" do + ast = Builder.call_expression( + Builder.identifier(:x), + [ + Builder.identifier(:a), + Builder.identifier(:b), + Builder.spread_element(Builder.identifier(:c)) + ] + ); + + assert_gen ast, "x(a, b, ...c)" + assert_gen ast, "x(a,b,...c)", beauty: false + end + + should "convert call expression when callee is member expression" do + ast = Builder.call_expression( + Builder.member_expression( + Builder.identifier(:x), + Builder.identifier(:y) + ), + [] + ); + + assert_gen ast, "x.y()" + assert_gen ast, "x.y()", beauty: false + end + + should "convert call expression when callee is binary expression" do + ast = Builder.call_expression( + Builder.member_expression( + Builder.binary_expression( + :+, + Builder.identifier(:a), + Builder.identifier(:b) + ), + Builder.identifier(:x) + ), + [] + ); + + assert_gen ast, "(a + b).x()" + assert_gen ast, "(a+b).x()", beauty: false + end + + should "convert call expression with one argument that is an await" do + ast = Builder.call_expression( + Builder.identifier(:x), + [ + Builder.await_expression( + Builder.identifier(:a) + ) + ] + ); + + assert_gen ast, "x((await a))" + assert_gen ast, "x((await a))", beauty: false + end + + should "convert call expression with one argument that is a yield" do + ast = Builder.call_expression( + Builder.identifier(:x), + [ + Builder.yield_expression( + Builder.identifier(:a) + ) + ] + ); + + assert_gen ast, "x((yield a))" + assert_gen ast, "x((yield a))", beauty: false + end + + should "convert call expression when callee is member expression and is an await" do + ast = Builder.call_expression( + Builder.member_expression( + Builder.await_expression( + Builder.identifier(:x) + ), + Builder.identifier(:y) + ), + [] + ); + + assert_gen ast, "(await x).y()" + assert_gen ast, "(await x).y()", beauty: false + end +end diff --git a/test/tools/generator/class_body_test.exs b/test/tools/generator/class_body_test.exs new file mode 100644 index 0000000..ab98bde --- /dev/null +++ b/test/tools/generator/class_body_test.exs @@ -0,0 +1,211 @@ +defmodule ESTree.Tools.Generator.ClassBody.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert class body with method definition constructor" do + ast = Builder.class_body([ + Builder.method_definition( + nil, + Builder.function_expression( + [], + [], + Builder.block_statement([ + Builder.expression_statement( + Builder.call_expression( + Builder.super(), + [] + ) + ) + ]) + ), + :constructor + ) + ]) + + assert_gen ast, "{\n constructor() {\n super();\n }\n}" + assert_gen ast, "{constructor(){super();}}", beauty: false + end + + should "convert class body with method definition" do + ast = Builder.class_body([ + Builder.method_definition( + Builder.identifier(:method), + Builder.function_expression( + [], + [], + Builder.block_statement([]) + ) + ) + ]) + + assert_gen ast, "{\n method() {}\n}" + assert_gen ast, "{method(){}}", beauty: false + end + + should "convert class body with method definition getter" do + ast = Builder.class_body([ + Builder.method_definition( + Builder.identifier(:property), + Builder.function_expression( + [], + [], + Builder.block_statement([]) + ), + :get + ) + ]) + + assert_gen ast, "{\n get property() {}\n}" + assert_gen ast, "{get property(){}}", beauty: false + end + + should "convert class body with method definition setter" do + ast = Builder.class_body([ + Builder.method_definition( + Builder.identifier(:property), + Builder.function_expression( + [Builder.identifier(:value)], + [], + Builder.block_statement([]) + ), + :set + ) + ]) + + assert_gen ast, "{\n set property(value) {}\n}" + assert_gen ast, "{set property(value){}}", beauty: false + end + + should "convert class body with method definition computed" do + ast = Builder.class_body([ + Builder.method_definition( + Builder.member_expression(Builder.identifier(:Symbol), Builder.identifier(:iterator)), + Builder.function_expression( + [], + [], + Builder.block_statement([]) + ), + :method, + true + ) + ]) + + assert_gen ast, "{\n [Symbol.iterator]() {}\n}" + assert_gen ast, "{[Symbol.iterator](){}}", beauty: false + end + + should "convert class body with method definition computed literal" do + ast = Builder.class_body([ + Builder.method_definition( + Builder.literal(:method), + Builder.function_expression( + [], + [], + Builder.block_statement([]) + ), + :method, + true + ) + ]) + + assert_gen ast, "{\n ['method']() {}\n}" + assert_gen ast, "{['method'](){}}", beauty: false + end + + should "convert class body with method definition static" do + ast = Builder.class_body([ + Builder.method_definition( + Builder.identifier("classMethod"), + Builder.function_expression( + [], + [], + Builder.block_statement([]) + ), + :method, + false, + true + ) + ]) + + assert_gen ast, "{\n static classMethod() {}\n}" + assert_gen ast, "{static classMethod(){}}", beauty: false + end + + should "convert class body with method definition static getter" do + ast = Builder.class_body([ + Builder.method_definition( + Builder.identifier(:property), + Builder.function_expression( + [], + [], + Builder.block_statement([]) + ), + :get, + false, + true + ) + ]) + + assert_gen ast, "{\n static get property() {}\n}" + assert_gen ast, "{static get property(){}}", beauty: false + end + + should "convert class body with method definition static setter" do + ast = Builder.class_body([ + Builder.method_definition( + Builder.identifier(:property), + Builder.function_expression( + [Builder.identifier(:value)], + [], + Builder.block_statement([]) + ), + :set, + false, + true + ) + ]) + + assert_gen ast, "{\n static set property(value) {}\n}" + assert_gen ast, "{static set property(value){}}", beauty: false + end + + should "convert class body with method definition static computed" do + ast = Builder.class_body([ + Builder.method_definition( + Builder.member_expression(Builder.identifier(:Symbol), Builder.identifier(:iterator)), + Builder.function_expression( + [], + [], + Builder.block_statement([]) + ), + :method, + true, + true + ) + ]) + + assert_gen ast, "{\n static [Symbol.iterator]() {}\n}" + assert_gen ast, "{static [Symbol.iterator](){}}", beauty: false + end + + should "convert class body with method definition static computed literal" do + ast = Builder.class_body([ + Builder.method_definition( + Builder.literal(:method), + Builder.function_expression( + [], + [], + Builder.block_statement([]) + ), + :method, + true, + true + ) + ]) + + assert_gen ast, "{\n static ['method']() {}\n}" + assert_gen ast, "{static ['method'](){}}", beauty: false + end +end diff --git a/test/tools/generator/class_declaration_test.exs b/test/tools/generator/class_declaration_test.exs new file mode 100644 index 0000000..54f0b08 --- /dev/null +++ b/test/tools/generator/class_declaration_test.exs @@ -0,0 +1,54 @@ +defmodule ESTree.Tools.Generator.ClassDeclaration.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert class declaration" do + ast = Builder.class_declaration( + Builder.identifier(:X), + Builder.class_body([]) + ) + + assert_gen ast, "class X {}" + assert_gen ast, "class X{}", beauty: false + end + + should "convert class declaration when it extends identifier" do + ast = Builder.class_declaration( + Builder.identifier(:X), + Builder.class_body([]), + Builder.identifier(:Y) + ) + + assert_gen ast, "class X extends Y {}" + assert_gen ast, "class X extends Y{}", beauty: false + end + + should "convert class declaration when it extends class declaration" do + ast = Builder.class_declaration( + Builder.identifier(:X), + Builder.class_body([]), + Builder.class_declaration( + Builder.identifier(:Y), + Builder.class_body([]) + ) + ) + + assert_gen ast, "class X extends class Y {} {}" + assert_gen ast, "class X extends class Y{}{}", beauty: false + end + + should "convert class declaration when it extends class expression" do + ast = Builder.class_declaration( + Builder.identifier(:X), + Builder.class_body([]), + Builder.class_expression( + Builder.class_body([]) + ) + ) + + assert_gen ast, "class X extends class {} {}" + assert_gen ast, "class X extends class{}{}", beauty: false + end +end diff --git a/test/tools/generator/class_expression_test.exs b/test/tools/generator/class_expression_test.exs new file mode 100644 index 0000000..a0c5e37 --- /dev/null +++ b/test/tools/generator/class_expression_test.exs @@ -0,0 +1,50 @@ +defmodule ESTree.Tools.Generator.ClassExpression.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert class expression" do + ast = Builder.class_expression( + Builder.class_body([]) + ) + + assert_gen ast, "class {}" + assert_gen ast, "class{}", beauty: false + end + + should "convert class expression when it extends identifier" do + ast = Builder.class_expression( + Builder.class_body([]), + Builder.identifier(:Y) + ) + + assert_gen ast, "class extends Y {}" + assert_gen ast, "class extends Y{}", beauty: false + end + + should "convert class expression when it extends class declaration" do + ast = Builder.class_expression( + Builder.class_body([]), + Builder.class_declaration( + Builder.identifier(:Y), + Builder.class_body([]) + ) + ) + + assert_gen ast, "class extends class Y {} {}" + assert_gen ast, "class extends class Y{}{}", beauty: false + end + + should "convert class expression when it extends class expression" do + ast = Builder.class_expression( + Builder.class_body([]), + Builder.class_expression( + Builder.class_body([]) + ) + ) + + assert_gen ast, "class extends class {} {}" + assert_gen ast, "class extends class{}{}", beauty: false + end +end diff --git a/test/tools/generator/conditional_statement_test.exs b/test/tools/generator/conditional_statement_test.exs new file mode 100644 index 0000000..f65e670 --- /dev/null +++ b/test/tools/generator/conditional_statement_test.exs @@ -0,0 +1,32 @@ +defmodule ESTree.Tools.Generator.ConditionalStatement.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert conditional statement" do + ast = Builder.conditional_statement( + Builder.identifier(:test), + Builder.identifier(:alternate), + Builder.identifier(:consequent) + ) + + assert_gen ast, "test ? consequent : alternate" + assert_gen ast, "test?consequent:alternate", beauty: false + end + + should "convert conditional statement precedence" do + ast = Builder.conditional_statement( + Builder.assignment_expression( + :=, + Builder.identifier(:test), + Builder.literal(1) + ), + Builder.identifier(:alternate), + Builder.identifier(:consequent) + ) + + assert_gen ast, "(test = 1) ? consequent : alternate" + assert_gen ast, "(test=1)?consequent:alternate", beauty: false + end +end diff --git a/test/tools/generator/export_declaration_test.exs b/test/tools/generator/export_declaration_test.exs index 5fe07cf..f004a43 100644 --- a/test/tools/generator/export_declaration_test.exs +++ b/test/tools/generator/export_declaration_test.exs @@ -1,61 +1,13 @@ defmodule ESTree.Tools.Generator.ExportDeclaration.Test do use ShouldI - alias ESTree.Tools.Builder - alias ESTree.Tools.Generator - - should "convert export named declaration with a declaration" do - ast = Builder.export_named_declaration( - Builder.identifier(:test) - ) - - assert Generator.generate(ast) == "export test;" - end - - should "convert export named declaration with one specifier" do - ast = Builder.export_named_declaration(nil, [ - Builder.export_specifier( - Builder.identifier(:test), - Builder.identifier(:test) - )] - ) - - assert Generator.generate(ast) == "export { test };" - end - - should "convert export named declaration with one specifier with an alias" do - ast = Builder.export_named_declaration(nil, [ - Builder.export_specifier( - Builder.identifier(:test), - Builder.identifier(:test1) - )] - ) - - assert Generator.generate(ast) == "export { test as test1 };" - end - - should "convert export declaration with more than one specifier" do - ast = Builder.export_named_declaration(nil, - [ - Builder.export_specifier( - Builder.identifier(:top), - Builder.identifier(:top) - ), - Builder.export_specifier( - Builder.identifier(:test), - Builder.identifier(:test1) - ) - ], - Builder.literal(:test) - ) - - assert Generator.generate(ast) == "export { top, test as test1 } from 'test';" - end + alias ESTree.Tools.Builder + import ESTree.Test.Support should "convert export all declaration" do ast = Builder.export_all_declaration(Builder.literal(:test)) - assert Generator.generate(ast) == "export * from 'test';" + assert_gen ast, "export * from 'test';" + assert_gen ast, "export*from'test';", beauty: false end - -end \ No newline at end of file +end diff --git a/test/tools/generator/export_default_declaration_test.exs b/test/tools/generator/export_default_declaration_test.exs new file mode 100644 index 0000000..540aa0c --- /dev/null +++ b/test/tools/generator/export_default_declaration_test.exs @@ -0,0 +1,132 @@ +defmodule ESTree.Tools.Generator.ExportDefaultDeclaration.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert export default declaration with function declaration" do + ast = Builder.export_default_declaration( + Builder.function_declaration( + Builder.identifier(:x), + [], + [], + Builder.block_statement([]) + ) + ) + + assert_gen ast, "export default function x() {}" + assert_gen ast, "export default function x(){}", beauty: false + end + + should "convert export default declaration with function expression" do + ast = Builder.export_default_declaration( + Builder.function_expression( + [], + [], + Builder.block_statement([]) + ) + ) + + assert_gen ast, "export default function() {};" + assert_gen ast, "export default function(){};", beauty: false + end + + should "convert export default declaration with function declaration generator" do + ast = Builder.export_default_declaration( + Builder.function_declaration( + Builder.identifier(:x), + [], + [], + Builder.block_statement([]), + true + ) + ) + + assert_gen ast, "export default function* x() {}" + assert_gen ast, "export default function*x(){}", beauty: false + end + + should "convert export default declaration with function expression generator" do + ast = Builder.export_default_declaration( + Builder.function_expression( + [], + [], + Builder.block_statement([]), + true + ) + ) + + assert_gen ast, "export default function*() {};" + assert_gen ast, "export default function*(){};", beauty: false + end + + should "convert export default declaration with class declaration" do + ast = Builder.export_default_declaration( + Builder.class_declaration( + Builder.identifier(:y), + Builder.class_body([]) + ) + ) + + assert_gen ast, "export default class y {}" + assert_gen ast, "export default class y{}", beauty: false + end + + should "convert export default declaration with class declaration extends" do + ast = Builder.export_default_declaration( + Builder.class_declaration( + Builder.identifier(:y), + Builder.class_body([]), + Builder.identifier(:z) + ) + ) + + assert_gen ast, "export default class y extends z {}" + assert_gen ast, "export default class y extends z{}", beauty: false + end + + should "convert export default declaration with class expression" do + ast = Builder.export_default_declaration( + Builder.class_expression( + Builder.class_body([]) + ) + ) + + assert_gen ast, "export default class {};" + assert_gen ast, "export default class{};", beauty: false + end + + should "convert export default declaration with assignment expression" do + ast = Builder.export_default_declaration( + Builder.assignment_expression( + :=, + Builder.identifier(:x), + Builder.literal(0) + ) + ) + + assert_gen ast, "export default x = 0;" + assert_gen ast, "export default x=0;", beauty: false + end + + should "convert export default declaration with literal" do + ast = Builder.export_default_declaration( + Builder.literal(0) + ) + + assert_gen ast, "export default 0;" + assert_gen ast, "export default 0;", beauty: false + end + + should "convert export default declaration with sequence expression" do + ast = Builder.export_default_declaration( + Builder.sequence_expression([ + Builder.literal(0), + Builder.literal(1) + ]) + ) + + assert_gen ast, "export default (0, 1);" + assert_gen ast, "export default (0,1);", beauty: false + end +end diff --git a/test/tools/generator/export_named_declaration_test.exs b/test/tools/generator/export_named_declaration_test.exs new file mode 100644 index 0000000..b4e593d --- /dev/null +++ b/test/tools/generator/export_named_declaration_test.exs @@ -0,0 +1,141 @@ +defmodule ESTree.Tools.Generator.ExportNamedDeclaration.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert export named declaration with one specifier" do + ast = Builder.export_named_declaration(nil, [ + Builder.export_specifier( + Builder.identifier(:test), + Builder.identifier(:test) + )] + ) + + assert_gen ast, "export { test };" + assert_gen ast, "export{test};", beauty: false + end + + should "convert export named declaration with one specifier with an alias" do + ast = Builder.export_named_declaration(nil, [ + Builder.export_specifier( + Builder.identifier(:test), + Builder.identifier(:test1) + )] + ) + + assert_gen ast, "export { test as test1 };" + assert_gen ast, "export{test as test1};", beauty: false + end + + should "convert export named declaration with more than one specifier" do + ast = Builder.export_named_declaration(nil, + [ + Builder.export_specifier( + Builder.identifier(:top), + Builder.identifier(:top) + ), + Builder.export_specifier( + Builder.identifier(:test), + Builder.identifier(:test1) + ) + ], + Builder.literal(:test) + ) + + assert_gen ast, "export { top, test as test1 } from 'test';" + assert_gen ast, "export{top,test as test1}from'test';", beauty: false + end + + should "convert export named declaration with variable declaration" do + ast = Builder.export_named_declaration( + Builder.variable_declaration([ + Builder.variable_declarator(Builder.identifier(:a)), + Builder.variable_declarator(Builder.identifier(:b), Builder.literal(0)) + ]) + ) + + assert_gen ast, "export var a,\n b = 0;" + assert_gen ast, "export var a,b=0;", beauty: false + end + + should "convert export named declaration with let variable declaration" do + ast = Builder.export_named_declaration( + Builder.variable_declaration([ + Builder.variable_declarator(Builder.identifier(:a)), + Builder.variable_declarator(Builder.identifier(:b), Builder.literal(0)) + ], + :let) + ) + + assert_gen ast, "export let a,\n b = 0;" + assert_gen ast, "export let a,b=0;", beauty: false + end + + should "convert export named declaration with const variable declaration" do + ast = Builder.export_named_declaration( + Builder.variable_declaration([ + Builder.variable_declarator(Builder.identifier(:a)), + Builder.variable_declarator(Builder.identifier(:b), Builder.literal(0)) + ], + :const) + ) + + assert_gen ast, "export const a,\n b = 0;" + assert_gen ast, "export const a,b=0;", beauty: false + end + + should "convert export named declaration with function declaration" do + ast = Builder.export_named_declaration( + Builder.function_declaration( + Builder.identifier(:x), + [], + [], + Builder.block_statement([]) + ) + ) + + assert_gen ast, "export function x() {}" + assert_gen ast, "export function x(){}", beauty: false + end + + should "convert export named declaration with function declaration generator" do + ast = Builder.export_named_declaration( + Builder.function_declaration( + Builder.identifier(:x), + [], + [], + Builder.block_statement([]), + true + ) + ) + + assert_gen ast, "export function* x() {}" + assert_gen ast, "export function*x(){}", beauty: false + end + + should "convert export named declaration with class declaration" do + ast = Builder.export_named_declaration( + Builder.class_declaration( + Builder.identifier(:y), + Builder.class_body([]) + ) + ) + + assert_gen ast, "export class y {}" + assert_gen ast, "export class y{}", beauty: false + end + + should "convert export named declaration with class declaration extends" do + ast = Builder.export_named_declaration( + Builder.class_declaration( + Builder.identifier(:y), + Builder.class_body([]), + Builder.identifier(:z) + ) + ) + + assert_gen ast, "export class y extends z {}" + assert_gen ast, "export class y extends z{}", beauty: false + end +end diff --git a/test/tools/generator/expression_statement_test.exs b/test/tools/generator/expression_statement_test.exs new file mode 100644 index 0000000..8382f87 --- /dev/null +++ b/test/tools/generator/expression_statement_test.exs @@ -0,0 +1,54 @@ +defmodule ESTree.Tools.Generator.ExpressionStatement.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert expression statement with class expression" do + ast = Builder.expression_statement( + Builder.class_expression( + Builder.class_body([]) + ) + ) + + assert_gen ast, "(class {});" + assert_gen ast, "(class{});", beauty: false + end + + should "convert expression statement with function expression" do + ast = Builder.expression_statement( + Builder.function_expression( + [], + [], + Builder.block_statement([]) + ) + ) + + assert_gen ast, "(function() {});" + assert_gen ast, "(function(){});", beauty: false + end + + should "convert expression statement with object expression" do + ast = Builder.expression_statement( + Builder.object_expression( + [] + ) + ) + + assert_gen ast, "({});" + assert_gen ast, "({});", beauty: false + end + + should "convert expression statement with assignment expression" do + ast = Builder.expression_statement( + Builder.assignment_expression( + :=, + Builder.object_pattern([]), + Builder.identifier(:a) + ) + ) + + assert_gen ast, "({} = a);" + assert_gen ast, "({}=a);", beauty: false + end +end diff --git a/test/tools/generator/for_in_statement_test.exs b/test/tools/generator/for_in_statement_test.exs new file mode 100644 index 0000000..3d3ad5b --- /dev/null +++ b/test/tools/generator/for_in_statement_test.exs @@ -0,0 +1,19 @@ +defmodule ESTree.Tools.Generator.ForInStatement.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert for in statement" do + ast = Builder.for_in_statement( + Builder.variable_declaration([ + Builder.variable_declarator(Builder.identifier(:a)) + ]), + Builder.identifier(:b), + Builder.block_statement([]) + ) + + assert_gen ast, "for (var a in b) {}" + assert_gen ast, "for(var a in b){}", beauty: false + end +end diff --git a/test/tools/generator/for_of_statement_test.exs b/test/tools/generator/for_of_statement_test.exs new file mode 100644 index 0000000..bd51be0 --- /dev/null +++ b/test/tools/generator/for_of_statement_test.exs @@ -0,0 +1,19 @@ +defmodule ESTree.Tools.Generator.ForOfStatement.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert for of statement" do + ast = Builder.for_of_statement( + Builder.variable_declaration([ + Builder.variable_declarator(Builder.identifier(:a)) + ]), + Builder.identifier(:b), + Builder.block_statement([]) + ) + + assert_gen ast, "for (var a of b) {}" + assert_gen ast, "for(var a of b){}", beauty: false + end +end diff --git a/test/tools/generator/for_statement_test.exs b/test/tools/generator/for_statement_test.exs new file mode 100644 index 0000000..adb0bb6 --- /dev/null +++ b/test/tools/generator/for_statement_test.exs @@ -0,0 +1,40 @@ +defmodule ESTree.Tools.Generator.ForStatement.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert for statement empty" do + ast = Builder.for_statement( + nil, + nil, + nil, + Builder.block_statement([]) + ) + + assert_gen ast, "for (; ; ) {}" + assert_gen ast, "for(;;){}", beauty: false + end + + should "convert for statement" do + ast = Builder.for_statement( + Builder.variable_declaration([ + Builder.variable_declarator(Builder.identifier(:i), Builder.literal(0)) + ]), + Builder.binary_expression( + :<, + Builder.identifier(:i), + Builder.identifier(:length) + ), + Builder.unary_expression( + :++, + false, + Builder.identifier(:i) + ), + Builder.block_statement([]) + ) + + assert_gen ast, "for (var i = 0; i < length; i++) {}" + assert_gen ast, "for(var i=0;i {}" + assert_gen ast, "async function() {\n return await x();\n}" + assert_gen ast, "async function(){return await x();}", beauty: false end - should "convert basic arrow function expression generator" do - ast = Builder.arrow_function_expression( - [], - [], - Builder.block_statement([]), - true, - false) - - assert Generator.generate(ast) == "()* => {}" - end - - should "convert arrow function with params" do - ast = Builder.arrow_function_expression( + should "convert function expression with params" do + ast = Builder.function_expression( [Builder.identifier(:one)], [], Builder.block_statement([]), false, - false) + false + ) - assert Generator.generate(ast) == "(one) => {}" + assert_gen ast, "function(one) {}" + assert_gen ast, "function(one){}", beauty: false - ast = Builder.arrow_function_expression( + ast = Builder.function_expression( [Builder.identifier(:one), Builder.identifier(:two)], [], Builder.block_statement([]), false, - false) + false + ) - assert Generator.generate(ast) == "(one,two) => {}" + assert_gen ast, "function(one, two) {}" + assert_gen ast, "function(one,two){}", beauty: false end - should "convert arrow function with default values" do - ast = Builder.arrow_function_expression( + should "convert function expression with default values" do + ast = Builder.function_expression( [Builder.identifier(:one), Builder.identifier(:two)], [nil, Builder.literal(1)], Builder.block_statement([]), false, - false) + false + ) - assert Generator.generate(ast) == "(one,two = 1) => {}" + assert_gen ast, "function(one, two = 1) {}" + assert_gen ast, "function(one,two=1){}", beauty: false end end diff --git a/test/tools/generator/identifier_test.exs b/test/tools/generator/identifier_test.exs index 86b98f2..46a29a4 100644 --- a/test/tools/generator/identifier_test.exs +++ b/test/tools/generator/identifier_test.exs @@ -1,13 +1,20 @@ defmodule ESTree.Tools.Generator.Identifier.Test do use ShouldI + alias ESTree.Tools.Builder - alias ESTree.Tools.Generator + import ESTree.Test.Support - should "convert identifiers" do + should "convert identifiers (binary)" do ast = Builder.identifier("hello") - assert Generator.generate(ast) == "hello" + assert_gen ast, "hello" + assert_gen ast, "hello", beauty: false + end + + should "convert identifiers (atom)" do ast = Builder.identifier(:hello) - assert Generator.generate(ast) == "hello" + + assert_gen ast, "hello" + assert_gen ast, "hello", beauty: false end end diff --git a/test/tools/generator/if_statement_test.exs b/test/tools/generator/if_statement_test.exs index f5820dc..c9b4d06 100644 --- a/test/tools/generator/if_statement_test.exs +++ b/test/tools/generator/if_statement_test.exs @@ -1,14 +1,17 @@ defmodule ESTree.Tools.Generator.IfStatement.Test do use ShouldI + alias ESTree.Tools.Builder - alias ESTree.Tools.Generator + import ESTree.Test.Support should "convert if without else" do ast = Builder.if_statement( Builder.identifier(:test), Builder.block_statement([]) ) - assert Generator.generate(ast) == "if(test) {}" + + assert_gen ast, "if (test) {}" + assert_gen ast, "if(test){}", beauty: false end should "convert if with else" do @@ -17,7 +20,9 @@ defmodule ESTree.Tools.Generator.IfStatement.Test do Builder.block_statement([]), Builder.block_statement([]) ) - assert Generator.generate(ast) == "if(test) {} else {}" + + assert_gen ast, "if (test) {} else {}" + assert_gen ast, "if(test){}else{}", beauty: false end should "convert if with else if" do @@ -30,7 +35,23 @@ defmodule ESTree.Tools.Generator.IfStatement.Test do Builder.block_statement([]) ) ) - assert Generator.generate(ast) == "if(test) {} else if(test) {} else {}" + + assert_gen ast, "if (test) {} else if (test) {} else {}" + assert_gen ast, "if(test){}else if(test){}else{}", beauty: false end + should "convert if statement with short syntax" do + ast = Builder.if_statement( + Builder.identifier(:test), + Builder.return_statement(Builder.literal(1)), + Builder.if_statement( + Builder.identifier(:test2), + Builder.return_statement(Builder.literal(2)), + Builder.return_statement(Builder.literal(3)) + ) + ) + + assert_gen ast, "if (test)\n return 1;\nelse if (test2)\n return 2;\nelse\n return 3;" + assert_gen ast, "if(test)return 1;else if(test2)return 2;else return 3;", beauty: false + end end diff --git a/test/tools/generator/import_declaration_test.exs b/test/tools/generator/import_declaration_test.exs index 23d547f..fede4b6 100644 --- a/test/tools/generator/import_declaration_test.exs +++ b/test/tools/generator/import_declaration_test.exs @@ -1,7 +1,8 @@ defmodule ESTree.Tools.Generator.ImportDeclaration.Test do use ShouldI + alias ESTree.Tools.Builder - alias ESTree.Tools.Generator + import ESTree.Test.Support should "convert import declaration with one specifier" do ast = Builder.import_declaration([ @@ -12,7 +13,8 @@ defmodule ESTree.Tools.Generator.ImportDeclaration.Test do Builder.literal(:test) ) - assert Generator.generate(ast) == "import { test } from 'test';" + assert_gen ast, "import { test } from 'test';" + assert_gen ast, "import{test}from'test';", beauty: false end should "convert import declaration with one specifier with an alias" do @@ -24,7 +26,8 @@ defmodule ESTree.Tools.Generator.ImportDeclaration.Test do Builder.literal(:test) ) - assert Generator.generate(ast) == "import { test as test1 } from 'test';" + assert_gen ast, "import { test as test1 } from 'test';" + assert_gen ast, "import{test as test1}from'test';", beauty: false end should "convert import declaration with more than one specifier" do @@ -42,7 +45,8 @@ defmodule ESTree.Tools.Generator.ImportDeclaration.Test do Builder.literal(:test) ) - assert Generator.generate(ast) == "import { top, test as test1 } from 'test';" + assert_gen ast, "import { top, test as test1 } from 'test';" + assert_gen ast, "import{top,test as test1}from'test';", beauty: false end @@ -55,7 +59,8 @@ defmodule ESTree.Tools.Generator.ImportDeclaration.Test do Builder.literal(:test) ) - assert Generator.generate(ast) == "import test from 'test';" + assert_gen ast, "import test from 'test';" + assert_gen ast, "import test from'test';", beauty: false end should "convert import declaration with a namespace specifier" do @@ -66,7 +71,7 @@ defmodule ESTree.Tools.Generator.ImportDeclaration.Test do Builder.literal(:test) ) - assert Generator.generate(ast) == "import * as test from 'test';" + assert_gen ast, "import * as test from 'test';" + assert_gen ast, "import*as test from'test';", beauty: false end - -end \ No newline at end of file +end diff --git a/test/tools/generator/jsx_test.exs b/test/tools/generator/jsx_test.exs index d08a9ff..2b31ad6 100644 --- a/test/tools/generator/jsx_test.exs +++ b/test/tools/generator/jsx_test.exs @@ -1,21 +1,27 @@ defmodule ESTree.Tools.Generator.JSX.Test do use ShouldI + alias ESTree.Tools.Builder - alias ESTree.Tools.Generator + import ESTree.Test.Support + should "handle atom and binary as identifier name" do + assert_gen Builder.jsx_identifier("Test"), "Test" + assert_gen Builder.jsx_identifier(:Test), "Test" + end should "handle no closing tag" do ast = Builder.jsx_element( Builder.jsx_opening_element( Builder.jsx_identifier( - "Test" + "Test" ), [], true ) ) - assert Generator.generate(ast) == "" + assert_gen ast, "" + assert_gen ast, "", beauty: false end should "handle closing tag" do @@ -33,7 +39,8 @@ defmodule ESTree.Tools.Generator.JSX.Test do ) ) - assert Generator.generate(ast) == "" + assert_gen ast, "" + assert_gen ast, "", beauty: false end @@ -57,7 +64,7 @@ defmodule ESTree.Tools.Generator.JSX.Test do Builder.jsx_spread_attribute( Builder.array_expression([ Builder.literal(1) - ]) + ]) ) ] ), @@ -69,9 +76,43 @@ defmodule ESTree.Tools.Generator.JSX.Test do ) ) - assert Generator.generate(ast) == "" + assert_gen ast, "" + assert_gen ast, "", beauty: false end + should "handle escaped attributes value" do + ast = fn x -> + Builder.jsx_element( + Builder.jsx_opening_element( + Builder.jsx_identifier("Test"), + [ + Builder.jsx_attribute( + Builder.jsx_identifier("className"), + x + ) + ] + ), + [], + Builder.jsx_closing_element( + Builder.jsx_identifier( + "Test" + ) + ) + ) + end + + assert_gen ast.(Builder.literal("\\")), "" + assert_gen ast.(Builder.literal("\n")), "" + assert_gen ast.(Builder.literal("\r")), "" + assert_gen ast.(Builder.literal("\t")), "" + assert_gen ast.(Builder.literal("\b")), "" + assert_gen ast.(Builder.literal("\f")), "" + assert_gen ast.(Builder.literal("\v")), "" + assert_gen ast.(Builder.literal("\u2028")), "" + assert_gen ast.(Builder.literal("\u2029")), "" + assert_gen ast.(Builder.literal("\ufeff")), "" + assert_gen ast.(Builder.literal("'")), "" + end should "handle element with elements inside" do ast = Builder.jsx_element( @@ -92,10 +133,10 @@ defmodule ESTree.Tools.Generator.JSX.Test do ) ) - assert Generator.generate(ast) == "
" + assert_gen ast, "
" + assert_gen ast, "
", beauty: false end - should "handle namespaced names" do ast = Builder.jsx_element( Builder.jsx_opening_element( @@ -119,10 +160,10 @@ defmodule ESTree.Tools.Generator.JSX.Test do ) ) - assert Generator.generate(ast) == "
" + assert_gen ast, "
" + assert_gen ast, "
", beauty: false end - should "handle member names" do ast = Builder.jsx_element( Builder.jsx_opening_element( @@ -146,6 +187,55 @@ defmodule ESTree.Tools.Generator.JSX.Test do ) ) - assert Generator.generate(ast) == "
" + assert_gen ast, "
" + assert_gen ast, "
", beauty: false + end + + should "handle inner text" do + ast = Builder.jsx_element( + Builder.jsx_opening_element( + Builder.jsx_identifier( + "Test" + ) + ), + [ + Builder.literal("counter: "), + Builder.jsx_expression_container( + Builder.identifier(:count) + ), + Builder.literal("."), + ], + Builder.jsx_closing_element( + Builder.jsx_identifier( + "Test" + ) + ) + ) + + assert_gen ast, "counter: {count}." + assert_gen ast, "counter: {count}.", beauty: false + end + + should "handle inner text escape" do + ast = fn x -> + Builder.jsx_element( + Builder.jsx_opening_element( + Builder.jsx_identifier( + "Test" + ) + ), + [x], + Builder.jsx_closing_element( + Builder.jsx_identifier( + "Test" + ) + ) + ) + end + + assert_gen ast.(Builder.literal("{")), "{" + assert_gen ast.(Builder.literal("}")), "}" + assert_gen ast.(Builder.literal("<")), "<" + assert_gen ast.(Builder.literal(">")), ">" end end diff --git a/test/tools/generator/literal_test.exs b/test/tools/generator/literal_test.exs index 18dc30f..ddac10a 100644 --- a/test/tools/generator/literal_test.exs +++ b/test/tools/generator/literal_test.exs @@ -1,49 +1,48 @@ defmodule ESTree.Generator.Literal.Test do use ShouldI + alias ESTree.Tools.Builder - alias ESTree.Tools.Generator + import ESTree.Test.Support should "convert numbers" do - ast = Builder.literal(1) - assert Generator.generate(ast) == "1" - - ast = Builder.literal(10) - assert Generator.generate(ast) == "10" - - ast = Builder.literal(1.012) - assert Generator.generate(ast) == "1.012" + assert_gen Builder.literal(1), "1" + assert_gen Builder.literal(10), "10" + assert_gen Builder.literal(1.012), "1.012" end should "convert booleans" do - ast = Builder.literal(true) - assert Generator.generate(ast) == "true" - - ast = Builder.literal(false) - assert Generator.generate(ast) == "false" + assert_gen Builder.literal(true), "true" + assert_gen Builder.literal(false), "false" end should "convert string" do - ast = Builder.literal("hello") - assert Generator.generate(ast) == "'hello'" + assert_gen Builder.literal("hello"), "'hello'" + assert_gen Builder.literal("goodbye"), "'goodbye'" + assert_gen Builder.literal(:hello), "'hello'" + assert_gen Builder.literal(:goodbye), "'goodbye'" + end - ast = Builder.literal("goodbye") - assert Generator.generate(ast) == "'goodbye'" + should "convert string escape" do + assert_gen Builder.literal("\\"), "'\\\\'" + assert_gen Builder.literal("\n"), "'\\n'" + assert_gen Builder.literal("\r"), "'\\r'" + assert_gen Builder.literal("\t"), "'\\t'" + assert_gen Builder.literal("\b"), "'\\b'" + assert_gen Builder.literal("\f"), "'\\f'" + assert_gen Builder.literal("\v"), "'\\v'" + assert_gen Builder.literal("\u2028"), "'\\u2028'" + assert_gen Builder.literal("\u2029"), "'\\u2029'" + assert_gen Builder.literal("\ufeff"), "'\\ufeff'" + assert_gen Builder.literal("'"), "'\\''" end should "convert null" do - ast = Builder.literal(nil) - assert Generator.generate(ast) == "null" + assert_gen Builder.literal(nil), "null" end should "convert regex" do - ast = Builder.literal(%{}, Builder.regex("^abc$", "i")) - assert Generator.generate(ast) == "/^abc$/i" - - ast = Builder.literal(%{}, Builder.regex("^abc$", "")) - assert Generator.generate(ast) == "/^abc$/" - - ast = Builder.literal(%{}, Builder.regex("", "")) - assert Generator.generate(ast) == "//" + assert_gen Builder.literal(%{}, Builder.regex("^abc$", "i")), "/^abc$/i" + assert_gen Builder.literal(%{}, Builder.regex("^abc$", "")), "/^abc$/" + assert_gen Builder.literal(%{}, Builder.regex("", "")), "//" end - end diff --git a/test/tools/generator/new_expression_test.exs b/test/tools/generator/new_expression_test.exs new file mode 100644 index 0000000..ad22e24 --- /dev/null +++ b/test/tools/generator/new_expression_test.exs @@ -0,0 +1,95 @@ +defmodule ESTree.Tools.Generator.NewExpression.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert new expression when arguments are empty" do + ast = Builder.new_expression( + Builder.identifier(:X), + [] + ) + + assert_gen ast, "new X()" + assert_gen ast, "new X()", beauty: false + end + + should "convert new expression when argument" do + ast = Builder.new_expression( + Builder.identifier(:X), + [Builder.identifier(:a)] + ) + + assert_gen ast, "new X(a)" + assert_gen ast, "new X(a)", beauty: false + end + + should "convert new expression when callee is member expression" do + ast = Builder.new_expression( + Builder.member_expression( + Builder.identifier(:x), + Builder.identifier(:Y) + ), + [] + ) + + assert_gen ast, "new x.Y()" + assert_gen ast, "new x.Y()", beauty: false + end + + should "convert new expression when callee is call expression" do + ast = Builder.new_expression( + Builder.member_expression( + Builder.call_expression( + Builder.identifier(:x), + [] + ), + Builder.identifier(:Y) + ), + [] + ) + + assert_gen ast, "new (x().Y)()" + assert_gen ast, "new (x().Y)()", beauty: false + end + + should "convert new expression when callee is new expression" do + ast = Builder.new_expression( + Builder.new_expression( + Builder.identifier(:X), + [] + ), + [] + ) + + assert_gen ast, "new new X()()" + assert_gen ast, "new new X()()", beauty: false + end + + should "convert new expression when callee is sequence expression" do + ast = Builder.new_expression( + Builder.sequence_expression([ + Builder.identifier(:X), + Builder.identifier(:Y) + ]), + [] + ) + + assert_gen ast, "new (X, Y)()" + assert_gen ast, "new (X,Y)()", beauty: false + end + + should "convert new expression when callee is conditional statement" do + ast = Builder.new_expression( + Builder.conditional_statement( + Builder.literal(true), + Builder.identifier(:Y), + Builder.identifier(:X) + ), + [] + ) + + assert_gen ast, "new (true ? X : Y)()" + assert_gen ast, "new (true?X:Y)()", beauty: false + end +end diff --git a/test/tools/generator/object_expression_test.exs b/test/tools/generator/object_expression_test.exs index 677aec3..8c63032 100644 --- a/test/tools/generator/object_expression_test.exs +++ b/test/tools/generator/object_expression_test.exs @@ -1,16 +1,21 @@ defmodule ESTree.Tools.Generator.ObjectExpression.Test do use ShouldI + alias ESTree.Tools.Builder - alias ESTree.Tools.Generator + import ESTree.Test.Support should "convert object when properties is nil" do ast = Builder.object_expression(nil) - assert Generator.generate(ast) == "{}" + + assert_gen ast, "{}" + assert_gen ast, "{}", beauty: false end should "convert object when properties is empty" do ast = Builder.object_expression([]) - assert Generator.generate(ast) == "{}" + + assert_gen ast, "{}" + assert_gen ast, "{}", beauty: false end should "convert object when properties contains one property" do @@ -20,7 +25,45 @@ defmodule ESTree.Tools.Generator.ObjectExpression.Test do Builder.identifier(:value) ) ]) - assert Generator.generate(ast) == "{key: value}" + + assert_gen ast, "{\n key: value\n}" + assert_gen ast, "{key:value}", beauty: false + end + + should "convert object when properties contains one property string" do + ast = Builder.object_expression([ + Builder.property( + Builder.literal(:key), + Builder.identifier(:value) + ) + ]) + + assert_gen ast, "{\n 'key': value\n}" + assert_gen ast, "{'key':value}", beauty: false + end + + should "convert object when properties contains computed properties" do + ast = Builder.object_expression([ + Builder.property( + Builder.identifier(:key), + Builder.identifier(:value), + :init, + false, + false, + true + ), + Builder.property( + Builder.literal(:key), + Builder.identifier(:value), + :init, + false, + false, + true + ) + ]) + + assert_gen ast, "{\n [key]: value,\n ['key']: value\n}" + assert_gen ast, "{[key]:value,['key']:value}", beauty: false end should "convert object when properties contains multiple properties" do @@ -34,7 +77,9 @@ defmodule ESTree.Tools.Generator.ObjectExpression.Test do Builder.identifier(:value1) ) ]) - assert Generator.generate(ast) == "{key: value, key1: value1}" + + assert_gen ast, "{\n key: value,\n key1: value1\n}" + assert_gen ast, "{key:value,key1:value1}", beauty: false end @@ -51,7 +96,9 @@ defmodule ESTree.Tools.Generator.ObjectExpression.Test do :set ), ]) - assert Generator.generate(ast) == "{get key() {}, set key(p) {}}" + + assert_gen ast, "{\n get key() {},\n set key(p) {}\n}" + assert_gen ast, "{get key(){},set key(p){}}", beauty: false end should "convert object when properties contains getter and setter methods" do @@ -71,7 +118,33 @@ defmodule ESTree.Tools.Generator.ObjectExpression.Test do true ), ]) - assert Generator.generate(ast) == "{key() {}, key(p) {}}" + + assert_gen ast, "{\n key() {},\n key(p) {}\n}" + assert_gen ast, "{key(){},key(p){}}", beauty: false + end + + should "convert object when properties are methods computed" do + ast = Builder.object_expression([ + Builder.property( + Builder.identifier(:key), + Builder.function_expression([], [], Builder.block_statement([])), + :init, + false, + true, + true + ), + Builder.property( + Builder.literal(:key), + Builder.function_expression([], [], Builder.block_statement([])), + :init, + false, + true, + true + ), + ]) + + assert_gen ast, "{\n [key]() {},\n ['key']() {}\n}" + assert_gen ast, "{[key](){},['key'](){}}", beauty: false end should "convert object when properties contains shorthand properties" do @@ -82,7 +155,8 @@ defmodule ESTree.Tools.Generator.ObjectExpression.Test do :init, true) ]) - assert Generator.generate(ast) == "{key}" + + assert_gen ast, "{\n key\n}" + assert_gen ast, "{key}", beauty: false end - end diff --git a/test/tools/generator/object_pattern_test.exs b/test/tools/generator/object_pattern_test.exs new file mode 100644 index 0000000..348a682 --- /dev/null +++ b/test/tools/generator/object_pattern_test.exs @@ -0,0 +1,57 @@ +defmodule ESTree.Generator.ObjectPattern.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert object pattern" do + ast = Builder.object_pattern([ + Builder.property( + Builder.identifier(:i), + nil, + :init, + true + ), + Builder.property( + Builder.identifier(:j), + Builder.identifier(:k) + ) + ]) + + assert_gen ast, "{i, j: k}" + assert_gen ast, "{i,j:k}", beauty: false + + ast = Builder.object_pattern([ + Builder.property( + Builder.identifier(:a), + Builder.array_pattern([ + Builder.identifier(:b), + Builder.object_pattern([ + Builder.property( + Builder.identifier(:c), + nil, + :init, + true + ) + ]) + ]) + ) + ]) + + assert_gen ast, "{a: [b, {c}]}" + assert_gen ast, "{a:[b,{c}]}", beauty: false + + ast = Builder.object_pattern([ + Builder.property( + Builder.identifier(:g), + Builder.assignment_expression( + :=, + Builder.identifier(:h), + Builder.literal(1) + ) + ) + ]) + + assert_gen ast, "{g: h = 1}" + end +end diff --git a/test/tools/generator/switch_statement_test.exs b/test/tools/generator/switch_statement_test.exs new file mode 100644 index 0000000..2fd1677 --- /dev/null +++ b/test/tools/generator/switch_statement_test.exs @@ -0,0 +1,49 @@ +defmodule ESTree.Tools.Generator.SwitchStatement.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert switch statement" do + ast = Builder.switch_statement( + Builder.identifier(:test), + [ + Builder.switch_case( + Builder.identifier(:b), + [Builder.break_statement()] + ), + Builder.switch_case( + Builder.literal("c"), + [Builder.break_statement()] + ), + Builder.switch_case( + Builder.literal(1), + [Builder.break_statement()] + ), + Builder.switch_case( + nil, + [Builder.break_statement()] + ) + ] + ) + + str = """ +switch (test) { +case b: + break; + +case 'c': + break; + +case 1: + break; + +default: + break; +} +""" + + assert_gen ast, String.trim(str) + assert_gen ast, "switch(test){case b:break;case 'c':break;case 1:break;default:break;}", beauty: false + end +end diff --git a/test/tools/generator/template_literal_test.exs b/test/tools/generator/template_literal_test.exs index 69cd774..955aa7f 100644 --- a/test/tools/generator/template_literal_test.exs +++ b/test/tools/generator/template_literal_test.exs @@ -1,66 +1,75 @@ defmodule ESTree.Tools.Generator.TemplateLiteral.Test do use ShouldI - alias ESTree.Tools.Builder - alias ESTree.Tools.Generator + alias ESTree.Tools.Builder + import ESTree.Test.Support should "convert empty template literal" do ast = Builder.template_literal([],[]) - assert Generator.generate(ast) == "``" + + assert_gen ast, "``" + assert_gen ast, "``", beauty: false end should "convert template literal with no expressions" do ast = Builder.template_literal([ - Builder.template_element("hello ", "hello ", false, + Builder.template_element("hello ", "hello ", false, Builder.source_location( nil, Builder.position(0, 1), Builder.position(0, 5) ) ), - Builder.template_element("there", "there", true, + Builder.template_element("there", "there", true, Builder.source_location( nil, Builder.position(0, 5), Builder.position(0, 10) ) ) - ],[]) - assert Generator.generate(ast) == "`hello there`" + ], []) + + assert_gen ast, "`hello there`" + assert_gen ast, "`hello there`", beauty: false end should "convert template literal with no quasis" do - ast = Builder.template_literal([],[ - Builder.identifier(:a, - Builder.source_location( - nil, - Builder.position(0, 1), - Builder.position(0, 2) + ast = Builder.template_literal( + [], + [ + Builder.identifier(:a, + Builder.source_location( + nil, + Builder.position(0, 1), + Builder.position(0, 2) + ) ) - ) - ]) - assert Generator.generate(ast) == "`${a}`" + ] + ) + + assert_gen ast, "`${a}`" + assert_gen ast, "`${a}`", beauty: false end should "convert template literal with interlaping quasis and expressions" do ast = Builder.template_literal([ - Builder.template_element("hello ", "hello ", false, + Builder.template_element("hello ", "hello ", false, Builder.source_location( nil, Builder.position(0, 1), Builder.position(0, 5) ) ), - Builder.template_element(" there", " there", true, + Builder.template_element(" there", " there", true, Builder.source_location( nil, Builder.position(0, 6), Builder.position(0, 12) ) ) - ],[ - Builder.identifier(:a, + ], [ + Builder.identifier(:a, Builder.source_location( nil, Builder.position(0, 5), @@ -68,38 +77,41 @@ defmodule ESTree.Tools.Generator.TemplateLiteral.Test do ) ) ]) - assert Generator.generate(ast) == "`hello ${a} there`" + + assert_gen ast, "`hello ${a} there`" + assert_gen ast, "`hello ${a} there`", beauty: false end should "convert tagged template expressions" do ast = Builder.tagged_template_expression( Builder.identifier(:tag), Builder.template_literal([ - Builder.template_element("hello ", "hello ", false, - Builder.source_location( - nil, - Builder.position(0, 1), - Builder.position(0, 5) - ) - ), - Builder.template_element(" there", " there", true, - Builder.source_location( - nil, - Builder.position(0, 6), - Builder.position(0, 12) - ) - ) - ],[ - Builder.identifier(:a, - Builder.source_location( - nil, - Builder.position(0, 5), - Builder.position(0, 6) - ) - ) - ]) + Builder.template_element("hello ", "hello ", false, + Builder.source_location( + nil, + Builder.position(0, 1), + Builder.position(0, 5) + ) + ), + Builder.template_element(" there", " there", true, + Builder.source_location( + nil, + Builder.position(0, 6), + Builder.position(0, 12) + ) + ) + ],[ + Builder.identifier(:a, + Builder.source_location( + nil, + Builder.position(0, 5), + Builder.position(0, 6) + ) + ) + ]) ) - assert Generator.generate(ast) == "tag `hello ${a} there`" - end -end \ No newline at end of file + assert_gen ast, "tag `hello ${a} there`" + assert_gen ast, "tag `hello ${a} there`", beauty: false + end +end diff --git a/test/tools/generator/throw_statement_test.exs b/test/tools/generator/throw_statement_test.exs new file mode 100644 index 0000000..4ea6476 --- /dev/null +++ b/test/tools/generator/throw_statement_test.exs @@ -0,0 +1,18 @@ +defmodule ESTree.Tools.Generator.ThrowStatement.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert throw statement" do + ast = Builder.throw_statement( + Builder.new_expression( + Builder.identifier(:Error), + [Builder.literal("error")] + ) + ) + + assert_gen ast, "throw new Error('error');" + assert_gen ast, "throw new Error('error');", beauty: false + end +end diff --git a/test/tools/generator/try_statement_test.exs b/test/tools/generator/try_statement_test.exs index 3a1b4d4..002f1ab 100644 --- a/test/tools/generator/try_statement_test.exs +++ b/test/tools/generator/try_statement_test.exs @@ -1,7 +1,8 @@ defmodule ESTree.Tools.Generator.TryStatement.Test do use ShouldI + alias ESTree.Tools.Builder - alias ESTree.Tools.Generator + import ESTree.Test.Support should "convert try with catch" do ast = Builder.try_statement( @@ -11,8 +12,9 @@ defmodule ESTree.Tools.Generator.TryStatement.Test do Builder.block_statement([]) ) ) - - assert Generator.generate(ast) == "try{}catch(e) {}" + + assert_gen ast, "try {} catch (e) {}" + assert_gen ast, "try{}catch(e){}", beauty: false end should "convert try with finally" do @@ -21,8 +23,9 @@ defmodule ESTree.Tools.Generator.TryStatement.Test do nil, Builder.block_statement([]) ) - - assert Generator.generate(ast) == "try{}finally{}" + + assert_gen ast, "try {} finally {}" + assert_gen ast, "try{}finally{}", beauty: false end should "convert try catch finally" do @@ -34,7 +37,8 @@ defmodule ESTree.Tools.Generator.TryStatement.Test do ), Builder.block_statement([]) ) - - assert Generator.generate(ast) == "try{}catch(e) {}finally{}" + + assert_gen ast, "try {} catch (e) {} finally {}" + assert_gen ast, "try{}catch(e){}finally{}", beauty: false end end diff --git a/test/tools/generator/unary_expression_test.exs b/test/tools/generator/unary_expression_test.exs new file mode 100644 index 0000000..6ae3c6f --- /dev/null +++ b/test/tools/generator/unary_expression_test.exs @@ -0,0 +1,84 @@ +defmodule ESTree.Generator.UnaryExpression.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "convert unary expression !true" do + ast = Builder.unary_expression( + :!, + true, + Builder.literal(true) + ) + + assert_gen ast, "!true" + assert_gen ast, "!true", beauty: false + end + + should "convert unary expression !false" do + ast = Builder.unary_expression( + :!, + true, + Builder.literal(false) + ) + + assert_gen ast, "!false" + assert_gen ast, "!false", beauty: false + end + + should "convert unary expression !typeof" do + ast = Builder.unary_expression( + :!, + true, + Builder.unary_expression( + :typeof, + true, + Builder.identifier(:x) + ) + ) + + assert_gen ast, "!typeof x" + assert_gen ast, "!typeof x", beauty: false + end + + should "convert unary expression ! and typeof with binary expression" do + ast = Builder.unary_expression( + :!, + true, + Builder.binary_expression( + :===, + Builder.unary_expression( + :typeof, + true, + Builder.identifier(:x) + ), + Builder.literal("boolean") + ) + ) + + assert_gen ast, "!(typeof x === 'boolean')" + assert_gen ast, "!(typeof x==='boolean')", beauty: false + end + + should "convert unary expression delete" do + ast = Builder.unary_expression( + :delete, + true, + Builder.identifier(:a) + ) + + assert_gen ast, "delete a" + assert_gen ast, "delete a", beauty: false + end + + should "convert unary expression void" do + ast = Builder.unary_expression( + :void, + true, + Builder.identifier(:a) + ) + + assert_gen ast, "void a" + assert_gen ast, "void a", beauty: false + end +end diff --git a/test/tools/generator/variable_declaration_test.exs b/test/tools/generator/variable_declaration_test.exs new file mode 100644 index 0000000..c9e0ac6 --- /dev/null +++ b/test/tools/generator/variable_declaration_test.exs @@ -0,0 +1,54 @@ +defmodule ESTree.Tools.Generator.VariableDeclaration.Test do + use ShouldI + + alias ESTree.Tools.Builder + import ESTree.Test.Support + + should "emit var declaration" do + ast = Builder.variable_declaration([]) + + assert_gen ast, "var ;" + assert_gen ast, "var ;", beauty: false + end + + should "emit let declaration" do + ast = Builder.variable_declaration([], :let) + + assert_gen ast, "let ;" + assert_gen ast, "let ;", beauty: false + end + + should "emit const declaration" do + ast = Builder.variable_declaration([], :const) + + assert_gen ast, "const ;" + assert_gen ast, "const ;", beauty: false + end + + should "add variable declarator" do + ast = Builder.variable_declaration([ + Builder.variable_declarator(Builder.identifier(:a)) + ]) + + assert_gen ast, "var a;" + assert_gen ast, "var a;", beauty: false + end + + should "add variable declarators with corresponding inits" do + ast = Builder.variable_declaration([ + Builder.variable_declarator(Builder.identifier(:a), Builder.literal(1)), + Builder.variable_declarator(Builder.identifier(:b)), + Builder.variable_declarator( + Builder.identifier(:c), + Builder.binary_expression( + :+, + Builder.identifier(:a), + Builder.literal(2) + ) + ) + ]) + + assert_gen ast, "var a = 1,\n b,\n c = a + 2;" + assert_gen ast, "var a=1,b,c=a+2;", beauty: false + end +end diff --git a/test/tools/generator/while_statement_test.exs b/test/tools/generator/while_statement_test.exs index 4af7991..0547259 100644 --- a/test/tools/generator/while_statement_test.exs +++ b/test/tools/generator/while_statement_test.exs @@ -1,15 +1,17 @@ defmodule ESTree.Tools.Generator.WhileStatement.Test do use ShouldI - alias ESTree.Tools.Builder - alias ESTree.Tools.Generator + alias ESTree.Tools.Builder + import ESTree.Test.Support should "convert while" do ast = Builder.while_statement( Builder.identifier(:test), Builder.block_statement([]) ) - assert Generator.generate(ast) == "while(test) {}" + + assert_gen ast, "while (test) {}" + assert_gen ast, "while(test){}", beauty: false end should "convert do while" do @@ -17,7 +19,8 @@ defmodule ESTree.Tools.Generator.WhileStatement.Test do Builder.block_statement([]), Builder.identifier(:test) ) - assert Generator.generate(ast) == "do {} while(test)" - end + assert_gen ast, "do {} while (test);" + assert_gen ast, "do{}while(test);", beauty: false + end end