@@ -33,6 +33,115 @@ type 'a children = ListLiteral of 'a | Exact of 'a
3333
3434type componentConfig = { propsName : string }
3535
36+ (* structure ref in order to avoid too many arg drilling *)
37+ let impl: Parsetree. structure ref = ref []
38+
39+ (* signature ref in order to avoid too many arg drilling *)
40+ let intf: Parsetree. signature ref = ref []
41+
42+ (* List.filter_map in 4.08.0 *)
43+ let filterMap f =
44+ let rec aux accu = function
45+ | [] -> List. rev accu
46+ | x :: l ->
47+ match f x with
48+ | None -> aux accu l
49+ | Some v -> aux (v :: accu) l
50+ in
51+ aux []
52+
53+ (* fold the Longident.t to string for record fields to be spread. Maybe Lident is needed. *)
54+ let stringOfLid lid = Longident. flatten lid.txt |> List. fold_left (fun acc x -> acc ^ x) " "
55+
56+ (* Look up the record to be spread and extract the fields *)
57+ let findRecordFields { pexp_desc } =
58+ let rec findRecordFieldsAux structure labels =
59+ match labels with
60+ | [] -> raise (Invalid_argument " JSX: spread props missing" )
61+ (* foo *)
62+ | [ label ] ->
63+ structure
64+ |> filterMap (fun { pstr_desc } ->
65+ match pstr_desc with
66+ | Pstr_value (_ , vbs ) -> (
67+ let matched_vbs =
68+ vbs
69+ |> List. filter (fun { pvb_pat = { ppat_desc } } ->
70+ match ppat_desc with
71+ | Ppat_var { Location. txt } -> txt = label
72+ | _ -> false )
73+ in
74+ match matched_vbs with
75+ | [] -> None
76+ | [ { pvb_expr = { pexp_desc } } ] | { pvb_expr = { pexp_desc } } :: _ ->
77+ begin
78+ match pexp_desc with
79+ | Pexp_record (fields , _ ) -> Some fields
80+ | _ -> None
81+ end )
82+ | _ -> None )
83+ (* Foo.name *)
84+ | label :: labels ->
85+ structure
86+ |> filterMap (fun { pstr_desc } ->
87+ match pstr_desc with
88+ (* module Foo = ... *)
89+ | Pstr_module
90+ {
91+ pmb_name;
92+ pmb_expr = { pmod_desc = Pmod_structure structure };
93+ } ->
94+ if pmb_name.txt = label then
95+ Some (findRecordFieldsAux structure labels)
96+ else None
97+ (* module Foo: Foo = ... *)
98+ | Pstr_module
99+ {
100+ pmb_name;
101+ pmb_expr =
102+ {
103+ pmod_desc =
104+ Pmod_constraint
105+ ({ pmod_desc = Pmod_structure structure }, _);
106+ };
107+ } ->
108+ if pmb_name.txt = label then
109+ Some (findRecordFieldsAux structure labels)
110+ else None
111+ | _ -> None )
112+ |> List. concat
113+ in
114+ (* foo, foo.name, Foo.name *)
115+ match pexp_desc with
116+ | Pexp_ident lid
117+ | Pexp_field (_ , lid ) ->
118+ begin
119+ let labels = Longident. flatten lid.txt in
120+ let recordFields = findRecordFieldsAux ! impl labels in
121+ (* last record fields of list is the closest one *)
122+ try recordFields |> List. rev |> List. hd with _ -> raise (Invalid_argument " JSX: can't find the spread prop record" )
123+ end
124+ | _ -> raise (Invalid_argument " JSX: can't find the spread prop record" )
125+
126+ (* spread props if exists *)
127+ let propsWithSpreadProps callArguments =
128+ let unitRef = ref None in
129+ let rec removeLastPositionUnitAux props acc =
130+ match props with
131+ | [] -> []
132+ | [ (Nolabel , { pexp_desc = Pexp_construct ({ txt = Lident " ()" }, None ) }) as u ] -> unitRef := Some u; acc
133+ | (Nolabel, _ ) :: _rest -> raise (Invalid_argument " JSX: found non-labelled argument before the last position" )
134+ | prop :: rest -> removeLastPositionUnitAux rest (prop :: acc)
135+ in
136+ let props, propsToSpread = removeLastPositionUnitAux callArguments [] |> List. rev |> List. partition (fun (label , _ ) -> label <> labelled " spreadProps" ) in
137+ let spreadProps = propsToSpread
138+ |> List. map (fun (_ , expression ) -> expression)
139+ |> List. map findRecordFields
140+ |> List. concat
141+ |> List. map (fun (lid , expression ) -> (labelled @@ stringOfLid lid, expression))
142+ in
143+ match ! unitRef with Some u -> props @ spreadProps @ [ u ] | None -> props @ spreadProps
144+
36145(* if children is a list, convert it to an array while mapping each element. If not, just map over it, as usual *)
37146let transformChildrenIfListUpper ~loc ~mapper theList =
38147 let rec transformChildren_ theList accum =
@@ -809,6 +918,7 @@ let jsxMapper () =
809918 in
810919
811920 let transformJsxCall mapper callExpression callArguments attrs =
921+ let callArguments = propsWithSpreadProps callArguments in
812922 match callExpression.pexp_desc with
813923 | Pexp_ident caller -> (
814924 match caller with
@@ -899,11 +1009,13 @@ let jsxMapper () =
8991009 [@@ raises Invalid_argument , Failure ]
9001010
9011011let rewrite_implementation (code : Parsetree.structure ) : Parsetree.structure =
1012+ impl := code;
9021013 let mapper = jsxMapper () in
9031014 mapper.structure mapper code
9041015 [@@ raises Invalid_argument , Failure ]
9051016
9061017let rewrite_signature (code : Parsetree.signature ) : Parsetree.signature =
1018+ intf := code;
9071019 let mapper = jsxMapper () in
9081020 mapper.signature mapper code
9091021 [@@ raises Invalid_argument , Failure ]
0 commit comments