Skip to content

Commit 1892e2f

Browse files
committed
Add inline and hidden source map modes
1 parent 0778383 commit 1892e2f

10 files changed

Lines changed: 430 additions & 56 deletions

File tree

compiler/bsc/rescript_compiler_main.ml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,14 @@ let[@inline] string_list_add s : Bsc_args.spec = String (String_list_add s)
212212
let parse_source_map value =
213213
Js_config.source_map :=
214214
match String.lowercase_ascii value with
215-
| "true" | "linked" -> Linked
215+
| "linked" -> Linked
216+
| "inline" -> Inline
217+
| "hidden" -> Hidden
216218
| "false" | "none" -> No_source_map
217-
| value -> Bsc_args.bad_arg ("Unsupported sourceMap value: " ^ value)
219+
| value ->
220+
Bsc_args.bad_arg
221+
("Unsupported sourceMap value: " ^ value
222+
^ ". Expected linked, inline, hidden, false, or none")
218223

219224
let parse_bool_ref target value =
220225
target :=

compiler/common/js_config.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
type jsx_version = Jsx_v4
2828
type jsx_module = React | Generic of {module_name: string}
29-
type source_map = No_source_map | Linked
29+
type source_map = No_source_map | Linked | Inline | Hidden
3030

3131
let no_version_header = ref false
3232

compiler/common/js_config.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
type jsx_version = Jsx_v4
2626
type jsx_module = React | Generic of {module_name: string}
27-
type source_map = No_source_map | Linked
27+
type source_map = No_source_map | Linked | Inline | Hidden
2828

2929
(* val get_packages_info :
3030
unit -> Js_packages_info.t *)

compiler/core/js_source_map.ml

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
generated position against the original ReScript location.
1515
4. After printing, the collected mappings are sorted and encoded into the
1616
compact Source Map v3 "mappings" field using base64 VLQ. The JSON map is
17-
emitted next to the generated JavaScript and the JS file receives a
18-
sourceMappingURL comment.
17+
either emitted next to the generated JavaScript or embedded into it,
18+
depending on the configured source map mode.
1919
2020
Original locations come from OCaml Location.t values, whose columns are byte
2121
offsets. When source contents are available, original columns are converted
@@ -52,7 +52,7 @@ let current : t option ref = ref None
5252
let source_loc_of_loc (loc : Location.t) =
5353
match !Js_config.source_map with
5454
| No_source_map -> None
55-
| Linked ->
55+
| Linked | Inline | Hidden ->
5656
if loc.loc_ghost || loc.loc_start.pos_cnum < 0 then None else Some loc
5757

5858
let with_builder builder f =
@@ -283,3 +283,35 @@ let json builder =
283283

284284
let linked_comment ~map_file =
285285
"//# sourceMappingURL=" ^ Filename.basename map_file ^ "\n"
286+
287+
let base64_chars =
288+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
289+
290+
(* OCaml Stdlib does not provide Base64, and inline source maps only need a
291+
small RFC 4648 encoder with padding for the data URI payload. Keep this
292+
local instead of adding a package dependency just for this narrow use. *)
293+
let base64_encode input =
294+
let len = String.length input in
295+
let output = Buffer.create ((len + 2) / 3 * 4) in
296+
let rec loop index =
297+
if index < len then (
298+
let b0 = Char.code input.[index] in
299+
let has_b1 = index + 1 < len in
300+
let has_b2 = index + 2 < len in
301+
let b1 = if has_b1 then Char.code input.[index + 1] else 0 in
302+
let b2 = if has_b2 then Char.code input.[index + 2] else 0 in
303+
let chunk = (b0 lsl 16) lor (b1 lsl 8) lor b2 in
304+
Buffer.add_char output base64_chars.[(chunk lsr 18) land 0x3f];
305+
Buffer.add_char output base64_chars.[(chunk lsr 12) land 0x3f];
306+
Buffer.add_char output
307+
(if has_b1 then base64_chars.[(chunk lsr 6) land 0x3f] else '=');
308+
Buffer.add_char output
309+
(if has_b2 then base64_chars.[chunk land 0x3f] else '=');
310+
loop (index + 3))
311+
in
312+
loop 0;
313+
Buffer.contents output
314+
315+
let inline_comment ~json =
316+
"//# sourceMappingURL=data:application/json;base64," ^ base64_encode json
317+
^ "\n"

compiler/core/js_source_map.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ val mark_source_loc : Ext_pp.t -> Location.t option -> unit
1212
val json : t -> string
1313

1414
val linked_comment : map_file:string -> string
15+
16+
val inline_comment : json:string -> string

compiler/core/lam_compile_main.ml

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,10 @@ let (//) = Filename.concat
300300
let source_map_enabled () =
301301
match !Js_config.source_map with
302302
| No_source_map -> false
303-
| Linked -> true
303+
| Linked | Inline | Hidden -> true
304304

305-
let dump_deps_program_with_source_map ~target_file ~output_prefix module_system
306-
lambda_output chan =
305+
let dump_deps_program_with_source_map ?(remove_stale_map = true) ~target_file
306+
~output_prefix module_system lambda_output chan =
307307
let builder =
308308
if source_map_enabled () then
309309
Some
@@ -315,11 +315,23 @@ let dump_deps_program_with_source_map ~target_file ~output_prefix module_system
315315
Js_source_map.with_builder builder (fun () ->
316316
Js_dump_program.pp_deps_program ~output_prefix module_system lambda_output
317317
(Ext_pp.from_channel chan));
318+
let map_file = target_file ^ ".map" in
319+
let remove_map_file () =
320+
if remove_stale_map && not !Clflags.dont_write_files then
321+
Misc.remove_file map_file
322+
in
318323
match (builder, !Js_config.source_map) with
319324
| Some builder, Linked ->
320-
let map_file = target_file ^ ".map" in
325+
let json = Js_source_map.json builder in
321326
output_string chan (Js_source_map.linked_comment ~map_file);
327+
Ext_io.write_file map_file json
328+
| Some builder, Hidden ->
322329
Ext_io.write_file map_file (Js_source_map.json builder)
330+
| Some builder, Inline ->
331+
output_string chan
332+
(Js_source_map.inline_comment ~json:(Js_source_map.json builder));
333+
remove_map_file ()
334+
| _, No_source_map -> remove_map_file ()
323335
| _ -> ()
324336

325337
let lambda_as_module
@@ -328,7 +340,18 @@ let lambda_as_module
328340
: unit =
329341
let package_info = Js_packages_state.get_packages_info () in
330342
if Js_packages_info.is_empty package_info && !Js_config.js_stdout then begin
331-
Js_dump_program.dump_deps_program ~output_prefix Commonjs (lambda_output) stdout
343+
match !Js_config.source_map with
344+
| Inline ->
345+
let target_file =
346+
Ext_namespace.change_ext_ns_suffix
347+
(Filename.basename output_prefix)
348+
Literals.suffix_js
349+
in
350+
dump_deps_program_with_source_map ~remove_stale_map:false ~target_file
351+
~output_prefix Commonjs lambda_output stdout
352+
| _ ->
353+
Js_dump_program.dump_deps_program ~output_prefix Commonjs lambda_output
354+
stdout
332355
end else
333356
Js_packages_info.iter package_info (fun {module_system; path; suffix} ->
334357
let basename =

docs/docson/build-schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
"description": "`dev` generates source maps only during watch mode. `always` generates source maps during both build and watch."
6262
},
6363
"mode": {
64-
"enum": ["linked"],
65-
"description": "Generate a separate .js.map file next to each generated JavaScript file and append a sourceMappingURL comment. Only linked source maps are supported for now."
64+
"enum": ["linked", "inline", "hidden"],
65+
"description": "`linked` generates a separate .js.map file and appends a sourceMappingURL comment. `inline` appends the source map as a data URI and does not generate a .js.map file. `hidden` generates a separate .js.map file without appending a sourceMappingURL comment. When `inline` is combined with `sourcesContent: true`, original .res source text is embedded directly into the generated JS."
6666
},
6767
"sourcesContent": {
6868
"type": "boolean",

rewatch/src/build/compile.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,8 @@ fn compile_file(
10671067
if source_map.exists() {
10681068
let _ = std::fs::copy(&source_map, &destination_map)
10691069
.expect("copying source map file failed");
1070+
} else {
1071+
let _ = std::fs::remove_file(&destination_map);
10701072
}
10711073
}
10721074
});

0 commit comments

Comments
 (0)