swc_config/
source_map.rs

1use anyhow::{bail, Context, Result};
2use bytes_str::BytesStr;
3use serde::{Deserialize, Serialize};
4use swc_sourcemap::{vlq::parse_vlq_segment, RawToken, SourceMap};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(untagged)]
8pub enum SourceMapContent {
9    Json(String),
10    #[serde(rename_all = "camelCase")]
11    Parsed {
12        #[serde(default)]
13        sources: Vec<BytesStr>,
14        #[serde(default)]
15        names: Vec<BytesStr>,
16        #[serde(default)]
17        mappings: String,
18        #[serde(default)]
19        range_mappings: String,
20        #[serde(default)]
21        file: Option<BytesStr>,
22        #[serde(default)]
23        source_root: Option<String>,
24        #[serde(default)]
25        sources_content: Option<Vec<Option<BytesStr>>>,
26    },
27}
28
29impl SourceMapContent {
30    pub fn to_sourcemap(&self) -> Result<SourceMap> {
31        match self {
32            SourceMapContent::Json(s) => {
33                SourceMap::from_slice(s.as_bytes()).context("failed to parse sourcemap")
34            }
35            SourceMapContent::Parsed {
36                sources,
37                names,
38                mappings,
39                range_mappings: _,
40                file,
41                source_root,
42                sources_content,
43            } => {
44                let mut dst_col;
45                let mut src_id = 0;
46                let mut src_line = 0;
47                let mut src_col = 0;
48                let mut name_id = 0;
49
50                let allocation_size = mappings.matches(&[',', ';'][..]).count() + 10;
51                let mut tokens = Vec::with_capacity(allocation_size);
52
53                let mut nums = Vec::with_capacity(6);
54
55                for (dst_line, line) in mappings.split(';').enumerate() {
56                    if line.is_empty() {
57                        continue;
58                    }
59
60                    dst_col = 0;
61
62                    for segment in line.split(',') {
63                        if segment.is_empty() {
64                            continue;
65                        }
66
67                        nums.clear();
68                        nums = parse_vlq_segment(segment)?;
69                        dst_col = (i64::from(dst_col) + nums[0]) as u32;
70
71                        let mut src = !0;
72                        let mut name = !0;
73
74                        if nums.len() > 1 {
75                            if nums.len() != 4 && nums.len() != 5 {
76                                bail!(
77                                    "invalid vlq segment size; expected 4 or 5, got {}",
78                                    nums.len()
79                                );
80                            }
81                            src_id = (i64::from(src_id) + nums[1]) as u32;
82                            if src_id >= sources.len() as u32 {
83                                bail!("invalid source reference: {}", src_id);
84                            }
85
86                            src = src_id;
87                            src_line = (i64::from(src_line) + nums[2]) as u32;
88                            src_col = (i64::from(src_col) + nums[3]) as u32;
89
90                            if nums.len() > 4 {
91                                name_id = (i64::from(name_id) + nums[4]) as u32;
92                                if name_id >= names.len() as u32 {
93                                    bail!("invalid name reference: {}", name_id);
94                                }
95                                name = name_id;
96                            }
97                        }
98
99                        tokens.push(RawToken {
100                            dst_line: dst_line as u32,
101                            dst_col,
102                            src_line,
103                            src_col,
104                            src_id: src,
105                            name_id: name,
106                            is_range: false,
107                        });
108                    }
109                }
110
111                let mut map = SourceMap::new(
112                    file.clone(),
113                    tokens,
114                    names.clone(),
115                    sources.clone(),
116                    sources_content.clone(),
117                );
118                map.set_source_root(source_root.clone());
119                Ok(map)
120            }
121        }
122    }
123}