Skip to content

Commit 39bbbbc

Browse files
authored
fix: use inline source maps when present in js (denoland#8995)
1 parent 8d1ee3b commit 39bbbbc

17 files changed

+157
-48
lines changed

.dprintrc.json

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"cli/dts/lib.webworker*.d.ts",
2222
"cli/dts/typescript.d.ts",
2323
"cli/tests/encoding",
24+
"cli/tests/inline_js_source_map*",
2425
"cli/tsc/*typescript.js",
2526
"test_util/wpt",
2627
"gh-pages",

cli/ops/errors.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ fn op_apply_source_map(
3737
let mut mappings_map: CachedMaps = HashMap::new();
3838
let program_state = state.borrow::<Arc<ProgramState>>().clone();
3939

40-
let (orig_file_name, orig_line_number, orig_column_number) =
40+
let (orig_file_name, orig_line_number, orig_column_number, _) =
4141
get_orig_position(
4242
args.file_name,
4343
args.line_number.into(),
4444
args.column_number.into(),
45+
None,
4546
&mut mappings_map,
4647
program_state,
4748
);

cli/program_state.rs

+23-17
Original file line numberDiff line numberDiff line change
@@ -300,24 +300,10 @@ impl SourceMapGetter for ProgramState {
300300
maybe_map
301301
} else {
302302
let code = String::from_utf8(code).unwrap();
303-
let lines: Vec<&str> = code.split('\n').collect();
304-
if let Some(last_line) = lines.last() {
305-
if last_line
306-
.starts_with("//# sourceMappingURL=data:application/json;base64,")
307-
{
308-
let input = last_line.trim_start_matches(
309-
"//# sourceMappingURL=data:application/json;base64,",
310-
);
311-
let decoded_map = base64::decode(input)
312-
.expect("Unable to decode source map from emitted file.");
313-
Some(decoded_map)
314-
} else {
315-
None
316-
}
317-
} else {
318-
None
319-
}
303+
source_map_from_code(code)
320304
}
305+
} else if let Ok(source) = self.load(specifier, None) {
306+
source_map_from_code(source.code)
321307
} else {
322308
None
323309
}
@@ -345,6 +331,26 @@ impl SourceMapGetter for ProgramState {
345331
}
346332
}
347333

334+
fn source_map_from_code(code: String) -> Option<Vec<u8>> {
335+
let lines: Vec<&str> = code.split('\n').collect();
336+
if let Some(last_line) = lines.last() {
337+
if last_line
338+
.starts_with("//# sourceMappingURL=data:application/json;base64,")
339+
{
340+
let input = last_line.trim_start_matches(
341+
"//# sourceMappingURL=data:application/json;base64,",
342+
);
343+
let decoded_map = base64::decode(input)
344+
.expect("Unable to decode source map from emitted file.");
345+
Some(decoded_map)
346+
} else {
347+
None
348+
}
349+
} else {
350+
None
351+
}
352+
}
353+
348354
#[test]
349355
fn thread_safe() {
350356
fn f<S: Send + Sync>(_: S) {}

cli/source_maps.rs

+53-29
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,15 @@ pub fn apply_source_map<G: SourceMapGetter>(
3333
// prepareStackTrace().
3434
let mut mappings_map: CachedMaps = HashMap::new();
3535

36-
let (script_resource_name, line_number, start_column) =
36+
let (script_resource_name, line_number, start_column, source_line) =
3737
get_maybe_orig_position(
3838
js_error.script_resource_name.clone(),
3939
js_error.line_number,
4040
// start_column is 0-based, we need 1-based here.
4141
js_error.start_column.map(|n| n + 1),
42+
js_error.source_line.clone(),
4243
&mut mappings_map,
43-
getter.clone(),
44+
getter,
4445
);
4546
let start_column = start_column.map(|n| n - 1);
4647
// It is better to just move end_column to be the same distance away from
@@ -56,20 +57,6 @@ pub fn apply_source_map<G: SourceMapGetter>(
5657
}
5758
_ => None,
5859
};
59-
// if there is a source line that we might be different in the source file, we
60-
// will go fetch it from the getter
61-
let source_line = match line_number {
62-
Some(ln)
63-
if js_error.source_line.is_some() && script_resource_name.is_some() =>
64-
{
65-
getter.get_source_line(
66-
&js_error.script_resource_name.clone().unwrap(),
67-
// Getter expects 0-based line numbers, but ours are 1-based.
68-
ln as usize - 1,
69-
)
70-
}
71-
_ => js_error.source_line.clone(),
72-
};
7360

7461
JsError {
7562
message: js_error.message.clone(),
@@ -87,28 +74,43 @@ fn get_maybe_orig_position<G: SourceMapGetter>(
8774
file_name: Option<String>,
8875
line_number: Option<i64>,
8976
column_number: Option<i64>,
77+
source_line: Option<String>,
9078
mappings_map: &mut CachedMaps,
9179
getter: Arc<G>,
92-
) -> (Option<String>, Option<i64>, Option<i64>) {
80+
) -> (Option<String>, Option<i64>, Option<i64>, Option<String>) {
9381
match (file_name, line_number, column_number) {
9482
(Some(file_name_v), Some(line_v), Some(column_v)) => {
95-
let (file_name, line_number, column_number) =
96-
get_orig_position(file_name_v, line_v, column_v, mappings_map, getter);
97-
(Some(file_name), Some(line_number), Some(column_number))
83+
let (file_name, line_number, column_number, source_line) =
84+
get_orig_position(
85+
file_name_v,
86+
line_v,
87+
column_v,
88+
source_line,
89+
mappings_map,
90+
getter,
91+
);
92+
(
93+
Some(file_name),
94+
Some(line_number),
95+
Some(column_number),
96+
source_line,
97+
)
9898
}
99-
_ => (None, None, None),
99+
_ => (None, None, None, source_line),
100100
}
101101
}
102102

103103
pub fn get_orig_position<G: SourceMapGetter>(
104104
file_name: String,
105105
line_number: i64,
106106
column_number: i64,
107+
source_line: Option<String>,
107108
mappings_map: &mut CachedMaps,
108109
getter: Arc<G>,
109-
) -> (String, i64, i64) {
110-
let maybe_source_map = get_mappings(&file_name, mappings_map, getter);
111-
let default_pos = (file_name, line_number, column_number);
110+
) -> (String, i64, i64, Option<String>) {
111+
let maybe_source_map = get_mappings(&file_name, mappings_map, getter.clone());
112+
let default_pos =
113+
(file_name, line_number, column_number, source_line.clone());
112114

113115
// Lookup expects 0-based line and column numbers, but ours are 1-based.
114116
let line_number = line_number - 1;
@@ -121,11 +123,33 @@ pub fn get_orig_position<G: SourceMapGetter>(
121123
None => default_pos,
122124
Some(token) => match token.get_source() {
123125
None => default_pos,
124-
Some(original) => (
125-
original.to_string(),
126-
i64::from(token.get_src_line()) + 1,
127-
i64::from(token.get_src_col()) + 1,
128-
),
126+
Some(original) => {
127+
let maybe_source_line =
128+
if let Some(source_view) = token.get_source_view() {
129+
source_view.get_line(token.get_src_line())
130+
} else {
131+
None
132+
};
133+
134+
let source_line = if let Some(source_line) = maybe_source_line {
135+
Some(source_line.to_string())
136+
} else if let Some(source_line) = getter.get_source_line(
137+
original,
138+
// Getter expects 0-based line numbers, but ours are 1-based.
139+
token.get_src_line() as usize,
140+
) {
141+
Some(source_line)
142+
} else {
143+
source_line
144+
};
145+
146+
(
147+
original.to_string(),
148+
i64::from(token.get_src_line()) + 1,
149+
i64::from(token.get_src_col()) + 1,
150+
source_line,
151+
)
152+
}
129153
},
130154
}
131155
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
[WILDCARD]error: Uncaught PermissionDenied: read access to "non-existent", run again with the --allow-read flag
2+
throw new ErrorClass(res.err.message);
3+
^
24
at [WILDCARD]

cli/tests/073_worker_error.ts.out

+2
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
at foo ([WILDCARD])
33
at [WILDCARD]
44
error: Uncaught (in promise) Error: Unhandled error event reached main worker.
5+
throw new Error("Unhandled error event reached main worker.");
6+
^
57
at Worker.#poll ([WILDCARD])

cli/tests/074_worker_nested_error.ts.out

+2
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
at foo ([WILDCARD])
33
at [WILDCARD]
44
error: Uncaught (in promise) Error: Unhandled error event reached main worker.
5+
throw new Error("Unhandled error event reached main worker.");
6+
^
57
at Worker.#poll ([WILDCARD])
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[WILDCARD]
2-
error: Found 2 not formatted files in [WILDCARD] files
2+
error: Found 5 not formatted files in [WILDCARD] files

cli/tests/inline_js_source_map.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
1 + 1;
2+
interface Test {
3+
hello: string;
4+
}
5+
6+
// throw new Error("Hello world!" as string);

cli/tests/inline_js_source_map_2.js

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
error: Uncaught Error: Hello world!
2+
throw new Error("Hello world!");
3+
^
4+
at http://localhost:4545/cli/tests/inline_js_source_map_2.ts:6:7

cli/tests/inline_js_source_map_2.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
1 + 1;
2+
interface Test {
3+
hello: string;
4+
}
5+
6+
throw new Error("Hello world!" as unknown as string);

cli/tests/inline_js_source_map_2_with_inline_contents.js

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
error: Uncaught Error: Hello world!
2+
throw new Error("Hello world!" as unknown as string);
3+
^
4+
at http://localhost:4545/cli/tests/inline_js_source_map_2.ts:6:7

cli/tests/inline_js_source_map_with_contents_from_graph.js

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
error: Uncaught Error: Hello world!
2+
// throw new Error("Hello world!" as string);
3+
^
4+
at http://localhost:4545/cli/tests/inline_js_source_map.ts:6:7

cli/tests/integration_tests.rs

+35
Original file line numberDiff line numberDiff line change
@@ -3446,6 +3446,41 @@ itest!(local_sources_not_cached_in_memory {
34463446
output: "no_mem_cache.js.out",
34473447
});
34483448

3449+
// This test checks that inline source map data is used. It uses a hand crafted
3450+
// source map that maps to a file that exists, but is not loaded into the module
3451+
// graph (inline_js_source_map_2.ts) (because there are no direct dependencies).
3452+
// Source line is not remapped because no inline source contents are included in
3453+
// the sourcemap and the file is not present in the dependency graph.
3454+
itest!(inline_js_source_map_2 {
3455+
args: "run --quiet inline_js_source_map_2.js",
3456+
output: "inline_js_source_map_2.js.out",
3457+
exit_code: 1,
3458+
});
3459+
3460+
// This test checks that inline source map data is used. It uses a hand crafted
3461+
// source map that maps to a file that exists, but is not loaded into the module
3462+
// graph (inline_js_source_map_2.ts) (because there are no direct dependencies).
3463+
// Source line remapped using th inline source contents that are included in the
3464+
// inline source map.
3465+
itest!(inline_js_source_map_2_with_inline_contents {
3466+
args: "run --quiet inline_js_source_map_2_with_inline_contents.js",
3467+
output: "inline_js_source_map_2_with_inline_contents.js.out",
3468+
exit_code: 1,
3469+
});
3470+
3471+
// This test checks that inline source map data is used. It uses a hand crafted
3472+
// source map that maps to a file that exists, and is loaded into the module
3473+
// graph because of a direct import statement (inline_js_source_map.ts). The
3474+
// source map was generated from an earlier version of this file, where the throw
3475+
// was not commented out. The source line is remapped using source contents that
3476+
// from the module graph.
3477+
itest!(inline_js_source_map_with_contents_from_graph {
3478+
args: "run --quiet inline_js_source_map_with_contents_from_graph.js",
3479+
output: "inline_js_source_map_with_contents_from_graph.js.out",
3480+
exit_code: 1,
3481+
http_server: true,
3482+
});
3483+
34493484
#[test]
34503485
fn cafile_env_fetch() {
34513486
use deno_core::url::Url;

0 commit comments

Comments
 (0)