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}