1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::collections::BTreeSet;
4use std::fmt;
5use std::io::{Read, Write};
6use std::path::Path;
7use std::sync::Arc;
8
9use crate::builder::SourceMapBuilder;
10use crate::decoder::{decode, decode_slice};
11use crate::encoder::encode;
12use crate::errors::{Error, Result};
13use crate::hermes::SourceMapHermes;
14use crate::sourceview::SourceView;
15use crate::utils::{find_common_prefix, greatest_lower_bound};
16
17use debugid::DebugId;
18
19#[derive(Debug, Clone)]
27pub struct RewriteOptions<'a> {
28 pub with_names: bool,
30 pub with_source_contents: bool,
32 #[cfg(any(unix, windows, target_os = "redox"))]
35 pub load_local_source_contents: bool,
36 pub base_path: Option<&'a Path>,
39 pub strip_prefixes: &'a [&'a str],
43}
44
45impl<'a> Default for RewriteOptions<'a> {
46 fn default() -> RewriteOptions<'a> {
47 RewriteOptions {
48 with_names: true,
49 with_source_contents: true,
50 #[cfg(any(unix, windows, target_os = "redox"))]
51 load_local_source_contents: false,
52 base_path: None,
53 strip_prefixes: &[][..],
54 }
55 }
56}
57
58#[derive(Debug, Clone, PartialEq)]
65pub enum DecodedMap {
66 Regular(SourceMap),
68 Index(SourceMapIndex),
70 Hermes(SourceMapHermes),
72}
73
74impl DecodedMap {
75 pub fn from_reader<R: Read>(rdr: R) -> Result<DecodedMap> {
77 decode(rdr)
78 }
79
80 pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
82 match *self {
83 DecodedMap::Regular(ref sm) => encode(sm, w),
84 DecodedMap::Index(ref smi) => encode(smi, w),
85 DecodedMap::Hermes(ref smh) => encode(smh, w),
86 }
87 }
88
89 pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
94 match *self {
95 DecodedMap::Regular(ref sm) => sm.lookup_token(line, col),
96 DecodedMap::Index(ref smi) => smi.lookup_token(line, col),
97 DecodedMap::Hermes(ref smh) => smh.lookup_token(line, col),
98 }
99 }
100
101 pub fn get_original_function_name(
107 &self,
108 line: u32,
109 col: u32,
110 minified_name: Option<&str>,
111 source_view: Option<&SourceView>,
112 ) -> Option<&str> {
113 match *self {
114 DecodedMap::Regular(ref sm) => {
115 sm.get_original_function_name(line, col, minified_name?, source_view?)
116 }
117 DecodedMap::Index(ref smi) => {
118 smi.get_original_function_name(line, col, minified_name?, source_view?)
119 }
120 DecodedMap::Hermes(ref smh) => {
121 if line != 0 {
122 return None;
123 }
124 smh.get_original_function_name(col)
125 }
126 }
127 }
128
129 pub fn debug_id(&self) -> Option<DebugId> {
131 match self {
132 DecodedMap::Regular(sm) => sm.get_debug_id(),
133 DecodedMap::Index(smi) => smi.debug_id(),
134 DecodedMap::Hermes(smh) => smh.get_debug_id(),
135 }
136 }
137
138 pub fn set_debug_id(&mut self, debug_id: Option<DebugId>) {
140 match self {
141 DecodedMap::Regular(sm) => sm.set_debug_id(debug_id),
142 DecodedMap::Index(smi) => smi.set_debug_id(debug_id),
143 DecodedMap::Hermes(smh) => smh.set_debug_id(debug_id),
144 }
145 }
146}
147
148#[derive(PartialEq, Eq, Copy, Clone, Debug)]
155pub struct RawToken {
156 pub dst_line: u32,
158 pub dst_col: u32,
160 pub src_line: u32,
162 pub src_col: u32,
164 pub src_id: u32,
166 pub name_id: u32,
168
169 pub is_range: bool,
173}
174
175#[derive(Copy, Clone)]
177pub struct Token<'a> {
178 raw: &'a RawToken,
179 pub(crate) sm: &'a SourceMap,
180 pub(crate) idx: usize,
181 offset: u32,
182}
183
184impl<'a> Token<'a> {
185 pub fn sourcemap(&self) -> &'a SourceMap {
187 self.sm
188 }
189}
190
191impl PartialEq for Token<'_> {
192 fn eq(&self, other: &Token<'_>) -> bool {
193 self.raw == other.raw
194 }
195}
196
197impl Eq for Token<'_> {}
198
199impl PartialOrd for Token<'_> {
200 fn partial_cmp(&self, other: &Token<'_>) -> Option<Ordering> {
201 Some(self.cmp(other))
202 }
203}
204
205impl Ord for Token<'_> {
206 fn cmp(&self, other: &Token<'_>) -> Ordering {
207 macro_rules! try_cmp {
208 ($a:expr, $b:expr) => {
209 match $a.cmp(&$b) {
210 Ordering::Equal => {}
211 x => {
212 return x;
213 }
214 }
215 };
216 }
217 try_cmp!(self.get_dst_line(), other.get_dst_line());
218 try_cmp!(self.get_dst_col(), other.get_dst_col());
219 try_cmp!(self.get_source(), other.get_source());
220 try_cmp!(self.get_src_line(), other.get_src_line());
221 try_cmp!(self.get_src_col(), other.get_src_col());
222 try_cmp!(self.get_name(), other.get_name());
223 try_cmp!(self.is_range(), other.is_range());
224
225 Ordering::Equal
226 }
227}
228
229impl<'a> Token<'a> {
230 pub fn get_dst_line(&self) -> u32 {
232 self.raw.dst_line
233 }
234
235 pub fn get_dst_col(&self) -> u32 {
237 self.raw.dst_col
238 }
239
240 pub fn get_dst(&self) -> (u32, u32) {
242 (self.get_dst_line(), self.get_dst_col())
243 }
244
245 pub fn get_src_line(&self) -> u32 {
250 self.raw.src_line
251 }
252
253 pub fn get_src_col(&self) -> u32 {
258 self.raw.src_col.saturating_add(self.offset)
259 }
260
261 pub fn get_src(&self) -> (u32, u32) {
263 (self.get_src_line(), self.get_src_col())
264 }
265
266 pub fn get_src_id(&self) -> u32 {
268 self.raw.src_id
269 }
270
271 pub fn get_source(&self) -> Option<&'a str> {
273 if self.raw.src_id == !0 {
274 None
275 } else {
276 self.sm.get_source(self.raw.src_id)
277 }
278 }
279
280 pub fn has_source(&self) -> bool {
282 self.raw.src_id != !0
283 }
284
285 pub fn get_name(&self) -> Option<&'a str> {
287 if self.raw.name_id == !0 {
288 None
289 } else {
290 self.sm.get_name(self.raw.name_id)
291 }
292 }
293
294 pub fn has_name(&self) -> bool {
296 self.get_name().is_some()
297 }
298
299 pub fn get_name_id(&self) -> u32 {
301 self.raw.name_id
302 }
303
304 pub fn to_tuple(&self) -> (&'a str, u32, u32, Option<&'a str>) {
307 (
308 self.get_source().unwrap_or(""),
309 self.get_src_line(),
310 self.get_src_col(),
311 self.get_name(),
312 )
313 }
314
315 pub fn get_raw_token(&self) -> RawToken {
317 *self.raw
318 }
319
320 pub fn get_source_view(&self) -> Option<&SourceView> {
322 self.sm.get_source_view(self.get_src_id())
323 }
324
325 pub fn is_range(&self) -> bool {
329 self.raw.is_range
330 }
331}
332
333pub struct TokenIter<'a> {
335 i: &'a SourceMap,
336 next_idx: usize,
337}
338
339impl TokenIter<'_> {
340 pub fn seek(&mut self, line: u32, col: u32) -> bool {
341 let token = self.i.lookup_token(line, col);
342 match token {
343 Some(token) => {
344 self.next_idx = token.idx + 1;
345 true
346 }
347 None => false,
348 }
349 }
350}
351
352impl<'a> Iterator for TokenIter<'a> {
353 type Item = Token<'a>;
354
355 fn next(&mut self) -> Option<Token<'a>> {
356 self.i.get_token(self.next_idx).inspect(|_| {
357 self.next_idx += 1;
358 })
359 }
360}
361
362pub struct SourceIter<'a> {
364 i: &'a SourceMap,
365 next_idx: u32,
366}
367
368impl<'a> Iterator for SourceIter<'a> {
369 type Item = &'a str;
370
371 fn next(&mut self) -> Option<&'a str> {
372 self.i.get_source(self.next_idx).inspect(|_| {
373 self.next_idx += 1;
374 })
375 }
376}
377
378pub struct SourceContentsIter<'a> {
380 i: &'a SourceMap,
381 next_idx: u32,
382}
383
384impl<'a> Iterator for SourceContentsIter<'a> {
385 type Item = Option<&'a str>;
386
387 fn next(&mut self) -> Option<Option<&'a str>> {
388 if self.next_idx >= self.i.get_source_count() {
389 None
390 } else {
391 let rv = Some(self.i.get_source_contents(self.next_idx));
392 self.next_idx += 1;
393 rv
394 }
395 }
396}
397
398pub struct NameIter<'a> {
400 i: &'a SourceMap,
401 next_idx: u32,
402}
403
404impl<'a> Iterator for NameIter<'a> {
405 type Item = &'a str;
406
407 fn next(&mut self) -> Option<&'a str> {
408 self.i.get_name(self.next_idx).inspect(|_| {
409 self.next_idx += 1;
410 })
411 }
412}
413
414impl fmt::Debug for Token<'_> {
415 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416 write!(f, "<Token {self:#}>")
417 }
418}
419
420impl fmt::Display for Token<'_> {
421 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422 write!(
423 f,
424 "{}:{}:{}{}",
425 self.get_source().unwrap_or("<unknown>"),
426 self.get_src_line(),
427 self.get_src_col(),
428 self.get_name()
429 .map(|x| format!(" name={x}"))
430 .unwrap_or_default()
431 )?;
432 if f.alternate() {
433 write!(
434 f,
435 " ({}:{}){}",
436 self.get_dst_line(),
437 self.get_dst_col(),
438 if self.is_range() { " (range)" } else { "" }
439 )?;
440 }
441 Ok(())
442 }
443}
444
445#[derive(Debug, Clone, PartialEq)]
447pub struct SourceMapSection {
448 offset: (u32, u32),
449 url: Option<String>,
450 map: Option<Box<DecodedMap>>,
451}
452
453pub struct SourceMapSectionIter<'a> {
455 i: &'a SourceMapIndex,
456 next_idx: u32,
457}
458
459impl<'a> Iterator for SourceMapSectionIter<'a> {
460 type Item = &'a SourceMapSection;
461
462 fn next(&mut self) -> Option<&'a SourceMapSection> {
463 self.i.get_section(self.next_idx).inspect(|_| {
464 self.next_idx += 1;
465 })
466 }
467}
468
469#[derive(Debug, Clone, PartialEq)]
471pub struct SourceMapIndex {
472 file: Option<String>,
473 sections: Vec<SourceMapSection>,
474 x_facebook_offsets: Option<Vec<Option<u32>>>,
475 x_metro_module_paths: Option<Vec<String>>,
476 debug_id: Option<DebugId>,
477}
478
479#[derive(Clone, Debug, PartialEq)]
485pub struct SourceMap {
486 pub(crate) file: Option<Arc<str>>,
487 pub(crate) tokens: Vec<RawToken>,
488 pub(crate) names: Vec<Arc<str>>,
489 pub(crate) source_root: Option<Arc<str>>,
490 pub(crate) sources: Vec<Arc<str>>,
491 pub(crate) sources_prefixed: Option<Vec<Arc<str>>>,
492 pub(crate) sources_content: Vec<Option<SourceView>>,
493 pub(crate) ignore_list: BTreeSet<u32>,
494 pub(crate) debug_id: Option<DebugId>,
495}
496
497impl SourceMap {
498 pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMap> {
519 match decode(rdr)? {
520 DecodedMap::Regular(sm) => Ok(sm),
521 _ => Err(Error::IncompatibleSourceMap),
522 }
523 }
524
525 pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
546 encode(self, w)
547 }
548
549 pub fn to_data_url(&self) -> Result<String> {
563 let mut buf = vec![];
564 encode(self, &mut buf)?;
565 let b64 = base64_simd::STANDARD.encode_to_string(&buf);
566 Ok(format!("data:application/json;charset=utf-8;base64,{b64}"))
567 }
568
569 pub fn from_slice(slice: &[u8]) -> Result<SourceMap> {
585 match decode_slice(slice)? {
586 DecodedMap::Regular(sm) => Ok(sm),
587 _ => Err(Error::IncompatibleSourceMap),
588 }
589 }
590
591 pub fn new(
600 file: Option<Arc<str>>,
601 mut tokens: Vec<RawToken>,
602 names: Vec<Arc<str>>,
603 sources: Vec<Arc<str>>,
604 sources_content: Option<Vec<Option<Arc<str>>>>,
605 ) -> SourceMap {
606 tokens.sort_unstable_by_key(|t| (t.dst_line, t.dst_col));
607 SourceMap {
608 file,
609 tokens,
610 names,
611 source_root: None,
612 sources,
613 sources_prefixed: None,
614 sources_content: sources_content
615 .unwrap_or_default()
616 .into_iter()
617 .map(|opt| opt.map(SourceView::new))
618 .collect(),
619 ignore_list: BTreeSet::default(),
620 debug_id: None,
621 }
622 }
623
624 pub fn get_debug_id(&self) -> Option<DebugId> {
626 self.debug_id
627 }
628
629 pub fn set_debug_id(&mut self, debug_id: Option<DebugId>) {
631 self.debug_id = debug_id
632 }
633
634 pub fn get_file(&self) -> Option<&str> {
636 self.file.as_deref()
637 }
638
639 pub fn set_file<T: Into<Arc<str>>>(&mut self, value: Option<T>) {
641 self.file = value.map(Into::into);
642 }
643
644 pub fn get_source_root(&self) -> Option<&str> {
646 self.source_root.as_deref()
647 }
648
649 fn prefix_source(source_root: &str, source: &str) -> Arc<str> {
650 let source_root = source_root.strip_suffix('/').unwrap_or(source_root);
651 let is_valid = !source.is_empty()
652 && (source.starts_with('/')
653 || source.starts_with("http:")
654 || source.starts_with("https:"));
655
656 if is_valid {
657 source.into()
658 } else {
659 format!("{source_root}/{source}").into()
660 }
661 }
662
663 pub fn set_source_root<T: Into<Arc<str>>>(&mut self, value: Option<T>) {
665 self.source_root = value.map(Into::into);
666
667 match self.source_root.as_deref().filter(|rs| !rs.is_empty()) {
668 Some(source_root) => {
669 let sources_prefixed = self
670 .sources
671 .iter()
672 .map(|source| Self::prefix_source(source_root, source))
673 .collect();
674 self.sources_prefixed = Some(sources_prefixed)
675 }
676 None => self.sources_prefixed = None,
677 }
678 }
679
680 pub fn add_to_ignore_list(&mut self, src_id: u32) {
681 self.ignore_list.insert(src_id);
682 }
683
684 pub fn ignore_list(&self) -> impl Iterator<Item = &u32> {
685 self.ignore_list.iter()
686 }
687
688 pub fn get_token(&self, idx: usize) -> Option<Token<'_>> {
690 self.tokens.get(idx).map(|raw| Token {
691 raw,
692 sm: self,
693 idx,
694 offset: 0,
695 })
696 }
697
698 pub fn get_token_count(&self) -> u32 {
700 self.tokens.len() as u32
701 }
702
703 pub fn tokens(&self) -> TokenIter<'_> {
705 TokenIter {
706 i: self,
707 next_idx: 0,
708 }
709 }
710
711 pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
713 let (idx, raw) =
714 greatest_lower_bound(&self.tokens, &(line, col), |t| (t.dst_line, t.dst_col))?;
715
716 let mut token = Token {
717 raw,
718 sm: self,
719 idx,
720 offset: 0,
721 };
722
723 if token.is_range() {
724 token.offset = col - token.get_dst_col();
725 }
726
727 Some(token)
728 }
729
730 pub fn get_original_function_name(
739 &self,
740 line: u32,
741 col: u32,
742 minified_name: &str,
743 sv: &SourceView,
744 ) -> Option<&str> {
745 self.lookup_token(line, col)
746 .and_then(|token| sv.get_original_function_name(token, minified_name))
747 }
748
749 pub fn get_source_count(&self) -> u32 {
751 self.sources.len() as u32
752 }
753
754 pub fn get_source(&self, idx: u32) -> Option<&str> {
756 let sources = self.sources_prefixed.as_deref().unwrap_or(&self.sources);
757 sources.get(idx as usize).map(|x| &x[..])
758 }
759
760 pub fn set_source(&mut self, idx: u32, value: &str) {
765 self.sources[idx as usize] = value.into();
766
767 if let Some(sources_prefixed) = self.sources_prefixed.as_mut() {
768 sources_prefixed[idx as usize] =
770 Self::prefix_source(self.source_root.as_deref().unwrap(), value);
771 }
772 }
773
774 pub fn sources(&self) -> SourceIter<'_> {
776 SourceIter {
777 i: self,
778 next_idx: 0,
779 }
780 }
781
782 pub fn get_source_view(&self, idx: u32) -> Option<&SourceView> {
784 self.sources_content
785 .get(idx as usize)
786 .and_then(Option::as_ref)
787 }
788
789 pub fn get_source_contents(&self, idx: u32) -> Option<&str> {
791 self.sources_content
792 .get(idx as usize)
793 .and_then(Option::as_ref)
794 .map(SourceView::source)
795 }
796
797 pub fn set_source_contents(&mut self, idx: u32, value: Option<&str>) {
799 if self.sources_content.len() != self.sources.len() {
800 self.sources_content.resize(self.sources.len(), None);
801 }
802 self.sources_content[idx as usize] = value.map(|x| SourceView::from_string(x.to_string()));
803 }
804
805 pub fn source_contents(&self) -> SourceContentsIter<'_> {
807 SourceContentsIter {
808 i: self,
809 next_idx: 0,
810 }
811 }
812
813 pub fn names(&self) -> NameIter<'_> {
815 NameIter {
816 i: self,
817 next_idx: 0,
818 }
819 }
820
821 pub fn get_name_count(&self) -> u32 {
823 self.names.len() as u32
824 }
825
826 pub fn has_names(&self) -> bool {
828 !self.names.is_empty()
829 }
830
831 pub fn get_name(&self, idx: u32) -> Option<&str> {
833 self.names.get(idx as usize).map(|x| &x[..])
834 }
835
836 pub fn remove_names(&mut self) {
838 self.names.clear();
839 }
840
841 pub fn rewrite(self, options: &RewriteOptions<'_>) -> Result<SourceMap> {
863 Ok(self.rewrite_with_mapping(options)?.0)
864 }
865
866 pub(crate) fn rewrite_with_mapping(
868 self,
869 options: &RewriteOptions<'_>,
870 ) -> Result<(SourceMap, Vec<u32>)> {
871 let mut builder = SourceMapBuilder::new(self.get_file());
872 builder.set_debug_id(self.debug_id);
873
874 for token in self.tokens() {
875 let raw = builder.add_token(&token, options.with_names);
876 if raw.src_id != !0
877 && options.with_source_contents
878 && !builder.has_source_contents(raw.src_id)
879 {
880 builder
881 .set_source_contents(raw.src_id, self.get_source_contents(token.get_src_id()));
882 }
883 }
884
885 #[cfg(any(unix, windows, target_os = "redox"))]
886 {
887 if options.load_local_source_contents {
888 builder.load_local_source_contents(options.base_path)?;
889 }
890 }
891
892 let mut prefixes = vec![];
893 let mut need_common_prefix = false;
894 for &prefix in options.strip_prefixes.iter() {
895 if prefix == "~" {
896 need_common_prefix = true;
897 } else {
898 prefixes.push(prefix.to_string());
899 }
900 }
901 if need_common_prefix {
902 if let Some(prefix) = find_common_prefix(self.sources.iter().map(AsRef::as_ref)) {
903 prefixes.push(prefix);
904 }
905 }
906 if !prefixes.is_empty() {
907 builder.strip_prefixes(&prefixes);
908 }
909
910 let mapping = builder.take_mapping();
911
912 let sm = builder.into_sourcemap();
913
914 Ok((sm, mapping))
915 }
916
917 pub fn adjust_mappings(&mut self, adjustment: &Self) {
933 #[derive(Debug, Clone, Copy)]
962 struct Range<'a> {
963 start: (u32, u32),
964 end: (u32, u32),
965 value: &'a RawToken,
966 }
967
968 #[allow(clippy::ptr_arg)]
970 fn create_ranges(
971 tokens: &mut [RawToken],
972 key: fn(&RawToken) -> (u32, u32),
973 ) -> Vec<Range<'_>> {
974 tokens.sort_unstable_by_key(key);
975
976 let mut token_iter = tokens.iter().peekable();
977 let mut ranges = Vec::new();
978
979 while let Some(t) = token_iter.next() {
980 let start = key(t);
981 let next_start = token_iter.peek().map_or((u32::MAX, u32::MAX), |t| key(t));
982 let end = std::cmp::min(next_start, (start.0, u32::MAX));
984 ranges.push(Range {
985 start,
986 end,
987 value: t,
988 });
989 }
990
991 ranges
992 }
993
994 let mut self_tokens = std::mem::take(&mut self.tokens);
1000 let original_ranges = create_ranges(&mut self_tokens, |t| (t.dst_line, t.dst_col));
1001 let mut adjustment_tokens = adjustment.tokens.clone();
1002 let adjustment_ranges = create_ranges(&mut adjustment_tokens, |t| (t.src_line, t.src_col));
1003
1004 let mut original_ranges_iter = original_ranges.iter();
1005
1006 let mut original_range = match original_ranges_iter.next() {
1007 Some(r) => r,
1008 None => return,
1009 };
1010
1011 'outer: for &adjustment_range in &adjustment_ranges {
1014 let (line_diff, col_diff) = (
1017 adjustment_range.value.dst_line as i32 - adjustment_range.value.src_line as i32,
1018 adjustment_range.value.dst_col as i32 - adjustment_range.value.src_col as i32,
1019 );
1020
1021 while original_range.end <= adjustment_range.start {
1023 match original_ranges_iter.next() {
1024 Some(r) => original_range = r,
1025 None => break 'outer,
1026 }
1027 }
1028
1029 while original_range.start < adjustment_range.end {
1033 let (dst_line, dst_col) =
1035 std::cmp::max(original_range.start, adjustment_range.start);
1036 let mut token = RawToken {
1037 dst_line,
1038 dst_col,
1039 ..*original_range.value
1040 };
1041
1042 token.dst_line = (token.dst_line as i32 + line_diff) as u32;
1043 token.dst_col = (token.dst_col as i32 + col_diff) as u32;
1044
1045 self.tokens.push(token);
1046
1047 if original_range.end >= adjustment_range.end {
1048 break;
1051 } else {
1052 match original_ranges_iter.next() {
1054 Some(r) => original_range = r,
1055 None => break 'outer,
1056 }
1057 }
1058 }
1059 }
1060
1061 self.tokens
1062 .sort_unstable_by_key(|t| (t.dst_line, t.dst_col));
1063 }
1064}
1065
1066impl SourceMapIndex {
1067 pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMapIndex> {
1072 match decode(rdr)? {
1073 DecodedMap::Index(smi) => Ok(smi),
1074 _ => Err(Error::IncompatibleSourceMap),
1075 }
1076 }
1077
1078 pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
1080 encode(self, w)
1081 }
1082
1083 pub fn from_slice(slice: &[u8]) -> Result<SourceMapIndex> {
1088 match decode_slice(slice)? {
1089 DecodedMap::Index(smi) => Ok(smi),
1090 _ => Err(Error::IncompatibleSourceMap),
1091 }
1092 }
1093
1094 pub fn new(file: Option<String>, sections: Vec<SourceMapSection>) -> SourceMapIndex {
1099 SourceMapIndex {
1100 file,
1101 sections,
1102 x_facebook_offsets: None,
1103 x_metro_module_paths: None,
1104 debug_id: None,
1105 }
1106 }
1107
1108 pub fn new_ram_bundle_compatible(
1116 file: Option<String>,
1117 sections: Vec<SourceMapSection>,
1118 x_facebook_offsets: Option<Vec<Option<u32>>>,
1119 x_metro_module_paths: Option<Vec<String>>,
1120 ) -> SourceMapIndex {
1121 SourceMapIndex {
1122 file,
1123 sections,
1124 x_facebook_offsets,
1125 x_metro_module_paths,
1126 debug_id: None,
1127 }
1128 }
1129
1130 pub(crate) fn debug_id(&self) -> Option<DebugId> {
1132 self.debug_id
1133 }
1134
1135 fn set_debug_id(&mut self, debug_id: Option<DebugId>) {
1136 self.debug_id = debug_id;
1137 }
1138
1139 pub(crate) fn with_debug_id(mut self, debug_id: Option<DebugId>) -> Self {
1141 self.set_debug_id(debug_id);
1142 self
1143 }
1144
1145 pub fn get_file(&self) -> Option<&str> {
1147 self.file.as_ref().map(|x| &x[..])
1148 }
1149
1150 pub fn set_file(&mut self, value: Option<&str>) {
1152 self.file = value.map(str::to_owned);
1153 }
1154
1155 pub fn get_section_count(&self) -> u32 {
1157 self.sections.len() as u32
1158 }
1159
1160 pub fn get_section(&self, idx: u32) -> Option<&SourceMapSection> {
1162 self.sections.get(idx as usize)
1163 }
1164
1165 pub fn get_section_mut(&mut self, idx: u32) -> Option<&mut SourceMapSection> {
1167 self.sections.get_mut(idx as usize)
1168 }
1169
1170 pub fn sections(&self) -> SourceMapSectionIter<'_> {
1172 SourceMapSectionIter {
1173 i: self,
1174 next_idx: 0,
1175 }
1176 }
1177
1178 pub fn get_original_function_name(
1187 &self,
1188 line: u32,
1189 col: u32,
1190 minified_name: &str,
1191 sv: &SourceView,
1192 ) -> Option<&str> {
1193 self.lookup_token(line, col)
1194 .and_then(|token| sv.get_original_function_name(token, minified_name))
1195 }
1196
1197 pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
1203 let (_section_idx, section) =
1204 greatest_lower_bound(&self.sections, &(line, col), SourceMapSection::get_offset)?;
1205 let map = section.get_sourcemap()?;
1206 let (off_line, off_col) = section.get_offset();
1207 map.lookup_token(
1208 line - off_line,
1209 if line == off_line { col - off_col } else { col },
1210 )
1211 }
1212
1213 pub fn flatten(&self) -> Result<SourceMap> {
1216 let mut builder = SourceMapBuilder::new(self.get_file());
1217
1218 for section in self.sections() {
1219 let (off_line, off_col) = section.get_offset();
1220 let map = match section.get_sourcemap() {
1221 Some(map) => match map {
1222 DecodedMap::Regular(sm) => Cow::Borrowed(sm),
1223 DecodedMap::Index(idx) => Cow::Owned(idx.flatten()?),
1224 DecodedMap::Hermes(smh) => Cow::Borrowed(&smh.sm),
1225 },
1226 None => {
1227 return Err(Error::CannotFlatten(format!(
1228 "Section has an unresolved \
1229 sourcemap: {}",
1230 section.get_url().unwrap_or("<unknown url>")
1231 )));
1232 }
1233 };
1234
1235 let mut src_id_map = Vec::<u32>::with_capacity(map.sources().count());
1236
1237 for (original_id, (source, contents)) in
1238 map.sources().zip(map.source_contents()).enumerate()
1239 {
1240 debug_assert_eq!(original_id, src_id_map.len());
1241 let src_id = builder.add_source(source);
1242
1243 src_id_map.push(src_id);
1244
1245 if let Some(contents) = contents {
1246 builder.set_source_contents(src_id, Some(contents));
1247 }
1248 }
1249
1250 let mut name_id_map = Vec::<u32>::with_capacity(map.names().count());
1251
1252 for (original_id, name) in map.names().enumerate() {
1253 debug_assert_eq!(original_id, name_id_map.len());
1254 let name_id = builder.add_name(name);
1255 name_id_map.push(name_id);
1256 }
1257
1258 for token in map.tokens() {
1259 let dst_col = if token.get_dst_line() == 0 {
1260 token.get_dst_col() + off_col
1261 } else {
1262 token.get_dst_col()
1263 };
1264
1265 let original_src_id = token.raw.src_id;
1267 let src_id = if original_src_id == !0 {
1268 None
1269 } else {
1270 src_id_map.get(original_src_id as usize).copied()
1271 };
1272
1273 let original_name_id = token.raw.name_id;
1274 let name_id = if original_name_id == !0 {
1275 None
1276 } else {
1277 name_id_map.get(original_name_id as usize).copied()
1278 };
1279
1280 let raw = builder.add_raw(
1281 token.get_dst_line() + off_line,
1282 dst_col,
1283 token.get_src_line(),
1284 token.get_src_col(),
1285 src_id,
1286 name_id,
1287 token.is_range(),
1288 );
1289
1290 if map.ignore_list.contains(&token.get_src_id()) {
1291 builder.add_to_ignore_list(raw.src_id);
1292 }
1293 }
1294 }
1295
1296 Ok(builder.into_sourcemap())
1297 }
1298
1299 pub fn flatten_and_rewrite(self, options: &RewriteOptions<'_>) -> Result<SourceMap> {
1303 self.flatten()?.rewrite(options)
1304 }
1305
1306 pub fn is_for_ram_bundle(&self) -> bool {
1308 self.x_facebook_offsets.is_some() && self.x_metro_module_paths.is_some()
1309 }
1310
1311 pub fn x_facebook_offsets(&self) -> Option<&[Option<u32>]> {
1313 self.x_facebook_offsets.as_ref().map(|x| &x[..])
1314 }
1315
1316 pub fn x_metro_module_paths(&self) -> Option<&[String]> {
1318 self.x_metro_module_paths.as_ref().map(|x| &x[..])
1319 }
1320
1321 pub fn adjust_sections_offset_rows(&mut self, amount: u32) -> bool {
1327 let adjusted_rows: Vec<_> = self
1328 .sections
1329 .iter()
1330 .filter_map(|section| section.offset.0.checked_add(amount))
1332 .collect();
1333
1334 if adjusted_rows.len() != self.sections.len() {
1335 return false;
1337 }
1338
1339 for (section, adjustment) in self.sections.iter_mut().zip(adjusted_rows) {
1340 section.offset.0 = adjustment;
1341 }
1342
1343 true
1344 }
1345}
1346
1347impl SourceMapSection {
1348 pub fn new(
1354 offset: (u32, u32),
1355 url: Option<String>,
1356 map: Option<DecodedMap>,
1357 ) -> SourceMapSection {
1358 SourceMapSection {
1359 offset,
1360 url,
1361 map: map.map(Box::new),
1362 }
1363 }
1364
1365 pub fn get_offset_line(&self) -> u32 {
1367 self.offset.0
1368 }
1369
1370 pub fn get_offset_col(&self) -> u32 {
1372 self.offset.1
1373 }
1374
1375 pub fn get_offset(&self) -> (u32, u32) {
1377 self.offset
1378 }
1379
1380 pub fn get_url(&self) -> Option<&str> {
1382 self.url.as_deref()
1383 }
1384
1385 pub fn set_url(&mut self, value: Option<&str>) {
1387 self.url = value.map(str::to_owned);
1388 }
1389
1390 pub fn get_sourcemap(&self) -> Option<&DecodedMap> {
1392 self.map.as_ref().map(Box::as_ref)
1393 }
1394
1395 pub fn get_sourcemap_mut(&mut self) -> Option<&mut DecodedMap> {
1397 self.map.as_mut().map(Box::as_mut)
1398 }
1399
1400 pub fn set_sourcemap(&mut self, sm: Option<DecodedMap>) {
1402 self.map = sm.map(Box::new);
1403 }
1404}
1405
1406#[cfg(test)]
1407mod tests {
1408 use std::collections::BTreeSet;
1409
1410 use super::{DecodedMap, RewriteOptions, SourceMap, SourceMapIndex, SourceMapSection};
1411 use debugid::DebugId;
1412
1413 #[test]
1414 fn test_rewrite_debugid() {
1415 let input: &[_] = br#"{
1416 "version":3,
1417 "sources":["coolstuff.js"],
1418 "names":["x","alert"],
1419 "mappings":"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM",
1420 "debug_id":"00000000-0000-0000-0000-000000000000"
1421 }"#;
1422
1423 let sm = SourceMap::from_slice(input).unwrap();
1424
1425 assert_eq!(sm.debug_id, Some(DebugId::default()));
1426
1427 let new_sm = sm
1428 .rewrite(&RewriteOptions {
1429 with_names: false,
1430 ..Default::default()
1431 })
1432 .unwrap();
1433
1434 assert_eq!(new_sm.debug_id, Some(DebugId::default()));
1435 }
1436
1437 #[test]
1438 fn test_debugid_alias() {
1439 let input: &[_] = br#"{
1440 "version":3,
1441 "sources":["coolstuff.js"],
1442 "names":["x","alert"],
1443 "mappings":"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM",
1444 "debug_id": "11111111-1111-1111-1111-111111111111",
1445 "debugId":"00000000-0000-0000-0000-000000000000"
1446 }"#;
1447
1448 let sm = SourceMap::from_slice(input).unwrap();
1449
1450 assert_eq!(sm.debug_id, Some(DebugId::default()));
1451 }
1452
1453 #[test]
1454 fn test_adjust_mappings_injection() {
1455 for bundler in ["esbuild", "rollup", "vite", "webpack", "rspack"] {
1470 let original_map_file = std::fs::File::open(format!(
1471 "tests/fixtures/adjust_mappings/{bundler}.bundle.js.map"
1472 ))
1473 .unwrap();
1474
1475 let injected_map_file = std::fs::File::open(format!(
1476 "tests/fixtures/adjust_mappings/{bundler}-injected.bundle.js.map"
1477 ))
1478 .unwrap();
1479
1480 let composed_map_file = std::fs::File::open(format!(
1481 "tests/fixtures/adjust_mappings/{bundler}-composed.bundle.js.map"
1482 ))
1483 .unwrap();
1484
1485 let mut original_map = SourceMap::from_reader(original_map_file).unwrap();
1486 let injected_map = SourceMap::from_reader(injected_map_file).unwrap();
1487 let composed_map = SourceMap::from_reader(composed_map_file).unwrap();
1488 original_map.adjust_mappings(&injected_map);
1489
1490 assert_eq!(
1491 original_map.tokens, composed_map.tokens,
1492 "bundler = {bundler}"
1493 );
1494 }
1495 }
1496
1497 #[test]
1498 fn test_roundtrip() {
1499 let sm = br#"{
1500 "version": 3,
1501 "file": "foo.js",
1502 "sources": [
1503 "./bar.js",
1504 "./baz.js"
1505 ],
1506 "sourceRoot": "webpack:///",
1507 "sourcesContent": [null, null],
1508 "names": [],
1509 "mappings": ""
1510 }"#;
1511
1512 let sm = SourceMap::from_slice(sm).unwrap();
1513 let mut out = Vec::new();
1514 sm.to_writer(&mut out).unwrap();
1515
1516 let sm_new = SourceMap::from_slice(&out).unwrap();
1517 assert_eq!(sm_new.sources, sm.sources);
1518 }
1519
1520 #[test]
1521 fn test_sourcemap_index_default_debug_id() {
1522 let sm = SourceMapIndex::new(None, vec![]);
1523 assert!(sm.debug_id().is_none());
1524 }
1525
1526 #[test]
1527 fn test_sourcemap_index_debug_id() {
1528 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
1529
1530 let sm = SourceMapIndex::new(None, vec![])
1531 .with_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")));
1532
1533 assert_eq!(
1534 sm.debug_id(),
1535 Some(DEBUG_ID.parse().expect("valid debug id"))
1536 );
1537 }
1538
1539 #[test]
1540 fn test_decoded_map_regular_debug_id() {
1541 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
1542
1543 let mut decoded_map = DecodedMap::Regular(SourceMap {
1544 file: None,
1545 tokens: vec![],
1546 names: vec![],
1547 source_root: None,
1548 sources: vec![],
1549 sources_prefixed: None,
1550 sources_content: vec![],
1551 ignore_list: BTreeSet::new(),
1552 debug_id: None,
1553 });
1554
1555 assert!(decoded_map.debug_id().is_none());
1556
1557 decoded_map.set_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")));
1558
1559 assert_eq!(
1560 decoded_map,
1561 DecodedMap::Regular(SourceMap {
1562 file: None,
1563 tokens: vec![],
1564 names: vec![],
1565 source_root: None,
1566 sources: vec![],
1567 sources_prefixed: None,
1568 sources_content: vec![],
1569 ignore_list: BTreeSet::new(),
1570 debug_id: Some(DEBUG_ID.parse().expect("valid debug id")),
1571 })
1572 );
1573
1574 assert_eq!(
1575 decoded_map.debug_id(),
1576 Some(DEBUG_ID.parse().expect("valid debug id"))
1577 );
1578 }
1579
1580 #[test]
1581 fn test_decoded_map_index_debug_id() {
1582 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
1583
1584 let mut decoded_map = DecodedMap::Index(SourceMapIndex {
1585 file: None,
1586 sections: vec![],
1587 x_facebook_offsets: None,
1588 x_metro_module_paths: None,
1589 debug_id: None,
1590 });
1591
1592 assert!(decoded_map.debug_id().is_none());
1593
1594 decoded_map.set_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")));
1595
1596 assert_eq!(
1597 decoded_map,
1598 DecodedMap::Index(SourceMapIndex {
1599 file: None,
1600 sections: vec![],
1601 x_facebook_offsets: None,
1602 x_metro_module_paths: None,
1603 debug_id: Some(DEBUG_ID.parse().expect("valid debug id")),
1604 })
1605 );
1606
1607 assert_eq!(
1608 decoded_map.debug_id(),
1609 Some(DEBUG_ID.parse().expect("valid debug id"))
1610 );
1611 }
1612
1613 #[test]
1614 fn test_adjust_sections_offset_rows_basic() {
1615 let mut smi = SourceMapIndex::new(
1617 Some("test.js".to_string()),
1618 vec![
1619 SourceMapSection::new((0, 0), None, None),
1620 SourceMapSection::new((10, 0), None, None),
1621 ],
1622 );
1623
1624 assert!(smi.adjust_sections_offset_rows(1));
1626
1627 assert_eq!(
1629 smi,
1630 SourceMapIndex::new(
1631 Some("test.js".to_string()),
1632 vec![
1633 SourceMapSection::new((1, 0), None, None),
1634 SourceMapSection::new((11, 0), None, None),
1635 ],
1636 )
1637 );
1638 }
1639
1640 #[test]
1641 fn test_adjust_sections_offset_rows_zero() {
1642 let mut smi = SourceMapIndex::new(
1644 Some("test.js".to_string()),
1645 vec![
1646 SourceMapSection::new((0, 0), None, None),
1647 SourceMapSection::new((10, 0), None, None),
1648 ],
1649 );
1650
1651 assert!(smi.adjust_sections_offset_rows(0));
1653
1654 assert_eq!(
1656 smi,
1657 SourceMapIndex::new(
1658 Some("test.js".to_string()),
1659 vec![
1660 SourceMapSection::new((0, 0), None, None),
1661 SourceMapSection::new((10, 0), None, None),
1662 ],
1663 )
1664 );
1665 }
1666
1667 #[test]
1668 fn test_adjust_sections_offset_rows_multiple_sections() {
1669 let mut smi = SourceMapIndex::new(
1671 Some("test.js".to_string()),
1672 vec![
1673 SourceMapSection::new((0, 0), None, None),
1674 SourceMapSection::new((10, 0), None, None),
1675 SourceMapSection::new((20, 10), None, None),
1676 SourceMapSection::new((30, 40), None, None),
1677 ],
1678 );
1679
1680 assert!(smi.adjust_sections_offset_rows(1));
1682
1683 assert_eq!(
1685 smi,
1686 SourceMapIndex::new(
1687 Some("test.js".to_string()),
1688 vec![
1689 SourceMapSection::new((1, 0), None, None),
1690 SourceMapSection::new((11, 0), None, None),
1691 SourceMapSection::new((21, 10), None, None),
1692 SourceMapSection::new((31, 40), None, None),
1693 ],
1694 )
1695 );
1696 }
1697
1698 #[test]
1699 fn test_adjust_sections_offset_rows_overflow() {
1700 let mut smi = SourceMapIndex::new(
1702 Some("test.js".to_string()),
1703 vec![
1704 SourceMapSection::new((0, 0), None, None),
1705 SourceMapSection::new((u32::MAX, 0), None, None),
1706 ],
1707 );
1708
1709 let original_smi = smi.clone();
1711
1712 assert!(!smi.adjust_sections_offset_rows(1));
1714
1715 assert_eq!(smi, original_smi);
1717 }
1718
1719 #[test]
1720 fn test_adjust_sections_offset_rows_partial_overflow() {
1721 let mut smi = SourceMapIndex::new(
1723 Some("test.js".to_string()),
1724 vec![
1725 SourceMapSection::new((0, 0), None, None),
1726 SourceMapSection::new((10, 0), None, None),
1727 SourceMapSection::new((20, 0), None, None),
1728 SourceMapSection::new((u32::MAX, 0), None, None),
1729 ],
1730 );
1731
1732 let original_smi = smi.clone();
1734
1735 assert!(!smi.adjust_sections_offset_rows(1));
1737
1738 assert_eq!(smi, original_smi);
1740 }
1741
1742 #[test]
1743 fn test_adjust_sections_offset_rows_large_amount() {
1744 let mut smi = SourceMapIndex::new(
1746 Some("test.js".to_string()),
1747 vec![
1748 SourceMapSection::new((0, 0), None, None),
1749 SourceMapSection::new((10, 0), None, None),
1750 ],
1751 );
1752
1753 assert!(smi.adjust_sections_offset_rows(1_000_000));
1754
1755 assert_eq!(
1757 smi,
1758 SourceMapIndex::new(
1759 Some("test.js".to_string()),
1760 vec![
1761 SourceMapSection::new((1_000_000, 0), None, None),
1762 SourceMapSection::new((1_000_010, 0), None, None),
1763 ],
1764 )
1765 );
1766 }
1767
1768 #[test]
1769 fn adjust_sections_offset_rows_large_amount_overflow() {
1770 let mut smi = SourceMapIndex::new(
1772 Some("test.js".to_string()),
1773 vec![
1774 SourceMapSection::new((0, 0), None, None),
1775 SourceMapSection::new((10, 0), None, None),
1776 ],
1777 );
1778
1779 let original_smi = smi.clone();
1781
1782 assert!(!smi.adjust_sections_offset_rows(u32::MAX));
1784
1785 assert_eq!(smi, original_smi);
1787 }
1788
1789 #[test]
1790 fn adjust_sections_offset_rows_no_sections() {
1791 let mut smi = SourceMapIndex::new(Some("test.js".to_string()), vec![]);
1793
1794 assert!(smi.adjust_sections_offset_rows(1));
1796
1797 assert_eq!(
1799 smi,
1800 SourceMapIndex::new(Some("test.js".to_string()), vec![])
1801 );
1802 }
1803
1804 mod prop {
1805 use magic_string::MagicString;
1824 use proptest::prelude::*;
1825
1826 use crate::SourceMap;
1827
1828 #[derive(Debug, Clone)]
1830 enum FirstEdit {
1831 Insert(u32, String),
1833 Delete(i64, i64),
1835 }
1836
1837 impl FirstEdit {
1838 fn apply(&self, line: usize, ms: &mut MagicString) {
1840 let line_offset = line * 11;
1842 match self {
1843 FirstEdit::Insert(col, s) => {
1844 ms.append_left(line_offset as u32 + *col, s).unwrap();
1845 }
1846 FirstEdit::Delete(start, end) => {
1847 ms.remove(line_offset as i64 + *start, line_offset as i64 + *end)
1848 .unwrap();
1849 }
1850 }
1851 }
1852 }
1853
1854 fn nth_line_start_end(n: usize, s: &str) -> (usize, usize) {
1857 let line = s.lines().nth(n).unwrap();
1858 let start = line.as_ptr() as usize - s.as_ptr() as usize;
1859 let end = if n == 9 {
1861 start + line.len()
1862 } else {
1863 start + line.len() + 1
1864 };
1865 (start, end)
1866 }
1867
1868 #[derive(Debug, Clone)]
1870 enum SecondEdit {
1871 Prepend(String),
1873 Append(String),
1875 Insert(usize, String),
1877 Delete(usize),
1879 }
1880
1881 impl SecondEdit {
1882 fn apply(&self, orig: &str, ms: &mut MagicString) {
1887 match self {
1888 SecondEdit::Prepend(s) => {
1889 ms.prepend(s).unwrap();
1890 }
1891 SecondEdit::Append(s) => {
1892 ms.append(s).unwrap();
1893 }
1894 SecondEdit::Insert(line, s) => {
1895 let (start, _) = nth_line_start_end(*line, orig);
1896 ms.prepend_left(start as u32, s).unwrap();
1897 }
1898 SecondEdit::Delete(line) => {
1899 let (start, end) = nth_line_start_end(*line, orig);
1900 ms.remove(start as i64, end as i64).unwrap();
1901 }
1902 }
1903 }
1904 }
1905
1906 fn starting_string() -> impl Strategy<Value = String> {
1908 (vec!["[a-z]{10}"; 10]).prop_map(|v| v.join("\n"))
1909 }
1910
1911 fn first_edit() -> impl Strategy<Value = FirstEdit> {
1913 prop_oneof![
1914 (1u32..9, "[a-z]{5}").prop_map(|(c, s)| FirstEdit::Insert(c, s)),
1915 (1i64..10)
1916 .prop_flat_map(|end| (0..end, Just(end)))
1917 .prop_map(|(a, b)| FirstEdit::Delete(a, b))
1918 ]
1919 }
1920
1921 fn first_edit_sequence() -> impl Strategy<Value = Vec<FirstEdit>> {
1925 let mut vec = Vec::with_capacity(10);
1926
1927 for _ in 0..10 {
1928 vec.push(first_edit())
1929 }
1930
1931 vec
1932 }
1933
1934 fn second_edit_sequence() -> impl Strategy<Value = Vec<SecondEdit>> {
1939 let edits = (0..10)
1940 .map(|i| {
1941 prop_oneof![
1942 "[a-z\n]{12}".prop_map(SecondEdit::Prepend),
1943 "[a-z\n]{12}".prop_map(SecondEdit::Append),
1944 "[a-z\n]{11}\n".prop_map(move |s| SecondEdit::Insert(i, s)),
1945 Just(SecondEdit::Delete(i)),
1946 ]
1947 })
1948 .collect::<Vec<_>>();
1949
1950 edits.prop_shuffle()
1951 }
1952
1953 proptest! {
1954 #[test]
1955 fn test_composition_identity(
1956 input in starting_string(),
1957 first_edits in first_edit_sequence(),
1958 second_edits in second_edit_sequence(),
1959 ) {
1960
1961 let mut ms1 = MagicString::new(&input);
1964
1965 for (line, first_edit) in first_edits.iter().enumerate() {
1966 first_edit.apply(line, &mut ms1);
1967 }
1968
1969 let first_map = ms1.generate_map(Default::default()).unwrap().to_string().unwrap();
1970 let mut first_map = SourceMap::from_slice(first_map.as_bytes()).unwrap();
1971
1972 let transformed_input = ms1.to_string();
1973
1974 let mut ms2 = MagicString::new(&transformed_input);
1975
1976 for second_edit in second_edits.iter() {
1977 second_edit.apply(&transformed_input, &mut ms2);
1978 }
1979
1980 let output_1 = ms2.to_string();
1981
1982 let second_map = ms2.generate_map(Default::default()).unwrap().to_string().unwrap();
1983 let second_map = SourceMap::from_slice(second_map.as_bytes()).unwrap();
1984
1985 let mut ms3 = MagicString::new(&input);
1988
1989 for (line, first_edit) in first_edits.iter().enumerate() {
1990 first_edit.apply(line, &mut ms3);
1991 }
1992
1993 for second_edit in second_edits.iter() {
1994 second_edit.apply(&input, &mut ms3);
1995 }
1996
1997 let output_2 = ms3.to_string();
1998
1999 let third_map = ms3.generate_map(Default::default()).unwrap().to_string().unwrap();
2000 let third_map = SourceMap::from_slice(third_map.as_bytes()).unwrap();
2001
2002 assert_eq!(output_1, output_2);
2004
2005 first_map.adjust_mappings(&second_map);
2006
2007 assert_eq!(first_map.tokens, third_map.tokens);
2008 }
2009 }
2010 }
2011}