diff --git a/CHANGELOG.md b/CHANGELOG.md index a26e9d35..026e2767 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ - Fix parsing (hence pretty printing) of expressions with underscore `_` and comments. - Fix printing of comments inside JSX tag https://github.com/rescript-lang/syntax/pull/664 - Fix issue where formatter erases tail comments inside JSX tag https://github.com/rescript-lang/syntax/issues/663 +- Fix issue where the JSX prop has type annotation of the first class module https://github.com/rescript-lang/syntax/pull/666 ## ReScript 10.0 diff --git a/cli/reactjs_jsx_v4.ml b/cli/reactjs_jsx_v4.ml index a7a016d7..dd77662c 100644 --- a/cli/reactjs_jsx_v4.ml +++ b/cli/reactjs_jsx_v4.ml @@ -1005,9 +1005,13 @@ let transformStructureItem ~config mapper item = ] (Exp.ident ~loc:pstr_loc {loc = emptyLoc; txt = Lident txt}) in - let stripConstraint pattern = + let rec stripConstraintUnpack ~label pattern = match pattern with - | {ppat_desc = Ppat_constraint (pattern, _)} -> pattern + | {ppat_desc = Ppat_constraint (pattern, _)} -> + stripConstraintUnpack ~label pattern + | {ppat_desc = Ppat_unpack _; ppat_loc} -> + (* remove unpack e.g. model: module(T) *) + Pat.var ~loc:ppat_loc {txt = label; loc = ppat_loc} | _ -> pattern in let rec returnedExpression patternsWithLabel patternsWithNolabel @@ -1029,7 +1033,9 @@ let transformStructureItem ~config mapper item = | Pexp_fun (arg_label, _default, ({ppat_loc; ppat_desc} as pattern), expr) -> ( - let patternWithoutConstraint = stripConstraint pattern in + let patternWithoutConstraint = + stripConstraintUnpack ~label:(getLabel arg_label) pattern + in if isLabelled arg_label || isOptional arg_label then returnedExpression (( {loc = ppat_loc; txt = Lident (getLabel arg_label)}, diff --git a/tests/ppx/react/expected/firstClassModules.res.txt b/tests/ppx/react/expected/firstClassModules.res.txt new file mode 100644 index 00000000..c55d9e8b --- /dev/null +++ b/tests/ppx/react/expected/firstClassModules.res.txt @@ -0,0 +1,108 @@ +@@jsxConfig({version: 3}) + +module Select = { + module type T = { + type key + type t + } + @obj + external makeProps: ( + ~model: module(T with type t = '\"type-a" and type key = '\"type-key"), + ~selected: option<'\"type-key">, + ~onChange: option<'\"type-key"> => unit, + ~items: array<'\"type-a">, + ~key: string=?, + unit, + ) => { + "model": module(T with type t = '\"type-a" and type key = '\"type-key"), + "selected": option<'\"type-key">, + "onChange": option<'\"type-key"> => unit, + "items": array<'\"type-a">, + } = "" + + @react.component + let make = ( + type a key, + ~model as module(T: T with type t = a and type key = key), + ~selected: option, + ~onChange: option => unit, + ~items: array, + ) => { + let _ = (model, selected, onChange, items) + ReactDOMRe.createDOMElementVariadic("div", []) + } + let make = { + let \"FirstClassModules$Select" = ( + \"Props": { + "model": module(T with type t = '\"type-a" and type key = '\"type-key"), + "selected": option<'\"type-key">, + "onChange": option<'\"type-key"> => unit, + "items": array<'\"type-a">, + }, + ) => + make( + ~items=\"Props"["items"], + ~onChange=\"Props"["onChange"], + ~selected=\"Props"["selected"], + ~model=\"Props"["model"], + ) + \"FirstClassModules$Select" + } +} + +@@jsxConfig({version: 4, mode: "classic"}) + +module Select = { + module type T = { + type key + type t + } + type props<'model, 'selected, 'onChange, 'items> = { + model: 'model, + selected: 'selected, + onChange: 'onChange, + items: 'items, + } + + @react.component + let make = ( + {model, selected, onChange, items, _}: props< + module(T with type t = '\"type-a" and type key = '\"type-key"), + option<'\"type-key">, + option<'\"type-key"> => unit, + array<'\"type-a">, + >, + ) => { + let _ = (model, selected, onChange, items) + ReactDOM.createDOMElementVariadic("div", []) + } + let make = { + let \"FirstClassModules$Select" = (props: props<_>) => make(props) + + \"FirstClassModules$Select" + } +} + +module External = { + module type T = { + type key + type t + } + type props<'model, 'selected, 'onChange, 'items> = { + model: 'model, + selected: 'selected, + onChange: 'onChange, + items: 'items, + } + + @module("c") + external make: React.componentLike< + props< + module(T with type t = 'a and type key = 'key), + option<'key>, + (option<'key> => unit), + array<'a>, + >, + React.element, + > = "default" +} diff --git a/tests/ppx/react/expected/firstClassModules.resi.txt b/tests/ppx/react/expected/firstClassModules.resi.txt new file mode 100644 index 00000000..e5ec53af --- /dev/null +++ b/tests/ppx/react/expected/firstClassModules.resi.txt @@ -0,0 +1,57 @@ +@@jsxConfig({version: 3}) + +module Select: { + module type T = { + type key + type t + } + + @obj + external makeProps: ( + ~model: module(T with type t = 'a and type key = 'key), + ~selected: option<'key>, + ~onChange: option<'key> => unit, + ~items: array<'a>, + ~key: string=?, + unit, + ) => { + "model": module(T with type t = 'a and type key = 'key), + "selected": option<'key>, + "onChange": (option<'key> => unit), + "items": array<'a>, + } = "" + let make: React.componentLike< + { + "model": module(T with type t = 'a and type key = 'key), + "selected": option<'key>, + "onChange": (option<'key> => unit), + "items": array<'a>, + }, + React.element, + > +} + +@@jsxConfig({version: 4, mode: "classic"}) + +module Select: { + module type T = { + type key + type t + } + type props<'model, 'selected, 'onChange, 'items> = { + model: 'model, + selected: 'selected, + onChange: 'onChange, + items: 'items, + } + + let make: React.componentLike< + props< + module(T with type t = 'a and type key = 'key), + option<'key>, + (option<'key> => unit), + array<'a>, + >, + React.element, + > +} diff --git a/tests/ppx/react/firstClassModules.res b/tests/ppx/react/firstClassModules.res new file mode 100644 index 00000000..9567f435 --- /dev/null +++ b/tests/ppx/react/firstClassModules.res @@ -0,0 +1,56 @@ +@@jsxConfig({version: 3}) + +module Select = { + module type T = { + type key + type t + } + + @react.component + let make = ( + type a key, + ~model as module(T: T with type t = a and type key = key), + ~selected: option, + ~onChange: option => unit, + ~items: array, + ) => { + let _ = (model, selected, onChange, items) +
+ } +} + +@@jsxConfig({version: 4, mode: "classic"}) + +module Select = { + module type T = { + type key + type t + } + + @react.component + let make = ( + type a key, + ~model as module(T: T with type t = a and type key = key), + ~selected: option, + ~onChange: option => unit, + ~items: array, + ) => { + let _ = (model, selected, onChange, items) +
+ } +} + +module External = { + module type T = { + type key + type t + } + + @react.component @module("c") + external make: ( + ~model: module(T with type t = 'a and type key = 'key), + ~selected: option<'key>, + ~onChange: option<'key> => unit, + ~items: array<'a>, + ) => React.element = "default" +} diff --git a/tests/ppx/react/firstClassModules.resi b/tests/ppx/react/firstClassModules.resi new file mode 100644 index 00000000..9f601b3a --- /dev/null +++ b/tests/ppx/react/firstClassModules.resi @@ -0,0 +1,33 @@ +@@jsxConfig({version: 3}) + +module Select: { + module type T = { + type key + type t + } + + @react.component + let make: ( + ~model: module(T with type t = 'a and type key = 'key), + ~selected: option<'key>, + ~onChange: option<'key> => unit, + ~items: array<'a>, + ) => React.element +} + +@@jsxConfig({version: 4, mode: "classic"}) + +module Select: { + module type T = { + type key + type t + } + + @react.component + let make: ( + ~model: module(T with type t = 'a and type key = 'key), + ~selected: option<'key>, + ~onChange: option<'key> => unit, + ~items: array<'a>, + ) => React.element +}