swc_config/
source_map.rs

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