From 23cb57d6fed91a6dddafe16ee7131d6ffe3b147f Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 24 Jul 2025 10:02:54 +0200 Subject: [PATCH 1/3] Add asyncComponent to jsx --- compiler/syntax/src/jsx_common.ml | 5 +-- runtime/Jsx.res | 2 + .../ppx/react/expected/asyncAwait.res.txt | 4 +- .../expected/sharedPropsWithProps.res.txt | 6 +-- tests/tests/src/async_jsx.mjs | 41 +++++++++++++++++++ tests/tests/src/async_jsx.res | 30 ++++++++++++++ 6 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 tests/tests/src/async_jsx.mjs create mode 100644 tests/tests/src/async_jsx.res diff --git a/compiler/syntax/src/jsx_common.ml b/compiler/syntax/src/jsx_common.ml index 6937bd14d0..db82704349 100644 --- a/compiler/syntax/src/jsx_common.ml +++ b/compiler/syntax/src/jsx_common.ml @@ -54,9 +54,6 @@ let async_component ~async expr = let open Ast_helper in Exp.apply (Exp.ident - { - loc = Location.none; - txt = Ldot (Lident "JsxPPXReactSupport", "asyncComponent"); - }) + {loc = Location.none; txt = Ldot (Lident "Jsx", "asyncComponent")}) [(Nolabel, expr)] else expr diff --git a/runtime/Jsx.res b/runtime/Jsx.res index 05442ad822..51b7a8139c 100644 --- a/runtime/Jsx.res +++ b/runtime/Jsx.res @@ -38,3 +38,5 @@ type component<'props> = componentLike<'props, element> /* this function exists to prepare for making `component` abstract */ external component: componentLike<'props, element> => component<'props> = "%identity" + +external asyncComponent: promise => element = "%identity" diff --git a/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt b/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt index 37df951e28..d5349389cc 100644 --- a/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt @@ -9,7 +9,7 @@ module C0 = { ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})}) } let make = { - let \"AsyncAwait$C0" = (props: props<_>) => JsxPPXReactSupport.asyncComponent(make(props)) + let \"AsyncAwait$C0" = (props: props<_>) => Jsx.asyncComponent(make(props)) \"AsyncAwait$C0" } @@ -26,7 +26,7 @@ module C1 = { } } let make = { - let \"AsyncAwait$C1" = (props: props<_>) => JsxPPXReactSupport.asyncComponent(make(props)) + let \"AsyncAwait$C1" = (props: props<_>) => Jsx.asyncComponent(make(props)) \"AsyncAwait$C1" } diff --git a/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt b/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt index b1f4205c36..d0a5c773e4 100644 --- a/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt @@ -45,8 +45,7 @@ module V4A5 = { ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})}) } let make = { - let \"SharedPropsWithProps$V4A5" = (props: props<_>) => - JsxPPXReactSupport.asyncComponent(make(props)) + let \"SharedPropsWithProps$V4A5" = (props: props<_>) => Jsx.asyncComponent(make(props)) \"SharedPropsWithProps$V4A5" } } @@ -60,8 +59,7 @@ module V4A6 = { } } let make = { - let \"SharedPropsWithProps$V4A6" = (props: props<_>) => - JsxPPXReactSupport.asyncComponent(make(props)) + let \"SharedPropsWithProps$V4A6" = (props: props<_>) => Jsx.asyncComponent(make(props)) \"SharedPropsWithProps$V4A6" } } diff --git a/tests/tests/src/async_jsx.mjs b/tests/tests/src/async_jsx.mjs new file mode 100644 index 0000000000..795c65b52c --- /dev/null +++ b/tests/tests/src/async_jsx.mjs @@ -0,0 +1,41 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as JsxRuntime from "react/jsx-runtime"; + +function getNow() { + return new Promise((res, param) => { + setTimeout(() => res(new Date()), 1000); + }); +} + +async function make(param) { + let now = await getNow(); + return
+

+ {now.toLocaleString()} +

+
; +} + +let Async_jsx$Foo = make; + +let Foo = { + make: Async_jsx$Foo +}; + +function Async_jsx$Bar(props) { + return
+ +
; +} + +let Bar = { + make: Async_jsx$Bar +}; + +export { + getNow, + Foo, + Bar, +} +/* react/jsx-runtime Not a pure module */ diff --git a/tests/tests/src/async_jsx.res b/tests/tests/src/async_jsx.res new file mode 100644 index 0000000000..277e357164 --- /dev/null +++ b/tests/tests/src/async_jsx.res @@ -0,0 +1,30 @@ +@@config({ + flags: ["-bs-jsx", "4", "-bs-jsx-preserve"], +}) + +let getNow = () => { + Promise.make((res, _) => { + setTimeout(() => { + res(Date.make()) + }, 1000)->ignore + }) +} + +module Foo = { + @react.component + let make = async () => { + let now = await getNow() +
+

{React.string(now->Date.toLocaleString)}

+
+ } +} + +module Bar = { + @react.component + let make = () => { +
+ +
+ } +} From 69e9b953927e1e8de38aa6aa188382da941b506f Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 24 Jul 2025 10:04:37 +0200 Subject: [PATCH 2/3] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3e87162f2..467ff9b707 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ - Fix `--create-sourcedirs` generation with for a single project. https://github.com/rescript-lang/rescript/pull/7671 - Fix rewatch not recompiling on changes under windows. https://github.com/rescript-lang/rescript/pull/7690 - Fix locations of regex literals. https://github.com/rescript-lang/rescript/pull/7683 +- Fix async React component compilation. https://github.com/rescript-lang/rescript/pull/7704 # 12.0.0-beta.2 From 6f2880e4b02031d00119adde9c1cc91ec6d67f5d Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 24 Jul 2025 11:44:46 +0200 Subject: [PATCH 3/3] Rename to promise --- compiler/syntax/src/jsx_common.ml | 3 +-- runtime/Jsx.res | 4 +--- tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt | 4 ++-- .../data/ppx/react/expected/sharedPropsWithProps.res.txt | 4 ++-- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/compiler/syntax/src/jsx_common.ml b/compiler/syntax/src/jsx_common.ml index db82704349..a48cf11bc9 100644 --- a/compiler/syntax/src/jsx_common.ml +++ b/compiler/syntax/src/jsx_common.ml @@ -53,7 +53,6 @@ let async_component ~async expr = if async then let open Ast_helper in Exp.apply - (Exp.ident - {loc = Location.none; txt = Ldot (Lident "Jsx", "asyncComponent")}) + (Exp.ident {loc = Location.none; txt = Ldot (Lident "Jsx", "promise")}) [(Nolabel, expr)] else expr diff --git a/runtime/Jsx.res b/runtime/Jsx.res index 51b7a8139c..7cc7ade95c 100644 --- a/runtime/Jsx.res +++ b/runtime/Jsx.res @@ -30,13 +30,11 @@ type element external float: float => element = "%identity" external int: int => element = "%identity" external string: string => element = "%identity" - external array: array => element = "%identity" +external promise: promise => element = "%identity" type componentLike<'props, 'return> = 'props => 'return type component<'props> = componentLike<'props, element> /* this function exists to prepare for making `component` abstract */ external component: componentLike<'props, element> => component<'props> = "%identity" - -external asyncComponent: promise => element = "%identity" diff --git a/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt b/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt index d5349389cc..66441d1f69 100644 --- a/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt @@ -9,7 +9,7 @@ module C0 = { ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})}) } let make = { - let \"AsyncAwait$C0" = (props: props<_>) => Jsx.asyncComponent(make(props)) + let \"AsyncAwait$C0" = (props: props<_>) => Jsx.promise(make(props)) \"AsyncAwait$C0" } @@ -26,7 +26,7 @@ module C1 = { } } let make = { - let \"AsyncAwait$C1" = (props: props<_>) => Jsx.asyncComponent(make(props)) + let \"AsyncAwait$C1" = (props: props<_>) => Jsx.promise(make(props)) \"AsyncAwait$C1" } diff --git a/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt b/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt index d0a5c773e4..72072f8985 100644 --- a/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt @@ -45,7 +45,7 @@ module V4A5 = { ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})}) } let make = { - let \"SharedPropsWithProps$V4A5" = (props: props<_>) => Jsx.asyncComponent(make(props)) + let \"SharedPropsWithProps$V4A5" = (props: props<_>) => Jsx.promise(make(props)) \"SharedPropsWithProps$V4A5" } } @@ -59,7 +59,7 @@ module V4A6 = { } } let make = { - let \"SharedPropsWithProps$V4A6" = (props: props<_>) => Jsx.asyncComponent(make(props)) + let \"SharedPropsWithProps$V4A6" = (props: props<_>) => Jsx.promise(make(props)) \"SharedPropsWithProps$V4A6" } }