Skip to content
This repository was archived by the owner on Jun 15, 2023. It is now read-only.

Commit

Permalink
Fix issue with JSX V4 and newtype
Browse files Browse the repository at this point in the history
This is a port of PR rescript-lang/rescript#6029
  • Loading branch information
cristianoc committed Mar 3, 2023
1 parent 52cfbb3 commit a0d8494
Show file tree
Hide file tree
Showing 17 changed files with 113 additions and 71 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
51 changes: 13 additions & 38 deletions cli/reactjs_jsx_v4.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions tests/ppx/react/expected/aliasProps.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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 => ""
Expand Down
2 changes: 1 addition & 1 deletion tests/ppx/react/expected/commentAtTop.res.txt
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down
4 changes: 2 additions & 2 deletions tests/ppx/react/expected/defaultValueProp.res.txt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/ppx/react/expected/fileLevelConfig.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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 = {
Expand Down
9 changes: 5 additions & 4 deletions tests/ppx/react/expected/firstClassModules.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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<key>,
option<key> => unit,
array<a>,
>,
) => {
let _ = (model, selected, onChange, items)
Expand Down
7 changes: 2 additions & 5 deletions tests/ppx/react/expected/forwardRef.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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<ReactRef.currentDomRef>,
) =>
ReactDOM.createDOMElementVariadic(
Expand Down Expand Up @@ -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",
{
Expand Down
2 changes: 1 addition & 1 deletion tests/ppx/react/expected/interface.res.txt
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
6 changes: 2 additions & 4 deletions tests/ppx/react/expected/mangleKeyword.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)

Expand Down
50 changes: 48 additions & 2 deletions tests/ppx/react/expected/newtype.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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<option<[#Foo('\"type-a")]>>, 'a>) =>
let make = (type a, {a, b, c, _}: props<a, array<option<[#Foo(a)]>>, 'a>) =>
ReactDOM.createDOMElementVariadic("div", [])
let make = {
let \"Newtype$V4C" = (props: props<_>) => make(props)
Expand All @@ -42,11 +42,57 @@ module V4A = {
type props<'a, 'b, 'c> = {a: 'a, b: 'b, c: 'c}

@react.component
let make = ({a, b, c, _}: props<'\"type-a", array<option<[#Foo('\"type-a")]>>, 'a>) =>
let make = (type a, {a, b, c, _}: props<a, array<option<[#Foo(a)]>>, 'a>) =>
ReactDOM.jsx("div", {})
let make = {
let \"Newtype$V4A" = (props: props<_>) => make(props)

\"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<x, array<y>, '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 with type t = a)>) => {
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"
}
}
2 changes: 1 addition & 1 deletion tests/ppx/react/expected/optimizeAutomaticMode.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
4 changes: 2 additions & 2 deletions tests/ppx/react/expected/removedKeyProp.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions tests/ppx/react/expected/topLevel.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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", [])
}
Expand All @@ -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", {})
}
Expand Down
5 changes: 2 additions & 3 deletions tests/ppx/react/expected/typeConstraint.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion tests/ppx/react/expected/v4.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading

0 comments on commit a0d8494

Please sign in to comment.