js_source_scopes/name_resolver.rs
1use sourcemap::DecodedMap;
2
3use crate::{NameComponent, ScopeName, SourceContext};
4
5/// A structure for resolving [`ScopeName`]s in minified code to their original names
6/// using information contained in a [`DecodedMap`].
7pub struct NameResolver<'a, T> {
8 ctx: &'a SourceContext<T>,
9 sourcemap: &'a DecodedMap,
10}
11
12impl<'a, T: AsRef<str>> NameResolver<'a, T> {
13 /// Construct a new [`NameResolver`] from a [`SourceContext`] (for the minified source) and a [`DecodedMap`].
14 pub fn new(ctx: &'a SourceContext<T>, sourcemap: &'a DecodedMap) -> Self {
15 Self { ctx, sourcemap }
16 }
17
18 /// Resolves the given minified [`ScopeName`] to the original name.
19 ///
20 /// This tries to resolve each [`NameComponent`] by looking up its source
21 /// range in the [`DecodedMap`], using the token's `name` (as defined in the
22 /// sourcemap `names`) when possible.
23 pub fn resolve_name(&self, name: &ScopeName) -> String {
24 name.components()
25 .map(|c| self.try_map_token(c).unwrap_or_else(|| c.text()))
26 .collect::<String>()
27 }
28
29 fn try_map_token(&self, c: &NameComponent) -> Option<&str> {
30 let range = c.range()?;
31 let source_position = self.ctx.offset_to_position(range.start)?;
32 let token = self
33 .sourcemap
34 .lookup_token(source_position.line, source_position.column)?;
35
36 let is_exactish_match = token.get_dst_line() == source_position.line
37 && token.get_dst_col() >= source_position.column.saturating_sub(1);
38
39 if is_exactish_match {
40 if let Some(name) = token.get_name() {
41 return Some(name);
42 }
43
44 // If the token at the identifier position has no name, check the
45 // immediately preceding token. Some source map generators (e.g.
46 // TypeScript) attach the original function name to the `function`
47 // keyword token rather than the identifier that follows it.
48 // We only use the preceding token's name if it maps to the same
49 // original source position, indicating it's part of the same mapping.
50 if token.get_dst_col() > 0 {
51 if let Some(prev_token) = self
52 .sourcemap
53 .lookup_token(token.get_dst_line(), token.get_dst_col() - 1)
54 {
55 if prev_token.get_src_id() == token.get_src_id()
56 && prev_token.get_src_line() == token.get_src_line()
57 && prev_token.get_src_col() == token.get_src_col()
58 {
59 return prev_token.get_name();
60 }
61 }
62 }
63 }
64
65 None
66 }
67}