@@ -47,8 +47,8 @@ let labelled str = Labelled str
4747let optional str = Optional str
4848
4949module Binding = struct
50- (* Binding is the interface that the ppx uses to interact with the bindings.
51- Here we define the same APIs as the bindings but it generates Parsetree *)
50+ (* Binding is the interface that the ppx relies on to interact with the react bindings.
51+ Here we define the same APIs as the bindings but it generates Parsetree nodes *)
5252 module ReactDOM = struct
5353 let domProps ~applyLoc ~loc props =
5454 Builder. pexp_apply ~loc: applyLoc
@@ -58,9 +58,6 @@ module Binding = struct
5858 end
5959
6060 module React = struct
61- let null ~loc =
62- Builder. pexp_ident ~loc { loc; txt = Ldot (Lident " React" , " null" ) }
63-
6461 let array ~loc children =
6562 Builder. pexp_apply ~loc
6663 (Builder. pexp_ident ~loc
@@ -98,18 +95,22 @@ let rec find_opt p = function
9895 | [] -> None
9996 | x :: l -> if p x then Some x else find_opt p l
10097
101- let getLabel str =
98+ let getLabelOrEmpty str =
10299 match str with Optional str | Labelled str -> str | Nolabel -> " "
103100
101+ let getLabel str =
102+ match str with Optional str | Labelled str -> Some str | Nolabel -> None
103+
104104let optionIdent = Lident " option"
105105
106106let constantString ~loc str =
107107 Builder. pexp_constant ~loc (Pconst_string (str, Location. none, None ))
108108
109109let safeTypeFromValue valueStr =
110- let valueStr = getLabel valueStr in
111- match String. sub valueStr 0 1 with "_" -> " T" ^ valueStr | _ -> valueStr
112- [@@ raises Invalid_argument ]
110+ match getLabel valueStr with
111+ | Some valueStr when String. sub valueStr 0 1 = " _" -> (" T" ^ valueStr)
112+ | Some valueStr -> valueStr
113+ | None -> " "
113114
114115let keyType loc = Builder. ptyp_constr ~loc { loc; txt = Lident " string" } []
115116
@@ -224,14 +225,12 @@ let otherAttrsPure { attr_name = loc; _ } = loc.txt <> "react.component"
224225let hasAttrOnBinding { pvb_attributes; _ } =
225226 find_opt hasAttr pvb_attributes <> None
226227
227- (* Finds the name of the variable the binding is assigned to, otherwise raises
228- Invalid_argument *)
228+ (* Finds the name of the variable the binding is assigned to, otherwise raises an error *)
229229let getFnName binding =
230230 match binding with
231231 | { pvb_pat = { ppat_desc = Ppat_var { txt; _ } ; _ } ; _ } -> txt
232- | _ ->
233- raise (Invalid_argument " react.component calls cannot be destructured." )
234- [@@ raises Invalid_argument ]
232+ | { pvb_loc; _} ->
233+ Location. raise_errorf ~loc: pvb_loc " [@react.component] cannot be used with a destructured binding. Please use it on a `let make = ...` binding instead."
235234
236235let makeNewBinding binding expression newName =
237236 match binding with
@@ -243,22 +242,17 @@ let makeNewBinding binding expression newName =
243242 pvb_expr = expression;
244243 pvb_attributes = [ merlinFocus ];
245244 }
246- | _ ->
247- raise (Invalid_argument " react.component calls cannot be destructured." )
248- [@@ raises Invalid_argument ]
245+ | { pvb_loc; _ } ->
246+ Location. raise_errorf ~loc: pvb_loc " [@react.component] cannot be used with a destructured binding. Please use it on a `let make = ...` binding instead."
249247
250- (* Lookup the value of `props` otherwise raise Invalid_argument error *)
251- let getPropsNameValue _acc (loc , exp ) =
252- match (loc, exp ) with
248+ (* Lookup the value of `props` otherwise raise errorf *)
249+ let getPropsNameValue _acc (loc , expr ) =
250+ match (loc, expr ) with
253251 | ( { txt = Lident " props" ; _ },
254252 { pexp_desc = Pexp_ident { txt = Lident str; _ }; _ } ) ->
255253 { propsName = str }
256- | { txt; _ } , _ ->
257- raise
258- (Invalid_argument
259- (" react.component only accepts props as an option, given: "
260- ^ Longident. last_exn txt))
261- [@@ raises Invalid_argument ]
254+ | { txt; loc } , _ ->
255+ Location. raise_errorf ~loc " [@react.component] only accepts 'props' as a field, given: %s" (Longident. last_exn txt)
262256
263257(* Lookup the `props` record or string as part of [@react.component] and store
264258 the name for use when rewriting *)
@@ -284,12 +278,10 @@ let getPropsAttr payload =
284278 }
285279 :: _rest)) ->
286280 { propsName = " props" }
287- | Some (PStr ({ pstr_desc = Pstr_eval (_ , _ ); _ } :: _rest )) ->
288- raise
289- (Invalid_argument
290- " react.component accepts a record config with props as an options." )
281+ | Some (PStr ({ pstr_desc = Pstr_eval (_ , _ ); pstr_loc; _ } :: _rest )) ->
282+ Location. raise_errorf ~loc: pstr_loc
283+ " [@react.component] accepts a record config with 'props' as a field."
291284 | _ -> defaultProps
292- [@@ raises Invalid_argument ]
293285
294286(* Plucks the label, loc, and type_ from an AST node *)
295287let pluckLabelDefaultLocType (label , default , _ , _ , loc , type_ ) =
@@ -370,7 +362,6 @@ let rec recursivelyMakeNamedArgsForExternal ~types_come_from_signature list args
370362 | _label , Some type_ , _ -> type_)
371363 args)
372364 | [] -> args
373- [@@ raises Invalid_argument ]
374365
375366(* Build an AST node for the [@bs.obj] representing props for a component *)
376367let makePropsValue fnName ~types_come_from_signature loc
@@ -400,7 +391,6 @@ let makePropsValue fnName ~types_come_from_signature loc
400391 ];
401392 pval_loc = loc;
402393 }
403- [@@ raises Invalid_argument ]
404394
405395(* Build an AST node representing an `external` with the definition of the
406396 [@bs.obj] *)
@@ -413,7 +403,6 @@ let makePropsExternal fnName loc ~component_is_external
413403 (makePropsValue ~types_come_from_signature: component_is_external fnName
414404 loc namedArgListWithKeyAndRef propsType);
415405 }
416- [@@ raises Invalid_argument ]
417406
418407(* Build an AST node for the signature of the `external` definition *)
419408let makePropsExternalSig fnName loc namedArgListWithKeyAndRef propsType =
@@ -424,7 +413,6 @@ let makePropsExternalSig fnName loc namedArgListWithKeyAndRef propsType =
424413 (makePropsValue ~types_come_from_signature: true fnName loc
425414 namedArgListWithKeyAndRef propsType);
426415 }
427- [@@ raises Invalid_argument ]
428416
429417(* Build an AST node for the props name when converted to an object inside the
430418 function signature *)
@@ -518,7 +506,6 @@ let makeExternalDecl fnName loc namedArgListWithKeyAndRef namedTypeList =
518506 makePropsExternal ~component_is_external: false fnName loc
519507 (List. map pluckLabelDefaultLocType namedArgListWithKeyAndRef)
520508 (makePropsType ~loc namedTypeList)
521- [@@ raises Invalid_argument ]
522509
523510(* TODO: some line number might still be wrong *)
524511let jsxMapper =
@@ -529,7 +516,7 @@ let jsxMapper =
529516 let argsForMake = argsWithLabels in
530517 let keyProps, otherProps =
531518 List. partition
532- (fun (arg_label , _ ) -> " key" = getLabel arg_label)
519+ (fun (arg_label , _ ) -> " key" = getLabelOrEmpty arg_label)
533520 argsForMake
534521 in
535522 let jsxExpr, key, childrenProp =
@@ -543,10 +530,12 @@ let jsxMapper =
543530 (label, mapper#expression ctxt expression))
544531 in
545532 let isCap str =
546- let first = String. sub str 0 1 [@@ raises Invalid_argument ] in
547- let capped = String. uppercase_ascii first in
548- first = capped
549- [@@ raises Invalid_argument ]
533+ match String. length str with
534+ | 0 -> false
535+ | _ ->
536+ let first = String. sub str 0 1 in
537+ let capped = String. uppercase_ascii first in
538+ first = capped
550539 in
551540 let ident =
552541 match modulePath with
@@ -608,7 +597,7 @@ let jsxMapper =
608597 let componentNameExpr = constantString ~loc: callerLoc id in
609598 let keyProps, nonChildrenProps =
610599 List. partition
611- (fun (arg_label , _ ) -> " key" = getLabel arg_label)
600+ (fun (arg_label , _ ) -> " key" = getLabelOrEmpty arg_label)
612601 nonChildrenProps
613602 in
614603
@@ -657,17 +646,9 @@ let jsxMapper =
657646 let rec recursivelyTransformNamedArgsForMake ~ctxt mapper expr list =
658647 let expr = mapper#expression ctxt expr in
659648 match expr.pexp_desc with
660- (* TODO: make this show up with a loc. * )
661649 | Pexp_fun (Labelled "key" , _ , _ , _ ) | Pexp_fun (Optional "key" , _ , _ , _ ) ->
662- raise
663- (Invalid_argument
664- " Key cannot be accessed inside of a component. Don't worry - you \
665- can always key a component from its parent!" )
666- | Pexp_fun (Labelled "ref" , _ , _ , _ ) | Pexp_fun (Optional "ref" , _ , _ , _ ) ->
667- raise
668- (Invalid_argument
669- " Ref cannot be passed as a normal prop. Please use `forwardRef` \
670- API instead." )
650+ Location. raise_errorf ~loc: expr.pexp_loc
651+ (" ~key cannot be accessed from the component props. Please set the key where the component is being used." )
671652 | Pexp_fun
672653 ( ((Optional label | Labelled label) as arg),
673654 default,
@@ -714,7 +695,6 @@ let jsxMapper =
714695 " reason-react-ppx: react.component refs only support plain arguments \
715696 and type annotations."
716697 | _ -> (list , None )
717- [@@ raises Invalid_argument ]
718698 in
719699
720700 let argToType types (name , default , _noLabelName , _alias , loc , type_ ) =
@@ -736,7 +716,7 @@ let jsxMapper =
736716 } )
737717 :: types
738718 | Some type_ , name , Some _default ->
739- ( getLabel name,
719+ ( getLabelOrEmpty name,
740720 [] ,
741721 {
742722 ptyp_desc = Ptyp_constr ({ loc; txt = optionIdent }, [ type_ ]);
@@ -745,7 +725,7 @@ let jsxMapper =
745725 ptyp_attributes = [] ;
746726 } )
747727 :: types
748- | Some type_ , name , _ -> (getLabel name, [] , type_) :: types
728+ | Some type_ , name , _ -> (getLabelOrEmpty name, [] , type_) :: types
749729 | None , Optional label , _ ->
750730 ( label,
751731 [] ,
@@ -777,7 +757,6 @@ let jsxMapper =
777757 } )
778758 :: types
779759 | _ -> types
780- [@@ raises Invalid_argument ]
781760 in
782761
783762 let argToConcreteType types (name , loc , type_ ) =
@@ -1110,7 +1089,7 @@ let jsxMapper =
11101089 in
11111090 let pluckArg (label , _ , _ , alias , loc , _ ) =
11121091 ( label,
1113- match getLabel label with
1092+ match getLabelOrEmpty label with
11141093 | "" -> Builder. pexp_ident ~loc { txt = Lident alias; loc }
11151094 | labelString ->
11161095 Builder. pexp_apply ~loc
0 commit comments