Skip to content

Commit b8c4157

Browse files
authored
Support async component for React Server component in JSX v4 (#6399)
* add tests * support async in JSX ppx * update changelog * convert async component type * eof * use asyncComponent func * fix polymorphic type inference * remove duplicated function * relocate ast_await into ml * remove duplicated is_async function
1 parent 4b06ba9 commit b8c4157

10 files changed

+83
-8
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
- Untagged variants: Support `promise`, RegExes, Dates, File and Blob. https://github.com/rescript-lang/rescript-compiler/pull/6383
2626
- Untagged variants: Support `bool`. https://github.com/rescript-lang/rescript-compiler/pull/6368
2727
- Support aliased types as payloads to untagged variants. https://github.com/rescript-lang/rescript-compiler/pull/6394
28+
- Support the async component for React Server Component in JSX V4. https://github.com/rescript-lang/rescript-compiler/pull/6399
2829

2930
#### :boom: Breaking Change
3031
- Add smart printer for pipe-chains. https://github.com/rescript-lang/rescript-compiler/pull/6411 (the formatter will reformat existing code in certain cases)

jscomp/frontend/ast_attributes.ml

+2-8
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,8 @@ let is_inline : attr -> bool =
166166

167167
let has_inline_payload (attrs : t) = Ext_list.find_first attrs is_inline
168168

169-
let is_await : attr -> bool =
170-
fun ({txt}, _) -> txt = "await" || txt = "res.await"
171-
172-
let is_async : attr -> bool =
173-
fun ({txt}, _) -> txt = "async" || txt = "res.async"
174-
175-
let has_await_payload (attrs : t) = Ext_list.find_first attrs is_await
176-
let has_async_payload (attrs : t) = Ext_list.find_first attrs is_async
169+
let has_await_payload (attrs : t) = Ext_list.find_first attrs Ast_await.is_await
170+
let has_async_payload (attrs : t) = Ext_list.find_first attrs Ast_async.is_async
177171

178172
type derive_attr = {bs_deriving: Ast_payload.action list option} [@@unboxed]
179173

jscomp/frontend/ast_async.ml jscomp/ml/ast_async.ml

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
let is_async : Parsetree.attribute -> bool =
2+
fun ({txt}, _) -> txt = "async" || txt = "res.async"
3+
14
let add_promise_type ?(loc = Location.none) ~async
25
(result : Parsetree.expression) =
36
if async then

jscomp/frontend/ast_await.ml jscomp/ml/ast_await.ml

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
let is_await : Parsetree.attribute -> bool =
2+
fun ({txt}, _) -> txt = "await" || txt = "res.await"
3+
14
let create_await_expression (e : Parsetree.expression) =
25
let loc = e.pexp_loc in
36
let unsafe_await =

jscomp/others/jsxPPXReactSupportC.res

+2
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,5 @@ let createElementWithKey = (~key=?, component, props) =>
4848

4949
let createElementVariadicWithKey = (~key=?, component, props, elements) =>
5050
createElementVariadic(component, addKeyProp(~key?, props), elements)
51+
52+
external asyncComponent: promise<Jsx.element> => Jsx.element = "%identity"

jscomp/others/jsxPPXReactSupportU.res

+2
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,5 @@ let createElementWithKey = (~key=?, component, props) =>
5050

5151
let createElementVariadicWithKey = (~key=?, component, props, elements) =>
5252
createElementVariadic(component, addKeyProp(~key?, props), elements)
53+
54+
external asyncComponent: promise<Jsx.element> => Jsx.element = "%identity"

jscomp/syntax/src/react_jsx_common.ml

+12
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,15 @@ let removeArity binding =
6363
| _ -> expr
6464
in
6565
{binding with pvb_expr = removeArityRecord binding.pvb_expr}
66+
67+
let async_component ~async expr =
68+
if async then
69+
let open Ast_helper in
70+
Exp.apply
71+
(Exp.ident
72+
{
73+
loc = Location.none;
74+
txt = Ldot (Lident "JsxPPXReactSupport", "asyncComponent");
75+
})
76+
[(Nolabel, expr)]
77+
else expr

jscomp/syntax/src/reactjs_jsx_v4.ml

+8
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,10 @@ let mapBinding ~config ~emptyLoc ~pstr_loc ~fileName ~recFlag binding =
909909
let bindingWrapper, hasForwardRef, expression =
910910
modifiedBinding ~bindingLoc ~bindingPatLoc ~fnName binding
911911
in
912+
let isAsync =
913+
Ext_list.find_first binding.pvb_expr.pexp_attributes Ast_async.is_async
914+
|> Option.is_some
915+
in
912916
(* do stuff here! *)
913917
let namedArgList, newtypes, _typeConstraints =
914918
recursivelyTransformNamedArgsForMake
@@ -942,6 +946,9 @@ let mapBinding ~config ~emptyLoc ~pstr_loc ~fileName ~recFlag binding =
942946
(Pat.var @@ Location.mknoloc "props")
943947
(Typ.constr (Location.mknoloc @@ Lident "props") [Typ.any ()])
944948
in
949+
let innerExpression =
950+
React_jsx_common.async_component ~async:isAsync innerExpression
951+
in
945952
let fullExpression =
946953
(* React component name should start with uppercase letter *)
947954
(* let make = { let \"App" = props => make(props); \"App" } *)
@@ -1083,6 +1090,7 @@ let mapBinding ~config ~emptyLoc ~pstr_loc ~fileName ~recFlag binding =
10831090
| _ -> [Typ.any ()]))))
10841091
expression
10851092
in
1093+
let expression = Ast_async.add_async_attribute ~async:isAsync expression in
10861094
let expression =
10871095
(* Add new tupes (type a,b,c) to make's definition *)
10881096
newtypes
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
let f = a => Js.Promise.resolve(a + a)
2+
3+
module C0 = {
4+
@react.component
5+
let make = async (~a) => {
6+
let a = await f(a)
7+
<div> {React.int(a)} </div>
8+
}
9+
}
10+
11+
module C1 = {
12+
@react.component
13+
let make = async (~status) => {
14+
switch status {
15+
| #on => React.string("on")
16+
| #off => React.string("off")
17+
}
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
let f = a => Js.Promise.resolve(a + a)
2+
3+
module C0 = {
4+
type props<'a> = {a: 'a}
5+
6+
let make = async ({a, _}: props<_>) => {
7+
let a = await f(a)
8+
ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})})
9+
}
10+
let make = {
11+
let \"AsyncAwait$C0" = (props: props<_>) => JsxPPXReactSupport.asyncComponent(make(props))
12+
13+
\"AsyncAwait$C0"
14+
}
15+
}
16+
17+
module C1 = {
18+
type props<'status> = {status: 'status}
19+
20+
let make = async ({status, _}: props<_>) => {
21+
switch status {
22+
| #on => React.string("on")
23+
| #off => React.string("off")
24+
}
25+
}
26+
let make = {
27+
let \"AsyncAwait$C1" = (props: props<_>) => JsxPPXReactSupport.asyncComponent(make(props))
28+
29+
\"AsyncAwait$C1"
30+
}
31+
}

0 commit comments

Comments
 (0)