Skip to content

Commit 6e8a298

Browse files
committed
test(codegen): add test that verifies stack traces are correct
1 parent bc0b471 commit 6e8a298

File tree

2 files changed

+328
-1
lines changed

2 files changed

+328
-1
lines changed
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
---
2+
source: crates/oxc_codegen/tests/integration/sourcemap.rs
3+
---
4+
## Input
5+
const fn = () => {
6+
Error.stackTraceLimit = 2;
7+
throw new Error()
8+
};
9+
fn()
10+
11+
## Output
12+
const fn = () => {
13+
Error.stackTraceLimit = 2;
14+
throw new Error();
15+
};
16+
fn();
17+
18+
19+
## Stderr
20+
/project/input.js:3
21+
throw new Error()
22+
^
23+
24+
25+
Error
26+
at fn (/project/input.js:3:11)
27+
at <anonymous> (/project/input.js:5:1)
28+
29+
------------------------------------------------------
30+
## Input
31+
const obj = {
32+
fn() {
33+
Error.stackTraceLimit = 2;
34+
throw new Error()
35+
}
36+
}
37+
obj.fn()
38+
39+
## Output
40+
const obj = { fn() {
41+
Error.stackTraceLimit = 2;
42+
throw new Error();
43+
} };
44+
obj.fn();
45+
46+
47+
## Stderr
48+
/project/input.js:4
49+
throw new Error()
50+
^
51+
52+
53+
Error
54+
at Object.fn (/project/input.js:4:15)
55+
at <anonymous> (/project/input.js:7:5)
56+
57+
------------------------------------------------------
58+
## Input
59+
const obj = {
60+
obj2: {
61+
fn() {
62+
Error.stackTraceLimit = 2;
63+
throw new Error()
64+
}
65+
}
66+
}
67+
obj.obj2.fn()
68+
69+
## Output
70+
const obj = { obj2: { fn() {
71+
Error.stackTraceLimit = 2;
72+
throw new Error();
73+
} } };
74+
obj.obj2.fn();
75+
76+
77+
## Stderr
78+
/project/input.js:5
79+
throw new Error()
80+
^
81+
82+
83+
Error
84+
at Object.fn (/project/input.js:5:19)
85+
at <anonymous> (/project/input.js:9:10)
86+
87+
------------------------------------------------------
88+
## Input
89+
const obj = {
90+
fn() {
91+
return function fn2() {
92+
Error.stackTraceLimit = 2;
93+
throw new Error()
94+
}
95+
}
96+
}
97+
obj.fn()()
98+
99+
## Output
100+
const obj = { fn() {
101+
return function fn2() {
102+
Error.stackTraceLimit = 2;
103+
throw new Error();
104+
};
105+
} };
106+
obj.fn()();
107+
108+
109+
## Stderr
110+
/project/input.js:5
111+
throw new Error()
112+
^
113+
114+
115+
Error
116+
at fn2 (/project/input.js:5:19)
117+
at <anonymous> (/project/input.js:9:5)
118+
119+
------------------------------------------------------
120+
## Input
121+
const obj = {
122+
fn() {
123+
return () => {
124+
Error.stackTraceLimit = 2;
125+
throw new Error()
126+
}
127+
}
128+
}
129+
obj.fn([1])()
130+
131+
## Output
132+
const obj = { fn() {
133+
return () => {
134+
Error.stackTraceLimit = 2;
135+
throw new Error();
136+
};
137+
} };
138+
obj.fn([1])();
139+
140+
141+
## Stderr
142+
/project/input.js:5
143+
throw new Error()
144+
^
145+
146+
147+
Error
148+
at <anonymous> (/project/input.js:5:19)
149+
at <anonymous> (/project/input.js:9:9)
150+
151+
------------------------------------------------------
152+
## Input
153+
var a
154+
const obj = {
155+
fn() {
156+
return () => {
157+
Error.stackTraceLimit = 2;
158+
throw new Error()
159+
}
160+
}
161+
}
162+
obj.fn({a})()
163+
164+
## Output
165+
var a;
166+
const obj = { fn() {
167+
return () => {
168+
Error.stackTraceLimit = 2;
169+
throw new Error();
170+
};
171+
} };
172+
obj.fn({ a })();
173+
174+
175+
## Stderr
176+
/project/input.js:6
177+
throw new Error()
178+
^
179+
180+
181+
Error
182+
at <anonymous> (/project/input.js:6:19)
183+
at <anonymous> (/project/input.js:10:9)
184+
185+
------------------------------------------------------
186+
## Input
187+
const fn = (name, cb) => {
188+
cb()
189+
}
190+
fn('name', () => {
191+
Error.stackTraceLimit = 2;
192+
throw new Error()
193+
})
194+
195+
## Output
196+
const fn = (name, cb) => {
197+
cb();
198+
};
199+
fn("name", () => {
200+
Error.stackTraceLimit = 2;
201+
throw new Error();
202+
});
203+
204+
205+
## Stderr
206+
/project/input.js:6
207+
throw new Error()
208+
^
209+
210+
211+
Error
212+
at <anonymous> (/project/input.js:6:11)
213+
at fn (/project/input.js:2:5)

crates/oxc_codegen/tests/integration/sourcemap.rs

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
use std::{env, path::PathBuf};
2+
3+
use cow_utils::CowUtils;
14
use oxc_allocator::Allocator;
25
use oxc_ast::ast::{Expression, Statement};
3-
use oxc_codegen::Codegen;
6+
use oxc_codegen::{Codegen, CodegenOptions};
47
use oxc_parser::Parser;
58
use oxc_span::{SourceType, Span};
69

@@ -27,3 +30,114 @@ fn incorrect_ast() {
2730
let ret = Codegen::new().with_options(default_options()).build(&program);
2831
assert!(ret.map.is_some(), "sourcemap exists");
2932
}
33+
34+
#[test]
35+
fn stacktrace_is_correct() {
36+
let cases = &[
37+
"\
38+
const fn = () => {
39+
Error.stackTraceLimit = 2;
40+
throw new Error()
41+
};
42+
fn()",
43+
"\
44+
const obj = {
45+
fn() {
46+
Error.stackTraceLimit = 2;
47+
throw new Error()
48+
}
49+
}
50+
obj.fn()",
51+
"\
52+
const obj = {
53+
obj2: {
54+
fn() {
55+
Error.stackTraceLimit = 2;
56+
throw new Error()
57+
}
58+
}
59+
}
60+
obj.obj2.fn()",
61+
"\
62+
const obj = {
63+
fn() {
64+
return function fn2() {
65+
Error.stackTraceLimit = 2;
66+
throw new Error()
67+
}
68+
}
69+
}
70+
obj.fn()()",
71+
"\
72+
const obj = {
73+
fn() {
74+
return () => {
75+
Error.stackTraceLimit = 2;
76+
throw new Error()
77+
}
78+
}
79+
}
80+
obj.fn([1])()",
81+
"\
82+
var a
83+
const obj = {
84+
fn() {
85+
return () => {
86+
Error.stackTraceLimit = 2;
87+
throw new Error()
88+
}
89+
}
90+
}
91+
obj.fn({a})()",
92+
"\
93+
const fn = (name, cb) => {
94+
cb()
95+
}
96+
fn('name', () => {
97+
Error.stackTraceLimit = 2;
98+
throw new Error()
99+
})",
100+
];
101+
102+
insta::with_settings!({ prepend_module_to_snapshot => false, snapshot_suffix => "", omit_expression => true }, {
103+
insta::assert_snapshot!(
104+
"stacktrace_is_correct",
105+
cases.iter().map(|s| {
106+
let (output, sourcemap_url) = codegen(s);
107+
format!("## Input\n{}\n\n## Output\n{}\n\n## Stderr\n{}", s, output, execute_with_node(&output, &sourcemap_url))
108+
}).collect::<Vec<_>>().join("\n------------------------------------------------------\n")
109+
);
110+
});
111+
}
112+
113+
fn codegen(code: &str) -> (String, String) {
114+
let allocator = Allocator::default();
115+
let ret = Parser::new(&allocator, code, SourceType::mjs()).parse();
116+
let ret = Codegen::new()
117+
.with_options(CodegenOptions {
118+
source_map_path: Some(PathBuf::from("input.js")),
119+
..Default::default()
120+
})
121+
.build(&ret.program);
122+
(ret.code, ret.map.unwrap().to_data_url())
123+
}
124+
125+
fn execute_with_node(code: &str, sourcemap_url: &str) -> String {
126+
let cwd = env::current_dir().unwrap().join("input.js");
127+
let cwd = cwd.to_str().unwrap();
128+
129+
let code = format!("{code}\n//# sourceMappingURL={sourcemap_url}\n");
130+
131+
let output = std::process::Command::new("node")
132+
.arg("--enable-source-maps")
133+
.args(["--input-type", "module"])
134+
.args(["--eval", &code])
135+
.output()
136+
.unwrap();
137+
String::from_utf8_lossy(&output.stderr)
138+
.cow_replace(cwd, "/project/input.js")
139+
.lines()
140+
.filter(|line| !line.starts_with("Node.js v"))
141+
.collect::<Vec<_>>()
142+
.join("\n")
143+
}

0 commit comments

Comments
 (0)