Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support async component for React Server component in JSX v4 #6399

Merged
merged 10 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- Add builtin abstract types for File and Blob APIs. https://github.com/rescript-lang/rescript-compiler/pull/6383
- Untagged variants: Support `promise`, RegExes, Dates, File and Blob. https://github.com/rescript-lang/rescript-compiler/pull/6383
- Support aliased types as payloads to untagged variants. https://github.com/rescript-lang/rescript-compiler/pull/6394
- Support the async component for React Server Component in JSX V4. https://github.com/rescript-lang/rescript-compiler/pull/6399

#### :nail_care: Polish

Expand Down
10 changes: 2 additions & 8 deletions jscomp/frontend/ast_attributes.ml
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,8 @@ let is_inline : attr -> bool =

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

let is_await : attr -> bool =
fun ({txt}, _) -> txt = "await" || txt = "res.await"

let is_async : attr -> bool =
fun ({txt}, _) -> txt = "async" || txt = "res.async"

let has_await_payload (attrs : t) = Ext_list.find_first attrs is_await
let has_async_payload (attrs : t) = Ext_list.find_first attrs is_async
let has_await_payload (attrs : t) = Ext_list.find_first attrs Ast_await.is_await
let has_async_payload (attrs : t) = Ext_list.find_first attrs Ast_async.is_async

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

Expand Down
3 changes: 3 additions & 0 deletions jscomp/frontend/ast_async.ml → jscomp/ml/ast_async.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
let is_async : Parsetree.attribute -> bool =
fun ({txt}, _) -> txt = "async" || txt = "res.async"

let add_promise_type ?(loc = Location.none) ~async
(result : Parsetree.expression) =
if async then
Expand Down
3 changes: 3 additions & 0 deletions jscomp/frontend/ast_await.ml → jscomp/ml/ast_await.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
let is_await : Parsetree.attribute -> bool =
fun ({txt}, _) -> txt = "await" || txt = "res.await"

let create_await_expression (e : Parsetree.expression) =
let loc = e.pexp_loc in
let unsafe_await =
Expand Down
2 changes: 2 additions & 0 deletions jscomp/others/jsxPPXReactSupportC.res
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ let createElementWithKey = (~key=?, component, props) =>

let createElementVariadicWithKey = (~key=?, component, props, elements) =>
createElementVariadic(component, addKeyProp(~key?, props), elements)

external asyncComponent: promise<Jsx.element> => Jsx.element = "%identity"
2 changes: 2 additions & 0 deletions jscomp/others/jsxPPXReactSupportU.res
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,5 @@ let createElementWithKey = (~key=?, component, props) =>

let createElementVariadicWithKey = (~key=?, component, props, elements) =>
createElementVariadic(component, addKeyProp(~key?, props), elements)

external asyncComponent: promise<Jsx.element> => Jsx.element = "%identity"
12 changes: 12 additions & 0 deletions jscomp/syntax/src/react_jsx_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,15 @@ let removeArity binding =
| _ -> expr
in
{binding with pvb_expr = removeArityRecord binding.pvb_expr}

let async_component ~async expr =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should not move as it's specific to the ppx

if async then
let open Ast_helper in
Exp.apply
(Exp.ident
{
loc = Location.none;
txt = Ldot (Lident "JsxPPXReactSupport", "asyncComponent");
})
[(Nolabel, expr)]
else expr
8 changes: 8 additions & 0 deletions jscomp/syntax/src/reactjs_jsx_v4.ml
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,10 @@ let mapBinding ~config ~emptyLoc ~pstr_loc ~fileName ~recFlag binding =
let bindingWrapper, hasForwardRef, expression =
modifiedBinding ~bindingLoc ~bindingPatLoc ~fnName binding
in
let isAsync =
Ext_list.find_first binding.pvb_expr.pexp_attributes Ast_async.is_async
|> Option.is_some
in
(* do stuff here! *)
let namedArgList, newtypes, _typeConstraints =
recursivelyTransformNamedArgsForMake
Expand Down Expand Up @@ -942,6 +946,9 @@ let mapBinding ~config ~emptyLoc ~pstr_loc ~fileName ~recFlag binding =
(Pat.var @@ Location.mknoloc "props")
(Typ.constr (Location.mknoloc @@ Lident "props") [Typ.any ()])
in
let innerExpression =
React_jsx_common.async_component ~async:isAsync innerExpression
in
let fullExpression =
(* React component name should start with uppercase letter *)
(* let make = { let \"App" = props => make(props); \"App" } *)
Expand Down Expand Up @@ -1083,6 +1090,7 @@ let mapBinding ~config ~emptyLoc ~pstr_loc ~fileName ~recFlag binding =
| _ -> [Typ.any ()]))))
expression
in
let expression = Ast_async.add_async_attribute ~async:isAsync expression in
let expression =
(* Add new tupes (type a,b,c) to make's definition *)
newtypes
Expand Down
19 changes: 19 additions & 0 deletions jscomp/syntax/tests/ppx/react/asyncAwait.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
let f = a => Js.Promise.resolve(a + a)

module C0 = {
@react.component
let make = async (~a) => {
let a = await f(a)
<div> {React.int(a)} </div>
}
}

module C1 = {
@react.component
let make = async (~status) => {
switch status {
| #on => React.string("on")
| #off => React.string("off")
}
}
}
31 changes: 31 additions & 0 deletions jscomp/syntax/tests/ppx/react/expected/asyncAwait.res.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
let f = a => Js.Promise.resolve(a + a)

module C0 = {
type props<'a> = {a: 'a}

let make = async ({a, _}: props<_>) => {
let a = await f(a)
ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})})
}
let make = {
let \"AsyncAwait$C0" = (props: props<_>) => JsxPPXReactSupport.asyncComponent(make(props))

\"AsyncAwait$C0"
}
}

module C1 = {
type props<'status> = {status: 'status}

let make = async ({status, _}: props<_>) => {
switch status {
| #on => React.string("on")
| #off => React.string("off")
}
}
let make = {
let \"AsyncAwait$C1" = (props: props<_>) => JsxPPXReactSupport.asyncComponent(make(props))

\"AsyncAwait$C1"
}
}