Skip to content

Commit 0f66af5

Browse files
cristianocclaude
andcommitted
Single source of truth for ML export names
The JS export name of an ML binding (identifier conversion, with the ES default export kept literal) was computed independently at export emission, qualified-access printing, and the dynamic-import emitter, agreeing only by convention - with silent undefined-resolution as the failure mode if any site drifted. Extract Js_dump_import_export.js_export_name and use it at all four sites. Byte-identical by construction; the existing regression cases ($$case, Operator$$plus, default) now pin one function instead of a convention. Signed-Off-By: Cristiano Calcagno <ccrisccris@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCtQiaDijUqA2fujQXvKUw
1 parent ee2386d commit 0f66af5

4 files changed

Lines changed: 24 additions & 18 deletions

File tree

compiler/core/js_dump.ml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -496,9 +496,7 @@ and vident cxt f (v : J.vident) =
496496
| Qualified ({id; kind = Ml | Runtime}, Some name) ->
497497
let cxt = Ext_pp_scope.ident cxt f id in
498498
P.string f L.dot;
499-
P.string f
500-
(if name = Js_dump_import_export.default_export then name
501-
else Ext_ident.convert name);
499+
P.string f (Js_dump_import_export.js_export_name name);
502500
cxt
503501
| Qualified ({id; kind = External _}, Some name) ->
504502
let cxt = Ext_pp_scope.ident cxt f id in

compiler/core/js_dump_import_export.ml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ module L = Js_dump_lit
2727

2828
let default_export = "default"
2929

30+
(* The emitted export name of an ML binding: JS identifier conversion, with
31+
the ES default export kept literal. Every site that emits, reads, or
32+
reconstructs an ML module's export name must agree with this - the
33+
qualified-access printer in [Js_dump] and the dynamic-import emitter in
34+
[Lam_compile_primitive] included. *)
35+
let js_export_name name =
36+
if name = default_export then name else Ext_ident.convert name
37+
3038
let es_module = ("__esModule", "true")
3139
(* Exports printer *)
3240

@@ -45,12 +53,12 @@ let exports cxt f (idents : Ident.t list) =
4553
let outer_cxt, reversed_list =
4654
Ext_list.fold_left idents (cxt, []) (fun (cxt, acc) id ->
4755
let id_name = id.name in
48-
let s = Ext_ident.convert id_name in
56+
let s = js_export_name id_name in
4957
let str, cxt = Ext_pp_scope.str_of_ident cxt id in
5058
( cxt,
5159
if id_name = default_export then
5260
(* TODO check how it will affect AMDJS*)
53-
es_module :: (default_export, str) :: acc
61+
es_module :: (s, str) :: acc
5462
else (s, str) :: acc ))
5563
in
5664
P.at_least_two_lines f;
@@ -77,11 +85,9 @@ let esmodule_export cxt f (idents : Ident.t list) =
7785
let outer_cxt, reversed_list =
7886
Ext_list.fold_left idents (cxt, []) (fun (cxt, acc) id ->
7987
let id_name = id.name in
80-
let s = Ext_ident.convert id_name in
88+
let s = js_export_name id_name in
8189
let str, cxt = Ext_pp_scope.str_of_ident cxt id in
82-
( cxt,
83-
if id_name = default_export then (default_export, str) :: acc
84-
else (s, str) :: acc ))
90+
(cxt, (s, str) :: acc))
8591
in
8692
P.string f L.export;
8793
P.space f;

compiler/core/js_dump_import_export.mli

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424

2525
val default_export : string
2626

27+
val js_export_name : string -> string
28+
(** The emitted export name of an ML binding: JS identifier conversion,
29+
with the ES default export kept literal. The single source of truth
30+
for export emission, qualified-access printing, and dynamic-import
31+
reconstruction. *)
32+
2733
val exports : Ext_pp_scope.t -> Ext_pp.t -> Ident.t list -> Ext_pp_scope.t
2834

2935
val esmodule_export :

compiler/core/lam_compile_primitive.ml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,11 @@ let translate output_prefix loc (cxt : Lam_compile_context.t)
131131
let oid = Lam_module_ident.of_ml ~dynamic_import:true module_ in
132132
Lam_compile_env.register_ml_module ~dynamic_import:true module_;
133133
let import = import_of_path (import_path oid) in
134-
(* a ReScript module's JS export names go through the same identifier
135-
conversion js_dump applies to qualified access and export lists:
136-
a binding named [case] is exported as [$$case], and a hoisted
137-
[Operator.\"+"] whose cmj metadata stores the raw flattened name
138-
[Operator$+] is exported as [Operator$$plus] *)
139-
let ml_export_name name =
140-
if name = Js_dump_import_export.default_export then name
141-
else Ext_ident.convert name
142-
in
134+
(* the single source of truth for ML export names: a binding named
135+
[case] is exported as [$$case], and a hoisted [Operator.\"+"] whose
136+
cmj metadata stores the raw flattened name [Operator$+] is
137+
exported as [Operator$$plus] *)
138+
let ml_export_name = Js_dump_import_export.js_export_name in
143139
match path with
144140
| [] -> import
145141
| [name] -> wrap_then import (ml_export_name name)

0 commit comments

Comments
 (0)