From 15affd0068ed721db3e9357c3337a791d5b9d7f1 Mon Sep 17 00:00:00 2001 From: David Sancho Date: Wed, 17 Jul 2024 11:27:04 +0200 Subject: [PATCH 1/9] Add CSS Box Alignment Module Level 3 (#847) --- src/ReactDOM.re | 10 +++++++--- src/ReactDOM.rei | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ReactDOM.re b/src/ReactDOM.re index ecc27cb7f..bedb622f2 100644 --- a/src/ReactDOM.re +++ b/src/ReactDOM.re @@ -276,12 +276,12 @@ module Style = { ~gridAutoRows: string=?, ~gridColumn: string=?, ~gridColumnEnd: string=?, - ~gridColumnGap: string=?, + ~gridColumnGap: string=?, /* Deprecated in favor of column-gap */ ~gridColumnStart: string=?, - ~gridGap: string=?, + ~gridGap: string=?, /* Deprecated in favor of gap */ ~gridRow: string=?, ~gridRowEnd: string=?, - ~gridRowGap: string=?, + ~gridRowGap: string=?, /* Deprecated in favor of row-gap */ ~gridRowStart: string=?, ~gridTemplate: string=?, ~gridTemplateAreas: string=?, @@ -392,6 +392,10 @@ module Style = { ~rubyAlign: string=?, ~rubyMerge: string=?, ~rubyPosition: string=?, + /* CSS Box Alignment Module Level 3 */ + ~gap: string=?, + ~columnGap: string=?, + ~rowGap: string=?, /* Lists and Counters Level 3 - WD */ /* listStyle - already defined by CSS2Properties */ /* listStyleImage - already defined by CSS2Properties */ diff --git a/src/ReactDOM.rei b/src/ReactDOM.rei index 0d80a6a92..2310ca3e2 100644 --- a/src/ReactDOM.rei +++ b/src/ReactDOM.rei @@ -392,6 +392,10 @@ module Style: { ~rubyAlign: string=?, ~rubyMerge: string=?, ~rubyPosition: string=?, + /* CSS Box Alignment Module Level 3 */ + ~gap: string=?, + ~columnGap: string=?, + ~rowGap: string=?, /* Lists and Counters Level 3 - WD */ /* listStyle - already defined by CSS2Properties */ /* listStyleImage - already defined by CSS2Properties */ From ae18db0de9b8c5ad6b38a018e1b1cc3c52e3c906 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Mon, 22 Jul 2024 21:20:16 -0700 Subject: [PATCH 2/9] test: repro #840 (#842) --- ppx/test/hover.t | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 ppx/test/hover.t diff --git a/ppx/test/hover.t b/ppx/test/hover.t new file mode 100644 index 000000000..7b147d8ac --- /dev/null +++ b/ppx/test/hover.t @@ -0,0 +1,76 @@ +Test some locations in reason-react components, reproduces #840 + + $ cat >dune-project < (lang dune 3.13) + > (using melange 0.1) + > EOF + + $ cat >dune < (melange.emit + > (alias foo) + > (target foo) + > (libraries reason-react) + > (emit_stdlib false) + > (preprocess + > (pps melange.ppx reason-react-ppx))) + > EOF + + $ cat >component.ml < let[@react.component] make ~foo ~bar = + > (div + > ~children:[ React.string foo; bar |> string_of_int |> React.string ] + > () [@JSX]) + > EOF + $ dune build @foo + +Let's test hovering over parts of the component + +`React.string`: + + $ ocamlmerlin single type-enclosing -position 3:25 -verbosity 0 \ + > -filename component.ml < component.ml | jq '.value' + [ + { + "start": { + "line": 3, + "col": 17 + }, + "end": { + "line": 3, + "col": 29 + }, + "type": "string -> React.element", + "tail": "no" + }, + { + "start": { + "line": 1, + "col": 0 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "< bar : int; foo : string > Js.t -> React.element", + "tail": "no" + } + ] + +The `foo` variable inside the component body + + $ ocamlmerlin single type-enclosing -position 3:31 -verbosity 0 \ + > -filename component.ml < component.ml | jq '.value' + [ + { + "start": { + "line": 1, + "col": 0 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "< bar : int; foo : string > Js.t -> React.element", + "tail": "no" + } + ] From 1b97ad52014f847bfccfe3f71e24f64886cf8ab0 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sun, 11 Aug 2024 16:53:23 -0700 Subject: [PATCH 3/9] fix: re-enable failing tests + fix location tests (#850) * fix: re-enable failing tests + fix location tests * update flakes * fix tests --- dune-project | 6 +- flake.lock | 14 +-- ppx/test/dune | 5 - ppx/test/hover.t | 188 ++++++++++++++++++++++++++++++++- ppx/test/location.t/run.t | 6 +- ppx/test/simple.t/run.t | 216 -------------------------------------- reason-react-ppx.opam | 4 +- reason-react.opam | 2 +- 8 files changed, 200 insertions(+), 241 deletions(-) diff --git a/dune-project b/dune-project index b71369d9d..e37df57d3 100644 --- a/dune-project +++ b/dune-project @@ -39,7 +39,7 @@ (reason-react-ppx (= :version)) (reason - (>= 3.10.0)) + (>= 3.12.0)) (ocaml-lsp-server :with-dev-setup) (opam-check-npm-deps (and @@ -57,9 +57,9 @@ (depends ocaml (reason - (>= 3.10.0)) + (>= 3.12.0)) (ppxlib - (>= 0.28.0)) + (>= 0.33.0)) (merlin :with-test) (ocamlformat (and diff --git a/flake.lock b/flake.lock index fd24c3ed4..aa12f2919 100644 --- a/flake.lock +++ b/flake.lock @@ -41,11 +41,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1718486791, - "narHash": "sha256-Et9ljkLbBEtyyFCV57CH7WQM08ELMAcwcQYUdNz01Wg=", + "lastModified": 1723398011, + "narHash": "sha256-LgKXKfdRhV1+drzKpsljUFhseRGL8eLKJR+DWp7nFuE=", "owner": "nix-ocaml", "repo": "nix-overlays", - "rev": "3f444cc0c8612c2bf0bf8822da98efbb977e2ff8", + "rev": "f5b06554ac5b1ac77c7911e35ee7f74b4aa8785a", "type": "github" }, "original": { @@ -56,17 +56,17 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1718083092, - "narHash": "sha256-EQsPXycAbmby4meQUNLYfFaGOiqz2J9AlwFRV4UiHnY=", + "lastModified": 1723316219, + "narHash": "sha256-2B9qh8QBvw3kV/8cHc7ZJcrbVsRwP8wKjkwPXTSz76Y=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "aa1ebdaf49a606e21c06e0f6ed7aece9a41831c3", + "rev": "bef98989a27429e1cb9e3d9c25701ba2da742af2", "type": "github" }, "original": { "owner": "NixOS", "repo": "nixpkgs", - "rev": "aa1ebdaf49a606e21c06e0f6ed7aece9a41831c3", + "rev": "bef98989a27429e1cb9e3d9c25701ba2da742af2", "type": "github" } }, diff --git a/ppx/test/dune b/ppx/test/dune index 1e2c1cb88..1e0a153a6 100644 --- a/ppx/test/dune +++ b/ppx/test/dune @@ -8,8 +8,3 @@ %{bin:jq} %{bin:ocamlmerlin} ppx.sh)) - -(cram - (applies_to simple uppercase) - ;; Disabled until https://github.com/reasonml/reason/issues/2737 is fixed - (enabled_if false)) diff --git a/ppx/test/hover.t b/ppx/test/hover.t index 7b147d8ac..16aecf194 100644 --- a/ppx/test/hover.t +++ b/ppx/test/hover.t @@ -42,16 +42,100 @@ Let's test hovering over parts of the component "type": "string -> React.element", "tail": "no" }, + { + "start": { + "line": 3, + "col": 17 + }, + "end": { + "line": 3, + "col": 33 + }, + "type": "React.element", + "tail": "no" + }, + { + "start": { + "line": 2, + "col": 2 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "React.element array", + "tail": "no" + }, + { + "start": { + "line": 2, + "col": 2 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "React.element", + "tail": "no" + }, + { + "start": { + "line": 2, + "col": 2 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "React.element option", + "tail": "no" + }, + { + "start": { + "line": 2, + "col": 2 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "ReactDOM.domProps", + "tail": "no" + }, + { + "start": { + "line": 2, + "col": 2 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "React.element", + "tail": "no" + }, { "start": { "line": 1, - "col": 0 + "col": 32 }, "end": { "line": 4, "col": 15 }, - "type": "< bar : int; foo : string > Js.t -> React.element", + "type": "bar:int -> React.element", + "tail": "no" + }, + { + "start": { + "line": 1, + "col": 27 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "foo:string -> bar:int -> React.element", "tail": "no" } ] @@ -61,16 +145,112 @@ The `foo` variable inside the component body $ ocamlmerlin single type-enclosing -position 3:31 -verbosity 0 \ > -filename component.ml < component.ml | jq '.value' [ + { + "start": { + "line": 3, + "col": 30 + }, + "end": { + "line": 3, + "col": 33 + }, + "type": "string", + "tail": "no" + }, + { + "start": { + "line": 3, + "col": 17 + }, + "end": { + "line": 3, + "col": 33 + }, + "type": "React.element", + "tail": "no" + }, + { + "start": { + "line": 2, + "col": 2 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "React.element array", + "tail": "no" + }, + { + "start": { + "line": 2, + "col": 2 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "React.element", + "tail": "no" + }, + { + "start": { + "line": 2, + "col": 2 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "React.element option", + "tail": "no" + }, + { + "start": { + "line": 2, + "col": 2 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "ReactDOM.domProps", + "tail": "no" + }, + { + "start": { + "line": 2, + "col": 2 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "React.element", + "tail": "no" + }, + { + "start": { + "line": 1, + "col": 32 + }, + "end": { + "line": 4, + "col": 15 + }, + "type": "bar:int -> React.element", + "tail": "no" + }, { "start": { "line": 1, - "col": 0 + "col": 27 }, "end": { "line": 4, "col": 15 }, - "type": "< bar : int; foo : string > Js.t -> React.element", + "type": "foo:string -> bar:int -> React.element", "tail": "no" } ] diff --git a/ppx/test/location.t/run.t b/ppx/test/location.t/run.t index 0bb05666b..ead4d380a 100644 --- a/ppx/test/location.t/run.t +++ b/ppx/test/location.t/run.t @@ -2177,9 +2177,9 @@ ((pos_fname output.ml) (pos_lnum 3) (pos_bol 151) (pos_cnum 179))) (loc_end - ((pos_fname output.ml) (pos_lnum 3) (pos_bol 151) - (pos_cnum 181))) - (loc_ghost false))) + ((pos_fname output.ml) (pos_lnum 7) (pos_bol 359) + (pos_cnum 371))) + (loc_ghost true))) (pexp_loc_stack ()) (pexp_attributes ())))) (pexp_loc ((loc_start diff --git a/ppx/test/simple.t/run.t b/ppx/test/simple.t/run.t index 8250340b6..a2c13f98b 100644 --- a/ppx/test/simple.t/run.t +++ b/ppx/test/simple.t/run.t @@ -59,30 +59,6 @@ Let's test hovering over parts of the component "type": "React.element", "tail": "no" }, - { - "start": { - "line": 3, - "col": 31 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, - { - "start": { - "line": 3, - "col": 13 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, { "start": { "line": 3, @@ -174,30 +150,6 @@ Let's test hovering over parts of the component "type": "React.element", "tail": "no" }, - { - "start": { - "line": 3, - "col": 31 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, - { - "start": { - "line": 3, - "col": 13 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, { "start": { "line": 3, @@ -313,30 +265,6 @@ Let's test hovering over parts of the component "type": "React.element", "tail": "no" }, - { - "start": { - "line": 3, - "col": 31 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, - { - "start": { - "line": 3, - "col": 13 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, { "start": { "line": 3, @@ -476,30 +404,6 @@ Let's test hovering over parts of the component "type": "React.element", "tail": "no" }, - { - "start": { - "line": 3, - "col": 31 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, - { - "start": { - "line": 3, - "col": 13 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, { "start": { "line": 3, @@ -651,30 +555,6 @@ Let's test hovering over parts of the component "type": "React.element", "tail": "no" }, - { - "start": { - "line": 3, - "col": 31 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, - { - "start": { - "line": 3, - "col": 13 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, { "start": { "line": 3, @@ -838,30 +718,6 @@ Let's test hovering over parts of the component "type": "React.element", "tail": "no" }, - { - "start": { - "line": 3, - "col": 31 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, - { - "start": { - "line": 3, - "col": 13 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, { "start": { "line": 3, @@ -989,30 +845,6 @@ Let's test hovering over parts of the component "type": "React.element", "tail": "no" }, - { - "start": { - "line": 3, - "col": 31 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, - { - "start": { - "line": 3, - "col": 13 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, { "start": { "line": 3, @@ -1140,30 +972,6 @@ Let's test hovering over parts of the component "type": "React.element", "tail": "no" }, - { - "start": { - "line": 3, - "col": 31 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, - { - "start": { - "line": 3, - "col": 13 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, { "start": { "line": 3, @@ -1267,30 +1075,6 @@ Closing `` "type": "React.element", "tail": "no" }, - { - "start": { - "line": 3, - "col": 31 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, - { - "start": { - "line": 3, - "col": 13 - }, - "end": { - "line": 9, - "col": 3 - }, - "type": "unit => React.element", - "tail": "no" - }, { "start": { "line": 3, diff --git a/reason-react-ppx.opam b/reason-react-ppx.opam index ca0563a7f..944c8fdbe 100644 --- a/reason-react-ppx.opam +++ b/reason-react-ppx.opam @@ -16,8 +16,8 @@ bug-reports: "https://github.com/reasonml/reason-react/issues" depends: [ "dune" {>= "3.9"} "ocaml" - "reason" {>= "3.10.0"} - "ppxlib" {>= "0.28.0"} + "reason" {>= "3.12.0"} + "ppxlib" {>= "0.33.0"} "merlin" {with-test} "ocamlformat" {= "0.24.0" & with-dev-setup} "odoc" {with-doc} diff --git a/reason-react.opam b/reason-react.opam index 3894550a4..e2329051c 100644 --- a/reason-react.opam +++ b/reason-react.opam @@ -21,7 +21,7 @@ depends: [ "ocaml" "melange" {>= "3.0.0"} "reason-react-ppx" {= version} - "reason" {>= "3.10.0"} + "reason" {>= "3.12.0"} "ocaml-lsp-server" {with-dev-setup} "opam-check-npm-deps" {= "1.0.0" & with-dev-setup} "ocamlformat" {= "0.24.0" & with-dev-setup} From a9f8c77abc8463adafc30981306a5c5347cc9970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Ch=C3=A1varri?= Date: Thu, 15 Aug 2024 12:47:47 +0200 Subject: [PATCH 4/9] Add locations-check test (#844) * add locations-check test * update tests --- ppx/test/locations-check.t | 66 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 ppx/test/locations-check.t diff --git a/ppx/test/locations-check.t b/ppx/test/locations-check.t new file mode 100644 index 000000000..0520bb96e --- /dev/null +++ b/ppx/test/locations-check.t @@ -0,0 +1,66 @@ +Test the preprocessed reason-react components have well-formed locations. +Uses https://github.com/ocaml-ppx/ppxlib/blob/44583fc14c3cc39ee6269ffd69f52146283f72c0/src/location_check.mli + +With no annotations (ppx does nothing) + + $ cat >input.ml < let make ~foo ~bar = + > (div + > ~children:[ React.string foo; bar |> string_of_int |> React.string ] + > ()) + > EOF + + $ reason-react-ppx -check -locations-check input.ml + let make ~foo ~bar = + div ~children:[React.string foo; (bar |> string_of_int) |> React.string] () + +With JSX annotation + + $ cat >input.ml < let make ~foo ~bar = + > (div + > ~children:[ React.string foo; bar |> string_of_int |> React.string ] + > () [@JSX]) + > EOF + + $ reason-react-ppx -check -locations-check input.ml + File "input.ml", line 2, characters 3-6: + 2 | (div + ^^^ + Error: invalid output from ppx, expression overlaps with expression at location: + File "input.ml", line 2, characters 2-96: + [1] + +With @react.component annotation + + $ cat >input.ml < let[@react.component] make ~foo ~bar = + > (div + > ~children:[ React.string foo; bar |> string_of_int |> React.string ] + > () [@JSX]) + > EOF + + $ reason-react-ppx -check -locations-check input.ml + File "input.ml", line 1, characters 33-36: + 1 | let[@react.component] make ~foo ~bar = + ^^^ + Error: invalid output from ppx, core type overlaps with core type at location: + File "input.ml", line 1, characters 33-36: + [1] + +Everything + + $ cat >input.ml < let[@react.component] make ~foo ~bar = + > (div + > ~children:[ React.string foo; bar |> string_of_int |> React.string ] + > () [@JSX]) + > EOF + + $ reason-react-ppx -check -locations-check input.ml + File "input.ml", line 1, characters 33-36: + 1 | let[@react.component] make ~foo ~bar = + ^^^ + Error: invalid output from ppx, core type overlaps with core type at location: + File "input.ml", line 1, characters 33-36: + [1] From b60bd369730ba929d3244bad5a36a88c20680712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Ch=C3=A1varri?= Date: Thu, 15 Aug 2024 14:57:10 +0200 Subject: [PATCH 5/9] update compiler version in makefile cmd (#851) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9cf988a8e..3512619a7 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ install: ## Update the package dependencies when new deps are added to dune-proj .PHONY: init create-switch: ## Create a local opam switch - @opam switch create . 5.1.1 --no-install + @opam switch create . 5.2.0 --no-install .PHONY: init init: create-switch install ## Create a local opam switch, install deps From 8d6b3264135c0b4a627a6abbf47c8d5c996b8638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Ch=C3=A1varri?= Date: Fri, 16 Aug 2024 08:21:21 +0200 Subject: [PATCH 6/9] Fix multi-child fragment (#852) * fix multi-child fragment * revert ocamlformat change * refactor: share more code * +changelog --------- Co-authored-by: Antonio Nuno Monteiro --- CHANGES.md | 3 ++- ppx/reason_react_ppx.ml | 46 +++++++++++++++++++++++++++++--------- ppx/test/component.t/run.t | 2 +- ppx/test/fragment.t/run.t | 6 ++--- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d1dd3df05..5e633fd35 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,7 +3,8 @@ * Convert `ReasonReactErrorBoundary` to Reason instead of `%raw` JS. This has the benefit of skipping a hardcoded `require('react')` call (@anmonteiro in [#839](https://github.com/reasonml/reason-react/pull/839)) - +* Fix: Remove "unique `key` prop" warnings from multi-child fragment elements + (@jchavarri in https://github.com/reasonml/reason-react/pull/852) # 0.14.1 diff --git a/ppx/reason_react_ppx.ml b/ppx/reason_react_ppx.ml index cbceb3437..9646da10c 100644 --- a/ppx/reason_react_ppx.ml +++ b/ppx/reason_react_ppx.ml @@ -72,19 +72,25 @@ module Binding = struct ( { loc; txt = Ldot (Lident "React", "componentLike") }, [ props; return ] ) - let jsxFragment ~loc ~attrs children = + let makeJsxFragment api ~loc ~attrs children = let fragment = Builder.pexp_ident ~loc { loc; txt = Ldot (Lident "React", "jsxFragment") } in Builder.pexp_apply ~loc ~attrs - (Builder.pexp_ident ~loc { loc; txt = Ldot (Lident "React", "jsx") }) + (Builder.pexp_ident ~loc { loc; txt = Ldot (Lident "React", api) }) [ (nolabel, fragment); ( nolabel, ReactDOM.domProps ~applyLoc:loc ~loc [ (labelled "children", children); (nolabel, Builder.unit) ] ); ] + + let jsxFragment ~loc ~attrs children = + makeJsxFragment "jsx" ~loc ~attrs children + + let jsxsFragment ~loc ~attrs children = + makeJsxFragment "jsxs" ~loc ~attrs children end end @@ -1393,13 +1399,10 @@ let jsxMapper = transformJsxCall ~ctxt parentExpLoc self callExpression callArguments nonJSXAttributes) (* is it a list with jsx attribute? Reason <>foo desugars to - [@JSX][foo]*) + [@JSX][foo] + This will match either <> or <> foo *) | { - pexp_desc = - ( Pexp_construct - ( { txt = Lident "::"; loc }, - Some { pexp_desc = Pexp_tuple _; _ } ) - | Pexp_construct ({ txt = Lident "[]"; loc }, None) ); + pexp_desc = Pexp_construct ({ txt = Lident ("[]" | "::"); loc }, lst); pexp_attributes; _; } as listItems -> ( @@ -1411,13 +1414,34 @@ let jsxMapper = match (jsxAttribute, nonJSXAttributes) with (* no JSX attribute *) | [], _ -> super#expression ctxt expr - | _, nonJSXAttributes -> + | _, nonJSXAttributes -> ( let childrenExpr = transformChildrenIfList ~loc ~ctxt ~mapper:self listItems in (* throw away the [@JSX] attribute and keep the others, if any *) - Binding.React.jsxFragment ~loc ~attrs:nonJSXAttributes - (Binding.React.array ~loc childrenExpr)) + match lst with + | None + | Some + { + pexp_desc = + Pexp_tuple + [ + _; + { + pexp_desc = + Pexp_construct ({ txt = Lident "[]"; _ }, None); + _; + }; + ]; + _; + } -> + Binding.React.jsxFragment ~loc ~attrs:nonJSXAttributes + (Binding.React.array ~loc childrenExpr) + | Some { pexp_desc = Pexp_tuple (_ :: _); _ } -> + (* Fragment with two or more children: <> foo bar *) + Binding.React.jsxsFragment ~loc ~attrs:nonJSXAttributes + (Binding.React.array ~loc childrenExpr) + | _ -> assert false)) (* Delegate to the default mapper, a deep identity traversal *) | e -> super#expression ctxt e [@@raises Invalid_argument] diff --git a/ppx/test/component.t/run.t b/ppx/test/component.t/run.t index 29ff6dd64..aa9583ce1 100644 --- a/ppx/test/component.t/run.t +++ b/ppx/test/component.t/run.t @@ -27,7 +27,7 @@ We need to output ML syntax here, otherwise refmt could not parse it. ""[@@mel.obj ] let make = ((fun ?(name= "") -> - React.jsx React.jsxFragment + React.jsxs React.jsxFragment (((ReactDOM.domProps)[@merlin.hide ]) ~children:(React.array [|(ReactDOM.jsx "div" diff --git a/ppx/test/fragment.t/run.t b/ppx/test/fragment.t/run.t index 62789e138..be148546c 100644 --- a/ppx/test/fragment.t/run.t +++ b/ppx/test/fragment.t/run.t @@ -11,7 +11,7 @@ ([@merlin.hide] ReactDOM.domProps)(~children=React.array([|bar|]), ()), ); let poly_children_fragment = (foo, bar) => - React.jsx( + React.jsxs( React.jsxFragment, ([@merlin.hide] ReactDOM.domProps)( ~children=React.array([|foo, bar|]), @@ -19,13 +19,13 @@ ), ); let nested_fragment = (foo, bar, baz) => - React.jsx( + React.jsxs( React.jsxFragment, ([@merlin.hide] ReactDOM.domProps)( ~children= React.array([| foo, - React.jsx( + React.jsxs( React.jsxFragment, ([@merlin.hide] ReactDOM.domProps)( ~children=React.array([|bar, baz|]), From c575d278daa73618a7883290fffe7397447c10a9 Mon Sep 17 00:00:00 2001 From: Javier Chavarri Date: Mon, 19 Aug 2024 11:09:04 +0000 Subject: [PATCH 7/9] add missing entries to changelog --- CHANGES.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 5e633fd35..d9e8c08eb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,14 @@ -# Unreleased +# 0.15.0 +* Add `isValidElement` (@r17x in + https://github.com/reasonml/reason-react/pull/837) +* Add `startTransition` (@r17x in + https://github.com/reasonml/reason-react/pull/838) * Convert `ReasonReactErrorBoundary` to Reason instead of `%raw` JS. This has the benefit of skipping a hardcoded `require('react')` call (@anmonteiro in [#839](https://github.com/reasonml/reason-react/pull/839)) +* Add CSS Box Alignment Module Level 3 (@davesnx in + https://github.com/reasonml/reason-react/pull/847) * Fix: Remove "unique `key` prop" warnings from multi-child fragment elements (@jchavarri in https://github.com/reasonml/reason-react/pull/852) From fa71fa8a2d628016a83ef6d343227b9c1c351c87 Mon Sep 17 00:00:00 2001 From: Javier Chavarri Date: Mon, 19 Aug 2024 13:37:48 +0000 Subject: [PATCH 8/9] reason-react-ppx: + lower bound in ocaml --- dune-project | 3 ++- reason-react-ppx.opam | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dune-project b/dune-project index e37df57d3..fbeb92d80 100644 --- a/dune-project +++ b/dune-project @@ -55,7 +55,8 @@ (synopsis "React.js JSX PPX") (description "ReasonReact JSX PPX") (depends - ocaml + (ocaml + (>= 4.14)) (reason (>= 3.12.0)) (ppxlib diff --git a/reason-react-ppx.opam b/reason-react-ppx.opam index 944c8fdbe..e9f168131 100644 --- a/reason-react-ppx.opam +++ b/reason-react-ppx.opam @@ -15,7 +15,7 @@ doc: "https://reasonml.github.io/reason-react" bug-reports: "https://github.com/reasonml/reason-react/issues" depends: [ "dune" {>= "3.9"} - "ocaml" + "ocaml" {>= "4.14"} "reason" {>= "3.12.0"} "ppxlib" {>= "0.33.0"} "merlin" {with-test} From c07e413750c5ca1632e6d3405a3cfbdf8d24c84e Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Thu, 5 Sep 2024 17:25:14 +0200 Subject: [PATCH 9/9] fix: type of pipeable stream to allow objects with keys (#854) * fix: type of pipeable stream to allow objects with keys * fix: test --- src/ReactDOMServerNode.re | 7 ++++--- src/ReactDOMServerNode.rei | 6 +++--- test/ReactDOM__test.re | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ReactDOMServerNode.re b/src/ReactDOMServerNode.re index eea22122b..6bdabed44 100644 --- a/src/ReactDOMServerNode.re +++ b/src/ReactDOMServerNode.re @@ -24,14 +24,15 @@ type options = { progressiveChunkSize: option(int), }; -type pipeableStream = { +type pipeableStream('a) = { /* Using empty object instead of Node.stream since Melange don't provide a binding to node's Stream (https://nodejs.org/api/stream.html) */ - pipe: Js.t({.}) => unit, + pipe: Js.t({..} as 'a) => unit, abort: unit => unit, }; [@mel.module "react-dom/server"] -external renderToPipeableStream: (React.element, options) => pipeableStream = +external renderToPipeableStream: + (React.element, options) => pipeableStream('a) = "renderToPipeableStream"; let renderToPipeableStream = diff --git a/src/ReactDOMServerNode.rei b/src/ReactDOMServerNode.rei index 2153fbfee..2301cc2ce 100644 --- a/src/ReactDOMServerNode.rei +++ b/src/ReactDOMServerNode.rei @@ -24,9 +24,9 @@ type options = { progressiveChunkSize: option(int), }; -type pipeableStream = { +type pipeableStream('a) = { /* Using empty object instead of Node.stream since Melange don't provide a binding to node's Stream (https://nodejs.org/api/stream.html) */ - pipe: Js.t({.}) => unit, + pipe: Js.t({..} as 'a) => unit, abort: unit => unit, }; @@ -45,4 +45,4 @@ let renderToPipeableStream: ~progressiveChunkSize: int=?, React.element ) => - pipeableStream; + pipeableStream('a); diff --git a/test/ReactDOM__test.re b/test/ReactDOM__test.re index 73f770ad3..7d82ad6eb 100644 --- a/test/ReactDOM__test.re +++ b/test/ReactDOM__test.re @@ -53,7 +53,7 @@ describe("ReactDOM", () => { hasErrored := true; }, ); - let {pipe, abort: _}: ReactDOMServerNode.pipeableStream = + let {pipe, abort: _}: ReactDOMServerNode.pipeableStream(_) = ReactDOMServerNode.renderToPipeableStream(
"Hello world!"->React.string
, );