@@ -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 *)
@@ -458,7 +446,14 @@ let makePropsType ~loc namedTypeList =
458446 };
459447 ]
460448
461- let jsxExprAndChildren ~ident ~loc ~ctxt mapper ~keyProps children =
449+ type component_type = Uppercase | Lowercase
450+
451+ let jsxExprAndChildren ~component_type ~loc ~ctxt mapper ~keyProps children =
452+ let ident =
453+ match component_type with
454+ | Uppercase -> Lident " React"
455+ | Lowercase -> Lident " ReactDOM"
456+ in
462457 let childrenExpr =
463458 Option. map (transformChildrenIfListUpper ~loc ~mapper ~ctxt ) children
464459 in
@@ -492,23 +487,25 @@ let jsxExprAndChildren ~ident ~loc ~ctxt mapper ~keyProps children =
492487 children *)
493488 ( Builder. pexp_ident ~loc { loc; txt = Ldot (ident, " jsxs" ) },
494489 None ,
495- Some (Binding.React. array ~loc children) )
490+ Some
491+ (match component_type with
492+ | Uppercase -> children
493+ | Lowercase -> Binding.React. array ~loc children) )
496494 | None , (label , key ) :: _ ->
497495 ( Builder. pexp_ident ~loc { loc; txt = Ldot (ident, " jsxKeyed" ) },
498496 Some (label, key),
499497 None )
500498 | None , [] ->
501499 (Builder. pexp_ident ~loc { loc; txt = Ldot (ident, " jsx" ) }, None , None )
502500
503- let reactJsxExprAndChildren = jsxExprAndChildren ~ident: ( Lident " React " )
504- let reactDomJsxExprAndChildren = jsxExprAndChildren ~ident: ( Lident " ReactDOM " )
501+ let reactJsxExprAndChildren = jsxExprAndChildren ~component_type: Uppercase
502+ let reactDomJsxExprAndChildren = jsxExprAndChildren ~component_type: Lowercase
505503
506504(* Builds an AST node for the entire `external` definition of props *)
507505let makeExternalDecl fnName loc namedArgListWithKeyAndRef namedTypeList =
508506 makePropsExternal ~component_is_external: false fnName loc
509507 (List. map pluckLabelDefaultLocType namedArgListWithKeyAndRef)
510508 (makePropsType ~loc namedTypeList)
511- [@@ raises Invalid_argument ]
512509
513510(* TODO: some line number might still be wrong *)
514511let jsxMapper =
@@ -519,7 +516,7 @@ let jsxMapper =
519516 let argsForMake = argsWithLabels in
520517 let keyProps, otherProps =
521518 List. partition
522- (fun (arg_label , _ ) -> " key" = getLabel arg_label)
519+ (fun (arg_label , _ ) -> " key" = getLabelOrEmpty arg_label)
523520 argsForMake
524521 in
525522 let jsxExpr, key, childrenProp =
@@ -533,10 +530,12 @@ let jsxMapper =
533530 (label, mapper#expression ctxt expression))
534531 in
535532 let isCap str =
536- let first = String. sub str 0 1 [@@ raises Invalid_argument ] in
537- let capped = String. uppercase_ascii first in
538- first = capped
539- [@@ 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
540539 in
541540 let ident =
542541 match modulePath with
@@ -598,7 +597,7 @@ let jsxMapper =
598597 let componentNameExpr = constantString ~loc: callerLoc id in
599598 let keyProps, nonChildrenProps =
600599 List. partition
601- (fun (arg_label , _ ) -> " key" = getLabel arg_label)
600+ (fun (arg_label , _ ) -> " key" = getLabelOrEmpty arg_label)
602601 nonChildrenProps
603602 in
604603
@@ -647,17 +646,9 @@ let jsxMapper =
647646 let rec recursivelyTransformNamedArgsForMake ~ctxt mapper expr list =
648647 let expr = mapper#expression ctxt expr in
649648 match expr.pexp_desc with
650- (* TODO: make this show up with a loc. * )
651649 | Pexp_fun (Labelled "key" , _ , _ , _ ) | Pexp_fun (Optional "key" , _ , _ , _ ) ->
652- raise
653- (Invalid_argument
654- " Key cannot be accessed inside of a component. Don't worry - you \
655- can always key a component from its parent!" )
656- | Pexp_fun (Labelled "ref" , _ , _ , _ ) | Pexp_fun (Optional "ref" , _ , _ , _ ) ->
657- raise
658- (Invalid_argument
659- " Ref cannot be passed as a normal prop. Please use `forwardRef` \
660- 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." )
661652 | Pexp_fun
662653 ( ((Optional label | Labelled label) as arg),
663654 default,
@@ -704,7 +695,6 @@ let jsxMapper =
704695 " reason-react-ppx: react.component refs only support plain arguments \
705696 and type annotations."
706697 | _ -> (list , None )
707- [@@ raises Invalid_argument ]
708698 in
709699
710700 let argToType types (name , default , _noLabelName , _alias , loc , type_ ) =
@@ -726,7 +716,7 @@ let jsxMapper =
726716 } )
727717 :: types
728718 | Some type_ , name , Some _default ->
729- ( getLabel name,
719+ ( getLabelOrEmpty name,
730720 [] ,
731721 {
732722 ptyp_desc = Ptyp_constr ({ loc; txt = optionIdent }, [ type_ ]);
@@ -735,7 +725,7 @@ let jsxMapper =
735725 ptyp_attributes = [] ;
736726 } )
737727 :: types
738- | Some type_ , name , _ -> (getLabel name, [] , type_) :: types
728+ | Some type_ , name , _ -> (getLabelOrEmpty name, [] , type_) :: types
739729 | None , Optional label , _ ->
740730 ( label,
741731 [] ,
@@ -767,7 +757,6 @@ let jsxMapper =
767757 } )
768758 :: types
769759 | _ -> types
770- [@@ raises Invalid_argument ]
771760 in
772761
773762 let argToConcreteType types (name , loc , type_ ) =
@@ -1100,7 +1089,7 @@ let jsxMapper =
11001089 in
11011090 let pluckArg (label , _ , _ , alias , loc , _ ) =
11021091 ( label,
1103- match getLabel label with
1092+ match getLabelOrEmpty label with
11041093 | "" -> Builder. pexp_ident ~loc { txt = Lident alias; loc }
11051094 | labelString ->
11061095 Builder. pexp_apply ~loc
0 commit comments