Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/component-with-props-payload.polish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Accept a payload on `@react.componentWithProps` again, as in 12.x, with a deprecation warning: it never had an effect.
51 changes: 40 additions & 11 deletions compiler/syntax/src/jsx_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,36 @@ type jsx_config = {
mutable has_component: bool;
}

(* The JSX transform used to ignore a payload on [@react.componentWithProps],
so code may carry one, such as [(: props)] copied from [@react.component].
The node cannot hold it, so the attribute stays for formatting to keep; take
it here as the component, with a deprecation warning, unless the definition
already has one. *)
let take_component_with_props_payload jsx_component attributes =
match jsx_component with
| Some _ -> (jsx_component, attributes)
| None ->
let rec go seen = function
| [] -> (None, attributes)
| ((({txt; loc}, _) : attribute) as attr) :: rest -> (
let namespace =
match txt with
| "react.componentWithProps" -> Some Pjc_react
| "jsx.componentWithProps" -> Some Pjc_jsx
| _ -> None
in
match namespace with
| Some pjc_namespace
when Option.is_none (Ast_helper.Jsx_component.of_attribute attr) ->
Location.deprecated ~use:loc loc
(Printf.sprintf
"@%s takes no payload. The payload has no effect; remove it." txt);
( Some {pjc_kind = Pjc_with_props; pjc_namespace; pjc_loc = loc},
List.rev_append seen rest )
| _ -> go (attr :: seen) rest)
in
go [] attributes

(* [Ast_helper] takes the first [@react.component], or failing that the first
[@react.componentWithProps], into the binding's or description's component
node. One still among the attributes is a second annotation, or one with a
Expand All @@ -19,18 +49,17 @@ let reject_leftover_component_attrs attributes =
match txt with
| "react.component" | "jsx.component" | "react.componentWithProps"
| "jsx.componentWithProps" -> (
match Ast_helper.Jsx_component.of_attribute attr with
| Some _ ->
match (txt, Ast_helper.Jsx_component.of_attribute attr) with
| ("react.component" | "jsx.component"), None ->
Location.raise_errorf ~loc
"A definition can have only one JSX component annotation."
| None -> (
match txt with
| "react.component" | "jsx.component" ->
Location.raise_errorf ~loc
"@@%s takes no payload, or the props type to share, as in @@%s(: \
props)."
txt txt
| _ -> Location.raise_errorf ~loc "@@%s takes no payload." txt))
"@@%s takes no payload, or the props type to share, as in @@%s(: \
props)."
txt txt
| _ ->
(* [take_component_with_props_payload] has taken a lone
[@react.componentWithProps] whatever its payload. *)
Location.raise_errorf ~loc
"A definition can have only one JSX component annotation.")
| _ -> ())
attributes

Expand Down
17 changes: 15 additions & 2 deletions compiler/syntax/src/jsx_v4.ml
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,11 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name binding =
{expr with pexp_desc = Pexp_sequence (first, constrain_jsx_return second)}
| _ -> jsx_element_constraint expr
in
let pvb_jsx_component, pvb_attributes =
Jsx_common.take_component_with_props_payload binding.pvb_jsx_component
binding.pvb_attributes
in
let binding = {binding with pvb_jsx_component; pvb_attributes} in
Jsx_common.reject_leftover_component_attrs binding.pvb_attributes;
match binding.pvb_jsx_component with
| Some {pjc_kind = Pjc_labelled core_type_of_attr} ->
Expand Down Expand Up @@ -948,8 +953,12 @@ let transform_structure_item ~config item =
pstr_desc =
Pstr_primitive ({pval_attributes; pval_type} as value_description);
} as pstr -> (
let pval_jsx_component, pval_attributes =
Jsx_common.take_component_with_props_payload
value_description.pval_jsx_component pval_attributes
in
Jsx_common.reject_leftover_component_attrs pval_attributes;
match value_description.pval_jsx_component with
match pval_jsx_component with
| None -> [item]
| Some {pjc_kind = Pjc_with_props} ->
Jsx_common.raise_error ~loc:pstr_loc
Expand Down Expand Up @@ -1048,8 +1057,12 @@ let transform_signature_item ~config item =
psig_loc;
psig_desc = Psig_value ({pval_attributes; pval_type} as psig_desc);
} as psig -> (
let pval_jsx_component, pval_attributes =
Jsx_common.take_component_with_props_payload psig_desc.pval_jsx_component
pval_attributes
in
Jsx_common.reject_leftover_component_attrs pval_attributes;
match psig_desc.pval_jsx_component with
match pval_jsx_component with
(* [@react.componentWithProps] on a signature item has always been left
alone. *)
| None | Some {pjc_kind = Pjc_with_props} -> [item]
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

Warning number 3
/.../fixtures/react_componentWithProps_payload.res:12:3-27

10 │
11 │ module C = {
12 │ @react.componentWithProps(: props)
13 │ let make = (props: props) => React.string(props.label)
14 │ }

deprecated: @react.componentWithProps takes no payload. The payload has no effect; remove it.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module React = {
type element = Jsx.element
type componentLike<'props, 'return> = 'props => 'return
type component<'props> = Jsx.component<'props>
external component: componentLike<'props, element> => component<'props> = "%component_identity"
external string: string => element = "%identity"
}

type props = {label: string}

module C = {
@react.componentWithProps(: props)
let make = (props: props) => React.string(props.label)
}

let c = C.make
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@

Warning number 3
syntax_tests/data/ppx/react/sharedPropsWithProps.res:63:3-27

61 │
62 │ // The payload has no effect; it is accepted with a deprecation warni
│ ng.
63 │ @react.componentWithProps(: props)
64 │ let make = props => React.string(props.x)
65 │ }

deprecated: @react.componentWithProps takes no payload. The payload has no effect; remove it.

let f = a => Promise.resolve(a + a)

@@jsxConfig({version: 4})
Expand Down Expand Up @@ -76,3 +89,14 @@ module V4A7 = {
\"SharedPropsWithProps$V4A7"
})
}

module V4A8 = {
type props = {x: string}

// The payload has no effect; it is accepted with a deprecation warning.
let make = (props): React.element => React.string(props.x)
let make = React.component({
let \"SharedPropsWithProps$V4A8" = (props): React.element => make(props)
\"SharedPropsWithProps$V4A8"
})
}
8 changes: 8 additions & 0 deletions tests/syntax_tests/data/ppx/react/sharedPropsWithProps.res
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ module V4A7 = {
React.int(props.count)
}
}

module V4A8 = {
type props = {x: string}

// The payload has no effect; it is accepted with a deprecation warning.
@react.componentWithProps(: props)
let make = props => React.string(props.x)
}
Loading