Skip to content

Commit

Permalink
Merge branch 'main' of github.com:ml-in-barcelona/server-reason-react…
Browse files Browse the repository at this point in the history
… into render-rsc-payload

* 'main' of github.com:ml-in-barcelona/server-reason-react:
  [`React.cloneElement`] Raise in any other case than lowercase element (#164)
  Make Js.Math.min/max
  Add native code to Js.String.split (#168)
  Async components ppx support (#157)
  • Loading branch information
davesnx committed Aug 28, 2024
2 parents f0c56e2 + fb68360 commit 334e4eb
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 190 deletions.
22 changes: 17 additions & 5 deletions packages/melange.js/Js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,19 @@ end = struct
if start_idx >= end_idx then ""
else Stdlib.String.sub str start_idx (end_idx - start_idx)

let split ?sep ?limit _str = notImplemented "Js.String" "split"
let split ?sep ?limit str =
let sep = Option.value sep ~default:str in
let regexp = Str.regexp_string sep in
(* On js split, it don't return an empty string on end when separator is an empty string *)
(* but "split_delim" does *)
(* https://melange.re/unstable/playground/?language=OCaml&code=SnMubG9nKEpzLlN0cmluZy5zcGxpdCB%2Bc2VwOiIiICJzdGFydCIpOw%3D%3D&live=off *)
let split = if sep <> "" then Str.split_delim else Str.split in
let items = split regexp str |> Stdlib.Array.of_list in
let limit = Option.value limit ~default:(Stdlib.Array.length items) in
match limit with
| limit when limit >= 0 && limit < Stdlib.Array.length items ->
Stdlib.Array.sub items 0 limit
| _ -> items

let splitByRe ~regexp ?limit str =
let rev_array arr =
Expand Down Expand Up @@ -1531,13 +1543,13 @@ end = struct
let log1p _ = notImplemented "Js.Math" "log1p"
let log10 _ = notImplemented "Js.Math" "log10"
let log2 _ = notImplemented "Js.Math" "log2"
let max_int _ = notImplemented "Js.Math" "max_int"
let max_int (a : int) (b : int) = Stdlib.max a b
let maxMany_int _ = notImplemented "Js.Math" "maxMany_int"
let max_float _ = notImplemented "Js.Math" "max_float"
let max_float (a : float) (b : float) = Stdlib.max a b
let maxMany_float _ = notImplemented "Js.Math" "maxMany_float"
let min_int _ = notImplemented "Js.Math" "min_int"
let min_int (a : int) (b : int) = Stdlib.min a b
let minMany_int _ = notImplemented "Js.Math" "minMany_int"
let min_float _ = notImplemented "Js.Math" "min_float"
let min_float (a : float) (b : float) = Stdlib.min a b
let minMany_float _ = notImplemented "Js.Math" "minMany_float"
let pow_float ~base:_ ~exp:_ = notImplemented "Js.Math" "pow_float"
let random _ = notImplemented "Js.Math" "random"
Expand Down
12 changes: 0 additions & 12 deletions packages/melange.js/Js.mli
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,7 @@ module String : sig
not_implemented "is not implemented in native under server-reason-react.js"]

val slice : ?start:int -> ?end_:int -> t -> t

val split : ?sep:t -> ?limit:int -> t -> t array
[@@alert
not_implemented "is not implemented in native under server-reason-react.js"]

val splitByRe : regexp:Re.t -> ?limit:int -> t -> t nullable array
val startsWith : prefix:t -> ?start:int -> t -> bool
val substr : ?start:int -> ?len:int -> t -> t
Expand Down Expand Up @@ -1168,32 +1164,24 @@ module Math : sig
not_implemented "is not implemented in native under server-reason-react.js"]

val max_int : int -> int -> int
[@@alert
not_implemented "is not implemented in native under server-reason-react.js"]

val maxMany_int : int array -> int
[@@alert
not_implemented "is not implemented in native under server-reason-react.js"]

val max_float : float -> float -> float
[@@alert
not_implemented "is not implemented in native under server-reason-react.js"]

val maxMany_float : float array -> float
[@@alert
not_implemented "is not implemented in native under server-reason-react.js"]

val min_int : int -> int -> int
[@@alert
not_implemented "is not implemented in native under server-reason-react.js"]

val minMany_int : int array -> int
[@@alert
not_implemented "is not implemented in native under server-reason-react.js"]

val min_float : float -> float -> float
[@@alert
not_implemented "is not implemented in native under server-reason-react.js"]

val minMany_float : float array -> float
[@@alert
Expand Down
48 changes: 39 additions & 9 deletions packages/melange.js/test.ml
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,45 @@ let string_tests =
(* assert_string (Js.String.sliceToEnd ~from:(-2) "abcdefg") "fg"; *)
assert_string (Js.String.slice ~start:7 "abcdefg") "");
test "split" (fun () ->
(* assert_string_array (split "-" "2018-01-02") [| "2018"; "01"; "02" |];
assert_string_array (split "," "a,b,,c") [| "a"; "b"; ""; "c" |];
assert_string_array
(split "::" "good::bad as great::awful")
[| "good"; "bad as great"; "awful" |];
assert_string_array
(split ";" "has-no-delimiter")
[| "has-no-delimiter" |] *)
());
assert_string_array (Js.String.split ~sep:"" "") [||];
assert_string_array
(Js.String.split ~sep:"-" "2018-01-02")
[| "2018"; "01"; "02" |];
assert_string_array
(Js.String.split ~sep:"," "a,b,,c")
[| "a"; "b"; ""; "c" |];
assert_string_array
(Js.String.split ~sep:"::" "good::bad as great::awful")
[| "good"; "bad as great"; "awful" |];
assert_string_array
(Js.String.split ~sep:";" "has-no-delimiter")
[| "has-no-delimiter" |];
assert_string_array
(Js.String.split ~sep:"with" "with-sep-equals-to-beginning")
[| ""; "-sep-equals-to-beginning" |];
assert_string_array
(Js.String.split ~sep:"end" "with-sep-equals-to-end")
[| "with-sep-equals-to-"; "" |];
assert_string_array
(Js.String.split ~sep:"/" "/with-sep-on-beginning-and-end/")
[| ""; "with-sep-on-beginning-and-end"; "" |];
assert_string_array
(Js.String.split ~sep:"" "with-empty-sep")
[|
"w"; "i"; "t"; "h"; "-"; "e"; "m"; "p"; "t"; "y"; "-"; "s"; "e"; "p";
|];
assert_string_array
(Js.String.split ~sep:"-" "with-limit-equals-to-zero" ~limit:0)
[||];
assert_string_array
(Js.String.split ~sep:"-" "with-limit-equals-to-length" ~limit:5)
[| "with"; "limit"; "equals"; "to"; "length" |];
assert_string_array
(Js.String.split ~sep:"-" "with-limit-greater-than-length" ~limit:100)
[| "with"; "limit"; "greater"; "than"; "length" |];
assert_string_array
(Js.String.split ~sep:"-" "with-limit-less-than-zero" ~limit:(-2))
[| "with"; "limit"; "less"; "than"; "zero" |]);
test "splitAtMost" (fun () ->
(* assert_string_array
(splitAtMost "/" ~limit:3 "ant/bee/cat/dog/elk")
Expand Down
30 changes: 17 additions & 13 deletions packages/react/src/React.ml
Original file line number Diff line number Diff line change
Expand Up @@ -455,9 +455,9 @@ let createElement tag attributes children =
| true -> Lower_case_element { tag; attributes; children = [] }
| false -> Lower_case_element { tag; attributes; children }

(* cloneElements overrides childrens and props but is not clear
what to do with other components that are not lower_case_elements
Provider, Consumer or Suspense. TODO: Check original (JS) implementation *)
(* `cloneElement` overrides childrens and props on lower case components, It raises Invalid_argument for the rest.
React.js can clone uppercase components, since it stores their props on each element's object but since we just store the fn and don't have the props, we can't clone them).
TODO: Check original implementation for exact error message/exception type *)
let cloneElement element new_attributes =
match element with
| Lower_case_element { tag; attributes; children } ->
Expand All @@ -467,16 +467,20 @@ let cloneElement element new_attributes =
attributes = clone_attributes attributes new_attributes;
children;
}
| Fragment _childrens -> Fragment _childrens
| Text t -> Text t
| InnerHtml t -> InnerHtml t
| Empty -> Empty
| List l -> List l
| Provider child -> Provider child
| Consumer child -> Consumer child
| Upper_case_component f -> Upper_case_component f
| Async_component f -> Async_component f
| Suspense { fallback; children } -> Suspense { fallback; children }
| Upper_case_component _ ->
raise
(Invalid_argument "In server-reason-react, a component can't be cloned")
| Fragment _ -> raise (Invalid_argument "can't clone a fragment")
| Text _ -> raise (Invalid_argument "can't clone a text element")
| InnerHtml _ ->
raise (Invalid_argument "can't clone a dangerouslySetInnerHTML element")
| Empty -> raise (Invalid_argument "can't clone a null element")
| List _ -> raise (Invalid_argument "can't clone an array element")
| Provider _ -> raise (Invalid_argument "can't clone a Provider")
| Consumer _ -> raise (Invalid_argument "can't clone a Consumer")
| Async_component _ ->
raise (Invalid_argument "can't clone an async component")
| Suspense _ -> raise (Invalid_argument "can't clone a Supsense component")

module Fragment = struct
let make ~children ?key:_ () = Fragment children
Expand Down
7 changes: 7 additions & 0 deletions packages/server-reason-react-ppx/cram/component.t/input.re
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,10 @@ module Form_with_method = {
};

let a = <Uppercase> <div /> </Uppercase>;

module Async_component = {
[@react.async.component]
let make = (~children) => <form method_="GET"> children </form>;
};

let a = <Async_component> <div /> </Async_component>;
161 changes: 89 additions & 72 deletions packages/server-reason-react-ppx/cram/component.t/run.t
Original file line number Diff line number Diff line change
Expand Up @@ -2,102 +2,119 @@ Since we generate invalid syntax for the argument of the make fn `(Props : <>)`
We need to output ML syntax here, otherwise refmt could not parse it.
$ ../ppx.sh --output ml input.re
module React_component_with_props = struct
let make ?key:(_ : string option) =
fun [@warning "-16"] ~lola () ->
React.createElement "div" [] [ React.string lola ]
let make ?key:(_ : string option) ~lola () =
React.Upper_case_component
(fun () -> React.createElement "div" [] [ React.string lola ])
end

let react_component_with_props =
React.Upper_case_component
(fun () -> React_component_with_props.make ~lola:"flores" ())
React_component_with_props.make ~lola:"flores" ()

module Forward_Ref = struct
let make ?key:(_ : string option) ~children =
fun [@warning "-16"] ~buttonRef () ->
React.createElement "button"
(Stdlib.List.filter_map Fun.id
[
Some (React.JSX.Ref (buttonRef : React.domRef));
Some (React.JSX.String ("class", ("FancyButton" : string)));
])
[ children ]
let make ?key:(_ : string option) ~children ~buttonRef () =
React.Upper_case_component
(fun () ->
React.createElement "button"
(Stdlib.List.filter_map Fun.id
[
Some (React.JSX.Ref (buttonRef : React.domRef));
Some (React.JSX.String ("class", ("FancyButton" : string)));
])
[ children ])
end

module Onclick_handler_button = struct
let make ?key:(_ : string option) ~name =
fun [@warning "-16"] ?isDisabled () ->
let make ?key:(_ : string option) ~name ?isDisabled () =
let onClick event = Js.log event in
React.createElement "button"
(Stdlib.List.filter_map Fun.id
[
Some (React.JSX.String ("name", (name : string)));
Some
(React.JSX.Event
( "onClick",
React.JSX.Mouse (onClick : React.Event.Mouse.t -> unit) ));
Some (React.JSX.Bool ("disabled", (isDisabled : bool)));
])
[]
React.Upper_case_component
(fun () ->
React.createElement "button"
(Stdlib.List.filter_map Fun.id
[
Some (React.JSX.String ("name", (name : string)));
Some
(React.JSX.Event
( "onClick",
React.JSX.Mouse (onClick : React.Event.Mouse.t -> unit) ));
Some (React.JSX.Bool ("disabled", (isDisabled : bool)));
])
[])
end

module Children_as_string = struct
let make ?key:(_ : string option) =
fun [@warning "-16"] ?(name = "joe") () ->
React.createElement "div" []
[ Printf.sprintf "`name` is %s" name |> React.string ]
let make ?key:(_ : string option) ?(name = "joe") () =
React.Upper_case_component
(fun () ->
React.createElement "div" []
[ Printf.sprintf "`name` is %s" name |> React.string ])
end

let () = Dream.run ()
let l = 33

module Uppercase_with_SSR_components = struct
let make ?key:(_ : string option) ~children =
fun [@warning "-16"] ~moreProps () ->
React.createElement "html" []
[
React.createElement "head" []
let make ?key:(_ : string option) ~children ~moreProps () =
React.Upper_case_component
(fun () ->
React.createElement "html" []
[
React.createElement "title" []
[ React.string ("SSR React " ^ moreProps) ];
];
React.createElement "body" []
[
React.createElement "div"
(Stdlib.List.filter_map Fun.id
[ Some (React.JSX.String ("id", ("root" : string))) ])
[ children ];
React.createElement "script"
(Stdlib.List.filter_map Fun.id
[
Some
(React.JSX.String ("src", ("/static/client.js" : string)));
])
[];
];
]
React.createElement "head" []
[
React.createElement "title" []
[ React.string ("SSR React " ^ moreProps) ];
];
React.createElement "body" []
[
React.createElement "div"
(Stdlib.List.filter_map Fun.id
[ Some (React.JSX.String ("id", ("root" : string))) ])
[ children ];
React.createElement "script"
(Stdlib.List.filter_map Fun.id
[
Some
(React.JSX.String
("src", ("/static/client.js" : string)));
])
[];
];
])
end

module Upper_with_aria = struct
let make ?key:(_ : string option) =
fun [@warning "-16"] ~children () ->
React.createElement "div"
(Stdlib.List.filter_map Fun.id
[
Some
(React.JSX.String ("aria-hidden", string_of_bool ("true" : bool)));
])
[ children ]
let make ?key:(_ : string option) ~children () =
React.Upper_case_component
(fun () ->
React.createElement "div"
(Stdlib.List.filter_map Fun.id
[
Some
(React.JSX.String
("aria-hidden", string_of_bool ("true" : bool)));
])
[ children ])
end

module Form_with_method = struct
let make ?key:(_ : string option) =
fun [@warning "-16"] ~children () ->
React.createElement "form"
(Stdlib.List.filter_map Fun.id
[ Some (React.JSX.String ("method", ("GET" : string))) ])
[ children ]
let make ?key:(_ : string option) ~children () =
React.Upper_case_component
(fun () ->
React.createElement "form"
(Stdlib.List.filter_map Fun.id
[ Some (React.JSX.String ("method", ("GET" : string))) ])
[ children ])
end

let a = Uppercase.make ~children:(React.createElement "div" [] []) ()

module Async_component = struct
let make ?key:(_ : string option) ~children () =
React.Async_component
(fun () ->
React.createElement "form"
(Stdlib.List.filter_map Fun.id
[ Some (React.JSX.String ("method", ("GET" : string))) ])
[ children ])
end

let a =
React.Upper_case_component
(fun () -> Uppercase.make ~children:(React.createElement "div" [] []) ())
let a = Async_component.make ~children:(React.createElement "div" [] []) ()
Loading

0 comments on commit 334e4eb

Please sign in to comment.