turbopack_node/source_map/
trace.rs1use std::{borrow::Cow, fmt::Display};
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use turbopack_core::source_map::{SourceMap, Token};
6use turbopack_ecmascript::magic_identifier::unmangle_identifiers;
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
12pub struct StackFrame<'a> {
13 pub file: Cow<'a, str>,
14 #[serde(rename = "lineNumber")]
15 pub line: Option<u32>,
16 pub column: Option<u32>,
17 #[serde(rename = "methodName")]
18 pub name: Option<Cow<'a, str>>,
19}
20
21impl<'a> StackFrame<'a> {
22 pub fn unmangle_identifiers<T: Display>(&self, magic: impl Fn(String) -> T) -> StackFrame<'_> {
23 StackFrame {
24 file: Cow::Borrowed(self.file.as_ref()),
25 line: self.line,
26 column: self.column,
27 name: self
28 .name
29 .as_ref()
30 .map(|n| unmangle_identifiers(n.as_ref(), magic)),
31 }
32 }
33
34 pub fn with_path<'b>(&'a self, path: &'b str) -> StackFrame<'b>
35 where
36 'a: 'b,
37 {
38 StackFrame {
39 file: Cow::Borrowed(path),
40 line: self.line,
41 column: self.column,
42 name: self.name.as_ref().map(|n| Cow::Borrowed(n.as_ref())),
43 }
44 }
45
46 pub fn with_name<'b>(&'a self, name: Option<&'b str>) -> StackFrame<'b>
47 where
48 'a: 'b,
49 {
50 StackFrame {
51 file: Cow::Borrowed(self.file.as_ref()),
52 line: self.line,
53 column: self.column,
54 name: name.map(Cow::Borrowed),
55 }
56 }
57
58 pub fn get_pos(&self) -> Option<(u32, u32)> {
59 self.line.zip(self.column)
60 }
61}
62
63impl Display for StackFrame<'_> {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 match self.get_pos() {
66 Some((l, c)) => match &self.name.as_deref() {
67 None | Some("<unknown>") | Some("(anonymous)") => {
68 write!(f, "{}:{}:{}", self.file, l, c)
69 }
70 Some(n) => write!(f, "{} ({}:{}:{})", n, self.file, l, c),
71 },
72 None => write!(f, "{}", self.file),
73 }
74 }
75}
76
77#[derive(Debug)]
79pub enum TraceResult {
80 NotFound,
81 Found(StackFrame<'static>),
82}
83
84pub async fn trace_source_map(
95 map: &SourceMap,
96 line: u32,
97 column: u32,
98 name: Option<&str>,
99) -> Result<TraceResult> {
100 let token = map
101 .lookup_token(line.saturating_sub(1), column.saturating_sub(1))
102 .await?;
103 let result = match token {
104 Token::Original(t) => TraceResult::Found(StackFrame {
105 file: t.original_file.clone().into(),
106 line: Some(t.original_line.saturating_add(1)),
107 column: Some(t.original_column.saturating_add(1)),
108 name: t
109 .name
110 .clone()
111 .map(|v| v.into_owned())
112 .or_else(|| name.map(ToString::to_string))
113 .map(Cow::Owned),
114 }),
115 _ => TraceResult::NotFound,
116 };
117
118 Ok(result)
119}