@@ -515,7 +515,7 @@ and expression_desc cxt ~(level : int) f x : cxt =
515515    (*  TODO: dump for comments *) 
516516    pp_function ?directive ~is_method  ~return_unit  ~async 
517517      ~fn_state: default_fn_exp_state cxt f params body env
518-      (*  TODO:
518+   (*  TODO:
519519       when [e] is [Js_raw_code] with arity 
520520       print it in a more precise way 
521521       It seems the optimizer already did work to make sure 
@@ -524,6 +524,116 @@ and expression_desc cxt ~(level : int) f x : cxt =
524524         when Ext_list.length_equal el i 
525525       ]} 
526526    *)  
527+   (*  When -bs-preserve-jsx is enabled, we marked each transformed application node throughout the compilation.
528+      Here we print the transformed application node into a JSX syntax. 
529+      The JSX is slightly different from what a user would write, 
530+      but it is still valid JSX and is usable by tools like ESBuild. 
531+   *)  
532+   |  Call 
533+       ( ({
534+            expression_desc = 
535+              J. Var 
536+                (J. Qualified 
537+                   ( _,
538+                     Some  fnName
539+                     (*  We care about the function name when it is jsxs,
540+                        If this is the case, we need to unpack an array later on *)  
541+                   ));
542+          } as  e),
543+         el,
544+         {call_transformed_jsx =  true } )
545+     when  ! Js_config. jsx_preserve -> (
546+     (*  We match a JsxRuntime.jsx call *) 
547+     match  el with 
548+     |  [
549+      tag;
550+      {
551+        expression_desc = 
552+          (*  This is the props javascript object *) 
553+          Caml_block  (el, _mutable_flag, _, Lambda. Blk_record  {fields});
554+      };
555+     ] ->
556+       (*  We extract the props from the javascript object *) 
557+       let  fields = 
558+         Ext_list. array_list_filter_map fields el (fun  (f , opt ) x  ->
559+             match  x.expression_desc with 
560+             |  Undefined  _  when  opt -> None 
561+             |  _  -> Some  (f, x))
562+       in 
563+       print_jsx cxt ~level  f fnName tag fields
564+     |  [
565+      tag;
566+      {
567+        expression_desc = 
568+          Caml_block  (el, _mutable_flag, _, Lambda. Blk_record  {fields});
569+      };
570+      key;
571+     ] ->
572+       (*  When a component has a key the matching runtime function call will have a third argument being the key *) 
573+       let  fields = 
574+         Ext_list. array_list_filter_map fields el (fun  (f , opt ) x  ->
575+             match  x.expression_desc with 
576+             |  Undefined  _  when  opt -> None 
577+             |  _  -> Some  (f, x))
578+       in 
579+       print_jsx cxt ~level  ~key  f fnName tag fields
580+     |  [tag; ({expression_desc =  J. Seq  _} as  props)] ->
581+       (*  In the case of prop spreading, the expression will look like:
582+            (props.a = "Hello, world!", props) 
583+            which is equivalent to 
584+            <tag {...props} a="Hello, world!" /> 
585+ 
586+            We need to extract the props and the spread object. 
587+          *)  
588+       let  fields, spread_props = 
589+         let  rec  visit  acc  e  = 
590+           match  e.J. expression_desc with 
591+           |  J. Seq 
592+               ( {
593+                   J. expression_desc = 
594+                     J. Bin 
595+                       ( Js_op. Eq ,
596+                         {J. expression_desc =  J. Static_index  (_, name, _)},
597+                         value );
598+                 },
599+                 rest ) ->
600+             visit ((name, value) :: acc) rest
601+           |  _  -> (List. rev acc, e)
602+         in 
603+         visit []  props
604+       in 
605+       print_jsx cxt ~level  ~spread_props  f fnName tag fields
606+     |  [tag; ({expression_desc =  J. Seq  _} as  props); key] ->
607+       (*  In the case of props + prop spreading and key argument *) 
608+       let  fields, spread_props = 
609+         let  rec  visit  acc  e  = 
610+           match  e.J. expression_desc with 
611+           |  J. Seq 
612+               ( {
613+                   J. expression_desc = 
614+                     J. Bin 
615+                       ( Js_op. Eq ,
616+                         {J. expression_desc =  J. Static_index  (_, name, _)},
617+                         value );
618+                 },
619+                 rest ) ->
620+             visit ((name, value) :: acc) rest
621+           |  _  -> (List. rev acc, e)
622+         in 
623+         visit []  props
624+       in 
625+       print_jsx cxt ~level  ~spread_props  ~key  f fnName tag fields
626+     |  [tag; ({expression_desc =  J. Var  _} as  spread_props)] ->
627+       (*  All the props are spread *) 
628+       print_jsx cxt ~level  ~spread_props  f fnName tag [] 
629+     |  _  ->
630+       (*  This should not happen, we fallback to the general case *) 
631+       expression_desc cxt ~level  f
632+         (Call 
633+            ( e,
634+              el,
635+              {call_transformed_jsx =  false ; arity =  Full ; call_info =  Call_ml }
636+            )))
527637  |  Call  (e , el , info ) ->
528638    P. cond_paren_group f (level >  15 ) (fun  _  ->
529639        P. group f 0  (fun  _  ->
@@ -960,6 +1070,103 @@ and expression_desc cxt ~(level : int) f x : cxt =
9601070        P. string  f " ..."  ;
9611071        expression ~level: 13  cxt f e)
9621072
1073+ and  print_jsx  cxt  ?(spread_props  : J.expression option )
1074+     ?(key  : J.expression option ) ~(level  : int ) f  (fnName  : string )
1075+     (tag  : J.expression ) (fields  : (string * J.expression) list ) : cxt  = 
1076+   let  print_tag  cxt  = 
1077+     match  tag.expression_desc with 
1078+     (*  "div" or any other primitive tag *) 
1079+     |  J. Str  {txt}  ->
1080+       P. string  f txt;
1081+       cxt
1082+     (*  fragment *) 
1083+     |  J. Var  (J. Qualified ({id  = {name  = "JsxRuntime" } } , Some "Fragment" )) -> cxt
1084+     (*  A user defined component or external component *) 
1085+     |  _  -> expression ~level  cxt f tag
1086+   in 
1087+   let  children_opt = 
1088+     List. find_map
1089+       (fun  (n , e ) ->
1090+         if  n =  " children"   then 
1091+           if  fnName =  " jsxs"   then 
1092+             match  e.J. expression_desc with 
1093+             |  J. Array  (xs, _)
1094+             |  J. Optional_block  ({expression_desc  = J. Array  (xs , _ )} , _ ) ->
1095+               Some  xs
1096+             |  _  -> Some  [e]
1097+           else  Some  [e]
1098+         else  None )
1099+       fields
1100+   in 
1101+   let  print_props  cxt  = 
1102+     (*  If a key is present, should be printed before the spread props,
1103+     This is to ensure tools like ESBuild use the automatic JSX runtime *)  
1104+     let  cxt = 
1105+       match  key with 
1106+       |  None  -> cxt
1107+       |  Some  key  ->
1108+         P. string  f "  key={"  ;
1109+         let  cxt =  expression ~level: 0  cxt f key in 
1110+         P. string  f " } "  ;
1111+         cxt
1112+     in 
1113+     let  props =  List. filter (fun  (n , _ ) -> n <>  " children"  ) fields in 
1114+     let  cxt = 
1115+       match  spread_props with 
1116+       |  None  -> cxt
1117+       |  Some  spread  ->
1118+         P. string  f "  {..."  ;
1119+         let  cxt =  expression ~level: 0  cxt f spread in 
1120+         P. string  f " } "  ;
1121+         cxt
1122+     in 
1123+     if  List. length props =  0  then  cxt
1124+     else 
1125+       (List. fold_left (fun  acc  (n , x ) ->
1126+            P. space f;
1127+            P. string  f n;
1128+            P. string  f " ="  ;
1129+            P. string  f " {"  ;
1130+            let  next =  expression ~level: 0  acc f x in 
1131+            P. string  f " }"  ;
1132+            next))
1133+         cxt props
1134+   in 
1135+   match  children_opt with 
1136+   |  None  ->
1137+     P. string  f " <"  ;
1138+     let  cxt =  cxt |>  print_tag |>  print_props in 
1139+     P. string  f " />"  ;
1140+     cxt
1141+   |  Some  children  ->
1142+     let  child_is_jsx  child  = 
1143+       match  child.J. expression_desc with 
1144+       |  J. Call  (_ , _ , {call_transformed_jsx  = is_jsx } ) -> is_jsx
1145+       |  _  -> false 
1146+     in 
1147+ 
1148+     P. string  f " <"  ;
1149+     let  cxt =  cxt |>  print_tag |>  print_props in 
1150+ 
1151+     P. string  f " >"  ;
1152+     if  List. length children >  0  then  P. newline f;
1153+ 
1154+     let  cxt = 
1155+       List. fold_left
1156+         (fun  acc  e  ->
1157+           if  not  (child_is_jsx e) then  P. string  f " {"  ;
1158+           let  next =  expression ~level  acc f e in 
1159+           if  not  (child_is_jsx e) then  P. string  f " }"  ;
1160+           P. newline f;
1161+           next)
1162+         cxt children
1163+     in 
1164+ 
1165+     P. string  f " </"  ;
1166+     let  cxt =  print_tag cxt in 
1167+     P. string  f " >"  ;
1168+     cxt
1169+ 
9631170and  property_name_and_value_list  cxt  f  (l  : J.property_map ) = 
9641171  iter_lst cxt f l
9651172    (fun  cxt  f  (pn , e ) ->
0 commit comments