Skip to content

Commit f510d13

Browse files
authored
Import attributes (#6599)
* initial implementation of import attributes * change expected layout of import attributes in @module * quotation around keys * add test for regular import * refactor * revert * changelog
1 parent 7d1fed8 commit f510d13

18 files changed

+237
-56
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
1313
# 11.1.0-rc.2 (Unreleased)
1414

15+
#### :rocket: New Feature
16+
17+
- Support import attributes (https://github.com/tc39/proposal-import-attributes) in `@module()`. https://github.com/rescript-lang/rescript-compiler/pull/6599
18+
1519
#### :bug: Bug Fix
1620

1721
- Fix issue with async and newtype in uncurried mode. https://github.com/rescript-lang/rescript-compiler/pull/6601

jscomp/core/js_dump_import_export.ml

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,24 +120,49 @@ let requires require_lit cxt f (modules : (Ident.t * string * bool) list) =
120120
P.newline f);
121121
outer_cxt
122122

123+
let dumpImportAttributes f (importAttributes : External_ffi_types.import_attributes option) =
124+
match importAttributes with
125+
| None -> ()
126+
| Some importAttributes ->
127+
P.space f;
128+
P.string f "with";
129+
P.space f;
130+
let total = Hashtbl.length importAttributes in
131+
let idx = ref 1 in
132+
P.brace_group f 0 (
133+
fun _ ->
134+
importAttributes |> Hashtbl.iter(fun key value ->
135+
Js_dump_string.pp_string f key;
136+
P.string f L.colon_space;
137+
Js_dump_string.pp_string f value;
138+
let shouldAddComma = !idx < total in
139+
if shouldAddComma then (
140+
P.string f L.comma;
141+
P.space f
142+
);
143+
idx := !idx + 1;
144+
)
145+
)
146+
123147
(** ES6 module style imports *)
124-
let imports cxt f (modules : (Ident.t * string * bool) list) =
148+
let imports cxt f (modules : (Ident.t * string * bool * External_ffi_types.import_attributes option) list) =
125149
(* the context used to print the following program *)
126150
let outer_cxt, reversed_list =
127-
Ext_list.fold_left modules (cxt, []) (fun (cxt, acc) (id, s, b) ->
151+
Ext_list.fold_left modules (cxt, []) (fun (cxt, acc) (id, s, b, i) ->
128152
let str, cxt = Ext_pp_scope.str_of_ident cxt id in
129-
(cxt, (str, s, b) :: acc))
153+
(cxt, (str, s, b, i) :: acc))
130154
in
131155
P.at_least_two_lines f;
132-
Ext_list.rev_iter reversed_list (fun (s, file, default) ->
156+
Ext_list.rev_iter reversed_list (fun (s, file, default, import_attributes) ->
133157
P.string f L.import;
134158
P.space f;
135159
if default then (
136160
P.string f s;
137161
P.space f;
138162
P.string f L.from;
139163
P.space f;
140-
Js_dump_string.pp_string f file)
164+
Js_dump_string.pp_string f file;
165+
dumpImportAttributes f import_attributes)
141166
else (
142167
P.string f L.star;
143168
P.space f;
@@ -148,7 +173,8 @@ let imports cxt f (modules : (Ident.t * string * bool) list) =
148173
P.space f;
149174
P.string f L.from;
150175
P.space f;
151-
Js_dump_string.pp_string f file);
176+
Js_dump_string.pp_string f file;
177+
dumpImportAttributes f import_attributes);
152178
P.string f L.semi;
153179
P.newline f);
154180
outer_cxt

jscomp/core/js_dump_import_export.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ val requires :
3636
Ext_pp_scope.t
3737

3838
val imports :
39-
Ext_pp_scope.t -> Ext_pp.t -> (Ident.t * string * bool) list -> Ext_pp_scope.t
39+
Ext_pp_scope.t -> Ext_pp.t -> (Ident.t * string * bool * External_ffi_types.import_attributes option) list -> Ext_pp_scope.t

jscomp/core/js_dump_program.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ let es6_program ~output_dir fmt f (x : J.deps_program) =
9494
| false ->
9595
Some ( x.id,
9696
Js_name_of_module_id.string_of_module_id x ~output_dir fmt,
97-
is_default x.kind )))
97+
is_default x.kind,
98+
(match x.kind with | External {import_attributes} -> import_attributes | _ -> None) )))
9899
in
99100
let () = P.at_least_two_lines f in
100101
let cxt = Js_dump.statements true cxt f x.program.block in

jscomp/core/js_exp_make.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,20 +92,20 @@ let ml_var_dot ?comment ?(dynamic_import = false) (id : Ident.t) e : J.expressio
9292
var http = require("http")
9393
]}
9494
*)
95-
let external_var_field ?comment ~external_name:name (id : Ident.t) ~field
95+
let external_var_field ?import_attributes ?comment ~external_name:name (id : Ident.t) ~field
9696
~default : t =
9797
{
9898
expression_desc =
99-
Var (Qualified ({ id; kind = External { name; default }; dynamic_import = false }, Some field));
99+
Var (Qualified ({ id; kind = External { name; default; import_attributes }; dynamic_import = false }, Some field));
100100
comment;
101101
}
102102

103-
let external_var ?comment ~external_name (id : Ident.t) : t =
103+
let external_var ?import_attributes ?comment ~external_name (id : Ident.t) : t =
104104
{
105105
expression_desc =
106106
Var
107107
(Qualified
108-
( { id; kind = External { name = external_name; default = false }; dynamic_import = false },
108+
( { id; kind = External { name = external_name; default = false; import_attributes }; dynamic_import = false },
109109
None ));
110110
comment;
111111
}

jscomp/core/js_exp_make.mli

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ val ml_var_dot : ?comment:string -> ?dynamic_import:bool -> Ident.t -> string ->
5454
*)
5555

5656
val external_var_field :
57+
?import_attributes:External_ffi_types.import_attributes ->
5758
?comment:string ->
5859
external_name:string ->
5960
Ident.t ->
@@ -64,7 +65,7 @@ val external_var_field :
6465
Used in FFI
6566
*)
6667

67-
val external_var : ?comment:string -> external_name:string -> Ident.t -> t
68+
val external_var : ?import_attributes:External_ffi_types.import_attributes -> ?comment:string -> external_name:string -> Ident.t -> t
6869

6970
val ml_module_as_var : ?comment:string -> ?dynamic_import:bool -> Ident.t -> t
7071

jscomp/core/js_op.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ type int_op =
114114
*)
115115
type level = Log | Info | Warn | Error
116116

117-
type kind = Ml | Runtime | External of { name : string; default : bool }
117+
type kind = Ml | Runtime | External of { name : string; default : bool; import_attributes : External_ffi_types.import_attributes option }
118118

119119
type property = Lam_compat.let_kind = Strict | Alias | StrictOpt | Variable
120120

jscomp/core/lam_compile_env.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ let reset () =
5959
since when we print it in the end, it will
6060
be escaped quite ugly
6161
*)
62-
let add_js_module (hint_name : External_ffi_types.module_bind_name)
62+
let add_js_module ?import_attributes (hint_name : External_ffi_types.module_bind_name)
6363
(module_name : string) default : Ident.t =
6464
let id =
6565
Ident.create
@@ -71,7 +71,7 @@ let add_js_module (hint_name : External_ffi_types.module_bind_name)
7171
| Phint_nothing -> Ext_modulename.js_id_name_of_hint_name module_name)
7272
in
7373
let lam_module_ident : J.module_id =
74-
{ id; kind = External { name = module_name; default }; dynamic_import = false }
74+
{ id; kind = External { name = module_name; default; import_attributes }; dynamic_import = false }
7575
in
7676
match Lam_module_ident.Hash.find_key_opt cached_tbl lam_module_ident with
7777
| None ->

jscomp/core/lam_compile_env.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
val reset : unit -> unit
2828

2929
val add_js_module :
30-
External_ffi_types.module_bind_name -> string -> bool -> Ident.t
30+
?import_attributes:External_ffi_types.import_attributes -> External_ffi_types.module_bind_name -> string -> bool -> Ident.t
3131
(**
3232
[add_js_module hint_name module_name]
3333
Given a js module name and hint name, assign an id to it

jscomp/core/lam_compile_external_call.ml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ let splice_obj_apply obj name args =
5050
bundle *)
5151

5252
let external_var
53-
({ bundle; module_bind_name } : External_ffi_types.external_module_name) =
54-
let id = Lam_compile_env.add_js_module module_bind_name bundle false in
55-
E.external_var id ~external_name:bundle
53+
({ bundle; module_bind_name; import_attributes } : External_ffi_types.external_module_name) =
54+
let id = Lam_compile_env.add_js_module ?import_attributes module_bind_name bundle false in
55+
E.external_var ?import_attributes ~external_name:bundle id
5656

5757
(* let handle_external_opt
5858
(module_name : External_ffi_types.external_module_name option)
@@ -218,22 +218,22 @@ let translate_scoped_module_val
218218
(module_name : External_ffi_types.external_module_name option) (fn : string)
219219
(scopes : string list) =
220220
match module_name with
221-
| Some { bundle; module_bind_name } -> (
221+
| Some { bundle; module_bind_name; import_attributes } -> (
222222
match scopes with
223223
| [] ->
224224
let default = fn = "default" in
225225
let id =
226-
Lam_compile_env.add_js_module module_bind_name bundle default
226+
Lam_compile_env.add_js_module ?import_attributes module_bind_name bundle default
227227
in
228-
E.external_var_field ~external_name:bundle ~field:fn ~default id
228+
E.external_var_field ?import_attributes ~external_name:bundle ~field:fn ~default id
229229
| x :: rest ->
230230
(* TODO: what happens when scope contains "default" ?*)
231231
let default = false in
232232
let id =
233-
Lam_compile_env.add_js_module module_bind_name bundle default
233+
Lam_compile_env.add_js_module ?import_attributes module_bind_name bundle default
234234
in
235235
let start =
236-
E.external_var_field ~external_name:bundle ~field:x ~default id
236+
E.external_var_field ?import_attributes ~external_name:bundle ~field:x ~default id
237237
in
238238
Ext_list.fold_left (Ext_list.append rest [ fn ]) start E.dot)
239239
| None -> (

jscomp/core/lam_module_ident.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ module Cmp = struct
5252
type nonrec t = t
5353
let equal (x : t) y =
5454
match x.kind with
55-
| External {name = x_kind; default = x_default}->
55+
| External {name = x_kind; default = x_default; _} ->
5656
begin match y.kind with
57-
| External {name = y_kind; default = y_default} ->
57+
| External {name = y_kind; default = y_default; _} ->
5858
x_kind = (y_kind : string) && x_default = y_default
5959
| _ -> false
6060
end

jscomp/frontend/ast_external_process.ml

Lines changed: 115 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,12 @@ let parse_external_attributes (no_arguments : bool) (prim_name_check : string)
248248
{
249249
st with
250250
external_module_name =
251-
Some {bundle; module_bind_name = Phint_nothing};
251+
Some
252+
{
253+
bundle;
254+
module_bind_name = Phint_nothing;
255+
import_attributes = None;
256+
};
252257
} )
253258
else
254259
let action () =
@@ -262,30 +267,115 @@ let parse_external_attributes (no_arguments : bool) (prim_name_check : string)
262267
call_name = Some (name_from_payload_or_prim ~loc payload);
263268
}
264269
| "bs.module" | "module" -> (
265-
match Ast_payload.assert_strings loc payload with
266-
| [bundle] ->
267-
{
268-
st with
269-
external_module_name =
270-
Some {bundle; module_bind_name = Phint_nothing};
271-
}
272-
| [bundle; bind_name] ->
273-
{
274-
st with
275-
external_module_name =
276-
Some {bundle; module_bind_name = Phint_name bind_name};
277-
}
278-
| [] ->
279-
{
280-
st with
281-
module_as_val =
282-
Some
283-
{
284-
bundle = prim_name_or_pval_prim.name;
285-
module_bind_name = Phint_nothing;
286-
};
287-
}
288-
| _ -> Bs_syntaxerr.err loc Illegal_attribute)
270+
match payload with
271+
| PStr
272+
[
273+
{
274+
pstr_desc =
275+
Pstr_eval
276+
({pexp_loc; pexp_desc = Pexp_record (fields, _); _}, _);
277+
_;
278+
};
279+
] -> (
280+
let fromName = ref None in
281+
let with_ = ref None in
282+
fields
283+
|> List.iter
284+
(fun
285+
((l, exp) :
286+
Longident.t Location.loc * Parsetree.expression)
287+
->
288+
match (l, exp.pexp_desc) with
289+
| ( {txt = Lident "from"; _},
290+
Pexp_constant (Pconst_string (s, _)) ) ->
291+
fromName := Some s
292+
| {txt = Lident "with"; _}, Pexp_record (fields, _) ->
293+
with_ := Some fields
294+
| _ -> ());
295+
match (!fromName, !with_) with
296+
| None, _ ->
297+
Location.raise_errorf ~loc:pexp_loc
298+
"@module annotations with import attributes must have a \
299+
\"from\" field. This \"from\" field should point to the JS \
300+
module to import, just like the string payload to @module \
301+
normally does."
302+
| Some _, None ->
303+
Location.raise_errorf ~loc:pexp_loc
304+
"@module annotations with import attributes must have a \
305+
\"with\" field. This \"with\" field should hold a record of \
306+
the import attributes you want applied to the import."
307+
| Some fromName, Some withFields ->
308+
let importAttributesFromRecord =
309+
withFields
310+
|> List.filter_map
311+
(fun
312+
((l, exp) :
313+
Longident.t Location.loc * Parsetree.expression)
314+
->
315+
match exp.pexp_desc with
316+
| Pexp_constant (Pconst_string (s, _)) -> (
317+
match l.txt with
318+
| Longident.Lident "type_" -> Some ("type", s)
319+
| Longident.Lident txt -> Some (txt, s)
320+
| _ ->
321+
Location.raise_errorf ~loc:exp.pexp_loc
322+
"Field must be a regular key.")
323+
| _ ->
324+
Location.raise_errorf ~loc:exp.pexp_loc
325+
"Only string values are allowed here.")
326+
in
327+
let import_attributes =
328+
Hashtbl.create (List.length importAttributesFromRecord)
329+
in
330+
importAttributesFromRecord
331+
|> List.iter (fun (key, value) ->
332+
Hashtbl.replace import_attributes key value);
333+
{
334+
st with
335+
external_module_name =
336+
Some
337+
{
338+
bundle = fromName;
339+
module_bind_name = Phint_nothing;
340+
import_attributes = Some import_attributes;
341+
};
342+
})
343+
| _ -> (
344+
match Ast_payload.assert_strings loc payload with
345+
| [bundle] ->
346+
{
347+
st with
348+
external_module_name =
349+
Some
350+
{
351+
bundle;
352+
module_bind_name = Phint_nothing;
353+
import_attributes = None;
354+
};
355+
}
356+
| [bundle; bind_name] ->
357+
{
358+
st with
359+
external_module_name =
360+
Some
361+
{
362+
bundle;
363+
module_bind_name = Phint_name bind_name;
364+
import_attributes = None;
365+
};
366+
}
367+
| [] ->
368+
{
369+
st with
370+
module_as_val =
371+
Some
372+
{
373+
bundle = prim_name_or_pval_prim.name;
374+
module_bind_name = Phint_nothing;
375+
import_attributes = None;
376+
};
377+
}
378+
| _ -> Bs_syntaxerr.err loc Illegal_attribute))
289379
| "bs.scope" | "scope" -> (
290380
match Ast_payload.assert_strings loc payload with
291381
| [] -> Bs_syntaxerr.err loc Illegal_attribute

0 commit comments

Comments
 (0)