enhanced_magic_string/
magic_string.rs1use std::{
2 collections::{HashMap, HashSet},
3 sync::Arc,
4};
5
6use crate::{error::Result, utils::common::get_relative_path};
7use parking_lot::Mutex;
8use sourcemap::{SourceMap, SourceMapBuilder};
9
10use crate::{
11 chunk::Chunk,
12 mappings::Mappings,
13 types::SourceMapOptions,
14 utils::{char_string::CharString, get_locator::get_locator},
15};
16
17pub type ExclusionRange = (usize, usize);
18
19#[derive(Default)]
20pub struct MagicStringOptions {
21 pub filename: Option<String>,
22 pub indent_exclusion_ranges: Vec<ExclusionRange>,
23 pub ignore_list: Vec<CharString>,
24 pub source_map_chain: Vec<Arc<String>>,
25}
26
27pub struct MagicString {
28 pub original: CharString,
29 pub outro: CharString,
30 pub intro: CharString,
31
32 pub first_chunk: Arc<Mutex<Chunk>>,
33 pub last_chunk: Arc<Mutex<Chunk>>,
34 pub last_searched_chunk: Arc<Mutex<Chunk>>,
35 pub chunk_by_start: HashMap<usize, Arc<Mutex<Chunk>>>,
36 pub chunk_by_end: HashMap<usize, Arc<Mutex<Chunk>>>,
37
38 pub filename: Option<String>,
39 pub indent_exclusion_ranges: Vec<ExclusionRange>,
40 pub sourcemap_locations: HashSet<usize>,
41 pub stored_names: HashMap<CharString, bool>,
42 pub indent_str: Option<CharString>,
43 pub ignore_list: Vec<CharString>,
44 source_map_chain: Vec<Arc<String>>,
45
46 pub separator: char,
47}
48
49impl MagicString {
50 pub fn new(original: &str, options: Option<MagicStringOptions>) -> Self {
51 let options = options.unwrap_or_default();
52 let original = CharString::new(original);
53 let chunk = Arc::new(Mutex::new(Chunk::new(0, original.len(), original.clone())));
54
55 let mut magic_string = Self {
56 original: original.clone(),
57 outro: CharString::new(""),
58 intro: CharString::new(""),
59 first_chunk: chunk.clone(),
60 last_chunk: chunk.clone(),
61 last_searched_chunk: chunk,
62 chunk_by_start: HashMap::new(),
63 chunk_by_end: HashMap::new(),
64 filename: options.filename,
65 indent_exclusion_ranges: options.indent_exclusion_ranges,
66 sourcemap_locations: HashSet::new(),
67 stored_names: HashMap::new(),
68 indent_str: None,
69 ignore_list: options.ignore_list,
70 source_map_chain: options.source_map_chain,
71 separator: '\n',
72 };
73
74 magic_string
75 .chunk_by_start
76 .insert(0, magic_string.first_chunk.clone());
77 magic_string
78 .chunk_by_end
79 .insert(0, magic_string.last_chunk.clone());
80
81 magic_string
82 }
83
84 pub fn get_source_map_chain(&self) -> Vec<SourceMap> {
85 let mut chain = self
86 .source_map_chain
87 .iter()
88 .map(|source| SourceMap::from_slice(source.as_bytes()).unwrap())
89 .filter(|source| {
90 source.get_token_count() > 0
92 })
93 .collect::<Vec<_>>();
94 chain.reverse();
95
96 chain
97 }
98
99 pub fn generate_map(&self, opts: SourceMapOptions) -> Result<SourceMap> {
100 let source_index = 0;
101 let locate = get_locator(&self.original);
104 let mut mappings = Mappings::new(opts.hires.unwrap_or_default());
105
106 if !self.intro.is_empty() {
107 mappings.advance(&self.intro);
108 }
109
110 self.first_chunk.lock().each_next(|chunk| {
111 let loc = locate(chunk.start);
112
113 if !chunk.intro.is_empty() {
114 mappings.advance(&chunk.intro);
115 }
116
117 if !chunk.edited {
118 mappings.add_unedited_chunk(
119 source_index,
120 &chunk,
121 &self.original,
122 loc,
123 &self.sourcemap_locations,
124 )
125 } else {
126 unimplemented!("chunk.edited")
127 }
128
129 if !chunk.outro.is_empty() {
130 mappings.advance(&chunk.outro)
131 }
132 });
133
134 let source = if let Some(src) = &opts.source {
135 get_relative_path(opts.file.clone().unwrap_or_default().as_str(), src).unwrap()
136 } else {
137 opts.file.clone().unwrap_or_default()
138 };
139
140 let mut sourcemap_builder = SourceMapBuilder::new(opts.file.as_ref().map(|f| f.as_str()));
141 let src_id = sourcemap_builder.add_source(&source);
142
143 let inline_content = opts.include_content.unwrap_or(false);
144
145 let contet = if inline_content {
146 Some(self.original.to_string())
147 } else {
148 None
149 };
150 sourcemap_builder.set_source_contents(src_id, contet.as_deref());
151 mappings.into_sourcemap_mappings(&mut sourcemap_builder);
152 Ok(sourcemap_builder.into_sourcemap())
153 }
154
155 pub fn prepend(&mut self, str: &str) {
156 let mut new_intro = CharString::new(str);
157 new_intro.append(&self.intro);
158 self.intro = new_intro;
159 }
160
161 pub fn append(&mut self, str: &str) {
162 let mut new_outro = self.outro.clone();
163 new_outro.append_str(str);
164 self.outro = new_outro;
165 }
166}
167
168impl ToString for MagicString {
169 fn to_string(&self) -> String {
170 let mut str = self.intro.to_string();
171 let guard = self.first_chunk.lock();
172 let mut chunk = Some(&*guard);
173
174 while let Some(c) = chunk {
175 str += &c.to_string();
176 chunk = c.next();
177 }
178
179 str += &self.outro.to_string();
180 str
181 }
182}