@@ -1070,6 +1070,34 @@ and expression_desc cxt ~(level : int) f x : cxt =
10701070 P. string f " ..." ;
10711071 expression ~level: 13 cxt f e)
10721072
1073+ and print_indented_list (f : P.t ) (parent_expr_level : int ) (cxt : cxt )
1074+ (items : 'a list ) (print_item_func : int -> cxt -> P.t -> 'a -> cxt ) : cxt =
1075+ if List. length items = 0 then cxt
1076+ else
1077+ P. group f 1 (fun () ->
1078+ (* Increment indent level by 1 for this block of items *)
1079+ P. newline f;
1080+ (* Start the block on a new, fully indented line for the first item *)
1081+ let rec process_items current_cxt_for_fold remaining_items =
1082+ match remaining_items with
1083+ | [] ->
1084+ current_cxt_for_fold
1085+ (* Base case for recursion, though initial check avoids empty items *)
1086+ | [last_item] ->
1087+ (* Print the last item, but DO NOT print a newline after it *)
1088+ print_item_func parent_expr_level current_cxt_for_fold f last_item
1089+ | current_item :: next_items ->
1090+ let cxt_after_current =
1091+ print_item_func parent_expr_level current_cxt_for_fold f
1092+ current_item
1093+ in
1094+ P. newline f;
1095+ (* Add a newline AFTER the current item, to prepare for the NEXT item *)
1096+ process_items cxt_after_current next_items
1097+ in
1098+ (* Initial call to the recursive helper; initial check ensures items is not empty *)
1099+ process_items cxt items)
1100+
10731101and print_jsx cxt ?(spread_props : J.expression option )
10741102 ?(key : J.expression option ) ~(level : int ) f (fnName : string )
10751103 (tag : J.expression ) (fields : (string * J.expression) list ) : cxt =
@@ -1098,71 +1126,116 @@ and print_jsx cxt ?(spread_props : J.expression option)
10981126 else None )
10991127 fields
11001128 in
1101- let print_props cxt =
1129+ let print_props cxt props =
11021130 (* If a key is present, should be printed before the spread props,
11031131 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
1132+ let print_key key cxt =
1133+ P. string f " key={" ;
1134+ let cxt_k = expression ~level: 0 cxt f key in
1135+ P. string f " } " ;
1136+ cxt_k
11121137 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
1138+
1139+ let print_spread_props spread cxt =
1140+ P. string f " {..." ;
1141+ let cxt = expression ~level: 0 cxt f spread in
1142+ P. string f " } " ;
1143+ cxt
1144+ in
1145+
1146+ let print_prop n x ctx =
1147+ let prop_name = Js_dump_property. property_key_string n in
1148+ P. string f prop_name;
1149+ P. string f " =" ;
1150+ P. string f " {" ;
1151+ let next_cxt = expression ~level: 0 ctx f x in
1152+ P. string f " }" ;
1153+ next_cxt
11221154 in
1123- if List. length props = 0 then cxt
1155+ let printable_props =
1156+ (match key with
1157+ | None -> []
1158+ | Some k -> [print_key k])
1159+ @ (match spread_props with
1160+ | None -> []
1161+ | Some spread -> [print_spread_props spread])
1162+ @ List. map (fun (n , x ) -> print_prop n x) props
1163+ in
1164+ if List. length printable_props = 0 then (
1165+ match children_opt with
1166+ | Some _ -> cxt
1167+ | None ->
1168+ (* Put a space the tag name and /> *)
1169+ P. space f;
1170+ cxt)
11241171 else
1125- (List. fold_left (fun acc (n , x ) ->
1126- P. space f;
1127- let prop_name = Js_dump_property. property_key_string n in
1128-
1129- P. string f prop_name;
1130- P. string f " =" ;
1131- P. string f " {" ;
1132- let next = expression ~level: 0 acc f x in
1133- P. string f " }" ;
1134- next))
1135- cxt props
1172+ P. group f 1 (fun () ->
1173+ P. newline f;
1174+ let rec process_remaining_props acc_cxt printable_props =
1175+ match printable_props with
1176+ | [] -> acc_cxt
1177+ | print_prop :: [] -> print_prop acc_cxt
1178+ | print_prop :: tail ->
1179+ let next_cxt = print_prop acc_cxt in
1180+ P. newline f;
1181+ process_remaining_props next_cxt tail
1182+ in
1183+ process_remaining_props cxt printable_props)
11361184 in
1137- match children_opt with
1138- | None ->
1139- P. string f " <" ;
1140- let cxt = cxt |> print_tag |> print_props in
1141- P. string f " />" ;
1142- cxt
1143- | Some children ->
1144- let child_is_jsx child =
1145- match child.J. expression_desc with
1185+
1186+ let print_one_child expr_level_for_child current_cxt_for_child f_format
1187+ child_expr =
1188+ let child_is_jsx_itself =
1189+ match child_expr.J. expression_desc with
11461190 | J. Call (_ , _ , {call_transformed_jsx = is_jsx } ) -> is_jsx
11471191 | _ -> false
11481192 in
1193+ if not child_is_jsx_itself then P. string f_format " {" ;
1194+ let next_cxt =
1195+ expression ~level: expr_level_for_child current_cxt_for_child f_format
1196+ child_expr
1197+ in
1198+ if not child_is_jsx_itself then P. string f_format " }" ;
1199+ next_cxt
1200+ in
1201+
1202+ let props = List. filter (fun (n , _ ) -> n <> " children" ) fields in
1203+
1204+ (* Actual printing of JSX element starts here *)
1205+ P. string f " <" ;
1206+ let cxt = print_tag cxt in
1207+ let cxt = print_props cxt props in
1208+ (* print_props handles its own block and updates cxt *)
11491209
1150- P. string f " <" ;
1151- let cxt = cxt |> print_tag |> print_props in
1210+ let has_multiple_props = List. length props > 0 in
11521211
1212+ match children_opt with
1213+ | None ->
1214+ (* Self-closing tag *)
1215+ if has_multiple_props then P. newline f;
1216+ P. string f " />" ;
1217+ cxt
1218+ | Some children ->
1219+ (* Tag with children *)
1220+ let has_children = List. length children > 0 in
1221+ (* Newline for ">" only if props themselves were multi-line. Children alone don't push ">" to a new line. *)
1222+ if has_multiple_props then P. newline f;
11531223 P. string f " >" ;
1154- if List. length children > 0 then P. newline f;
11551224
1156- let cxt =
1157- List. fold_left
1158- (fun acc e ->
1159- if not (child_is_jsx e) then P. string f " {" ;
1160- let next = expression ~level acc f e in
1161- if not (child_is_jsx e) then P. string f " }" ;
1162- P. newline f;
1163- next)
1164- cxt children
1225+ let cxt_after_children =
1226+ if has_children then
1227+ (* Only call print_indented_list if there are children *)
1228+ print_indented_list f level cxt children print_one_child
1229+ else cxt
1230+ (* No children, no change to context here, no newlines from children block *)
11651231 in
1232+ let cxt = cxt_after_children in
1233+
1234+ (* The closing "</tag>" goes on a new line if the opening part was multi-line (due to props)
1235+ OR if there were actual children printed (which always makes the element multi-line).
1236+ *)
1237+ let element_content_was_multiline = has_multiple_props || has_children in
1238+ if element_content_was_multiline then P. newline f;
11661239
11671240 P. string f " </" ;
11681241 let cxt = print_tag cxt in
0 commit comments