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            token.get_name()
41        } else {
42            None
43        }
44    }
45}