diff --git a/CHANGELOG.md b/CHANGELOG.md index da897ab1..3f4856d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/cli/reactjs_jsx_v4.ml b/cli/reactjs_jsx_v4.ml index a237572a..8303ff85 100644 --- a/cli/reactjs_jsx_v4.ml +++ b/cli/reactjs_jsx_v4.ml @@ -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)) @@ -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 @@ -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} @@ -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"); @@ -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); ] @@ -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 @@ -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) @@ -1068,7 +1064,7 @@ let transformStructureItem ~config mapper item = { pattern with ppat_attributes = - optionalAttr @ pattern.ppat_attributes; + optionalAttrs @ pattern.ppat_attributes; } ) :: patternsWithNolabel) expr diff --git a/tests/ppx/react/expected/forwardRef.res.txt b/tests/ppx/react/expected/forwardRef.res.txt index a9f2701d..73af6f9c 100644 --- a/tests/ppx/react/expected/forwardRef.res.txt +++ b/tests/ppx/react/expected/forwardRef.res.txt @@ -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, diff --git a/tests/ppx/react/expected/lowercases.res.txt b/tests/ppx/react/expected/lowercases.res.txt new file mode 100644 index 00000000..262c7aac --- /dev/null +++ b/tests/ppx/react/expected/lowercases.res.txt @@ -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 _ =
+// let _ = diff --git a/tests/ppx/react/lowercases.res b/tests/ppx/react/lowercases.res new file mode 100644 index 00000000..1dba63d9 --- /dev/null +++ b/tests/ppx/react/lowercases.res @@ -0,0 +1,12 @@ +@@jsxConfig({version:4, mode:"classic"}) + +let _ = +let _ = +let _ = +let _ ={React.string(x)}