enhanced_magic_string/collapse_sourcemap/
mod.rs1use std::{
2 cell::{RefCell, RefMut},
3 path::PathBuf,
4};
5
6use farmfe_utils::file_url_to_path;
7use sourcemap::{SourceMap, SourceMapBuilder, Token};
8
9pub struct CollapseSourcemapOptions {
10 pub inline_content: bool,
13
14 pub remap_source: Option<Box<dyn Fn(&str) -> String>>,
15}
16
17impl Default for CollapseSourcemapOptions {
18 fn default() -> Self {
19 Self {
20 inline_content: true,
21 remap_source: None,
22 }
23 }
24}
25
26pub fn collapse_sourcemap_chain(
34 mut chain: Vec<SourceMap>,
35 opts: CollapseSourcemapOptions,
36) -> SourceMap {
37 chain.reverse();
38 chain = chain
39 .into_iter()
40 .filter(|map| map.get_token_count() > 0)
41 .collect();
42
43 if chain.is_empty() {
44 let builder = SourceMapBuilder::new(None);
45 return builder.into_sourcemap();
46 }
47
48 let dest_map = &chain[0];
49 let mut builder = SourceMapBuilder::new(None);
50 let mut mapped_src_cache = std::collections::HashMap::new();
51
52 for token in dest_map.tokens() {
54 let mut last_map_token = token;
55 let mut completed_trace = true;
56
57 if chain.len() > 1 {
58 for map in &chain[1..] {
59 if let Some(map_token) = lookup_token(
60 map,
61 last_map_token.get_src_line(),
62 last_map_token.get_src_col(),
63 ) {
64 last_map_token = map_token;
65 } else {
66 completed_trace = false;
67 break;
68 }
69 }
70 }
71
72 if !completed_trace {
74 continue;
76 }
77
78 let source = last_map_token.get_source();
79 let mut srd_id = None;
80
81 if let Some(src) = source {
82 let remapped_src = if let Some(remap_source) = &opts.remap_source {
83 mapped_src_cache
84 .entry(src)
85 .or_insert_with(|| remap_source(src))
86 .to_string()
87 } else {
88 src.to_string()
89 };
90
91 srd_id = Some(builder.add_source(&remapped_src));
92 }
93
94 let mut name_id = None;
95
96 if let Some(name) = last_map_token.get_name().or(token.get_name()) {
97 name_id = Some(builder.add_name(name));
98 }
99
100 let added_token = builder.add_raw(
101 token.get_dst_line(),
102 token.get_dst_col(),
103 last_map_token.get_src_line(),
104 last_map_token.get_src_col(),
105 srd_id,
106 name_id,
107 false,
108 );
109
110 if opts.inline_content && srd_id.is_some() && !builder.has_source_contents(srd_id.unwrap()) {
111 let src_content = read_source_content(last_map_token, chain.last().unwrap());
112
113 if let Some(src_content) = src_content {
114 builder.set_source_contents(added_token.src_id, Some(&src_content));
115 }
116 }
117 }
118
119 builder.into_sourcemap()
120}
121
122pub fn lookup_token<'a>(map: &'a SourceMap, line: u32, col: u32) -> Option<Token<'a>> {
125 let token = map.lookup_token(line, col);
126
127 if let Some(token) = token {
128 if line > 0 && token.get_dst_line() == line - 1 && token.get_dst_col() > 0 {
130 let next_token = map.lookup_token(line + 1, 0);
131
132 if let Some(next_token) = next_token {
133 if next_token.get_dst_line() == line {
134 return Some(next_token);
135 }
136 }
137 }
138 }
139
140 token
141}
142
143pub fn read_source_content(token: Token<'_>, map: &SourceMap) -> Option<String> {
144 if let Some(view) = token.get_source_view() {
145 Some(view.source().to_string())
146 } else if let Some(src) = token.get_source() {
147 let src = &file_url_to_path(src);
148 let map_file = map.get_file();
150
151 if PathBuf::from(src).is_absolute() || map_file.is_none() {
152 std::fs::read_to_string(src).ok()
153 } else if let Some(map_file) = map_file {
154 let src_file = PathBuf::from(map_file).parent().unwrap().join(src);
155 let src_content = std::fs::read_to_string(src_file).ok();
156
157 src_content
158 } else {
159 None
160 }
161 } else {
162 None
163 }
164}
165
166pub struct CollapsedSourceMap<'a> {
167 pub tokens: RefCell<Vec<Token<'a>>>,
168 pub map: SourceMap,
169}
170
171impl<'a> CollapsedSourceMap<'a> {
172 pub fn new(map: SourceMap) -> Self {
173 Self {
174 tokens: RefCell::new(vec![]),
175 map,
176 }
177 }
178
179 pub fn tokens(&'a self) -> RefMut<Vec<Token<'a>>> {
180 let mut tokens = self.tokens.borrow_mut();
181
182 if tokens.is_empty() {
183 *tokens = self.map.tokens().collect::<Vec<_>>();
184 }
185
186 tokens
187 }
188}