diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml
index e2d4a51f46..0970e5418b 100644
--- a/analysis/src/CompletionFrontEnd.ml
+++ b/analysis/src/CompletionFrontEnd.ml
@@ -267,33 +267,35 @@ let rec exprToContextPathInner ~(inJsxContext : bool) (e : Parsetree.expression)
};
args =
[(_, lhs); (_, {pexp_desc = Pexp_apply {funct = d; args; partial}})];
+ transformed_jsx;
} ->
(* Transform away pipe with apply call *)
exprToContextPath ~inJsxContext
{
pexp_desc =
- Pexp_apply {funct = d; args = (Nolabel, lhs) :: args; partial};
+ Pexp_apply
+ {funct = d; args = (Nolabel, lhs) :: args; partial; transformed_jsx};
pexp_loc;
pexp_attributes;
}
| Pexp_apply
- {
- funct = {pexp_desc = Pexp_ident {txt = Lident "->"}};
- args =
- [
- (_, lhs); (_, {pexp_desc = Pexp_ident id; pexp_loc; pexp_attributes});
- ];
- partial;
- } ->
+ ({
+ funct = {pexp_desc = Pexp_ident {txt = Lident "->"}};
+ args =
+ [
+ (_, lhs);
+ (_, {pexp_desc = Pexp_ident id; pexp_loc; pexp_attributes});
+ ];
+ } as app) ->
(* Transform away pipe with identifier *)
exprToContextPath ~inJsxContext
{
pexp_desc =
Pexp_apply
{
+ app with
funct = {pexp_desc = Pexp_ident id; pexp_loc; pexp_attributes};
args = [(Nolabel, lhs)];
- partial;
};
pexp_loc;
pexp_attributes;
diff --git a/compiler/bsc/rescript_compiler_main.ml b/compiler/bsc/rescript_compiler_main.ml
index a5950f198e..f9113c40b6 100644
--- a/compiler/bsc/rescript_compiler_main.ml
+++ b/compiler/bsc/rescript_compiler_main.ml
@@ -255,6 +255,7 @@ let buckle_script_flags : (string * Bsc_args.spec * string) array =
( "-bs-jsx-mode",
string_call ignore,
"*internal* Set jsx mode, this is no longer used and is a no-op." );
+ ("-bs-jsx-preserve", set Js_config.jsx_preserve, "*internal* Preserve jsx");
( "-bs-package-output",
string_call Js_packages_state.update_npm_package_path,
"*internal* Set npm-output-path: [opt_module]:path, for example: \
diff --git a/compiler/common/js_config.ml b/compiler/common/js_config.ml
index bf062d0f21..8c92235f04 100644
--- a/compiler/common/js_config.ml
+++ b/compiler/common/js_config.ml
@@ -50,6 +50,7 @@ let force_cmi = ref false
let force_cmj = ref false
let jsx_version = ref None
let jsx_module = ref React
+let jsx_preserve = ref false
let js_stdout = ref true
let all_module_aliases = ref false
let no_stdlib = ref false
diff --git a/compiler/common/js_config.mli b/compiler/common/js_config.mli
index ec6e1829da..253b567a34 100644
--- a/compiler/common/js_config.mli
+++ b/compiler/common/js_config.mli
@@ -80,6 +80,8 @@ val jsx_version : jsx_version option ref
val jsx_module : jsx_module ref
+val jsx_preserve : bool ref
+
val js_stdout : bool ref
val all_module_aliases : bool ref
diff --git a/compiler/core/js_call_info.ml b/compiler/core/js_call_info.ml
index 547f03c7f8..c58aad901f 100644
--- a/compiler/core/js_call_info.ml
+++ b/compiler/core/js_call_info.ml
@@ -33,10 +33,15 @@ type call_info =
{[ fun x y -> (f x y) === f ]} when [f] is an atom
*)
-type t = {call_info: call_info; arity: arity}
+type t = {call_info: call_info; arity: arity; call_transformed_jsx: bool}
-let dummy = {arity = NA; call_info = Call_na}
+let dummy = {arity = NA; call_info = Call_na; call_transformed_jsx = false}
-let builtin_runtime_call = {arity = Full; call_info = Call_builtin_runtime}
+let builtin_runtime_call =
+ {arity = Full; call_info = Call_builtin_runtime; call_transformed_jsx = false}
-let ml_full_call = {arity = Full; call_info = Call_ml}
+let ml_full_call =
+ {arity = Full; call_info = Call_ml; call_transformed_jsx = false}
+
+let na_full_call transformed_jsx =
+ {arity = Full; call_info = Call_na; call_transformed_jsx = transformed_jsx}
diff --git a/compiler/core/js_call_info.mli b/compiler/core/js_call_info.mli
index 0381c0cd2b..ff0d3ad875 100644
--- a/compiler/core/js_call_info.mli
+++ b/compiler/core/js_call_info.mli
@@ -35,10 +35,12 @@ type call_info =
{[ fun x y -> f x y === f ]} when [f] is an atom
*)
-type t = {call_info: call_info; arity: arity}
+type t = {call_info: call_info; arity: arity; call_transformed_jsx: bool}
val dummy : t
val builtin_runtime_call : t
val ml_full_call : t
+
+val na_full_call : bool -> t
diff --git a/compiler/core/js_dump.ml b/compiler/core/js_dump.ml
index bc174e7dd5..05c300d0a3 100644
--- a/compiler/core/js_dump.ml
+++ b/compiler/core/js_dump.ml
@@ -515,7 +515,7 @@ and expression_desc cxt ~(level : int) f x : cxt =
(* TODO: dump for comments *)
pp_function ?directive ~is_method ~return_unit ~async
~fn_state:default_fn_exp_state cxt f params body env
- (* TODO:
+ (* TODO:
when [e] is [Js_raw_code] with arity
print it in a more precise way
It seems the optimizer already did work to make sure
@@ -524,6 +524,116 @@ and expression_desc cxt ~(level : int) f x : cxt =
when Ext_list.length_equal el i
]}
*)
+ (* When -bs-preserve-jsx is enabled, we marked each transformed application node throughout the compilation.
+ Here we print the transformed application node into a JSX syntax.
+ The JSX is slightly different from what a user would write,
+ but it is still valid JSX and is usable by tools like ESBuild.
+ *)
+ | Call
+ ( ({
+ expression_desc =
+ J.Var
+ (J.Qualified
+ ( _,
+ Some fnName
+ (* We care about the function name when it is jsxs,
+ If this is the case, we need to unpack an array later on *)
+ ));
+ } as e),
+ el,
+ {call_transformed_jsx = true} )
+ when !Js_config.jsx_preserve -> (
+ (* We match a JsxRuntime.jsx call *)
+ match el with
+ | [
+ tag;
+ {
+ expression_desc =
+ (* This is the props javascript object *)
+ Caml_block (el, _mutable_flag, _, Lambda.Blk_record {fields});
+ };
+ ] ->
+ (* We extract the props from the javascript object *)
+ let fields =
+ Ext_list.array_list_filter_map fields el (fun (f, opt) x ->
+ match x.expression_desc with
+ | Undefined _ when opt -> None
+ | _ -> Some (f, x))
+ in
+ print_jsx cxt ~level f fnName tag fields
+ | [
+ tag;
+ {
+ expression_desc =
+ Caml_block (el, _mutable_flag, _, Lambda.Blk_record {fields});
+ };
+ key;
+ ] ->
+ (* When a component has a key the matching runtime function call will have a third argument being the key *)
+ let fields =
+ Ext_list.array_list_filter_map fields el (fun (f, opt) x ->
+ match x.expression_desc with
+ | Undefined _ when opt -> None
+ | _ -> Some (f, x))
+ in
+ print_jsx cxt ~level ~key f fnName tag fields
+ | [tag; ({expression_desc = J.Seq _} as props)] ->
+ (* In the case of prop spreading, the expression will look like:
+ (props.a = "Hello, world!", props)
+ which is equivalent to
+
+
+ We need to extract the props and the spread object.
+ *)
+ let fields, spread_props =
+ let rec visit acc e =
+ match e.J.expression_desc with
+ | J.Seq
+ ( {
+ J.expression_desc =
+ J.Bin
+ ( Js_op.Eq,
+ {J.expression_desc = J.Static_index (_, name, _)},
+ value );
+ },
+ rest ) ->
+ visit ((name, value) :: acc) rest
+ | _ -> (List.rev acc, e)
+ in
+ visit [] props
+ in
+ print_jsx cxt ~level ~spread_props f fnName tag fields
+ | [tag; ({expression_desc = J.Seq _} as props); key] ->
+ (* In the case of props + prop spreading and key argument *)
+ let fields, spread_props =
+ let rec visit acc e =
+ match e.J.expression_desc with
+ | J.Seq
+ ( {
+ J.expression_desc =
+ J.Bin
+ ( Js_op.Eq,
+ {J.expression_desc = J.Static_index (_, name, _)},
+ value );
+ },
+ rest ) ->
+ visit ((name, value) :: acc) rest
+ | _ -> (List.rev acc, e)
+ in
+ visit [] props
+ in
+ print_jsx cxt ~level ~spread_props ~key f fnName tag fields
+ | [tag; ({expression_desc = J.Var _} as spread_props)] ->
+ (* All the props are spread *)
+ print_jsx cxt ~level ~spread_props f fnName tag []
+ | _ ->
+ (* This should not happen, we fallback to the general case *)
+ expression_desc cxt ~level f
+ (Call
+ ( e,
+ el,
+ {call_transformed_jsx = false; arity = Full; call_info = Call_ml}
+ )))
| Call (e, el, info) ->
P.cond_paren_group f (level > 15) (fun _ ->
P.group f 0 (fun _ ->
@@ -956,6 +1066,103 @@ and expression_desc cxt ~(level : int) f x : cxt =
P.string f "...";
expression ~level:13 cxt f e)
+and print_jsx cxt ?(spread_props : J.expression option)
+ ?(key : J.expression option) ~(level : int) f (fnName : string)
+ (tag : J.expression) (fields : (string * J.expression) list) : cxt =
+ let print_tag cxt =
+ match tag.expression_desc with
+ (* "div" or any other primitive tag *)
+ | J.Str {txt} ->
+ P.string f txt;
+ cxt
+ (* fragment *)
+ | J.Var (J.Qualified ({id = {name = "JsxRuntime"}}, Some "Fragment")) -> cxt
+ (* A user defined component or external component *)
+ | _ -> expression ~level cxt f tag
+ in
+ let children_opt =
+ List.find_map
+ (fun (n, e) ->
+ if n = "children" then
+ if fnName = "jsxs" then
+ match e.J.expression_desc with
+ | J.Array (xs, _)
+ | J.Optional_block ({expression_desc = J.Array (xs, _)}, _) ->
+ Some xs
+ | _ -> Some [e]
+ else Some [e]
+ else None)
+ fields
+ in
+ let print_props cxt =
+ (* If a key is present, should be printed before the spread props,
+ This is to ensure tools like ESBuild use the automatic JSX runtime *)
+ let cxt =
+ match key with
+ | None -> cxt
+ | Some key ->
+ P.string f " key={";
+ let cxt = expression ~level:0 cxt f key in
+ P.string f "} ";
+ cxt
+ in
+ let props = List.filter (fun (n, _) -> n <> "children") fields in
+ let cxt =
+ match spread_props with
+ | None -> cxt
+ | Some spread ->
+ P.string f " {...";
+ let cxt = expression ~level:0 cxt f spread in
+ P.string f "} ";
+ cxt
+ in
+ if List.length props = 0 then cxt
+ else
+ (List.fold_left (fun acc (n, x) ->
+ P.space f;
+ P.string f n;
+ P.string f "=";
+ P.string f "{";
+ let next = expression ~level:0 acc f x in
+ P.string f "}";
+ next))
+ cxt props
+ in
+ match children_opt with
+ | None ->
+ P.string f "<";
+ let cxt = cxt |> print_tag |> print_props in
+ P.string f "/>";
+ cxt
+ | Some children ->
+ let child_is_jsx child =
+ match child.J.expression_desc with
+ | J.Call (_, _, {call_transformed_jsx = is_jsx}) -> is_jsx
+ | _ -> false
+ in
+
+ P.string f "<";
+ let cxt = cxt |> print_tag |> print_props in
+
+ P.string f ">";
+ if List.length children > 0 then P.newline f;
+
+ let cxt =
+ List.fold_left
+ (fun acc e ->
+ if not (child_is_jsx e) then P.string f "{";
+ let next = expression ~level acc f e in
+ if not (child_is_jsx e) then P.string f "}";
+ P.newline f;
+ next)
+ cxt children
+ in
+
+ P.string f "";
+ let cxt = print_tag cxt in
+ P.string f ">";
+ cxt
+
and property_name_and_value_list cxt f (l : J.property_map) =
iter_lst cxt f l
(fun cxt f (pn, e) ->
diff --git a/compiler/core/lam.ml b/compiler/core/lam.ml
index 77f991e181..1c20bb2e8e 100644
--- a/compiler/core/lam.ml
+++ b/compiler/core/lam.ml
@@ -81,7 +81,12 @@ module Types = struct
*)
and prim_info = {primitive: Lam_primitive.t; args: t list; loc: Location.t}
- and apply = {ap_func: t; ap_args: t list; ap_info: ap_info}
+ and apply = {
+ ap_func: t;
+ ap_args: t list;
+ ap_info: ap_info;
+ ap_transformed_jsx: bool;
+ }
and t =
| Lvar of ident
@@ -121,7 +126,12 @@ module X = struct
loc: Location.t;
}
- and apply = Types.apply = {ap_func: t; ap_args: t list; ap_info: ap_info}
+ and apply = Types.apply = {
+ ap_func: t;
+ ap_args: t list;
+ ap_info: ap_info;
+ ap_transformed_jsx: bool;
+ }
and lfunction = Types.lfunction = {
arity: int;
@@ -159,10 +169,10 @@ include Types
let inner_map (l : t) (f : t -> X.t) : X.t =
match l with
| Lvar (_ : ident) | Lconst (_ : Lam_constant.t) -> ((* Obj.magic *) l : X.t)
- | Lapply {ap_func; ap_args; ap_info} ->
+ | Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx} ->
let ap_func = f ap_func in
let ap_args = Ext_list.map ap_args f in
- Lapply {ap_func; ap_args; ap_info}
+ Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx}
| Lfunction {body; arity; params; attr} ->
let body = f body in
Lfunction {body; arity; params; attr}
@@ -279,7 +289,7 @@ let rec is_eta_conversion_exn params inner_args outer_args : t list =
| _, _, _ -> raise_notrace Not_simple_form
(** FIXME: more robust inlining check later, we should inline it before we add stub code*)
-let rec apply fn args (ap_info : ap_info) : t =
+let rec apply ?(ap_transformed_jsx = false) fn args (ap_info : ap_info) : t =
match fn with
| Lfunction
{
@@ -300,7 +310,7 @@ let rec apply fn args (ap_info : ap_info) : t =
Lprim
{primitive = wrap; args = [Lprim {primitive_call with args; loc}]; loc}
| exception Not_simple_form ->
- Lapply {ap_func = fn; ap_args = args; ap_info})
+ Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx})
| Lfunction
{
params;
@@ -308,7 +318,8 @@ let rec apply fn args (ap_info : ap_info) : t =
} -> (
match is_eta_conversion_exn params inner_args args with
| args -> Lprim {primitive_call with args; loc = ap_info.ap_loc}
- | exception _ -> Lapply {ap_func = fn; ap_args = args; ap_info})
+ | exception _ ->
+ Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx})
| Lfunction
{
params;
@@ -321,17 +332,17 @@ let rec apply fn args (ap_info : ap_info) : t =
| args ->
Lsequence (Lprim {primitive_call with args; loc = ap_info.ap_loc}, const)
| exception _ ->
- Lapply {ap_func = fn; ap_args = args; ap_info}
+ Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx}
(* | Lfunction {params;body} when Ext_list.same_length params args ->
Ext_list.fold_right2 (fun p arg acc ->
Llet(Strict,p,arg,acc)
) params args body *)
(* TODO: more rigirous analysis on [let_kind] *))
| Llet (kind, id, e, (Lfunction _ as fn)) ->
- Llet (kind, id, e, apply fn args ap_info)
+ Llet (kind, id, e, apply fn args ap_info ~ap_transformed_jsx)
(* | Llet (kind0, id0, e0, Llet (kind,id, e, (Lfunction _ as fn))) ->
Llet(kind0,id0,e0,Llet (kind, id, e, apply fn args loc status)) *)
- | _ -> Lapply {ap_func = fn; ap_args = args; ap_info}
+ | _ -> Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx}
let rec eq_approx (l1 : t) (l2 : t) =
match l1 with
@@ -712,10 +723,12 @@ let result_wrap loc (result_type : External_ffi_types.return_wrapper) result =
prim ~primitive:Pundefined_to_opt ~args:[result] loc
| Return_unset | Return_identity -> result
-let handle_bs_non_obj_ffi (arg_types : External_arg_spec.params)
+let handle_bs_non_obj_ffi ?(transformed_jsx = false)
+ (arg_types : External_arg_spec.params)
(result_type : External_ffi_types.return_wrapper) ffi args loc prim_name
~dynamic_import =
result_wrap loc result_type
(prim
- ~primitive:(Pjs_call {prim_name; arg_types; ffi; dynamic_import})
+ ~primitive:
+ (Pjs_call {prim_name; arg_types; ffi; dynamic_import; transformed_jsx})
~args loc)
diff --git a/compiler/core/lam.mli b/compiler/core/lam.mli
index 66858ac2a4..560d247669 100644
--- a/compiler/core/lam.mli
+++ b/compiler/core/lam.mli
@@ -41,7 +41,12 @@ type lambda_switch = {
sw_names: Ast_untagged_variants.switch_names option;
}
-and apply = private {ap_func: t; ap_args: t list; ap_info: ap_info}
+and apply = private {
+ ap_func: t;
+ ap_args: t list;
+ ap_info: ap_info;
+ ap_transformed_jsx: bool;
+}
and lfunction = {
arity: int;
@@ -85,6 +90,7 @@ and t = private
val inner_map : t -> (t -> t) -> t
val handle_bs_non_obj_ffi :
+ ?transformed_jsx:bool ->
External_arg_spec.params ->
External_ffi_types.return_wrapper ->
External_ffi_types.external_spec ->
@@ -103,7 +109,7 @@ val global_module : ?dynamic_import:bool -> ident -> t
val const : Lam_constant.t -> t
-val apply : t -> t list -> ap_info -> t
+val apply : ?ap_transformed_jsx:bool -> t -> t list -> ap_info -> t
val function_ :
attr:Lambda.function_attribute ->
diff --git a/compiler/core/lam_bounded_vars.ml b/compiler/core/lam_bounded_vars.ml
index 15ee9cff97..e038e56798 100644
--- a/compiler/core/lam_bounded_vars.ml
+++ b/compiler/core/lam_bounded_vars.ml
@@ -108,10 +108,10 @@ let rewrite (map : _ Hash_ident.t) (lam : Lam.t) : Lam.t =
(* here it makes sure that global vars are not rebound *)
Lam.prim ~primitive ~args:(Ext_list.map args aux) loc
| Lglobal_module _ -> lam
- | Lapply {ap_func; ap_args; ap_info} ->
+ | Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx} ->
let fn = aux ap_func in
let args = Ext_list.map ap_args aux in
- Lam.apply fn args ap_info
+ Lam.apply ~ap_transformed_jsx fn args ap_info
| Lswitch
( l,
{
diff --git a/compiler/core/lam_compile.ml b/compiler/core/lam_compile.ml
index ea865bbe6f..159b9d9012 100644
--- a/compiler/core/lam_compile.ml
+++ b/compiler/core/lam_compile.ml
@@ -31,12 +31,13 @@ let args_either_function_or_const (args : Lam.t list) =
| Lfunction _ | Lconst _ -> true
| _ -> false)
-let call_info_of_ap_status (ap_status : Lam.apply_status) : Js_call_info.t =
+let call_info_of_ap_status call_transformed_jsx (ap_status : Lam.apply_status) :
+ Js_call_info.t =
(* XXX *)
match ap_status with
- | App_infer_full -> {arity = Full; call_info = Call_ml}
- | App_uncurry -> {arity = Full; call_info = Call_na}
- | App_na -> {arity = NA; call_info = Call_ml}
+ | App_infer_full -> {arity = Full; call_info = Call_ml; call_transformed_jsx}
+ | App_uncurry -> {arity = Full; call_info = Call_na; call_transformed_jsx}
+ | App_na -> {arity = NA; call_info = Call_ml; call_transformed_jsx}
let rec apply_with_arity_aux (fn : J.expression) (arity : int list)
(args : E.t list) (len : int) : E.t =
@@ -49,7 +50,7 @@ let rec apply_with_arity_aux (fn : J.expression) (arity : int list)
if len >= x then
let first_part, continue = Ext_list.split_at args x in
apply_with_arity_aux
- (E.call ~info:{arity = Full; call_info = Call_ml} fn first_part)
+ (E.call ~info:Js_call_info.ml_full_call fn first_part)
rest continue (len - x)
else if
(* GPR #1423 *)
@@ -62,9 +63,7 @@ let rec apply_with_arity_aux (fn : J.expression) (arity : int list)
~async:false ~one_unit_arg:false
[
S.return_stmt
- (E.call
- ~info:{arity = Full; call_info = Call_ml}
- fn
+ (E.call ~info:Js_call_info.ml_full_call fn
(Ext_list.append args @@ Ext_list.map params E.var));
]
else E.call ~info:Js_call_info.dummy fn args
@@ -306,7 +305,9 @@ let compile output_prefix =
let expression =
match appinfo.ap_info.ap_status with
| (App_infer_full | App_uncurry) as ap_status ->
- E.call ~info:(call_info_of_ap_status ap_status) fn args
+ E.call
+ ~info:(call_info_of_ap_status appinfo.ap_transformed_jsx ap_status)
+ fn args
| App_na -> (
match ident_info.arity with
| Submodule _ | Single Arity_na ->
@@ -1439,6 +1440,7 @@ let compile output_prefix =
ap_func =
Lapply {ap_func; ap_args; ap_info = {ap_status = App_na; ap_inlined}};
ap_info = {ap_status = App_na} as outer_ap_info;
+ ap_transformed_jsx;
} ->
(* After inlining, we can generate such code, see {!Ari_regress_test}*)
let ap_info =
@@ -1446,7 +1448,9 @@ let compile output_prefix =
else {outer_ap_info with ap_inlined}
in
compile_lambda lambda_cxt
- (Lam.apply ap_func (Ext_list.append ap_args appinfo.ap_args) ap_info)
+ (Lam.apply ap_func
+ (Ext_list.append ap_args appinfo.ap_args)
+ ap_info ~ap_transformed_jsx)
(* External function call: it can not be tailcall in this case*)
| {
ap_func =
@@ -1529,7 +1533,9 @@ let compile output_prefix =
Js_output.output_of_block_and_expression lambda_cxt.continuation
args_code
(E.call
- ~info:(call_info_of_ap_status appinfo.ap_info.ap_status)
+ ~info:
+ (call_info_of_ap_status appinfo.ap_transformed_jsx
+ appinfo.ap_info.ap_status)
fn_code args))
and compile_prim (prim_info : Lam.prim_info)
(lambda_cxt : Lam_compile_context.t) =
diff --git a/compiler/core/lam_compile_external_call.ml b/compiler/core/lam_compile_external_call.ml
index 6d9056e570..34cdcbd9d0 100644
--- a/compiler/core/lam_compile_external_call.ml
+++ b/compiler/core/lam_compile_external_call.ml
@@ -267,9 +267,9 @@ let translate_scoped_access scopes obj =
| [] -> obj
| x :: xs -> Ext_list.fold_left xs (E.dot obj x) E.dot
-let translate_ffi (cxt : Lam_compile_context.t) arg_types
- (ffi : External_ffi_types.external_spec) (args : J.expression list)
- ~dynamic_import =
+let translate_ffi ?(transformed_jsx = false) (cxt : Lam_compile_context.t)
+ arg_types (ffi : External_ffi_types.external_spec)
+ (args : J.expression list) ~dynamic_import =
match ffi with
| Js_call
{external_module_name; name; splice : _; scopes; tagged_template = true}
@@ -287,7 +287,8 @@ let translate_ffi (cxt : Lam_compile_context.t) arg_types
| _ ->
let args, eff, dynamic = assemble_args_has_splice arg_types args in
let args = if dynamic then E.variadic_args args else args in
- add_eff eff (E.call ~info:{arity = Full; call_info = Call_na} fn args))
+ add_eff eff
+ (E.call ~info:(Js_call_info.na_full_call transformed_jsx) fn args))
| Js_call
{
external_module_name = module_name;
@@ -302,20 +303,31 @@ let translate_ffi (cxt : Lam_compile_context.t) arg_types
if splice then
let args, eff, dynamic = assemble_args_has_splice arg_types args in
let args = if dynamic then E.variadic_args args else args in
- add_eff eff (E.call ~info:{arity = Full; call_info = Call_na} fn args)
+ add_eff eff
+ (E.call ~info:(Js_call_info.na_full_call transformed_jsx) fn args)
else
let args, eff = assemble_args_no_splice arg_types args in
- add_eff eff @@ E.call ~info:{arity = Full; call_info = Call_na} fn args
+ add_eff eff
+ @@ E.call
+ ~info:
+ {
+ arity = Full;
+ call_info = Call_na;
+ call_transformed_jsx = transformed_jsx;
+ }
+ fn args
| Js_module_as_fn {external_module_name; splice} ->
let fn = external_var external_module_name ~dynamic_import in
if splice then
let args, eff, dynamic = assemble_args_has_splice arg_types args in
let args = if dynamic then E.variadic_args args else args in
- add_eff eff (E.call ~info:{arity = Full; call_info = Call_na} fn args)
+ add_eff eff
+ (E.call ~info:(Js_call_info.na_full_call transformed_jsx) fn args)
else
let args, eff = assemble_args_no_splice arg_types args in
(* TODO: fix in rest calling convention *)
- add_eff eff (E.call ~info:{arity = Full; call_info = Call_na} fn args)
+ add_eff eff
+ (E.call ~info:(Js_call_info.na_full_call transformed_jsx) fn args)
| Js_new {external_module_name = module_name; name = fn; splice; scopes} ->
(* handle [@@new]*)
(* This has some side effect, it will
@@ -362,14 +374,19 @@ let translate_ffi (cxt : Lam_compile_context.t) arg_types
add_eff eff
(let self = translate_scoped_access js_send_scopes self in
E.call
- ~info:{arity = Full; call_info = Call_na}
+ ~info:
+ {
+ arity = Full;
+ call_info = Call_na;
+ call_transformed_jsx = transformed_jsx;
+ }
(E.dot self name) args)
else
let args, eff = assemble_args_no_splice arg_types args in
add_eff eff
(let self = translate_scoped_access js_send_scopes self in
E.call
- ~info:{arity = Full; call_info = Call_na}
+ ~info:(Js_call_info.na_full_call transformed_jsx)
(E.dot self name) args)
| _ -> assert false)
| Js_module_as_var module_name -> external_var module_name ~dynamic_import
@@ -384,7 +401,7 @@ let translate_ffi (cxt : Lam_compile_context.t) arg_types
~dynamic_import
in
if args = [] then e
- else E.call ~info:{arity = Full; call_info = Call_na} e args
+ else E.call ~info:(Js_call_info.na_full_call transformed_jsx) e args
| Js_module_as_class module_name ->
let fn = external_var module_name ~dynamic_import in
let args, eff = assemble_args_no_splice arg_types args in
diff --git a/compiler/core/lam_compile_external_call.mli b/compiler/core/lam_compile_external_call.mli
index e8c974f10a..29e05c96f9 100644
--- a/compiler/core/lam_compile_external_call.mli
+++ b/compiler/core/lam_compile_external_call.mli
@@ -30,6 +30,7 @@ val ocaml_to_js_eff :
(** Compile ocaml external function call to JS IR. *)
val translate_ffi :
+ ?transformed_jsx:bool ->
Lam_compile_context.t ->
External_arg_spec.params ->
External_ffi_types.external_spec ->
diff --git a/compiler/core/lam_compile_primitive.ml b/compiler/core/lam_compile_primitive.ml
index aac979d926..1a52d835fd 100644
--- a/compiler/core/lam_compile_primitive.ml
+++ b/compiler/core/lam_compile_primitive.ml
@@ -54,17 +54,15 @@ let get_module_system () =
| [module_system] -> module_system
| _ -> Commonjs
+let call_info =
+ {Js_call_info.arity = Full; call_info = Call_na; call_transformed_jsx = false}
+
let import_of_path path =
- E.call
- ~info:{arity = Full; call_info = Call_na}
- (E.js_global "import")
- [E.str path]
+ E.call ~info:call_info (E.js_global "import") [E.str path]
let wrap_then import value =
let arg = Ident.create "m" in
- E.call
- ~info:{arity = Full; call_info = Call_na}
- (E.dot import "then")
+ E.call ~info:call_info (E.dot import "then")
[
E.ocaml_fun ~return_unit:false ~async:false ~one_unit_arg:false [arg]
[{statement_desc = J.Return (E.dot (E.var arg) value); comment = None}];
@@ -88,7 +86,7 @@ let translate output_prefix loc (cxt : Lam_compile_context.t)
| _ -> assert false)
| Pjs_apply -> (
match args with
- | fn :: rest -> E.call ~info:{arity = Full; call_info = Call_na} fn rest
+ | fn :: rest -> E.call ~info:call_info fn rest
| _ -> assert false)
| Pnull_to_opt -> (
match args with
@@ -594,9 +592,9 @@ let translate output_prefix loc (cxt : Lam_compile_context.t)
(* Lam_compile_external_call.translate loc cxt prim args *)
(* Test if the argument is a block or an immediate integer *)
| Pjs_object_create _ -> assert false
- | Pjs_call {arg_types; ffi; dynamic_import} ->
+ | Pjs_call {arg_types; ffi; dynamic_import; transformed_jsx} ->
Lam_compile_external_call.translate_ffi cxt arg_types ffi args
- ~dynamic_import
+ ~dynamic_import ~transformed_jsx
(* FIXME, this can be removed later *)
| Pisint -> E.is_type_number (Ext_list.singleton_exn args)
| Pis_poly_var_block -> E.is_type_object (Ext_list.singleton_exn args)
diff --git a/compiler/core/lam_convert.ml b/compiler/core/lam_convert.ml
index 3f252011ef..a6be3cc311 100644
--- a/compiler/core/lam_convert.ml
+++ b/compiler/core/lam_convert.ml
@@ -387,8 +387,8 @@ let convert (exports : Set_ident.t) (lam : Lambda.lambda) :
| Param_number i -> Ext_list.init i (fun _ -> External_arg_spec.dummy)
in
let args = Ext_list.map args convert_aux in
- Lam.handle_bs_non_obj_ffi arg_types result_type ffi args loc prim_name
- ~dynamic_import
+ Lam.handle_bs_non_obj_ffi ~transformed_jsx:a_prim.transformed_jsx
+ arg_types result_type ffi args loc prim_name ~dynamic_import
| Ffi_inline_const i -> Lam.const i
| Ffi_normal ->
Location.raise_errorf ~loc
@@ -414,11 +414,19 @@ let convert (exports : Set_ident.t) (lam : Lambda.lambda) :
let setter = Ext_string.ends_with name Literals.setter_suffix in
let _ = assert (not setter) in
prim ~primitive:(Pjs_unsafe_downgrade {name; setter}) ~args loc
- | Lapply {ap_func = fn; ap_args = args; ap_loc = loc; ap_inlined} ->
+ | Lapply
+ {
+ ap_func = fn;
+ ap_args = args;
+ ap_loc = loc;
+ ap_inlined;
+ ap_transformed_jsx;
+ } ->
(* we need do this eargly in case [aux fn] add some wrapper *)
Lam.apply (convert_aux fn)
(Ext_list.map args convert_aux)
{ap_loc = loc; ap_inlined; ap_status = App_uncurry}
+ ~ap_transformed_jsx
| Lfunction {params; body; attr} ->
let new_map, body =
rename_optional_parameters Map_ident.empty params body
@@ -571,8 +579,8 @@ let convert (exports : Set_ident.t) (lam : Lambda.lambda) :
when Ext_list.for_all2_no_exn inner_args params lam_is_var
&& Ext_list.length_larger_than_n inner_args args 1 ->
Lam.prim ~primitive ~args:(Ext_list.append_one args x) outer_loc
- | Lapply {ap_func; ap_args; ap_info} ->
- Lam.apply ap_func
+ | Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx} ->
+ Lam.apply ~ap_transformed_jsx ap_func
(Ext_list.append_one ap_args x)
{
ap_loc = outer_loc;
diff --git a/compiler/core/lam_pass_alpha_conversion.ml b/compiler/core/lam_pass_alpha_conversion.ml
index 3beadbeb0e..1d80ae16ed 100644
--- a/compiler/core/lam_pass_alpha_conversion.ml
+++ b/compiler/core/lam_pass_alpha_conversion.ml
@@ -23,14 +23,17 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
let alpha_conversion (meta : Lam_stats.t) (lam : Lam.t) : Lam.t =
- let rec populate_apply_info (args_arity : int list) (len : int) (fn : Lam.t)
- (args : Lam.t list) ap_info : Lam.t =
+ let rec populate_apply_info ?(ap_transformed_jsx = false)
+ (args_arity : int list) (len : int) (fn : Lam.t) (args : Lam.t list)
+ ap_info : Lam.t =
match args_arity with
- | 0 :: _ | [] -> Lam.apply (simpl fn) (Ext_list.map args simpl) ap_info
+ | 0 :: _ | [] ->
+ Lam.apply (simpl fn) (Ext_list.map args simpl) ap_info ~ap_transformed_jsx
| x :: _ ->
if x = len then
Lam.apply (simpl fn) (Ext_list.map args simpl)
{ap_info with ap_status = App_infer_full}
+ ~ap_transformed_jsx
else if x > len then
let fn = simpl fn in
let args = Ext_list.map args simpl in
@@ -39,7 +42,7 @@ let alpha_conversion (meta : Lam_stats.t) (lam : Lam.t) : Lam.t =
fn args
else
let first, rest = Ext_list.split_at args x in
- Lam.apply
+ Lam.apply ~ap_transformed_jsx
(Lam.apply (simpl fn) (Ext_list.map first simpl)
{ap_info with ap_status = App_infer_full})
(Ext_list.map rest simpl) ap_info
@@ -48,13 +51,14 @@ let alpha_conversion (meta : Lam_stats.t) (lam : Lam.t) : Lam.t =
match lam with
| Lconst _ -> lam
| Lvar _ -> lam
- | Lapply {ap_func; ap_args; ap_info} ->
+ | Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx} ->
(* detect functor application *)
let args_arity =
Lam_arity.extract_arity (Lam_arity_analysis.get_arity meta ap_func)
in
let len = List.length ap_args in
- populate_apply_info args_arity len ap_func ap_args ap_info
+ populate_apply_info ~ap_transformed_jsx args_arity len ap_func ap_args
+ ap_info
| Llet (str, v, l1, l2) -> Lam.let_ str v (simpl l1) (simpl l2)
| Lletrec (bindings, body) ->
let bindings = Ext_list.map_snd bindings simpl in
diff --git a/compiler/core/lam_pass_deep_flatten.ml b/compiler/core/lam_pass_deep_flatten.ml
index c55aeec841..0eddcb1a9d 100644
--- a/compiler/core/lam_pass_deep_flatten.ml
+++ b/compiler/core/lam_pass_deep_flatten.ml
@@ -224,8 +224,8 @@ let deep_flatten (lam : Lam.t) : Lam.t =
(* can we switch to the tupled backend? *\) *)
(* when List.length params = List.length args -> *)
(* aux (beta_reduce params body args) *)
- | Lapply {ap_func = l1; ap_args = ll; ap_info} ->
- Lam.apply (aux l1) (Ext_list.map ll aux) ap_info
+ | Lapply {ap_func = l1; ap_args = ll; ap_info; ap_transformed_jsx} ->
+ Lam.apply (aux l1) (Ext_list.map ll aux) ap_info ~ap_transformed_jsx
(* This kind of simple optimizations should be done each time
and as early as possible *)
| Lglobal_module _ -> lam
diff --git a/compiler/core/lam_pass_eliminate_ref.ml b/compiler/core/lam_pass_eliminate_ref.ml
index 4a251c1877..eb54fb2067 100644
--- a/compiler/core/lam_pass_eliminate_ref.ml
+++ b/compiler/core/lam_pass_eliminate_ref.ml
@@ -52,8 +52,10 @@ let rec eliminate_ref id (lam : Lam.t) =
Lam.assign id
(Lam.prim ~primitive:(Poffsetint delta) ~args:[Lam.var id] loc)
| Lconst _ -> lam
- | Lapply {ap_func = e1; ap_args = el; ap_info} ->
- Lam.apply (eliminate_ref id e1) (Ext_list.map el (eliminate_ref id)) ap_info
+ | Lapply {ap_func = e1; ap_args = el; ap_info; ap_transformed_jsx} ->
+ Lam.apply ~ap_transformed_jsx (eliminate_ref id e1)
+ (Ext_list.map el (eliminate_ref id))
+ ap_info
| Llet (str, v, e1, e2) ->
Lam.let_ str v (eliminate_ref id e1) (eliminate_ref id e2)
| Lletrec (idel, e2) ->
diff --git a/compiler/core/lam_pass_exits.ml b/compiler/core/lam_pass_exits.ml
index ceba4af6e5..2eb6295699 100644
--- a/compiler/core/lam_pass_exits.ml
+++ b/compiler/core/lam_pass_exits.ml
@@ -199,8 +199,10 @@ let subst_helper (subst : subst_tbl) (query : int -> int) (lam : Lam.t) : Lam.t
Lam.let_ Strict y l r)
| None -> Lam.staticraise i ls)
| Lvar _ | Lconst _ -> lam
- | Lapply {ap_func; ap_args; ap_info} ->
- Lam.apply (simplif ap_func) (Ext_list.map ap_args simplif) ap_info
+ | Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx} ->
+ Lam.apply (simplif ap_func)
+ (Ext_list.map ap_args simplif)
+ ap_info ~ap_transformed_jsx
| Lfunction {arity; params; body; attr} ->
Lam.function_ ~arity ~params ~body:(simplif body) ~attr
| Llet (kind, v, l1, l2) -> Lam.let_ kind v (simplif l1) (simplif l2)
diff --git a/compiler/core/lam_pass_lets_dce.ml b/compiler/core/lam_pass_lets_dce.ml
index ca6e32bc7c..04ab02d4bc 100644
--- a/compiler/core/lam_pass_lets_dce.ml
+++ b/compiler/core/lam_pass_lets_dce.ml
@@ -144,8 +144,9 @@ let lets_helper (count_var : Ident.t -> Lam_pass_count.used_info) lam : Lam.t =
(* *\) *)
(* when Ext_list.same_length params args -> *)
(* simplif (Lam_beta_reduce.beta_reduce params body args) *)
- | Lapply {ap_func = l1; ap_args = ll; ap_info} ->
+ | Lapply {ap_func = l1; ap_args = ll; ap_info; ap_transformed_jsx} ->
Lam.apply (simplif l1) (Ext_list.map ll simplif) ap_info
+ ~ap_transformed_jsx
| Lfunction {arity; params; body; attr} ->
Lam.function_ ~arity ~params ~body:(simplif body) ~attr
| Lconst _ -> lam
diff --git a/compiler/core/lam_pass_remove_alias.ml b/compiler/core/lam_pass_remove_alias.ml
index 065ea65edf..67472564fe 100644
--- a/compiler/core/lam_pass_remove_alias.ml
+++ b/compiler/core/lam_pass_remove_alias.ml
@@ -140,19 +140,23 @@ let simplify_alias (meta : Lam_stats.t) (lam : Lam.t) : Lam.t =
| _ -> true)
&& Lam_analysis.lfunction_can_be_inlined lfunction ->
simpl (Lam_beta_reduce.propagate_beta_reduce meta params body args)
- | _ -> Lam.apply (simpl l1) (Ext_list.map args simpl) ap_info)
+ | _ ->
+ Lam.apply (simpl l1) (Ext_list.map args simpl) ap_info
+ ?ap_transformed_jsx:None)
(* Function inlining interact with other optimizations...
- parameter attributes
- scope issues
- code bloat
*)
- | Lapply {ap_func = Lvar v as fn; ap_args; ap_info} -> (
+ | Lapply {ap_func = Lvar v as fn; ap_args; ap_info; ap_transformed_jsx} -> (
(* Check info for always inlining *)
(* Ext_log.dwarn __LOC__ "%s/%d" v.name v.stamp; *)
let ap_args = Ext_list.map ap_args simpl in
- let[@local] normal () = Lam.apply (simpl fn) ap_args ap_info in
+ let[@local] normal () =
+ Lam.apply (simpl fn) ap_args ap_info ~ap_transformed_jsx
+ in
match Hash_ident.find_opt meta.ident_tbl v with
| Some
(FunctionId
@@ -221,8 +225,8 @@ let simplify_alias (meta : Lam_stats.t) (lam : Lam.t) : Lam.t =
(* *\) *)
(* when Ext_list.same_length params args -> *)
(* simpl (Lam_beta_reduce.propogate_beta_reduce meta params body args) *)
- | Lapply {ap_func = l1; ap_args = ll; ap_info} ->
- Lam.apply (simpl l1) (Ext_list.map ll simpl) ap_info
+ | Lapply {ap_func = l1; ap_args = ll; ap_info; ap_transformed_jsx} ->
+ Lam.apply (simpl l1) (Ext_list.map ll simpl) ap_info ~ap_transformed_jsx
| Lfunction {arity; params; body; attr} ->
Lam.function_ ~arity ~params ~body:(simpl body) ~attr
| Lswitch
diff --git a/compiler/core/lam_primitive.ml b/compiler/core/lam_primitive.ml
index e28c652cd9..f07b5aa024 100644
--- a/compiler/core/lam_primitive.ml
+++ b/compiler/core/lam_primitive.ml
@@ -46,6 +46,7 @@ type t =
arg_types: External_arg_spec.params;
ffi: External_ffi_types.external_spec;
dynamic_import: bool;
+ transformed_jsx: bool;
}
| Pjs_object_create of External_arg_spec.obj_params
(* Exceptions *)
@@ -250,7 +251,7 @@ let eq_primitive_approx (lhs : t) (rhs : t) =
| Pmakeblock (i1, info1, flag1) ->
i0 = i1 && flag0 = flag1 && eq_tag_info info0 info1
| _ -> false)
- | Pjs_call {prim_name; arg_types; ffi; dynamic_import} -> (
+ | Pjs_call {prim_name; arg_types; ffi; dynamic_import; _} -> (
match rhs with
| Pjs_call rhs ->
prim_name = rhs.prim_name && arg_types = rhs.arg_types && ffi = rhs.ffi
diff --git a/compiler/core/lam_primitive.mli b/compiler/core/lam_primitive.mli
index 460ef392c4..19b10cf964 100644
--- a/compiler/core/lam_primitive.mli
+++ b/compiler/core/lam_primitive.mli
@@ -42,6 +42,7 @@ type t =
arg_types: External_arg_spec.params;
ffi: External_ffi_types.external_spec;
dynamic_import: bool;
+ transformed_jsx: bool;
}
| Pjs_object_create of External_arg_spec.obj_params
| Praise
diff --git a/compiler/core/lam_util.ml b/compiler/core/lam_util.ml
index 7ea859f6be..f85cdc0f41 100644
--- a/compiler/core/lam_util.ml
+++ b/compiler/core/lam_util.ml
@@ -66,7 +66,7 @@ let refine_let
(* let v= subst_lambda (Map_ident.singleton param arg ) l in *)
(* Ext_log.err "@[substitution << @]@."; *)
(* v *)
- | _, _, Lapply {ap_func=fn; ap_args = [Lvar w]; ap_info} when
+ | _, _, Lapply {ap_func=fn; ap_args = [Lvar w]; ap_info; ap_transformed_jsx} when
Ident.same w param &&
(not (Lam_hit.hit_variable param fn ))
->
@@ -79,7 +79,7 @@ let refine_let
]}
#1667 make sure body does not hit k
*)
- Lam.apply fn [arg] ap_info
+ Lam.apply fn [arg] ap_info ~ap_transformed_jsx
| (Strict | StrictOpt ),
( Lvar _ | Lconst _ |
Lprim {primitive = Pfield (_ , Fld_module _) ;
diff --git a/compiler/frontend/ast_compatible.ml b/compiler/frontend/ast_compatible.ml
index 04a1be4f4a..0610abb015 100644
--- a/compiler/frontend/ast_compatible.ml
+++ b/compiler/frontend/ast_compatible.ml
@@ -44,6 +44,7 @@ let apply_simple ?(loc = default_loc) ?(attrs = []) (fn : expression)
funct = fn;
args = Ext_list.map args (fun x -> (Asttypes.Nolabel, x));
partial = false;
+ transformed_jsx = false;
};
}
@@ -52,7 +53,13 @@ let app1 ?(loc = default_loc) ?(attrs = []) fn arg1 : expression =
pexp_loc = loc;
pexp_attributes = attrs;
pexp_desc =
- Pexp_apply {funct = fn; args = [(Nolabel, arg1)]; partial = false};
+ Pexp_apply
+ {
+ funct = fn;
+ args = [(Nolabel, arg1)];
+ partial = false;
+ transformed_jsx = false;
+ };
}
let app2 ?(loc = default_loc) ?(attrs = []) fn arg1 arg2 : expression =
@@ -61,7 +68,12 @@ let app2 ?(loc = default_loc) ?(attrs = []) fn arg1 arg2 : expression =
pexp_attributes = attrs;
pexp_desc =
Pexp_apply
- {funct = fn; args = [(Nolabel, arg1); (Nolabel, arg2)]; partial = false};
+ {
+ funct = fn;
+ args = [(Nolabel, arg1); (Nolabel, arg2)];
+ partial = false;
+ transformed_jsx = false;
+ };
}
let app3 ?(loc = default_loc) ?(attrs = []) fn arg1 arg2 arg3 : expression =
@@ -74,6 +86,7 @@ let app3 ?(loc = default_loc) ?(attrs = []) fn arg1 arg2 arg3 : expression =
funct = fn;
args = [(Nolabel, arg1); (Nolabel, arg2); (Nolabel, arg3)];
partial = false;
+ transformed_jsx = false;
};
}
@@ -121,6 +134,7 @@ let apply_labels ?(loc = default_loc) ?(attrs = []) fn
Ext_list.map args (fun (l, a) ->
(Asttypes.Labelled {txt = l; loc = Location.none}, a));
partial = false;
+ transformed_jsx = false;
};
}
diff --git a/compiler/frontend/ast_exp_apply.ml b/compiler/frontend/ast_exp_apply.ml
index fb5b500db9..afffea4e3c 100644
--- a/compiler/frontend/ast_exp_apply.ml
+++ b/compiler/frontend/ast_exp_apply.ml
@@ -88,11 +88,12 @@ let app_exp_mapper (e : exp) (self : Bs_ast_mapper.mapper) : exp =
{f with pexp_desc = Pexp_variant (label, Some a); pexp_loc = e.pexp_loc}
| Pexp_construct (ctor, None) ->
{f with pexp_desc = Pexp_construct (ctor, Some a); pexp_loc = e.pexp_loc}
- | Pexp_apply {funct = fn1; args; partial} ->
+ | Pexp_apply {funct = fn1; args; partial; transformed_jsx} ->
Bs_ast_invariant.warn_discarded_unused_attributes fn1.pexp_attributes;
{
pexp_desc =
- Pexp_apply {funct = fn1; args = (Nolabel, a) :: args; partial};
+ Pexp_apply
+ {funct = fn1; args = (Nolabel, a) :: args; partial; transformed_jsx};
pexp_loc = e.pexp_loc;
pexp_attributes = e.pexp_attributes @ f.pexp_attributes;
}
@@ -108,7 +109,7 @@ let app_exp_mapper (e : exp) (self : Bs_ast_mapper.mapper) : exp =
fn with
pexp_desc = Pexp_construct (ctor, Some bounded_obj_arg);
}
- | Pexp_apply {funct = fn; args} ->
+ | Pexp_apply {funct = fn; args; transformed_jsx} ->
Bs_ast_invariant.warn_discarded_unused_attributes
fn.pexp_attributes;
{
@@ -118,6 +119,7 @@ let app_exp_mapper (e : exp) (self : Bs_ast_mapper.mapper) : exp =
funct = fn;
args = (Nolabel, bounded_obj_arg) :: args;
partial = false;
+ transformed_jsx;
};
pexp_attributes = [];
pexp_loc = fn.pexp_loc;
diff --git a/compiler/frontend/ast_uncurry_gen.ml b/compiler/frontend/ast_uncurry_gen.ml
index 70e4e2d550..9e0a43fe37 100644
--- a/compiler/frontend/ast_uncurry_gen.ml
+++ b/compiler/frontend/ast_uncurry_gen.ml
@@ -75,4 +75,5 @@ let to_method_callback loc (self : Bs_ast_mapper.mapper) label
[Typ.any ~loc ()]) );
];
partial = false;
+ transformed_jsx = false;
}
diff --git a/compiler/frontend/bs_ast_mapper.ml b/compiler/frontend/bs_ast_mapper.ml
index fff7690b20..a6dfc764e3 100644
--- a/compiler/frontend/bs_ast_mapper.ml
+++ b/compiler/frontend/bs_ast_mapper.ml
@@ -330,8 +330,8 @@ module E = struct
fun_ ~loc ~attrs ~arity ~async lab
(map_opt (sub.expr sub) def)
(sub.pat sub p) (sub.expr sub e)
- | Pexp_apply {funct = e; args = l; partial} ->
- apply ~loc ~attrs ~partial (sub.expr sub e)
+ | Pexp_apply {funct = e; args = l; partial; transformed_jsx} ->
+ apply ~loc ~attrs ~partial ~transformed_jsx (sub.expr sub e)
(List.map (map_snd (sub.expr sub)) l)
| Pexp_match (e, pel) ->
match_ ~loc ~attrs (sub.expr sub e) (sub.cases sub pel)
diff --git a/compiler/frontend/external_ffi_types.ml b/compiler/frontend/external_ffi_types.ml
index 35419958fd..9016341603 100644
--- a/compiler/frontend/external_ffi_types.ml
+++ b/compiler/frontend/external_ffi_types.ml
@@ -246,6 +246,7 @@ let () =
prim_native_name;
prim_alloc = _;
prim_from_constructor = _;
+ transformed_jsx = _;
} :
Primitive.description)
(p2 : Primitive.description)
diff --git a/compiler/ml/ast_helper.ml b/compiler/ml/ast_helper.ml
index 347b5b5e0d..05c715e6f5 100644
--- a/compiler/ml/ast_helper.ml
+++ b/compiler/ml/ast_helper.ml
@@ -154,8 +154,9 @@ module Exp = struct
let fun_ ?loc ?attrs ?(async = false) ~arity a b c d =
mk ?loc ?attrs
(Pexp_fun {arg_label = a; default = b; lhs = c; rhs = d; arity; async})
- let apply ?loc ?attrs ?(partial = false) funct args =
- mk ?loc ?attrs (Pexp_apply {funct; args; partial})
+ let apply ?loc ?attrs ?(partial = false) ?(transformed_jsx = false) funct args
+ =
+ mk ?loc ?attrs (Pexp_apply {funct; args; partial; transformed_jsx})
let match_ ?loc ?attrs a b = mk ?loc ?attrs (Pexp_match (a, b))
let try_ ?loc ?attrs a b = mk ?loc ?attrs (Pexp_try (a, b))
let tuple ?loc ?attrs a = mk ?loc ?attrs (Pexp_tuple a)
diff --git a/compiler/ml/ast_helper.mli b/compiler/ml/ast_helper.mli
index d8cfef1c5e..467050bb5b 100644
--- a/compiler/ml/ast_helper.mli
+++ b/compiler/ml/ast_helper.mli
@@ -149,6 +149,7 @@ module Exp : sig
?loc:loc ->
?attrs:attrs ->
?partial:bool ->
+ ?transformed_jsx:bool ->
expression ->
(arg_label * expression) list ->
expression
diff --git a/compiler/ml/ast_mapper.ml b/compiler/ml/ast_mapper.ml
index ba678c1a85..dbaea2b466 100644
--- a/compiler/ml/ast_mapper.ml
+++ b/compiler/ml/ast_mapper.ml
@@ -293,8 +293,8 @@ module E = struct
fun_ ~loc ~attrs ~arity ~async lab
(map_opt (sub.expr sub) def)
(sub.pat sub p) (sub.expr sub e)
- | Pexp_apply {funct = e; args = l; partial} ->
- apply ~loc ~attrs ~partial (sub.expr sub e)
+ | Pexp_apply {funct = e; args = l; partial; transformed_jsx} ->
+ apply ~loc ~attrs ~partial ~transformed_jsx (sub.expr sub e)
(List.map (map_snd (sub.expr sub)) l)
| Pexp_match (e, pel) ->
match_ ~loc ~attrs (sub.expr sub e) (sub.cases sub pel)
diff --git a/compiler/ml/lambda.ml b/compiler/ml/lambda.ml
index 26aa8a8c74..dfb8c33d1b 100644
--- a/compiler/ml/lambda.ml
+++ b/compiler/ml/lambda.ml
@@ -383,6 +383,7 @@ and lambda_apply = {
ap_args: lambda list;
ap_loc: Location.t;
ap_inlined: inline_attribute;
+ ap_transformed_jsx: bool;
}
and lambda_switch = {
diff --git a/compiler/ml/lambda.mli b/compiler/ml/lambda.mli
index 9e1c9b9d7c..5927479bcc 100644
--- a/compiler/ml/lambda.mli
+++ b/compiler/ml/lambda.mli
@@ -352,6 +352,7 @@ and lambda_apply = {
ap_args: lambda list;
ap_loc: Location.t;
ap_inlined: inline_attribute; (* specified with the [@inlined] attribute *)
+ ap_transformed_jsx: bool;
}
and lambda_switch = {
diff --git a/compiler/ml/matching.ml b/compiler/ml/matching.ml
index fd90f38535..d450ab305d 100644
--- a/compiler/ml/matching.ml
+++ b/compiler/ml/matching.ml
@@ -1449,6 +1449,7 @@ let inline_lazy_force arg loc =
ap_inlined = Default_inline;
ap_args = [arg];
ap_loc = loc;
+ ap_transformed_jsx = false;
}
let make_lazy_matching def = function
| [] -> fatal_error "Matching.make_lazy_matching"
diff --git a/compiler/ml/parsetree.ml b/compiler/ml/parsetree.ml
index 1fefea4a2d..7374827192 100644
--- a/compiler/ml/parsetree.ml
+++ b/compiler/ml/parsetree.ml
@@ -244,6 +244,7 @@ and expression_desc =
funct: expression;
args: (arg_label * expression) list;
partial: bool;
+ transformed_jsx: bool;
}
(* E0 ~l1:E1 ... ~ln:En
li can be empty (non labeled argument) or start with '?'
diff --git a/compiler/ml/primitive.ml b/compiler/ml/primitive.ml
index 602cb22089..3ddc60a488 100644
--- a/compiler/ml/primitive.ml
+++ b/compiler/ml/primitive.ml
@@ -25,8 +25,11 @@ type description = {
prim_native_name: string; (* Name of C function for the nat. code gen. *)
prim_from_constructor: bool;
(* Is it from a type constructor instead of a concrete function type? *)
+ transformed_jsx: bool;
}
+let set_transformed_jsx d ~transformed_jsx = {d with transformed_jsx}
+
let coerce : (description -> description -> bool) ref =
ref (fun (p1 : description) (p2 : description) -> p1 = p2)
@@ -43,6 +46,7 @@ let parse_declaration valdecl ~arity ~from_constructor =
prim_alloc = true;
prim_native_name = native_name;
prim_from_constructor = from_constructor;
+ transformed_jsx = false;
}
open Outcometree
diff --git a/compiler/ml/primitive.mli b/compiler/ml/primitive.mli
index 9166ae1a5c..8f5c58100d 100644
--- a/compiler/ml/primitive.mli
+++ b/compiler/ml/primitive.mli
@@ -22,8 +22,11 @@ type description = private {
prim_native_name: string; (* Name of C function for the nat. code gen. *)
prim_from_constructor: bool;
(* Is it from a type constructor instead of a concrete function type? *)
+ transformed_jsx: bool;
}
+val set_transformed_jsx : description -> transformed_jsx:bool -> description
+
(* Invariant [List.length d.prim_native_repr_args = d.prim_arity] *)
val parse_declaration :
diff --git a/compiler/ml/printast.ml b/compiler/ml/printast.ml
index ded0cfd35b..66a53135c1 100644
--- a/compiler/ml/printast.ml
+++ b/compiler/ml/printast.ml
@@ -251,11 +251,12 @@ and expression i ppf x =
option i expression ppf eo;
pattern i ppf p;
expression i ppf e
- | Pexp_apply {funct = e; args = l; partial} ->
+ | Pexp_apply {funct = e; args = l; partial; transformed_jsx} ->
line i ppf "Pexp_apply\n";
if partial then line i ppf "partial\n";
expression i ppf e;
- list i label_x_expression ppf l
+ list i label_x_expression ppf l;
+ line i ppf "transformed_jsx: %b\n" transformed_jsx
| Pexp_match (e, l) ->
line i ppf "Pexp_match\n";
expression i ppf e;
diff --git a/compiler/ml/tast_mapper.ml b/compiler/ml/tast_mapper.ml
index 8064d65990..c2e33e7b4f 100644
--- a/compiler/ml/tast_mapper.ml
+++ b/compiler/ml/tast_mapper.ml
@@ -199,12 +199,13 @@ let expr sub x =
| Texp_function {arg_label; arity; param; case; partial; async} ->
Texp_function
{arg_label; arity; param; case = sub.case sub case; partial; async}
- | Texp_apply {funct = exp; args = list; partial} ->
+ | Texp_apply {funct = exp; args = list; partial; transformed_jsx} ->
Texp_apply
{
funct = sub.expr sub exp;
args = List.map (tuple2 id (opt (sub.expr sub))) list;
partial;
+ transformed_jsx;
}
| Texp_match (exp, cases, exn_cases, p) ->
Texp_match
diff --git a/compiler/ml/translcore.ml b/compiler/ml/translcore.ml
index 4cdeb34aa5..6340b8ca9d 100644
--- a/compiler/ml/translcore.ml
+++ b/compiler/ml/translcore.ml
@@ -710,6 +710,7 @@ and transl_exp0 (e : Typedtree.expression) : Lambda.lambda =
exp_type = prim_type;
} as funct;
args = oargs;
+ transformed_jsx;
}
when List.length oargs >= p.prim_arity
&& List.for_all (fun (_, arg) -> arg <> None) oargs -> (
@@ -720,7 +721,7 @@ and transl_exp0 (e : Typedtree.expression) : Lambda.lambda =
let inlined, _ =
Translattribute.get_and_remove_inlined_attribute funct
in
- transl_apply ~inlined f args' e.exp_loc
+ transl_apply ~inlined ~transformed_jsx f args' e.exp_loc
in
let args =
List.map
@@ -749,8 +750,12 @@ and transl_exp0 (e : Typedtree.expression) : Lambda.lambda =
| Ploc _, _ -> assert false
| _, _ -> (
match (prim, argl) with
+ | Pccall d, _ ->
+ wrap
+ (Lprim
+ (Pccall (set_transformed_jsx d ~transformed_jsx), argl, e.exp_loc))
| _ -> wrap (Lprim (prim, argl, e.exp_loc))))
- | Texp_apply {funct; args = oargs; partial} ->
+ | Texp_apply {funct; args = oargs; partial; transformed_jsx} ->
let inlined, funct =
Translattribute.get_and_remove_inlined_attribute funct
in
@@ -766,8 +771,8 @@ and transl_exp0 (e : Typedtree.expression) : Lambda.lambda =
| None -> None
else None
in
- transl_apply ~inlined ~uncurried_partial_application (transl_exp funct)
- oargs e.exp_loc
+ transl_apply ~inlined ~uncurried_partial_application ~transformed_jsx
+ (transl_exp funct) oargs e.exp_loc
| Texp_match (arg, pat_expr_list, exn_pat_expr_list, partial) ->
transl_match e arg pat_expr_list exn_pat_expr_list partial
| Texp_try (body, pat_expr_list) ->
@@ -948,9 +953,17 @@ and transl_case_try {c_lhs; c_guard; c_rhs} =
and transl_cases_try cases = List.map transl_case_try cases
and transl_apply ?(inlined = Default_inline)
- ?(uncurried_partial_application = None) lam sargs loc =
+ ?(uncurried_partial_application = None) ?(transformed_jsx = false) lam sargs
+ loc =
let lapply ap_func ap_args =
- Lapply {ap_loc = loc; ap_func; ap_args; ap_inlined = inlined}
+ Lapply
+ {
+ ap_loc = loc;
+ ap_func;
+ ap_args;
+ ap_inlined = inlined;
+ ap_transformed_jsx = transformed_jsx;
+ }
in
let rec build_apply lam args = function
| (None, optional) :: l ->
@@ -1008,7 +1021,14 @@ and transl_apply ?(inlined = Default_inline)
let extra_args = Ext_list.map extra_ids (fun id -> Lvar id) in
let ap_args = args @ extra_args in
let l0 =
- Lapply {ap_func = lam; ap_args; ap_inlined = inlined; ap_loc = loc}
+ Lapply
+ {
+ ap_func = lam;
+ ap_args;
+ ap_inlined = inlined;
+ ap_loc = loc;
+ ap_transformed_jsx = transformed_jsx;
+ }
in
Lfunction
{
diff --git a/compiler/ml/translmod.ml b/compiler/ml/translmod.ml
index f815a536c0..87471ac26b 100644
--- a/compiler/ml/translmod.ml
+++ b/compiler/ml/translmod.ml
@@ -100,6 +100,7 @@ and apply_coercion_result loc strict funct param arg cc_res =
ap_func = Lvar id;
ap_args = [arg];
ap_inlined = Default_inline;
+ ap_transformed_jsx = false;
});
})
@@ -276,6 +277,7 @@ and transl_module cc rootpath mexp =
ap_func = transl_module Tcoerce_none None funct;
ap_args = [transl_module ccarg None arg];
ap_inlined = inlined_attribute;
+ ap_transformed_jsx = false;
})
| Tmod_constraint (arg, _, _, ccarg) ->
transl_module (compose_coercions cc ccarg) rootpath arg
diff --git a/compiler/ml/typecore.ml b/compiler/ml/typecore.ml
index d1e593607f..a7ac76a341 100644
--- a/compiler/ml/typecore.ml
+++ b/compiler/ml/typecore.ml
@@ -2401,7 +2401,7 @@ and type_expect_ ?type_clash_context ?in_function ?(recarg = Rejected) env sexp
type_function ?in_function ~arity ~async loc sexp.pexp_attributes env
ty_expected l
[Ast_helper.Exp.case spat sbody]
- | Pexp_apply {funct = sfunct; args = sargs; partial} ->
+ | Pexp_apply {funct = sfunct; args = sargs; partial; transformed_jsx} ->
assert (sargs <> []);
begin_def ();
(* one more level for non-returning functions *)
@@ -2423,7 +2423,7 @@ and type_expect_ ?type_clash_context ?in_function ?(recarg = Rejected) env sexp
let mk_apply funct args =
rue
{
- exp_desc = Texp_apply {funct; args; partial};
+ exp_desc = Texp_apply {funct; args; partial; transformed_jsx};
exp_loc = loc;
exp_extra = [];
exp_type = ty_res;
diff --git a/compiler/ml/typedtree.ml b/compiler/ml/typedtree.ml
index 626950caec..f9769ee13b 100644
--- a/compiler/ml/typedtree.ml
+++ b/compiler/ml/typedtree.ml
@@ -87,6 +87,7 @@ and expression_desc =
funct: expression;
args: (Noloc.arg_label * expression option) list;
partial: bool;
+ transformed_jsx: bool;
}
| Texp_match of expression * case list * case list * partial
| Texp_try of expression * case list
diff --git a/compiler/ml/typedtree.mli b/compiler/ml/typedtree.mli
index 96da873af0..f3368c9539 100644
--- a/compiler/ml/typedtree.mli
+++ b/compiler/ml/typedtree.mli
@@ -150,6 +150,7 @@ and expression_desc =
funct: expression;
args: (Noloc.arg_label * expression option) list;
partial: bool;
+ transformed_jsx: bool;
}
(** E0 ~l1:E1 ... ~ln:En
diff --git a/compiler/syntax/src/jsx_v4.ml b/compiler/syntax/src/jsx_v4.ml
index febc245f21..89bb301f2d 100644
--- a/compiler/syntax/src/jsx_v4.ml
+++ b/compiler/syntax/src/jsx_v4.ml
@@ -1208,7 +1208,7 @@ let append_children_prop (config : Jsx_common.jsx_config) mapper
Exp.apply
(Exp.ident
{txt = Ldot (element_binding, "someElement"); loc = Location.none})
- [(Nolabel, child)]
+ [(Nolabel, mapper.expr mapper child)]
in
let is_optional =
match component_description with
@@ -1277,7 +1277,7 @@ let mk_react_jsx (config : Jsx_common.jsx_config) mapper loc attrs
[key_prop; (nolabel, unit_expr ~loc:Location.none)] )
in
let args = [(nolabel, elementTag); (nolabel, props_record)] @ key_and_unit in
- Exp.apply ~loc ~attrs jsx_expr args
+ Exp.apply ~loc ~attrs ~transformed_jsx:true jsx_expr args
(* In most situations, the component name is the make function from a module.
However, if the name contains a lowercase letter, it means it probably an external component.
diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml
index c9c36496c6..e610d4589d 100644
--- a/compiler/syntax/src/res_core.ml
+++ b/compiler/syntax/src/res_core.ml
@@ -2188,12 +2188,18 @@ and parse_binary_expr ?(context = OrdinaryExpr) ?a p prec =
let loc = mk_loc a.Parsetree.pexp_loc.loc_start b.pexp_loc.loc_end in
let expr =
match (token, b.pexp_desc) with
- | BarGreater, Pexp_apply {funct = fun_expr; args; partial} ->
+ | ( BarGreater,
+ Pexp_apply {funct = fun_expr; args; partial; transformed_jsx} ) ->
{
b with
pexp_desc =
Pexp_apply
- {funct = fun_expr; args = args @ [(Nolabel, a)]; partial};
+ {
+ funct = fun_expr;
+ args = args @ [(Nolabel, a)];
+ partial;
+ transformed_jsx;
+ };
}
| BarGreater, _ -> Ast_helper.Exp.apply ~loc b [(Nolabel, a)]
| _ ->
diff --git a/compiler/syntax/src/res_parsetree_viewer.ml b/compiler/syntax/src/res_parsetree_viewer.ml
index b2d6444e59..c23ed3b0f2 100644
--- a/compiler/syntax/src/res_parsetree_viewer.ml
+++ b/compiler/syntax/src/res_parsetree_viewer.ml
@@ -142,7 +142,13 @@ let rewrite_underscore_apply expr =
{
e with
pexp_desc =
- Pexp_apply {funct = call_expr; args = new_args; partial = false};
+ Pexp_apply
+ {
+ funct = call_expr;
+ args = new_args;
+ partial = false;
+ transformed_jsx = false;
+ };
}
| _ -> expr
diff --git a/scripts/test.js b/scripts/test.js
index c2edd0c4ae..b7fddb16ac 100644
--- a/scripts/test.js
+++ b/scripts/test.js
@@ -83,10 +83,22 @@ if (mochaTest) {
stdio: "inherit",
});
- await mocha(["-t", "10000", "tests/tests/**/*_test.mjs"], {
- cwd: projectDir,
- stdio: "inherit",
- });
+ await mocha(
+ [
+ "-t",
+ "10000",
+ "tests/tests/**/*_test.mjs",
+ // Ignore the preserve_jsx_test.mjs file.
+ // I can't run because Mocha doesn't support jsx.
+ // We also want to keep the output as is.
+ "--ignore",
+ "tests/tests/src/preserve_jsx_test.mjs",
+ ],
+ {
+ cwd: projectDir,
+ stdio: "inherit",
+ },
+ );
await node("tests/tests/src/core/Core_TestSuite.mjs", [], {
cwd: projectDir,
@@ -149,8 +161,8 @@ if (runtimeDocstrings) {
await execClean([], {
cwd: docstringTestDir,
- stdio: "inherit"
- })
+ stdio: "inherit",
+ });
await execBuild([], {
cwd: docstringTestDir,
@@ -177,12 +189,9 @@ if (runtimeDocstrings) {
});
console.log("Run mocha test");
- await mocha(
- [path.join(docstringTestDir, "generated_mocha_test.res.js")],
- {
- cwd: projectDir,
- stdio: "inherit",
- },
- );
+ await mocha([path.join(docstringTestDir, "generated_mocha_test.res.js")], {
+ cwd: projectDir,
+ stdio: "inherit",
+ });
}
}
diff --git a/tests/tests/src/preserve_jsx_test.mjs b/tests/tests/src/preserve_jsx_test.mjs
new file mode 100644
index 0000000000..0c25c00707
--- /dev/null
+++ b/tests/tests/src/preserve_jsx_test.mjs
@@ -0,0 +1,137 @@
+// Generated by ReScript, PLEASE EDIT WITH CARE
+
+import * as Primitive_option from "rescript/lib/es6/Primitive_option.js";
+import * as JsxRuntime from "react/jsx-runtime";
+
+let React = {};
+
+let ReactDOM = {};
+
+function Preserve_jsx_test$Icon(props) {
+ return ;
+}
+
+let Icon = {
+ make: Preserve_jsx_test$Icon
+};
+
+let _single_element_child =
+
+{"Hello, world!"}
+
+;
+
+let _multiple_element_children =
+
+{"Hello, world!"}
+
+
+
;
+
+let _single_element_fragment = <>
+{Primitive_option.some()}
+>;
+
+let _multiple_element_fragment = <>
+
+
+>;
+
+let _unary_element_with_props = ;
+
+let _container_element_with_props_and_children =
+{"Hello, world!"}
+
;
+
+let baseProps = {
+ className: "foo",
+ title: "foo"
+};
+
+let newrecord = {...baseProps};
+
+let _unary_element_with_spread_props = ;
+
+let newrecord$1 = {...baseProps};
+
+let _container_with_spread_props =
+{"Hello, world!"}
+
+
;
+
+let baseChildren = [
+
+ {"Hello, world!"}
+ ,
+
+ {"Hello, world!"}
+
+];
+
+let _container_with_spread_children =
+{baseChildren}
+
;
+
+let newrecord$2 = {...baseProps};
+
+let _container_with_spread_props_and_children =
+{baseChildren}
+
;
+
+let newrecord$3 = {...baseProps};
+
+let _unary_element_with_spread_props_keyed = ;
+
+let newrecord$4 = {...baseProps};
+
+let _container_with_spread_props_keyed =
+{"Hello, world!"}
+
+
;
+
+let _unary_element_with_only_spread_props = ;
+
+function QueryClientProvider(props) { return props.children }
+;
+
+let A = {};
+
+function Preserve_jsx_test$B(props) {
+ return
+ {"Hello, world!"}
+
;
+}
+
+let B = {
+ make: Preserve_jsx_test$B
+};
+
+let _external_component_with_children =
+
+
+;
+
+export {
+ React,
+ ReactDOM,
+ Icon,
+ _single_element_child,
+ _multiple_element_children,
+ _single_element_fragment,
+ _multiple_element_fragment,
+ _unary_element_with_props,
+ _container_element_with_props_and_children,
+ baseProps,
+ _unary_element_with_spread_props,
+ _container_with_spread_props,
+ baseChildren,
+ _container_with_spread_children,
+ _container_with_spread_props_and_children,
+ _unary_element_with_spread_props_keyed,
+ _container_with_spread_props_keyed,
+ _unary_element_with_only_spread_props,
+ A,
+ B,
+ _external_component_with_children,
+}
+/* _single_element_child Not a pure module */
diff --git a/tests/tests/src/preserve_jsx_test.res b/tests/tests/src/preserve_jsx_test.res
new file mode 100644
index 0000000000..81042f4b25
--- /dev/null
+++ b/tests/tests/src/preserve_jsx_test.res
@@ -0,0 +1,157 @@
+@@config({
+ flags: ["-bs-jsx", "4", "-bs-jsx-preserve"],
+})
+
+module React = {
+ type element = Jsx.element
+
+ @val external null: element = "null"
+
+ external float: float => element = "%identity"
+ external int: int => element = "%identity"
+ external string: string => element = "%identity"
+
+ external array: array => element = "%identity"
+
+ type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return>
+
+ type component<'props> = Jsx.component<'props>
+
+ external component: componentLike<'props, element> => component<'props> = "%identity"
+
+ @module("react")
+ external createElement: (component<'props>, 'props) => element = "createElement"
+
+ @module("react")
+ external cloneElement: (element, 'props) => element = "cloneElement"
+
+ @module("react")
+ external isValidElement: 'a => bool = "isValidElement"
+
+ @variadic @module("react")
+ external createElementVariadic: (component<'props>, 'props, array) => element =
+ "createElement"
+
+ @module("react/jsx-runtime")
+ external jsx: (component<'props>, 'props) => element = "jsx"
+
+ @module("react/jsx-runtime")
+ external jsxKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsx"
+
+ @module("react/jsx-runtime")
+ external jsxs: (component<'props>, 'props) => element = "jsxs"
+
+ @module("react/jsx-runtime")
+ external jsxsKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsxs"
+
+ type fragmentProps = {children?: element}
+
+ @module("react/jsx-runtime") external jsxFragment: component = "Fragment"
+}
+
+module ReactDOM = {
+ external someElement: React.element => option = "%identity"
+
+ @module("react/jsx-runtime")
+ external jsx: (string, JsxDOM.domProps) => Jsx.element = "jsx"
+
+ @module("react/jsx-runtime")
+ external jsxKeyed: (string, JsxDOM.domProps, ~key: string=?, @ignore unit) => Jsx.element = "jsx"
+
+ @module("react/jsx-runtime")
+ external jsxs: (string, JsxDOM.domProps) => Jsx.element = "jsxs"
+
+ @module("react/jsx-runtime")
+ external jsxsKeyed: (string, JsxDOM.domProps, ~key: string=?, @ignore unit) => Jsx.element =
+ "jsxs"
+}
+
+module Icon = {
+ @react.component
+ let make = () => {
+
+ }
+}
+
+let _single_element_child =
+
+
{React.string("Hello, world!")}
+
+
+let _multiple_element_children =
+
+
{React.string("Hello, world!")}
+
+
+
+let _single_element_fragment =
+ <>
+
+ >
+
+let _multiple_element_fragment =
+ <>
+
+
+ >
+
+let _unary_element_with_props =
+
+let _container_element_with_props_and_children =
+ {React.string("Hello, world!")}
+
+let baseProps: JsxDOM.domProps = {
+ title: "foo",
+ className: "foo",
+}
+
+let _unary_element_with_spread_props =
+
+let _container_with_spread_props =
+
+ {React.string("Hello, world!")}
+
+
+
+let baseChildren = React.array([
+ {React.string("Hello, world!")} ,
+ {React.string("Hello, world!")} ,
+])
+
+let _container_with_spread_children = ...baseChildren
+
+let _container_with_spread_props_and_children =
+ ...baseChildren
+
+let _unary_element_with_spread_props_keyed =
+
+let _container_with_spread_props_keyed =
+
+ {React.string("Hello, world!")}
+
+
+
+let _unary_element_with_only_spread_props =
+
+// Simulate an external component
+%%raw(`
+ function QueryClientProvider(props) { return props.children }
+ `)
+
+module A = {
+ @react.component
+ external make: (~children: React.element) => React.element = "QueryClientProvider"
+}
+
+module B = {
+ @react.component
+ let make = () => {
+ {React.string("Hello, world!")}
+ }
+}
+
+let _external_component_with_children =
+
+
+
+