Source file source_map.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
type t = {
files : (string, int) Hashtbl.t;
mutable next_file_idx : int;
mutable mappings : entry list;
}
and entry = Mapped of mapping | Unmapped of int
and mapping = {
generated_offset : int;
original_file_idx : int;
original_line : int;
original_column : int;
}
let create () = { files = Hashtbl.create 10; next_file_idx = 0; mappings = [] }
let entry_offset = function Mapped m -> m.generated_offset | Unmapped o -> o
let register_file t filename =
match Hashtbl.find_opt t.files filename with
| Some idx -> idx
| None ->
let idx = t.next_file_idx in
Hashtbl.add t.files filename idx;
t.next_file_idx <- idx + 1;
idx
let add_mapping_at t ~generated_offset ~(position : Lexing.position) =
let file_idx = register_file t position.Lexing.pos_fname in
let new_mapping =
{
generated_offset;
original_file_idx = file_idx;
original_line = position.Lexing.pos_lnum - 1 ;
original_column =
position.Lexing.pos_cnum - position.Lexing.pos_bol ;
}
in
t.mappings <- Mapped new_mapping :: t.mappings
let add_mapping t ~generated_offset ~original_location =
add_mapping_at t ~generated_offset ~position:original_location.Ast.loc_start
let add_absent_mapping t ~generated_offset =
t.mappings <- Unmapped generated_offset :: t.mappings
type checkpoint = entry list
let checkpoint t = t.mappings
let shift_since t (cp : checkpoint) ~delta =
if delta <> 0 then begin
let rec loop l =
if l == cp then l
else
match l with
| [] -> []
| Mapped m :: rest ->
Mapped { m with generated_offset = m.generated_offset + delta }
:: loop rest
| Unmapped o :: rest -> Unmapped (o + delta) :: loop rest
in
t.mappings <- loop t.mappings
end
let base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
let encode_vlq_int n =
let n' = ref (if n < 0 then (abs n lsl 1) lor 1 else n lsl 1) in
let result = ref [] in
while !n' <> 0 || !result = [] do
let digit = !n' land 0x1F in
n' := !n' lsr 5;
if !n' <> 0 then result := base64.[digit lor 0x20] :: !result
else result := base64.[digit] :: !result
done;
List.rev !result |> List.to_seq |> String.of_seq
let json_string s =
let buf = Buffer.create (String.length s + 2) in
Buffer.add_char buf '"';
String.iter
(fun c ->
match c with
| '"' -> Buffer.add_string buf "\\\""
| '\\' -> Buffer.add_string buf "\\\\"
| '\n' -> Buffer.add_string buf "\\n"
| '\r' -> Buffer.add_string buf "\\r"
| '\t' -> Buffer.add_string buf "\\t"
| c when Char.code c < 0x20 ->
Buffer.add_string buf (Printf.sprintf "\\u%04x" (Char.code c))
| c -> Buffer.add_char buf c)
s;
Buffer.add_char buf '"';
Buffer.contents buf
let to_json t ~file_name =
let sorted_mappings =
List.sort (fun a b -> compare (entry_offset a) (entry_offset b)) t.mappings
in
let sorted_mappings =
let rec dedup acc last = function
| [] -> List.rev acc
| Unmapped _ :: rest when last <> `Mapped -> dedup acc last rest
| (Unmapped _ as e) :: rest -> dedup (e :: acc) `Unmapped rest
| (Mapped _ as e) :: rest -> dedup (e :: acc) `Mapped rest
in
dedup [] `Unmapped sorted_mappings
in
let files_list =
Hashtbl.fold (fun f_name f_idx acc -> (f_idx, f_name) :: acc) t.files []
|> List.sort (fun (idx_a, _) (idx_b, _) -> compare idx_a idx_b)
|> List.map snd
in
let mappings_string =
let prev_gen_col = ref 0 in
let prev_orig_file_idx = ref 0 in
let prev_orig_line = ref 0 in
let prev_orig_col = ref 0 in
List.fold_left
(fun acc entry ->
let segment =
match entry with
| Unmapped generated_offset ->
let s = encode_vlq_int (generated_offset - !prev_gen_col) in
prev_gen_col := generated_offset;
s
| Mapped mapping ->
let s =
String.concat ""
[
encode_vlq_int (mapping.generated_offset - !prev_gen_col);
encode_vlq_int
(mapping.original_file_idx - !prev_orig_file_idx);
encode_vlq_int (mapping.original_line - !prev_orig_line);
encode_vlq_int (mapping.original_column - !prev_orig_col);
]
in
prev_gen_col := mapping.generated_offset;
prev_orig_file_idx := mapping.original_file_idx;
prev_orig_line := mapping.original_line;
prev_orig_col := mapping.original_column;
s
in
segment :: acc)
[] sorted_mappings
|> List.rev |> String.concat ","
in
Printf.sprintf
{|{
"version": 3,
"file": %s,
"sourceRoot": "",
"sources": [%s],
"sourcesContent": [],
"names": [],
"mappings": "%s"
}|}
(json_string file_name)
(String.concat "," (List.map json_string files_list))
mappings_string