Skip to content
This repository was archived by the owner on Jun 15, 2023. It is now read-only.

Commit 3ff277d

Browse files
committed
react.component sharedProps argument
1 parent 0a72ad2 commit 3ff277d

File tree

6 files changed

+252
-17
lines changed

6 files changed

+252
-17
lines changed

cli/react_jsx_common.ml

+19
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@ let hasAttr (loc, _) = loc.txt = "react.component"
1616
let hasAttrOnBinding {pvb_attributes} =
1717
List.find_opt hasAttr pvb_attributes <> None
1818

19+
let coreTypeOfAttrs attributes =
20+
List.find_map
21+
(fun ({txt}, payload) ->
22+
match (txt, payload) with
23+
| "react.component", PTyp coreType -> Some coreType
24+
| _ -> None)
25+
attributes
26+
27+
let typVarsOfCoreType {ptyp_desc} =
28+
match ptyp_desc with
29+
| Ptyp_constr (_, coreTypes) ->
30+
List.filter
31+
(fun {ptyp_desc} ->
32+
match ptyp_desc with
33+
| Ptyp_var _ -> true
34+
| _ -> false)
35+
coreTypes
36+
| _ -> []
37+
1938
let raiseError ~loc msg = Location.raise_errorf ~loc msg
2039

2140
let raiseErrorMultipleReactComponent ~loc =

cli/reactjs_jsx_v4.ml

+69-17
Original file line numberDiff line numberDiff line change
@@ -311,13 +311,30 @@ let makeTypeDecls propsName loc namedTypeList =
311311
~kind:(Ptype_record labelDeclList);
312312
]
313313

314+
let makeTypeDeclsWithCoreType propsName loc coreType typVars =
315+
[
316+
Type.mk ~loc {txt = propsName; loc} ~kind:Ptype_abstract
317+
~params:(typVars |> List.map (fun v -> (v, Invariant)))
318+
~manifest:coreType;
319+
]
320+
314321
(* type props<'x, 'y, ...> = { x: 'x, y?: 'y, ... } *)
315-
let makePropsRecordType propsName loc namedTypeList =
316-
Str.type_ Nonrecursive (makeTypeDecls propsName loc namedTypeList)
322+
let makePropsRecordType ~coreTypeOfAttr ~typVarsOfCoreType propsName loc
323+
namedTypeList =
324+
Str.type_ Nonrecursive
325+
(match coreTypeOfAttr with
326+
| None -> makeTypeDecls propsName loc namedTypeList
327+
| Some coreType ->
328+
makeTypeDeclsWithCoreType propsName loc coreType typVarsOfCoreType)
317329

318330
(* type props<'x, 'y, ...> = { x: 'x, y?: 'y, ... } *)
319-
let makePropsRecordTypeSig propsName loc namedTypeList =
320-
Sig.type_ Nonrecursive (makeTypeDecls propsName loc namedTypeList)
331+
let makePropsRecordTypeSig ~coreTypeOfAttr ~typVarsOfCoreType propsName loc
332+
namedTypeList =
333+
Sig.type_ Nonrecursive
334+
(match coreTypeOfAttr with
335+
| None -> makeTypeDecls propsName loc namedTypeList
336+
| Some coreType ->
337+
makeTypeDeclsWithCoreType propsName loc coreType typVarsOfCoreType)
321338

322339
let transformUppercaseCall3 ~config modulePath mapper jsxExprLoc callExprLoc
323340
attrs callArguments =
@@ -733,6 +750,12 @@ let transformStructureItem ~config mapper item =
733750
config.hasReactComponent <- true;
734751
check_string_int_attribute_iter.structure_item
735752
check_string_int_attribute_iter item;
753+
let coreTypeOfAttr = React_jsx_common.coreTypeOfAttrs pval_attributes in
754+
let typVarsOfCoreType =
755+
coreTypeOfAttr
756+
|> Option.map React_jsx_common.typVarsOfCoreType
757+
|> Option.value ~default:[]
758+
in
736759
let rec getPropTypes types ({ptyp_loc; ptyp_desc} as fullType) =
737760
match ptyp_desc with
738761
| Ptyp_arrow (name, type_, ({ptyp_desc = Ptyp_arrow _} as rest))
@@ -749,11 +772,14 @@ let transformStructureItem ~config mapper item =
749772
let retPropsType =
750773
Typ.constr ~loc:pstr_loc
751774
(Location.mkloc (Lident "props") pstr_loc)
752-
(makePropsTypeParams namedTypeList)
775+
(match coreTypeOfAttr with
776+
| None -> makePropsTypeParams namedTypeList
777+
| Some _ -> typVarsOfCoreType)
753778
in
754779
(* type props<'x, 'y> = { x: 'x, y?: 'y, ... } *)
755780
let propsRecordType =
756-
makePropsRecordType "props" pstr_loc namedTypeList
781+
makePropsRecordType ~coreTypeOfAttr ~typVarsOfCoreType "props"
782+
pstr_loc namedTypeList
757783
in
758784
(* can't be an arrow because it will defensively uncurry *)
759785
let newExternalType =
@@ -787,6 +813,14 @@ let transformStructureItem ~config mapper item =
787813
React_jsx_common.raiseErrorMultipleReactComponent ~loc:pstr_loc
788814
else (
789815
config.hasReactComponent <- true;
816+
let coreTypeOfAttr =
817+
React_jsx_common.coreTypeOfAttrs binding.pvb_attributes
818+
in
819+
let typVarsOfCoreType =
820+
coreTypeOfAttr
821+
|> Option.map React_jsx_common.typVarsOfCoreType
822+
|> Option.value ~default:[]
823+
in
790824
let bindingLoc = binding.pvb_loc in
791825
let bindingPatLoc = binding.pvb_pat.ppat_loc in
792826
let binding =
@@ -977,7 +1011,8 @@ let transformStructureItem ~config mapper item =
9771011
let vbMatchList = List.map vbMatch namedArgWithDefaultValueList in
9781012
(* type props = { ... } *)
9791013
let propsRecordType =
980-
makePropsRecordType "props" pstr_loc namedTypeList
1014+
makePropsRecordType ~coreTypeOfAttr ~typVarsOfCoreType "props"
1015+
pstr_loc namedTypeList
9811016
in
9821017
let innerExpression =
9831018
Exp.apply
@@ -989,19 +1024,23 @@ let transformStructureItem ~config mapper item =
9891024
[(Nolabel, Exp.ident (Location.mknoloc @@ Lident "ref"))]
9901025
| false -> [])
9911026
in
1027+
let makePropsPattern = function
1028+
| [] -> Pat.var @@ Location.mknoloc "props"
1029+
| _ ->
1030+
Pat.constraint_
1031+
(Pat.var @@ Location.mknoloc "props")
1032+
(Typ.constr (Location.mknoloc @@ Lident "props") [Typ.any ()])
1033+
in
9921034
let fullExpression =
9931035
(* React component name should start with uppercase letter *)
9941036
(* let make = { let \"App" = props => make(props); \"App" } *)
9951037
(* let make = React.forwardRef({
9961038
let \"App" = (props, ref) => make({...props, ref: @optional (Js.Nullabel.toOption(ref))})
9971039
})*)
9981040
Exp.fun_ nolabel None
999-
(match namedTypeList with
1000-
| [] -> Pat.var @@ Location.mknoloc "props"
1001-
| _ ->
1002-
Pat.constraint_
1003-
(Pat.var @@ Location.mknoloc "props")
1004-
(Typ.constr (Location.mknoloc @@ Lident "props") [Typ.any ()]))
1041+
(match coreTypeOfAttr with
1042+
| None -> makePropsPattern namedTypeList
1043+
| Some _ -> makePropsPattern typVarsOfCoreType)
10051044
(if hasForwardRef then
10061045
Exp.fun_ nolabel None
10071046
(Pat.var @@ Location.mknoloc "ref")
@@ -1105,8 +1144,12 @@ let transformStructureItem ~config mapper item =
11051144
(Pat.constraint_ recordPattern
11061145
(Typ.constr ~loc:emptyLoc
11071146
{txt = Lident "props"; loc = emptyLoc}
1108-
(makePropsTypeParams ~stripExplicitOption:true
1109-
~stripExplicitJsNullableOfRef:hasForwardRef namedTypeList)))
1147+
(match coreTypeOfAttr with
1148+
| None ->
1149+
makePropsTypeParams ~stripExplicitOption:true
1150+
~stripExplicitJsNullableOfRef:hasForwardRef
1151+
namedTypeList
1152+
| Some _ -> typVarsOfCoreType)))
11101153
expression
11111154
in
11121155
(* let make = ({id, name, ...}: props<'id, 'name, ...>) => { ... } *)
@@ -1182,6 +1225,12 @@ let transformSignatureItem ~config _mapper item =
11821225
check_string_int_attribute_iter.signature_item
11831226
check_string_int_attribute_iter item;
11841227
let hasForwardRef = ref false in
1228+
let coreTypeOfAttr = React_jsx_common.coreTypeOfAttrs pval_attributes in
1229+
let typVarsOfCoreType =
1230+
coreTypeOfAttr
1231+
|> Option.map React_jsx_common.typVarsOfCoreType
1232+
|> Option.value ~default:[]
1233+
in
11851234
let rec getPropTypes types ({ptyp_loc; ptyp_desc} as fullType) =
11861235
match ptyp_desc with
11871236
| Ptyp_arrow (name, type_, ({ptyp_desc = Ptyp_arrow _} as rest))
@@ -1204,10 +1253,13 @@ let transformSignatureItem ~config _mapper item =
12041253
let retPropsType =
12051254
Typ.constr
12061255
(Location.mkloc (Lident "props") psig_loc)
1207-
(makePropsTypeParams namedTypeList)
1256+
(match coreTypeOfAttr with
1257+
| None -> makePropsTypeParams namedTypeList
1258+
| Some _ -> typVarsOfCoreType)
12081259
in
12091260
let propsRecordType =
1210-
makePropsRecordTypeSig "props" psig_loc
1261+
makePropsRecordTypeSig ~coreTypeOfAttr ~typVarsOfCoreType "props"
1262+
psig_loc
12111263
((* If there is Nolabel arg, regard the type as ref in forwardRef *)
12121264
(if !hasForwardRef then [(true, "ref", [], refType Location.none)]
12131265
else [])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
@@jsxConfig({version: 4, mode: "classic"})
2+
3+
module V4C1 = {
4+
type props = sharedProps<string>
5+
6+
@react.component(: sharedProps<string>) let make = ({x, y, _}: props) => React.string(x ++ y)
7+
let make = {
8+
let \"SharedProps$V4C1" = props => make(props)
9+
10+
\"SharedProps$V4C1"
11+
}
12+
}
13+
14+
module V4C2 = {
15+
type props<'a> = sharedProps<'a>
16+
17+
@react.component(: sharedProps<'a>) let make = ({x, y, _}: props<'a>) => React.string(x ++ y)
18+
let make = {
19+
let \"SharedProps$V4C2" = (props: props<_>) => make(props)
20+
21+
\"SharedProps$V4C2"
22+
}
23+
}
24+
25+
module V4C3 = {
26+
type props = sharedProps<string>
27+
28+
external make: React.componentLike<props, React.element> = "default"
29+
}
30+
31+
module V4C4 = {
32+
type props<'a> = sharedProps<'a>
33+
34+
external make: React.componentLike<props<'a>, React.element> = "default"
35+
}
36+
37+
@@jsxConfig({version: 4, mode: "automatic"})
38+
39+
module V4A1 = {
40+
type props = sharedProps<string>
41+
42+
@react.component(: sharedProps<string>) let make = ({x, y, _}: props) => React.string(x ++ y)
43+
let make = {
44+
let \"SharedProps$V4A1" = props => make(props)
45+
46+
\"SharedProps$V4A1"
47+
}
48+
}
49+
50+
module V4A2 = {
51+
type props<'a> = sharedProps<'a>
52+
53+
@react.component(: sharedProps<'a>) let make = ({x, y, _}: props<'a>) => React.string(x ++ y)
54+
let make = {
55+
let \"SharedProps$V4A2" = (props: props<_>) => make(props)
56+
57+
\"SharedProps$V4A2"
58+
}
59+
}
60+
61+
module V4A3 = {
62+
type props = sharedProps<string>
63+
64+
external make: React.componentLike<props, React.element> = "default"
65+
}
66+
67+
module V4A4 = {
68+
type props<'a> = sharedProps<'a>
69+
70+
external make: React.componentLike<props<'a>, React.element> = "default"
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@@jsxConfig({version: 4, mode: "classic"})
2+
3+
module V4C1: {
4+
type props = sharedProps<string>
5+
6+
let make: React.componentLike<props, React.element>
7+
}
8+
9+
module V4C2: {
10+
type props<'a> = sharedProps<'a>
11+
12+
let make: React.componentLike<props<'a>, React.element>
13+
}
14+
15+
@@jsxConfig({version: 4, mode: "automatic"})
16+
17+
module V4A1: {
18+
type props = sharedProps<string>
19+
20+
let make: React.componentLike<props, React.element>
21+
}
22+
23+
module V4A2: {
24+
type props<'a> = sharedProps<'a>
25+
26+
let make: React.componentLike<props<'a>, React.element>
27+
}

tests/ppx/react/sharedProps.res

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@@jsxConfig({version:4, mode: "classic"})
2+
3+
module V4C1 = {
4+
@react.component(:sharedProps<string>)
5+
let make = (~x, ~y) => React.string(x ++ y)
6+
}
7+
8+
module V4C2 = {
9+
@react.component(:sharedProps<'a>)
10+
let make = (~x, ~y) => React.string(x ++ y)
11+
}
12+
13+
module V4C3 = {
14+
@react.component(:sharedProps<string>)
15+
external make: (~x: string, ~y: 'a) => React.element = "default"
16+
}
17+
18+
module V4C4 = {
19+
@react.component(:sharedProps<'a>)
20+
external make: (~x: string, ~y: 'a) => React.element = "default"
21+
}
22+
23+
@@jsxConfig({version:4, mode: "automatic"})
24+
25+
module V4A1 = {
26+
@react.component(:sharedProps<string>)
27+
let make = (~x, ~y) => React.string(x ++ y)
28+
}
29+
30+
module V4A2 = {
31+
@react.component(:sharedProps<'a>)
32+
let make = (~x, ~y) => React.string(x ++ y)
33+
}
34+
35+
module V4A3 = {
36+
@react.component(:sharedProps<string>)
37+
external make: (~x: string, ~y: 'a) => React.element = "default"
38+
}
39+
40+
module V4A4 = {
41+
@react.component(:sharedProps<'a>)
42+
external make: (~x: string, ~y: 'a) => React.element = "default"
43+
}

tests/ppx/react/sharedProps.resi

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@@jsxConfig({version:4, mode: "classic"})
2+
3+
module V4C1: {
4+
@react.component(:sharedProps<string>)
5+
let make: (~x: string) => React.element
6+
}
7+
8+
module V4C2: {
9+
@react.component(:sharedProps<'a>)
10+
let make: (~x: string) => React.element
11+
}
12+
13+
@@jsxConfig({version:4, mode: "automatic"})
14+
15+
module V4A1: {
16+
@react.component(:sharedProps<string>)
17+
let make: (~x: string) => React.element
18+
}
19+
20+
module V4A2: {
21+
@react.component(:sharedProps<'a>)
22+
let make: (~x: string) => React.element
23+
}

0 commit comments

Comments
 (0)