diff --git a/compiler/syntax/cli/res_cli.ml b/compiler/syntax/cli/res_cli.ml index 649d76cc04..1e8554e92b 100644 --- a/compiler/syntax/cli/res_cli.ml +++ b/compiler/syntax/cli/res_cli.ml @@ -165,6 +165,7 @@ module ResClflags : sig val jsx_module : string ref val jsx_mode : string ref val typechecker : bool ref + val test_ast_conversion : bool ref val parse : unit -> unit end = struct @@ -178,6 +179,7 @@ end = struct let jsx_mode = ref "automatic" let file = ref "" let typechecker = ref false + let test_ast_conversion = ref false let usage = "\n\ @@ -215,6 +217,9 @@ end = struct Arg.Unit (fun () -> typechecker := true), "Parses the ast as it would be passed to the typechecker and not the \ printer" ); + ( "-test-ast-conversion", + Arg.Unit (fun () -> test_ast_conversion := true), + "Test the ast conversion" ); ] let parse () = Arg.parse spec (fun f -> file := f) usage @@ -225,7 +230,7 @@ module CliArgProcessor = struct [@@unboxed] let process_file ~is_interface ~width ~recover ~target ~jsx_version - ~jsx_module ~jsx_mode ~typechecker filename = + ~jsx_module ~jsx_mode ~typechecker ~test_ast_conversion filename = let len = String.length filename in let process_interface = is_interface @@ -267,8 +272,17 @@ module CliArgProcessor = struct else exit 1) else let parsetree = - Jsx_ppx.rewrite_signature ~jsx_version ~jsx_module ~jsx_mode - parse_result.parsetree + if not test_ast_conversion then parse_result.parsetree + else + let tree0 = + Ast_mapper_to0.default_mapper.signature + Ast_mapper_to0.default_mapper parse_result.parsetree + in + Ast_mapper_from0.default_mapper.signature + Ast_mapper_from0.default_mapper tree0 + in + let parsetree = + Jsx_ppx.rewrite_signature ~jsx_version ~jsx_module ~jsx_mode parsetree in print_engine.print_interface ~width ~filename ~comments:parse_result.comments parsetree @@ -282,9 +296,19 @@ module CliArgProcessor = struct ~comments:parse_result.comments parse_result.parsetree else exit 1) else + let parsetree = + if not test_ast_conversion then parse_result.parsetree + else + let tree0 = + Ast_mapper_to0.default_mapper.structure + Ast_mapper_to0.default_mapper parse_result.parsetree + in + Ast_mapper_from0.default_mapper.structure + Ast_mapper_from0.default_mapper tree0 + in let parsetree = Jsx_ppx.rewrite_implementation ~jsx_version ~jsx_module ~jsx_mode - parse_result.parsetree + parsetree in print_engine.print_implementation ~width ~filename ~comments:parse_result.comments parsetree @@ -298,5 +322,6 @@ let () = ~width:!ResClflags.width ~recover:!ResClflags.recover ~target:!ResClflags.print ~jsx_version:!ResClflags.jsx_version ~jsx_module:!ResClflags.jsx_module ~jsx_mode:!ResClflags.jsx_mode - ~typechecker:!ResClflags.typechecker !ResClflags.file) + ~typechecker:!ResClflags.typechecker !ResClflags.file + ~test_ast_conversion:!ResClflags.test_ast_conversion) [@@raises exit] diff --git a/scripts/test_syntax.sh b/scripts/test_syntax.sh index 080a9e64a6..78d7c9ae93 100755 --- a/scripts/test_syntax.sh +++ b/scripts/test_syntax.sh @@ -42,6 +42,12 @@ while read file; do $DUNE_BIN_DIR/res_parser $file &> $(exp $file) & maybeWait done temp/files.txt +while read file; do + $DUNE_BIN_DIR/res_parser -test-ast-conversion -jsx-version 4 -jsx-mode "automatic" $file &> $(exp $file) & maybeWait +done temp/files.txt while read file; do diff --git a/tests/syntax_tests/data/ast-mapping/JSXElements.res b/tests/syntax_tests/data/ast-mapping/JSXElements.res new file mode 100644 index 0000000000..5321c91674 --- /dev/null +++ b/tests/syntax_tests/data/ast-mapping/JSXElements.res @@ -0,0 +1,25 @@ +let emptyUnary = + +let emptyNonunary =
+ +let emptyUnaryWithAttributes = + +let emptyNonunaryWithAttributes =
+ +let elementWithChildren =
+

{React.string("Hi")}

+

{React.string("Hello")}

+
+ +let elementWithChildrenAndAttributes =
+

{React.string("Hi")}

+

{React.string("Hello")}

+
+ +let elementWithConditionalChildren =
+ {if true { +

{React.string("Hi")}

+ } else { + React.null + }} +
\ No newline at end of file diff --git a/tests/syntax_tests/data/ast-mapping/JSXFragments.res b/tests/syntax_tests/data/ast-mapping/JSXFragments.res new file mode 100644 index 0000000000..e106a14fa1 --- /dev/null +++ b/tests/syntax_tests/data/ast-mapping/JSXFragments.res @@ -0,0 +1,14 @@ +let empty = <> + +let fragmentWithBracedExpresssion = <>{React.int(1 + 2)} + +let fragmentWithJSXElements = <> +

{React.string("Hi")}

+

{React.string("Hello")}

+ + +let nestedFragments = <> +

{React.string("Hi")}

+

{React.string("Hello")}

+ <>{React.string("Bye")} + \ No newline at end of file diff --git a/tests/syntax_tests/data/ast-mapping/expected/JSXElements.res.txt b/tests/syntax_tests/data/ast-mapping/expected/JSXElements.res.txt new file mode 100644 index 0000000000..f151681291 --- /dev/null +++ b/tests/syntax_tests/data/ast-mapping/expected/JSXElements.res.txt @@ -0,0 +1,41 @@ +let emptyUnary = ReactDOM.jsx("input", {}) + +let emptyNonunary = ReactDOM.jsx("div", {}) + +let emptyUnaryWithAttributes = ReactDOM.jsx("input", {type_: "text"}) + +let emptyNonunaryWithAttributes = ReactDOM.jsx("div", {className: "container"}) + +let elementWithChildren = ReactDOM.jsxs( + "div", + { + children: React.array([ + ReactDOM.jsx("h1", {children: ?ReactDOM.someElement({React.string("Hi")})}), + ReactDOM.jsx("p", {children: ?ReactDOM.someElement({React.string("Hello")})}), + ]), + }, +) + +let elementWithChildrenAndAttributes = ReactDOM.jsxs( + "div", + { + className: "container", + children: React.array([ + ReactDOM.jsx("h1", {children: ?ReactDOM.someElement({React.string("Hi")})}), + ReactDOM.jsx("p", {children: ?ReactDOM.someElement({React.string("Hello")})}), + ]), + }, +) + +let elementWithConditionalChildren = ReactDOM.jsx( + "div", + { + children: ?ReactDOM.someElement({ + if true { + ReactDOM.jsx("h1", {children: ?ReactDOM.someElement({React.string("Hi")})}) + } else { + React.null + } + }), + }, +) diff --git a/tests/syntax_tests/data/ast-mapping/expected/JSXFragments.res.txt b/tests/syntax_tests/data/ast-mapping/expected/JSXFragments.res.txt new file mode 100644 index 0000000000..6ed9d65c47 --- /dev/null +++ b/tests/syntax_tests/data/ast-mapping/expected/JSXFragments.res.txt @@ -0,0 +1,24 @@ +let empty = React.jsx(React.jsxFragment, {}) + +let fragmentWithBracedExpresssion = React.jsx(React.jsxFragment, {children: {React.int(1 + 2)}}) + +let fragmentWithJSXElements = React.jsxs( + React.jsxFragment, + { + children: React.array([ + ReactDOM.jsx("h1", {children: ?ReactDOM.someElement({React.string("Hi")})}), + ReactDOM.jsx("p", {children: ?ReactDOM.someElement({React.string("Hello")})}), + ]), + }, +) + +let nestedFragments = React.jsxs( + React.jsxFragment, + { + children: React.array([ + ReactDOM.jsx("h1", {children: ?ReactDOM.someElement({React.string("Hi")})}), + ReactDOM.jsx("p", {children: ?ReactDOM.someElement({React.string("Hello")})}), + React.jsx(React.jsxFragment, {children: {React.string("Bye")}}), + ]), + }, +)