Skip to content

Commit 9b9b8cb

Browse files
authored
fix: Ensure exact token match in NameResolver (#14)
Previously we would use *any* preceding token from the SourceMap, and may end up outputting `Foo.Foo.bar` for `Foo.prototype.bar` when the `prototype` token itself does not appear precisely in the SourceMap.
1 parent a4ad2dc commit 9b9b8cb

File tree

7 files changed

+44
-1
lines changed

7 files changed

+44
-1
lines changed

src/name_resolver.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ impl<'a, T: AsRef<str>> NameResolver<'a, T> {
3232
let token = self
3333
.sourcemap
3434
.lookup_token(source_position.line, source_position.column)?;
35-
token.get_name()
35+
let is_exact_match = token.get_dst() == (source_position.line, source_position.column);
36+
37+
if is_exact_match {
38+
token.get_name()
39+
} else {
40+
None
41+
}
3642
}
3743
}

tests/fixtures/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# Minifying code and creating SourceMaps:
2+
3+
For example:
4+
5+
```
6+
./node_modules/.bin/terser -c -m --module tests/fixtures/simple/original.js --source-map includeSources -o tests/fixtures/simple/minified.js
7+
```
8+
19
# trace
210

311
A sync and async stack trace through various constructs, with named and anonymous
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
The SourceMap was hand-edited to remove the `prototype` token:
2+
3+
The `EAAIC,UAAUC` portion in the middle of the mappings was replaced by `YAAcC`,
4+
as well as the `IAAKF,GAAKE` portion at the end was replaced by `IAAKD,GAAKC`,
5+
and the `"prototype"` was removed from `names`.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
function t(){}t.prototype.bar=()=>{};export default(new t).bar();

tests/fixtures/prototype-chain/minified.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function Foo() {}
2+
Foo.prototype.bar = () => {};
3+
export default (new Foo).bar();

tests/integration.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ use js_source_scopes::{
55
SourcePosition,
66
};
77

8+
fn fixture(name: &str) -> String {
9+
std::fs::read_to_string(format!("tests/fixtures/{name}")).unwrap()
10+
}
11+
812
fn resolve_original_scopes(
913
minified: &str,
1014
map: &str,
@@ -199,3 +203,18 @@ fn parses_large_vendors() {
199203
println!(" original: {original:?}");
200204
}
201205
}
206+
207+
#[test]
208+
fn should_only_resolve_exact() {
209+
let minified = fixture("prototype-chain/minified.js");
210+
let map = fixture("prototype-chain/minified.js.map");
211+
212+
let scopes = extract_scope_names(&minified).unwrap();
213+
214+
// The sourcemap was manually edited to remove the `prototype` token/name.
215+
// When resolving names, we should make sure we have exact matches, otherwise
216+
// we would use the preceding token and end up with `Foo.Foo.bar`.
217+
let resolved_scopes = resolve_original_scopes(&minified, &map, scopes);
218+
219+
assert_eq!(resolved_scopes[1].2, Some("Foo.prototype.bar".into()));
220+
}

0 commit comments

Comments
 (0)