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

JSX v4 record-based representation for lowercase #665

Merged
merged 4 commits into from
Oct 8, 2022
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@

- Functions with consecutive dots now print as multiple arrow functions like in JavaScript.

#### :nail_care Polish

- Change the internal representation of props for the lowercase components to record. https://github.com/rescript-lang/syntax/pull/665

## ReScript 10.0

- Fix printing for inline nullary functor types [#477](https://github.com/rescript-lang/syntax/pull/477)
Expand Down
30 changes: 13 additions & 17 deletions cli/reactjs_jsx_v4.ml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ let getLabel str =
| Optional str | Labelled str -> str
| Nolabel -> ""

let optionalAttr = [({txt = "ns.optional"; loc = Location.none}, PStr [])]
let optionalAttr = ({txt = "ns.optional"; loc = Location.none}, PStr [])
let optionalAttrs = [optionalAttr]

let constantString ~loc str =
Ast_helper.Exp.constant ~loc (Pconst_string (str, None))
Expand Down Expand Up @@ -211,7 +212,7 @@ let recordFromProps ~loc ~removeKey callArguments =
let id = getLabel arg_label in
if isOptional arg_label then
( {txt = Lident id; loc = pexp_loc},
{pexpr with pexp_attributes = optionalAttr} )
{pexpr with pexp_attributes = optionalAttrs} )
else ({txt = Lident id; loc = pexp_loc}, pexpr)
in
let fields = props |> List.map processProp in
Expand Down Expand Up @@ -288,9 +289,9 @@ let makeLabelDecls ~loc namedTypeList =
namedTypeList
|> List.map (fun (isOptional, label, _, interiorType) ->
if label = "key" then
Type.field ~loc ~attrs:optionalAttr {txt = label; loc} interiorType
Type.field ~loc ~attrs:optionalAttrs {txt = label; loc} interiorType
else if isOptional then
Type.field ~loc ~attrs:optionalAttr {txt = label; loc}
Type.field ~loc ~attrs:optionalAttrs {txt = label; loc}
(Typ.var @@ safeTypeFromValue @@ Labelled label)
else
Type.field ~loc {txt = label; loc}
Expand Down Expand Up @@ -462,7 +463,7 @@ let transformLowercaseCall3 ~config mapper jsxExprLoc callExprLoc attrs
| Exact children ->
[
( labelled "children",
Exp.apply ~attrs:optionalAttr
Exp.apply ~attrs:optionalAttrs
(Exp.ident
{
txt = Ldot (Lident "ReactDOM", "someElement");
Expand Down Expand Up @@ -543,19 +544,14 @@ let transformLowercaseCall3 ~config mapper jsxExprLoc callExprLoc attrs
(nolabel, childrenExpr);
]
| nonEmptyProps ->
let propsCall =
Exp.apply
(Exp.ident
{loc = Location.none; txt = Ldot (Lident "ReactDOM", "domProps")})
(nonEmptyProps
|> List.map (fun (label, expression) ->
(label, mapper.expr mapper expression)))
let propsRecord =
recordFromProps ~loc:Location.none ~removeKey:false nonEmptyProps
in
[
(* "div" *)
(nolabel, componentNameExpr);
(* ReactDOM.domProps(~className=blabla, ~foo=bar, ()) *)
(labelled "props", propsCall);
(labelled "props", propsRecord);
(* [|moreCreateElementCallsHere|] *)
(nolabel, childrenExpr);
]
Expand Down Expand Up @@ -688,14 +684,14 @@ let argToType ~newtypes ~(typeConstraints : core_type option) types
in
match (type_, name, default) with
| Some type_, name, _ when isOptional name ->
(true, getLabel name, [], {type_ with ptyp_attributes = optionalAttr})
(true, getLabel name, [], {type_ with ptyp_attributes = optionalAttrs})
:: types
| Some type_, name, _ -> (false, getLabel name, [], type_) :: types
| None, name, _ when isOptional name ->
( true,
getLabel name,
[],
Typ.var ~loc ~attrs:optionalAttr (safeTypeFromValue name) )
Typ.var ~loc ~attrs:optionalAttrs (safeTypeFromValue name) )
:: types
| None, name, _ when isLabelled name ->
(false, getLabel name, [], Typ.var ~loc (safeTypeFromValue name)) :: types
Expand Down Expand Up @@ -1052,7 +1048,7 @@ let transformStructureItem ~config mapper item =
{
patternWithoutConstraint with
ppat_attributes =
(if isOptional arg_label then optionalAttr else [])
(if isOptional arg_label then optionalAttrs else [])
@ pattern.ppat_attributes;
} )
:: patternsWithLabel)
Expand All @@ -1068,7 +1064,7 @@ let transformStructureItem ~config mapper item =
{
pattern with
ppat_attributes =
optionalAttr @ pattern.ppat_attributes;
optionalAttrs @ pattern.ppat_attributes;
} )
:: patternsWithNolabel)
expr
Expand Down
11 changes: 5 additions & 6 deletions tests/ppx/react/expected/forwardRef.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,11 @@ module V4C = {
[
ReactDOM.createDOMElementVariadic(
"input",
~props=ReactDOM.domProps(
~type_="text",
~className?,
~ref=?{Js.Nullable.toOption(ref)->Belt.Option.map(React.Ref.domRef)},
(),
),
~props={
type_: "text",
?className,
ref: ?Js.Nullable.toOption(ref)->Belt.Option.map(React.Ref.domRef),
},
[],
),
children,
Expand Down
15 changes: 15 additions & 0 deletions tests/ppx/react/expected/lowercases.res.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@@jsxConfig({version: 4, mode: "classic"})

let _ = ReactDOM.createDOMElementVariadic("div", [])
let _ = ReactDOM.createDOMElementVariadic("div", ~props={key: "k"}, [])
let _ = ReactDOM.createDOMElementVariadic("div", ~props={x: x}, [])
let _ = ReactDOM.createDOMElementVariadic(
"div",
[ReactDOM.createDOMElementVariadic("p", [{React.string(x)}])],
)
let _ = ReactDOM.createDOMElementVariadic("div", ~props=str, [])
let _ = ReactDOM.createDOMElementVariadic("div", ~props={...str, x: "x"}, [])

// syntax error
// let _ = <div x="x" {...str} />
// let _ = <div {...str} {...str} />
12 changes: 12 additions & 0 deletions tests/ppx/react/lowercases.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@@jsxConfig({version:4, mode:"classic"})

let _ = <div />
let _ = <div key="k" />
let _ = <div x />
let _ = <div><p>{React.string(x)}</p></div>
let _ = <div {...str} />
let _ = <div {...str} x="x" />

// syntax error
// let _ = <div x="x" {...str} />
// let _ = <div {...str} {...str} />