Skip to content

Commit e31a6fd

Browse files
committed
Native: keep labeled-arg make, use old-style JSX calls
On native, don't generate the makeProps/wrapper pattern for [@react.component]. Keep make with its original labeled-arg signature for backward compatibility with .rei files, .ml call sites, and first-class modules. Native JSX now generates old-style labeled-arg calls (Component.make ~prop1:v1 ~children:... ()) instead of Component.make(Component.makeProps(~prop1:v1, ...)).
1 parent 7fa29cb commit e31a6fd

5 files changed

Lines changed: 42 additions & 66 deletions

File tree

packages/react/src/React.ml

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -512,15 +512,10 @@ let rec cloneElement element new_attributes =
512512
| Suspense _ -> raise (Invalid_argument "React.cloneElement: cannot clone a Suspense")
513513

514514
module Fragment = struct
515-
let makeProps ~children () : < children : element > Js.t =
516-
object
517-
method children = children
518-
end
519-
520-
let make ?key:_ props = Fragment props#children
515+
let make ?key:_ ~children () = Fragment children
521516
end
522517

523-
let fragment children = Fragment.make (Fragment.makeProps ~children ())
518+
let fragment children = Fragment.make ~children ()
524519

525520
(* ReasonReact APIs *)
526521
let string txt = Text txt
@@ -542,13 +537,7 @@ module Context = struct
542537
consumer : children:element -> element;
543538
}
544539

545-
let makeProps ~value ~children () : < value : 'a ; children : element > Js.t =
546-
object
547-
method value = value
548-
method children = children
549-
end
550-
551-
let provider ctx ?key:_ props = ctx.provider ~value:props#value ~children:props#children ()
540+
let provider ctx ?key:_ ~value ~children () = ctx.provider ~value ~children ()
552541
end
553542

554543
let createContext (initial_value : 'a) : 'a Context.t =
@@ -573,14 +562,8 @@ let createContext (initial_value : 'a) : 'a Context.t =
573562
module Suspense = struct
574563
let or_react_null = function None -> null | Some x -> x
575564

576-
let makeProps ?fallback ?children () : < fallback : element option ; children : element option > Js.t =
577-
object
578-
method fallback = fallback
579-
method children = children
580-
end
581-
582-
let make ?key props =
583-
Suspense { key; fallback = or_react_null props#fallback; children = or_react_null props#children }
565+
let make ?key ?fallback ?children () =
566+
Suspense { key; fallback = or_react_null fallback; children = or_react_null children }
584567
end
585568

586569
module Cache = struct

packages/react/src/React.mli

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -608,8 +608,7 @@ and model_value = element Model.t
608608
exception Invalid_children of string
609609

610610
module Fragment : sig
611-
val makeProps : children:element -> unit -> < children : element > Js.t
612-
val make : (< children : element > Js.t, element) componentLike
611+
val make : ?key:string -> children:element -> unit -> element
613612
end
614613

615614
val createElement : string -> JSX.prop list -> element list -> element
@@ -633,17 +632,13 @@ module Context : sig
633632
consumer : children:element -> element;
634633
}
635634

636-
val makeProps : value:'a -> children:element -> unit -> < value : 'a ; children : element > Js.t
637-
val provider : 'a t -> (< value : 'a ; children : element > Js.t, element) componentLike
635+
val provider : 'a t -> ?key:string -> value:'a -> children:element -> unit -> element
638636
end
639637

640638
val createContext : 'a -> 'a Context.t
641639

642640
module Suspense : sig
643-
val makeProps :
644-
?fallback:element -> ?children:element -> unit -> < fallback : element option ; children : element option > Js.t
645-
646-
val make : (< fallback : element option ; children : element option > Js.t, element) componentLike
641+
val make : ?key:string -> ?fallback:element -> ?children:element -> unit -> element
647642
end
648643

649644
module Cache : sig

packages/server-reason-react-ppx/server_reason_react_ppx.ml

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ let component_make_props_ident tag =
9999

100100
let is_key_arg (label, _) = match label with Optional "key" | Labelled "key" -> true | _ -> false
101101

102-
let rewrite_component ~loc tag args children =
102+
(* JSX rewrite for JS/melange: Component.make(Component.makeProps(~prop1:v1, ...)) *)
103+
let rewrite_component_js ~loc tag args children =
103104
let component = pexp_ident ~loc tag in
104105
let props_args =
105106
match children with
@@ -116,6 +117,32 @@ let rewrite_component ~loc tag args children =
116117
in
117118
pexp_apply ~loc component make_args
118119

120+
(* JSX rewrite for native: Component.make(~prop1:v1, ~children:..., ()) -- old-style labeled args.
121+
Note: args already includes a trailing (Nolabel, ()) from the JSX parse, so we don't add another. *)
122+
let rewrite_component_native ~loc tag args children =
123+
(* tag is already Ldot(Module, "make") from the caller (line ~1507),
124+
or Lident "local_fn" from line ~1510. For Lident, append .make. *)
125+
let make_ident =
126+
match tag with
127+
| { txt = Ldot (_, "make"); _ } -> pexp_ident ~loc tag (* already Module.make *)
128+
| { txt = Lident name; loc } -> pexp_ident ~loc { txt = Ldot (Lident name, "make"); loc }
129+
| { txt = Ldot (path, name); loc } -> pexp_ident ~loc { txt = Ldot (Ldot (path, name), "make"); loc }
130+
| { txt = Lapply _; loc } -> raise_errorf ~loc "jsx: can't use functor application in JSX"
131+
in
132+
let args_no_unit = strip_unit_args args in
133+
let props_args =
134+
match children with
135+
| None -> args_no_unit
136+
| Some [ children ] -> (Labelled "children", children) :: args_no_unit
137+
| Some children -> (Labelled "children", [%expr React.list [%e pexp_list ~loc children]]) :: args_no_unit
138+
in
139+
pexp_apply ~loc make_ident (props_args @ [ (Nolabel, [%expr ()]) ])
140+
141+
let rewrite_component ~loc tag args children =
142+
match mode.contents with
143+
| Js -> rewrite_component_js ~loc tag args children
144+
| Native -> rewrite_component_native ~loc tag args children
145+
119146
let validate_prop ~loc id name =
120147
match DomProps.findByJsxName ~tag:id name with
121148
| Ok p -> p
@@ -1356,41 +1383,14 @@ let rewrite_structure_item ~nested_module_names structure_item =
13561383
else None
13571384
in
13581385
let rewrites = List.map value_bindings ~f:rewrite_component_binding in
1359-
let make_props_bindings =
1360-
List.filter_map rewrites ~f:(function
1361-
| Some (_, make_props_binding, _) -> Some make_props_binding
1362-
| None -> None)
1363-
in
1364-
let public_bindings =
1365-
List.filter_map rewrites ~f:(function Some (_, _, public_binding) -> Some public_binding | None -> None)
1366-
in
13671386
let internal_bindings =
13681387
List.map2 value_bindings rewrites ~f:(fun vb rewrite ->
13691388
match rewrite with Some (internal_binding, _, _) -> internal_binding | None -> vb)
13701389
in
1371-
match make_props_bindings with
1372-
| [] -> pstr_value ~loc:structure_item.pstr_loc rec_flag internal_bindings
1373-
| _ -> (
1374-
let loc = structure_item.pstr_loc in
1375-
(* Propagate non-react attributes from the original bindings to the
1376-
include struct. This ensures attributes like [@platform js] or
1377-
[@browser_only] are visible to subsequent ppxes (e.g. browser_ppx) so they can drop the entire include when needed. *)
1378-
let propagated_attrs =
1379-
List.concat_map value_bindings ~f:(fun vb ->
1380-
List.filter vb.pvb_attributes ~f:(fun attr -> not (is_react_attr attr)))
1381-
in
1382-
let include_stri =
1383-
[%stri
1384-
include struct
1385-
[%%i pstr_value ~loc:structure_item.pstr_loc Nonrecursive make_props_bindings]
1386-
[%%i pstr_value ~loc:structure_item.pstr_loc rec_flag internal_bindings]
1387-
[%%i pstr_value ~loc:structure_item.pstr_loc Nonrecursive public_bindings]
1388-
end]
1389-
in
1390-
match (propagated_attrs, include_stri.pstr_desc) with
1391-
| _ :: _, Pstr_include incl ->
1392-
{ include_stri with pstr_desc = Pstr_include { incl with pincl_attributes = propagated_attrs } }
1393-
| _ -> include_stri)
1390+
(* On native, keep make with labeled args (no wrapper) for backward
1391+
compatibility with .rei files, .ml call sites, and first-class modules.
1392+
Native JSX uses old-style labeled arg calls, so makeProps/wrapper aren't needed. *)
1393+
pstr_value ~loc:structure_item.pstr_loc rec_flag internal_bindings
13941394
with Error err -> pstr_eval ~loc:structure_item.pstr_loc err [])
13951395
| _ -> structure_item
13961396

tasks/plan-08-action-formdata-protocol-progressive-ench.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,9 @@ React's client-side `encodeFormAction` callback converts a server reference into
111111
- [ ] Replace the `useActionState` stub in `packages/react/src/React.ml:878` with a real implementation that works with the form state protocol.
112112
- [ ] This enables progressive enhancement for stateful form actions (showing optimistic updates, handling errors).
113113

114-
### Phase 6: Bound server references (optional, future)
114+
### Phase 6: Bound server references → moved to plan-12
115115

116-
- [ ] Support `$ACTION_REF_` for bound server references (server functions with pre-filled arguments via `.bind()`).
117-
- [ ] This requires changes to `Runtime.server_function` to carry a `bound` field and to the PPX to support partial application of server functions.
118-
- [ ] The `action_to_json` function in `ReactServerDOM.ml:198` currently always emits `"bound": null`; this would need to emit the bound arguments when present.
116+
Moved to `tasks/plan-12-bound-server-references.md`. Includes `$ACTION_REF_` support, `encode_bound` on `Runtime.server_function`, and PPX changes for partial application.
119117

120118
## Design
121119

File renamed without changes.

0 commit comments

Comments
 (0)