@@ -33,6 +33,115 @@ type 'a children = ListLiteral of 'a | Exact of 'a
33
33
34
34
type componentConfig = { propsName : string }
35
35
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
+
36
145
(* if children is a list, convert it to an array while mapping each element. If not, just map over it, as usual *)
37
146
let transformChildrenIfListUpper ~loc ~mapper theList =
38
147
let rec transformChildren_ theList accum =
@@ -809,6 +918,7 @@ let jsxMapper () =
809
918
in
810
919
811
920
let transformJsxCall mapper callExpression callArguments attrs =
921
+ let callArguments = propsWithSpreadProps callArguments in
812
922
match callExpression.pexp_desc with
813
923
| Pexp_ident caller -> (
814
924
match caller with
@@ -898,11 +1008,13 @@ let jsxMapper () =
898
1008
[@@ raises Invalid_argument , Failure ]
899
1009
900
1010
let rewrite_implementation (code : Parsetree.structure ) : Parsetree.structure =
1011
+ impl := code;
901
1012
let mapper = jsxMapper () in
902
1013
mapper.structure mapper code
903
1014
[@@ raises Invalid_argument , Failure ]
904
1015
905
1016
let rewrite_signature (code : Parsetree.signature ) : Parsetree.signature =
1017
+ intf := code;
906
1018
let mapper = jsxMapper () in
907
1019
mapper.signature mapper code
908
1020
[@@ raises Invalid_argument , Failure ]
0 commit comments