diff --git a/CHANGELOG.md b/CHANGELOG.md index 14c3b02f..7ad54874 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ - Fix issue with using alias and default value together https://github.com/rescript-lang/syntax/pull/734 - Fix formatting of `switch` expressions that contain braced `cases` inside https://github.com/rescript-lang/syntax/pull/735 - Fix formatting of props spread for multiline JSX expression https://github.com/rescript-lang/syntax/pull/736 +- Fix issue with JSX V4 and newtype https://github.com/rescript-lang/syntax/pull/737 #### :eyeglasses: Spec Compliance diff --git a/cli/reactjs_jsx_v4.ml b/cli/reactjs_jsx_v4.ml index c53a467d..b4a3b932 100644 --- a/cli/reactjs_jsx_v4.ml +++ b/cli/reactjs_jsx_v4.ml @@ -277,7 +277,7 @@ let makePropsTypeParams ?(stripExplicitOption = false) For example, if JSX ppx is used for React Native, type would be different. *) match interiorType with - | {ptyp_desc = Ptyp_var "ref"} -> Some (refType loc) + | {ptyp_desc = Ptyp_any} -> Some (refType loc) | _ -> (* Strip explicit Js.Nullable.t in case of forwardRef *) if stripExplicitJsNullableOfRef then stripJsNullable interiorType @@ -687,46 +687,18 @@ let rec recursivelyTransformNamedArgsForMake mapper expr args newtypes coreType (Some coreType) | _ -> (args, newtypes, coreType) -let newtypeToVar newtype type_ = - let var_desc = Ptyp_var ("type-" ^ newtype) in - let typ (mapper : Ast_mapper.mapper) typ = - match typ.ptyp_desc with - | Ptyp_constr ({txt = Lident name}, _) when name = newtype -> - {typ with ptyp_desc = var_desc} - | _ -> Ast_mapper.default_mapper.typ mapper typ - in - let mapper = {Ast_mapper.default_mapper with typ} in - mapper.typ mapper type_ - -let argToType ~newtypes ~(typeConstraints : core_type option) types +let argToType types ((name, default, {ppat_attributes = attrs}, _alias, loc, type_) : arg_label * expression option * pattern * label * 'loc * core_type option) = - let rec getType name coreType = - match coreType with - | {ptyp_desc = Ptyp_arrow (arg, c1, c2)} -> - if name = arg then Some c1 else getType name c2 - | _ -> None - in - let typeConst = Option.bind typeConstraints (getType name) in - let type_ = - List.fold_left - (fun type_ newtype -> - match (type_, typeConst) with - | _, Some typ | Some typ, None -> Some (newtypeToVar newtype.txt typ) - | _ -> None) - type_ newtypes - in match (type_, name, default) with | Some type_, name, _ when isOptional name -> (true, getLabel name, attrs, loc, type_) :: types | Some type_, name, _ -> (false, getLabel name, attrs, loc, type_) :: types | None, name, _ when isOptional name -> - (true, getLabel name, attrs, loc, Typ.var ~loc (safeTypeFromValue name)) - :: types + (true, getLabel name, attrs, loc, Typ.any ~loc ()) :: types | None, name, _ when isLabelled name -> - (false, getLabel name, attrs, loc, Typ.var ~loc (safeTypeFromValue name)) - :: types + (false, getLabel name, attrs, loc, Typ.any ~loc ()) :: types | _ -> types let hasDefaultValue nameArgList = @@ -1006,16 +978,12 @@ let transformStructureItem ~config mapper item = modifiedBinding binding in (* do stuff here! *) - let namedArgList, newtypes, typeConstraints = + let namedArgList, newtypes, _typeConstraints = recursivelyTransformNamedArgsForMake mapper (modifiedBindingOld binding) [] [] None in - let namedTypeList = - List.fold_left - (argToType ~newtypes ~typeConstraints) - [] namedArgList - in + let namedTypeList = List.fold_left argToType [] namedArgList in let vbMatch (name, default, _, alias, loc, _) = let label = getLabel name in match default with @@ -1208,6 +1176,13 @@ let transformStructureItem ~config mapper item = | _ -> [Typ.any ()])))) expression in + let expression = + (* Add new tupes (type a,b,c) to make's definition *) + newtypes + |> List.fold_left + (fun e newtype -> Exp.newtype newtype e) + expression + in (* let make = ({id, name, ...}: props<'id, 'name, ...>) => { ... } *) let bindings, newBinding = match recFlag with diff --git a/tests/ppx/react/expected/aliasProps.res.txt b/tests/ppx/react/expected/aliasProps.res.txt index ece2f660..3857f28f 100644 --- a/tests/ppx/react/expected/aliasProps.res.txt +++ b/tests/ppx/react/expected/aliasProps.res.txt @@ -4,7 +4,7 @@ module C0 = { type props<'priority, 'text> = {priority: 'priority, text?: 'text} @react.component - let make = (props: props<'priority, 'text>) => { + let make = (props: props<_, _>) => { let _ = props.priority let text = switch props.text { | Some(text) => text @@ -24,7 +24,7 @@ module C1 = { type props<'priority, 'text> = {priority: 'priority, text?: 'text} @react.component - let make = (props: props<'priority, 'text>) => { + let make = (props: props<_, _>) => { let p = props.priority let text = switch props.text { | Some(text) => text @@ -44,7 +44,7 @@ module C2 = { type props<'foo> = {foo?: 'foo} @react.component - let make = (props: props<'foo>) => { + let make = (props: props<_>) => { let bar = switch props.foo { | Some(foo) => foo | None => "" diff --git a/tests/ppx/react/expected/commentAtTop.res.txt b/tests/ppx/react/expected/commentAtTop.res.txt index 4c7eba77..af2649b0 100644 --- a/tests/ppx/react/expected/commentAtTop.res.txt +++ b/tests/ppx/react/expected/commentAtTop.res.txt @@ -1,7 +1,7 @@ type props<'msg> = {msg: 'msg} // test React JSX file @react.component -let make = ({msg, _}: props<'msg>) => { +let make = ({msg, _}: props<_>) => { ReactDOM.jsx("div", {children: ?ReactDOM.someElement({msg->React.string})}) } let make = { diff --git a/tests/ppx/react/expected/defaultValueProp.res.txt b/tests/ppx/react/expected/defaultValueProp.res.txt index dc8f002c..a72ee93b 100644 --- a/tests/ppx/react/expected/defaultValueProp.res.txt +++ b/tests/ppx/react/expected/defaultValueProp.res.txt @@ -1,7 +1,7 @@ module C0 = { type props<'a, 'b> = {a?: 'a, b?: 'b} @react.component - let make = (props: props<'a, 'b>) => { + let make = (props: props<_, _>) => { let a = switch props.a { | Some(a) => a | None => 2 @@ -23,7 +23,7 @@ module C1 = { type props<'a, 'b> = {a?: 'a, b: 'b} @react.component - let make = (props: props<'a, 'b>) => { + let make = (props: props<_, _>) => { let a = switch props.a { | Some(a) => a | None => 2 diff --git a/tests/ppx/react/expected/fileLevelConfig.res.txt b/tests/ppx/react/expected/fileLevelConfig.res.txt index c1f3c9d1..4927786e 100644 --- a/tests/ppx/react/expected/fileLevelConfig.res.txt +++ b/tests/ppx/react/expected/fileLevelConfig.res.txt @@ -21,7 +21,7 @@ module V4C = { type props<'msg> = {msg: 'msg} @react.component - let make = ({msg, _}: props<'msg>) => { + let make = ({msg, _}: props<_>) => { ReactDOM.createDOMElementVariadic("div", [{msg->React.string}]) } let make = { @@ -37,7 +37,7 @@ module V4A = { type props<'msg> = {msg: 'msg} @react.component - let make = ({msg, _}: props<'msg>) => { + let make = ({msg, _}: props<_>) => { ReactDOM.jsx("div", {children: ?ReactDOM.someElement({msg->React.string})}) } let make = { diff --git a/tests/ppx/react/expected/firstClassModules.res.txt b/tests/ppx/react/expected/firstClassModules.res.txt index c55d9e8b..689760fc 100644 --- a/tests/ppx/react/expected/firstClassModules.res.txt +++ b/tests/ppx/react/expected/firstClassModules.res.txt @@ -66,11 +66,12 @@ module Select = { @react.component let make = ( + type a key, {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">, + module(T with type t = a and type key = key), + option, + option => unit, + array, >, ) => { let _ = (model, selected, onChange, items) diff --git a/tests/ppx/react/expected/forwardRef.res.txt b/tests/ppx/react/expected/forwardRef.res.txt index cb7b143f..a26c2fae 100644 --- a/tests/ppx/react/expected/forwardRef.res.txt +++ b/tests/ppx/react/expected/forwardRef.res.txt @@ -77,7 +77,7 @@ module V4C = { @react.component let make = ( - {?className, children, _}: props<'className, 'children, ReactRef.currentDomRef>, + {?className, children, _}: props<_, _, ReactRef.currentDomRef>, ref: Js.Nullable.t, ) => ReactDOM.createDOMElementVariadic( @@ -135,10 +135,7 @@ module V4A = { } @react.component - let make = ( - {?className, children, _}: props<'className, 'children, ReactDOM.Ref.currentDomRef>, - ref, - ) => + let make = ({?className, children, _}: props<_, _, ReactDOM.Ref.currentDomRef>, ref) => ReactDOM.jsxs( "div", { diff --git a/tests/ppx/react/expected/interface.res.txt b/tests/ppx/react/expected/interface.res.txt index 9b6062dd..77c60724 100644 --- a/tests/ppx/react/expected/interface.res.txt +++ b/tests/ppx/react/expected/interface.res.txt @@ -1,6 +1,6 @@ module A = { type props<'x> = {x: 'x} - @react.component let make = ({x, _}: props<'x>) => React.string(x) + @react.component let make = ({x, _}: props<_>) => React.string(x) let make = { let \"Interface$A" = (props: props<_>) => make(props) \"Interface$A" diff --git a/tests/ppx/react/expected/mangleKeyword.res.txt b/tests/ppx/react/expected/mangleKeyword.res.txt index e216b703..f5642f49 100644 --- a/tests/ppx/react/expected/mangleKeyword.res.txt +++ b/tests/ppx/react/expected/mangleKeyword.res.txt @@ -23,8 +23,7 @@ module C4C0 = { type props<'T_open, 'T_type> = {@as("open") _open: 'T_open, @as("type") _type: 'T_type} @react.component - let make = ({@as("open") _open, @as("type") _type, _}: props<'T_open, string>) => - React.string(_open) + let make = ({@as("open") _open, @as("type") _type, _}: props<_, string>) => React.string(_open) let make = { let \"MangleKeyword$C4C0" = (props: props<_>) => make(props) @@ -46,8 +45,7 @@ module C4A0 = { type props<'T_open, 'T_type> = {@as("open") _open: 'T_open, @as("type") _type: 'T_type} @react.component - let make = ({@as("open") _open, @as("type") _type, _}: props<'T_open, string>) => - React.string(_open) + let make = ({@as("open") _open, @as("type") _type, _}: props<_, string>) => React.string(_open) let make = { let \"MangleKeyword$C4A0" = (props: props<_>) => make(props) diff --git a/tests/ppx/react/expected/newtype.res.txt b/tests/ppx/react/expected/newtype.res.txt index fb810ec5..178ce71d 100644 --- a/tests/ppx/react/expected/newtype.res.txt +++ b/tests/ppx/react/expected/newtype.res.txt @@ -27,7 +27,7 @@ module V4C = { type props<'a, 'b, 'c> = {a: 'a, b: 'b, c: 'c} @react.component - let make = ({a, b, c, _}: props<'\"type-a", array>, 'a>) => + let make = (type a, {a, b, c, _}: props>, 'a>) => ReactDOM.createDOMElementVariadic("div", []) let make = { let \"Newtype$V4C" = (props: props<_>) => make(props) @@ -42,7 +42,7 @@ module V4A = { type props<'a, 'b, 'c> = {a: 'a, b: 'b, c: 'c} @react.component - let make = ({a, b, c, _}: props<'\"type-a", array>, 'a>) => + let make = (type a, {a, b, c, _}: props>, 'a>) => ReactDOM.jsx("div", {}) let make = { let \"Newtype$V4A" = (props: props<_>) => make(props) @@ -50,3 +50,49 @@ module V4A = { \"Newtype$V4A" } } + +module V4A1 = { + type props<'a, 'b, 'c> = {a: 'a, b: 'b, c: 'c} + + @react.component + let make = (type x y, {a, b, c, _}: props, 'a>) => ReactDOM.jsx("div", {}) + let make = { + let \"Newtype$V4A1" = (props: props<_>) => make(props) + + \"Newtype$V4A1" + } +} + +module type T = { + type t +} + +module V4A2 = { + type props<'foo> = {foo: 'foo} + + @react.component + let make = (type a, {foo, _}: props) => { + module T = unpack(foo) + ReactDOM.jsx("div", {}) + } + let make = { + let \"Newtype$V4A2" = (props: props<_>) => make(props) + + \"Newtype$V4A2" + } +} + +module V4A3 = { + type props<'foo> = {foo: 'foo} + + @react.component + let make = (type a, {foo, _}: props<_>) => { + module T = unpack(foo: T with type t = a) + foo + } + let make = { + let \"Newtype$V4A3" = (props: props<_>) => make(props) + + \"Newtype$V4A3" + } +} diff --git a/tests/ppx/react/expected/optimizeAutomaticMode.res.txt b/tests/ppx/react/expected/optimizeAutomaticMode.res.txt index 25c30a12..5234e02a 100644 --- a/tests/ppx/react/expected/optimizeAutomaticMode.res.txt +++ b/tests/ppx/react/expected/optimizeAutomaticMode.res.txt @@ -7,7 +7,7 @@ module User = { type props<'doctor> = {doctor: 'doctor} @react.component - let make = ({doctor, _}: props<'doctor>) => { + let make = ({doctor, _}: props<_>) => { ReactDOM.jsx("h1", {id: "h1", children: ?ReactDOM.someElement({React.string(format(doctor))})}) } let make = { diff --git a/tests/ppx/react/expected/removedKeyProp.res.txt b/tests/ppx/react/expected/removedKeyProp.res.txt index a7e5a464..83d11e9b 100644 --- a/tests/ppx/react/expected/removedKeyProp.res.txt +++ b/tests/ppx/react/expected/removedKeyProp.res.txt @@ -3,7 +3,7 @@ module Foo = { type props<'x, 'y> = {x: 'x, y: 'y} - @react.component let make = ({x, y, _}: props<'x, 'y>) => React.string(x ++ y) + @react.component let make = ({x, y, _}: props<_, _>) => React.string(x ++ y) let make = { let \"RemovedKeyProp$Foo" = (props: props<_>) => make(props) @@ -15,7 +15,7 @@ module HasChildren = { type props<'children> = {children: 'children} @react.component - let make = ({children, _}: props<'children>) => ReactDOM.createElement(React.fragment, [children]) + let make = ({children, _}: props<_>) => ReactDOM.createElement(React.fragment, [children]) let make = { let \"RemovedKeyProp$HasChildren" = (props: props<_>) => make(props) diff --git a/tests/ppx/react/expected/topLevel.res.txt b/tests/ppx/react/expected/topLevel.res.txt index bf6f95ec..5268d7e8 100644 --- a/tests/ppx/react/expected/topLevel.res.txt +++ b/tests/ppx/react/expected/topLevel.res.txt @@ -25,7 +25,7 @@ module V4C = { type props<'a, 'b> = {a: 'a, b: 'b} @react.component - let make = ({a, b, _}: props<'a, 'b>) => { + let make = ({a, b, _}: props<_, _>) => { Js.log("This function should be named 'TopLevel.react'") ReactDOM.createDOMElementVariadic("div", []) } @@ -42,7 +42,7 @@ module V4A = { type props<'a, 'b> = {a: 'a, b: 'b} @react.component - let make = ({a, b, _}: props<'a, 'b>) => { + let make = ({a, b, _}: props<_, _>) => { Js.log("This function should be named 'TopLevel.react'") ReactDOM.jsx("div", {}) } diff --git a/tests/ppx/react/expected/typeConstraint.res.txt b/tests/ppx/react/expected/typeConstraint.res.txt index e7b3e483..1ce2faaf 100644 --- a/tests/ppx/react/expected/typeConstraint.res.txt +++ b/tests/ppx/react/expected/typeConstraint.res.txt @@ -20,8 +20,7 @@ module V4C = { type props<'a, 'b> = {a: 'a, b: 'b} @react.component - let make = ({a, b, _}: props<'\"type-a", '\"type-a">) => - ReactDOM.createDOMElementVariadic("div", []) + let make = (type a, {a, b, _}: props<_, _>) => ReactDOM.createDOMElementVariadic("div", []) let make = { let \"TypeConstraint$V4C" = (props: props<_>) => make(props) @@ -34,7 +33,7 @@ module V4C = { module V4A = { type props<'a, 'b> = {a: 'a, b: 'b} - @react.component let make = ({a, b, _}: props<'\"type-a", '\"type-a">) => ReactDOM.jsx("div", {}) + @react.component let make = (type a, {a, b, _}: props<_, _>) => ReactDOM.jsx("div", {}) let make = { let \"TypeConstraint$V4A" = (props: props<_>) => make(props) diff --git a/tests/ppx/react/expected/v4.res.txt b/tests/ppx/react/expected/v4.res.txt index 9e709259..7412b812 100644 --- a/tests/ppx/react/expected/v4.res.txt +++ b/tests/ppx/react/expected/v4.res.txt @@ -9,7 +9,7 @@ module AnotherName = { type // Component with another name than "make" props<'x> = {x: 'x} - @react.component let anotherName = ({x, _}: props<'x>) => React.string(x) + @react.component let anotherName = ({x, _}: props<_>) => React.string(x) let anotherName = { let \"V4$AnotherName$anotherName" = (props: props<_>) => anotherName(props) diff --git a/tests/ppx/react/newtype.res b/tests/ppx/react/newtype.res index 27a834eb..c77634ed 100644 --- a/tests/ppx/react/newtype.res +++ b/tests/ppx/react/newtype.res @@ -18,3 +18,28 @@ module V4A = { @react.component let make = (type a, ~a: a, ~b: array>, ~c: 'a, _) =>
} + +module V4A1 = { + @react.component + let make = (type x y, ~a:x, ~b:array, ~c:'a) =>
+} + +module type T = { + type t +} + +module V4A2 = { + @react.component + let make = (type a, ~foo: module(T with type t = a)) => { + module T = unpack(foo) +
+ } +} + +module V4A3 = { + @react.component + let make = (type a, ~foo) => { + module T = unpack(foo: T with type t = a) + foo + } +}