diff --git a/CHANGELOG.md b/CHANGELOG.md index a109a16b62..40a1414e78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,11 @@ These are only breaking changes for unformatted code. # 10.1.1 +#### :boom: Breaking Change + +- The prop names duplicated to keyword are not mangled automatically in JSX v4. + - Use `@as` instead + #### :rocket: New Feature - Add support for empty inlined record literal `{}` for inlined records where all fields are optional https://github.com/rescript-lang/rescript-compiler/pull/5900 @@ -70,6 +75,7 @@ These are only breaking changes for unformatted code. - Prevent inlining of async functions in additional cases https://github.com/rescript-lang/rescript-compiler/issues/5860 - Fix build error where aliasing arguments to `_` in the make function with JSX V4. https://github.com/rescript-lang/rescript-compiler/pull/5881 - Fix parsing of spread props as an expression in JSX V4 https://github.com/rescript-lang/rescript-compiler/pull/5885 +- Fix dropping attributes from props in make function in JSX V4 https://github.com/rescript-lang/rescript-compiler/pull/5905 # 10.1.0 diff --git a/res_syntax/src/reactjs_jsx_v4.ml b/res_syntax/src/reactjs_jsx_v4.ml index ae586bcb61..183e61df1a 100644 --- a/res_syntax/src/reactjs_jsx_v4.ml +++ b/res_syntax/src/reactjs_jsx_v4.ml @@ -289,14 +289,15 @@ let makePropsTypeParams ?(stripExplicitOption = false) let makeLabelDecls ~loc namedTypeList = namedTypeList - |> List.map (fun (isOptional, label, _, interiorType) -> + |> List.map (fun (isOptional, label, attrs, interiorType) -> if label = "key" then - Type.field ~loc ~attrs:optionalAttrs {txt = label; loc} interiorType + Type.field ~loc ~attrs:(optionalAttrs @ attrs) {txt = label; loc} + interiorType else if isOptional then - Type.field ~loc ~attrs:optionalAttrs {txt = label; loc} + Type.field ~loc ~attrs:(optionalAttrs @ attrs) {txt = label; loc} (Typ.var @@ safeTypeFromValue @@ Labelled label) else - Type.field ~loc {txt = label; loc} + Type.field ~loc ~attrs {txt = label; loc} (Typ.var @@ safeTypeFromValue @@ Labelled label)) let makeTypeDecls propsName loc namedTypeList = @@ -681,7 +682,9 @@ let newtypeToVar newtype type_ = mapper.typ mapper type_ let argToType ~newtypes ~(typeConstraints : core_type option) types - (name, default, _noLabelName, _alias, loc, type_) = + ((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)} -> @@ -699,17 +702,18 @@ 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 = optionalAttrs}) + (true, getLabel name, attrs, {type_ with ptyp_attributes = optionalAttrs}) :: types - | Some type_, name, _ -> (false, getLabel name, [], type_) :: types + | Some type_, name, _ -> (false, getLabel name, attrs, type_) :: types | None, name, _ when isOptional name -> ( true, getLabel name, - [], + attrs, Typ.var ~loc ~attrs:optionalAttrs (safeTypeFromValue name) ) :: types | None, name, _ when isLabelled name -> - (false, getLabel name, [], Typ.var ~loc (safeTypeFromValue name)) :: types + (false, getLabel name, attrs, Typ.var ~loc (safeTypeFromValue name)) + :: types | _ -> types let argWithDefaultValue (name, default, _, _, _, _) = @@ -717,10 +721,10 @@ let argWithDefaultValue (name, default, _, _, _, _) = | Some default when isOptional name -> Some (getLabel name, default) | _ -> None -let argToConcreteType types (name, _loc, type_) = +let argToConcreteType types (name, attrs, _loc, type_) = match name with - | name when isLabelled name -> (false, getLabel name, [], type_) :: types - | name when isOptional name -> (true, getLabel name, [], type_) :: types + | name when isLabelled name -> (false, getLabel name, attrs, type_) :: types + | name when isOptional name -> (true, getLabel name, attrs, type_) :: types | _ -> types let check_string_int_attribute_iter = @@ -763,15 +767,19 @@ let transformStructureItem ~config mapper item = |> Option.map React_jsx_common.typVarsOfCoreType |> Option.value ~default:[] in - let rec getPropTypes types ({ptyp_loc; ptyp_desc} as fullType) = + let rec getPropTypes types + ({ptyp_loc; ptyp_desc; ptyp_attributes} as fullType) = match ptyp_desc with | Ptyp_arrow (name, type_, ({ptyp_desc = Ptyp_arrow _} as rest)) when isLabelled name || isOptional name -> - getPropTypes ((name, ptyp_loc, type_) :: types) rest + getPropTypes + ((name, ptyp_attributes, ptyp_loc, type_) :: types) + rest | Ptyp_arrow (Nolabel, _type, rest) -> getPropTypes types rest | Ptyp_arrow (name, type_, returnValue) when isLabelled name || isOptional name -> - (returnValue, (name, returnValue.ptyp_loc, type_) :: types) + ( returnValue, + (name, ptyp_attributes, returnValue.ptyp_loc, type_) :: types ) | _ -> (fullType, types) in let innerType, propTypes = getPropTypes [] pval_type in @@ -1264,9 +1272,12 @@ let transformSignatureItem ~config _mapper item = in let rec getPropTypes types ({ptyp_loc; ptyp_desc} as fullType) = match ptyp_desc with - | Ptyp_arrow (name, type_, ({ptyp_desc = Ptyp_arrow _} as rest)) + | Ptyp_arrow + ( name, + ({ptyp_attributes = attrs} as type_), + ({ptyp_desc = Ptyp_arrow _} as rest) ) when isOptional name || isLabelled name -> - getPropTypes ((name, ptyp_loc, type_) :: types) rest + getPropTypes ((name, attrs, ptyp_loc, type_) :: types) rest | Ptyp_arrow (Nolabel, {ptyp_desc = Ptyp_constr ({txt = Lident "unit"}, _)}, rest) -> @@ -1274,9 +1285,9 @@ let transformSignatureItem ~config _mapper item = | Ptyp_arrow (Nolabel, _type, rest) -> hasForwardRef := true; getPropTypes types rest - | Ptyp_arrow (name, type_, returnValue) + | Ptyp_arrow (name, ({ptyp_attributes = attrs} as type_), returnValue) when isOptional name || isLabelled name -> - (returnValue, (name, returnValue.ptyp_loc, type_) :: types) + (returnValue, (name, attrs, returnValue.ptyp_loc, type_) :: types) | _ -> (fullType, types) in let innerType, propTypes = getPropTypes [] pval_type in diff --git a/res_syntax/tests/ppx/react/expected/mangleKeyword.res.txt b/res_syntax/tests/ppx/react/expected/mangleKeyword.res.txt new file mode 100644 index 0000000000..78108dd056 --- /dev/null +++ b/res_syntax/tests/ppx/react/expected/mangleKeyword.res.txt @@ -0,0 +1,76 @@ +@@jsxConfig({version: 3}) + +module C30 = { + @obj external makeProps: (~_open: 'T_open, ~key: string=?, unit) => {"_open": 'T_open} = "" + + @react.component let make = @warning("-16") (~_open) => React.string(_open) + let make = { + let \"MangleKeyword$C30" = (\"Props": {"_open": 'T_open}) => make(~_open=\"Props"["_open"]) + \"MangleKeyword$C30" + } +} +module C31 = { + @obj external makeProps: (~_open: string, ~key: string=?, unit) => {"_open": string} = "" + external make: React.componentLike<{"_open": string}, React.element> = "default" +} + +let c30 = React.createElement(C30.make, C30.makeProps(~_open="x", ())) +let c31 = React.createElement(C31.make, C31.makeProps(~_open="x", ())) + +@@jsxConfig({version: 4, mode: "classic"}) + +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 = { + let \"MangleKeyword$C4C0" = (props: props<_>) => make(props) + + \"MangleKeyword$C4C0" + } +} +module C4C1 = { + type props<'T_open, 'T_type> = { + @as("open") _open: 'T_open, + @as("type") _type: 'T_type, + } + + external make: @as("open") React.componentLike, React.element> = "default" +} + +let c4c0 = React.createElement(C4C0.make, {_open: "x", _type: "t"}) +let c4c1 = React.createElement(C4C1.make, {_open: "x", _type: "t"}) + +@@jsxConfig({version: 4, mode: "automatic"}) + +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 = { + let \"MangleKeyword$C4A0" = (props: props<_>) => make(props) + + \"MangleKeyword$C4A0" + } +} +module C4A1 = { + type props<'T_open, 'T_type> = { + @as("open") _open: 'T_open, + @as("type") _type: 'T_type, + } + + external make: @as("open") React.componentLike, React.element> = "default" +} + +let c4a0 = React.jsx(C4A0.make, {_open: "x", _type: "t"}) +let c4a1 = React.jsx(C4A1.make, {_open: "x", _type: "t"}) diff --git a/res_syntax/tests/ppx/react/mangleKeyword.res b/res_syntax/tests/ppx/react/mangleKeyword.res new file mode 100644 index 0000000000..2e0fa7c344 --- /dev/null +++ b/res_syntax/tests/ppx/react/mangleKeyword.res @@ -0,0 +1,45 @@ +@@jsxConfig({version: 3}) + +module C30 = { + @react.component + let make = (~_open) => React.string(_open) +} +module C31 = { + @react.component + external make: (~_open: string) => React.element = "default" +} + +let c30 = +let c31 = + +@@jsxConfig({version: 4, mode: "classic"}) + +module C4C0 = { + @react.component + let make = + (@as("open") ~_open, @as("type") ~_type: string) => React.string(_open) +} +module C4C1 = { + @react.component + external make: (@as("open") ~_open: string, @as("type") ~_type: string) => React.element = + "default" +} + +let c4c0 = +let c4c1 = + +@@jsxConfig({version: 4, mode: "automatic"}) + +module C4A0 = { + @react.component + let make = + (@as("open") ~_open, @as("type") ~_type: string) => React.string(_open) +} +module C4A1 = { + @react.component + external make: (@as("open") ~_open: string, @as("type") ~_type: string) => React.element = + "default" +} + +let c4a0 = +let c4a1 =