Skip to content

Commit 2dc0103

Browse files
authored
JSX preserve mode: fix "make is not a valid component name" (#7831)
* JSX preserve mode: fix "make is not a valid component name" * More selective fix for lam_pass_remove_alias * Selective fix for js_pass_flatten_and_mark_dead * Add tests * CHANGELOG
1 parent a09a3d7 commit 2dc0103

File tree

6 files changed

+97
-21
lines changed

6 files changed

+97
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- Show `Stdlib.TypedArray` completions for typed arrays. https://github.com/rescript-lang/rescript/pull/7827
2424
- Show `Stdlib.Null` and `Stdlib.Nullable` completions for `Stdlib.null<'a>` and `Stdlib.nullable<'a>` types, respectively. https://github.com/rescript-lang/rescript/pull/7826
2525
- Fix generation of interfaces for module types containing multiple type constraints. https://github.com/rescript-lang/rescript/pull/7825
26+
- JSX preserve mode: fix "make is not a valid component name". https://github.com/rescript-lang/rescript/pull/7831
2627

2728
#### :memo: Documentation
2829

compiler/core/js_pass_flatten_and_mark_dead.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@ let subst_map (substitution : J.expression Hash_ident.t) =
245245
turn a runtime crash into compile time crash : )
246246
*)
247247
match Ext_list.nth_opt ls (Int32.to_int i) with
248+
(* 7432: prevent optimization in JSX preserve mode *)
249+
| Some {expression_desc = J.Var (Id {name = "make"})}
250+
when !Js_config.jsx_preserve ->
251+
super.expression self x
248252
| Some
249253
({expression_desc = J.Var _ | Number _ | Str _ | Undefined _} as
250254
x) ->

compiler/core/lam_pass_remove_alias.ml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ let simplify_alias (meta : Lam_stats.t) (lam : Lam.t) : Lam.t =
4545
let rec simpl (lam : Lam.t) : Lam.t =
4646
match lam with
4747
| Lvar _ -> lam
48+
(* 7432: prevent optimization in JSX preserve mode *)
49+
| Lprim
50+
{
51+
primitive = Pjs_call {prim_name = "jsx" | "jsxs"} as primitive;
52+
args = (Lprim {primitive = Pfield (_, _)} as field_arg) :: rest;
53+
loc;
54+
}
55+
when !Js_config.jsx_preserve ->
56+
Lam.prim ~primitive ~args:(field_arg :: Ext_list.map rest simpl) loc
4857
| Lprim {primitive = Pfield (i, info) as primitive; args = [arg]; loc} -> (
4958
(* ATTENTION:
5059
Main use case, we should detect inline all immutable block .. *)

tests/tests/src/async_jsx.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ let Foo = {
2525

2626
function Async_jsx$Bar(props) {
2727
return <div>
28-
<Async_jsx$Foo />
28+
<Foo.make />
2929
</div>;
3030
}
3131

tests/tests/src/jsx_preserve_test.mjs

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Generated by ReScript, PLEASE EDIT WITH CARE
22

3+
import * as React from "react";
34
import * as JsxRuntime from "react/jsx-runtime";
45

56
function Jsx_preserve_test$Icon(props) {
@@ -20,7 +21,7 @@ let _multiple_element_children = <div>
2021
<h1>
2122
{"Hello, world!"}
2223
</h1>
23-
<Jsx_preserve_test$Icon />
24+
<Icon.make />
2425
</div>;
2526

2627
let _single_element_fragment = <>
@@ -141,7 +142,7 @@ let B = {
141142

142143
let _external_component_with_children = <QueryClientProvider>
143144
<strong />
144-
<Jsx_preserve_test$B />
145+
<B.make />
145146
</QueryClientProvider>;
146147

147148
function Jsx_preserve_test$MyWeirdComponent(props) {
@@ -155,7 +156,7 @@ let MyWeirdComponent = {
155156
make: Jsx_preserve_test$MyWeirdComponent
156157
};
157158

158-
let _escaped_jsx_prop = <Jsx_preserve_test$MyWeirdComponent
159+
let _escaped_jsx_prop = <MyWeirdComponent.make
159160
MyWeirdProp={"bar"}
160161
/>;
161162

@@ -197,7 +198,7 @@ let ComponentWithOptionalProps = {
197198
make: Jsx_preserve_test$ComponentWithOptionalProps
198199
};
199200

200-
let _optional_props = <Jsx_preserve_test$ComponentWithOptionalProps
201+
let _optional_props = <ComponentWithOptionalProps.make
201202
i={1}
202203
s={"test"}
203204
element={<div />}
@@ -208,11 +209,9 @@ let _props_with_hyphen = <label
208209
data-testid={"test"}
209210
/>;
210211

211-
let React = {};
212-
213-
let _fragment = <Fragment>
212+
let _fragment = <>
214213
{"Hello, world!"}
215-
</Fragment>;
214+
</>;
216215

217216
let _youtube_iframe = <iframe
218217
allow={"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"}
@@ -224,6 +223,47 @@ let _youtube_iframe = <iframe
224223
referrerPolicy={"strict-origin-when-cross-origin"}
225224
/>;
226225

226+
function make(_props) {
227+
return null;
228+
}
229+
230+
let X = {
231+
make: make
232+
};
233+
234+
<X.make />;
235+
236+
function Jsx_preserve_test$Y(props) {
237+
return null;
238+
}
239+
240+
let make$1 = React.memo(Jsx_preserve_test$Y);
241+
242+
let Y = {
243+
x: 42,
244+
make: make$1
245+
};
246+
247+
<Y.make />;
248+
249+
let context = React.createContext(0);
250+
251+
let make$2 = context.Provider;
252+
253+
let ContextProvider = {
254+
make: make$2
255+
};
256+
257+
function Jsx_preserve_test(props) {
258+
return <ContextProvider.make
259+
value={42}
260+
>
261+
{props.children}
262+
</ContextProvider.make>;
263+
}
264+
265+
let make$3 = Jsx_preserve_test;
266+
227267
export {
228268
Icon,
229269
_single_element_child,
@@ -250,8 +290,12 @@ export {
250290
ComponentWithOptionalProps,
251291
_optional_props,
252292
_props_with_hyphen,
253-
React,
254293
_fragment,
255294
_youtube_iframe,
295+
X,
296+
Y,
297+
context,
298+
ContextProvider,
299+
make$3 as make,
256300
}
257301
/* _single_element_child Not a pure module */

tests/tests/src/jsx_preserve_test.res

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -129,17 +129,6 @@ let _optional_props = <ComponentWithOptionalProps i=1 s="test" element={<div />}
129129

130130
let _props_with_hyphen = <label ariaLabel={"close sidebar"} dataTestId="test" />
131131

132-
module React = {
133-
type component<'props> = Jsx.component<'props>
134-
type element = Jsx.element
135-
136-
external jsx: (component<'props>, 'props) => element = "jsx"
137-
138-
type fragmentProps = {children?: element}
139-
140-
external jsxFragment: component<fragmentProps> = "Fragment"
141-
}
142-
143132
let _fragment = <> {Jsx.string("Hello, world!")} </>
144133

145134
let _youtube_iframe =
@@ -153,3 +142,32 @@ let _youtube_iframe =
153142
allowFullScreen={true}
154143
>
155144
</iframe>
145+
146+
module X = {
147+
type props = {}
148+
let make = (_props: props) => React.null
149+
}
150+
151+
let _ = <X />
152+
153+
module Y = {
154+
let x = 42
155+
156+
@react.component
157+
let make = () => React.null
158+
159+
let make = React.memo(make)
160+
}
161+
162+
let _ = <Y />
163+
164+
let context = React.createContext(0)
165+
166+
module ContextProvider = {
167+
let make = React.Context.provider(context)
168+
}
169+
170+
@react.component
171+
let make = (~children) => {
172+
<ContextProvider value=42> {children} </ContextProvider>
173+
}

0 commit comments

Comments
 (0)