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;
18use rustc_hash::FxHashMap;
19
20#[derive(Debug, Clone)]
28pub struct RewriteOptions<'a> {
29 pub with_names: bool,
31 pub with_source_contents: bool,
33 #[cfg(any(unix, windows, target_os = "redox"))]
36 pub load_local_source_contents: bool,
37 pub base_path: Option<&'a Path>,
40 pub strip_prefixes: &'a [&'a str],
44}
45
46impl<'a> Default for RewriteOptions<'a> {
47 fn default() -> RewriteOptions<'a> {
48 RewriteOptions {
49 with_names: true,
50 with_source_contents: true,
51 #[cfg(any(unix, windows, target_os = "redox"))]
52 load_local_source_contents: false,
53 base_path: None,
54 strip_prefixes: &[][..],
55 }
56 }
57}
58
59#[derive(Debug, Clone, PartialEq)]
66pub enum DecodedMap {
67 Regular(SourceMap),
69 Index(SourceMapIndex),
71 Hermes(SourceMapHermes),
73}
74
75impl DecodedMap {
76 pub fn from_reader<R: Read>(rdr: R) -> Result<DecodedMap> {
78 decode(rdr)
79 }
80
81 pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
83 match *self {
84 DecodedMap::Regular(ref sm) => encode(sm, w),
85 DecodedMap::Index(ref smi) => encode(smi, w),
86 DecodedMap::Hermes(ref smh) => encode(smh, w),
87 }
88 }
89
90 pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
95 match *self {
96 DecodedMap::Regular(ref sm) => sm.lookup_token(line, col),
97 DecodedMap::Index(ref smi) => smi.lookup_token(line, col),
98 DecodedMap::Hermes(ref smh) => smh.lookup_token(line, col),
99 }
100 }
101
102 pub fn get_original_function_name(
108 &self,
109 line: u32,
110 col: u32,
111 minified_name: Option<&str>,
112 source_view: Option<&SourceView>,
113 ) -> Option<&str> {
114 match *self {
115 DecodedMap::Regular(ref sm) => {
116 sm.get_original_function_name(line, col, minified_name?, source_view?)
117 }
118 DecodedMap::Index(ref smi) => {
119 smi.get_original_function_name(line, col, minified_name?, source_view?)
120 }
121 DecodedMap::Hermes(ref smh) => {
122 if line != 0 {
123 return None;
124 }
125 smh.get_original_function_name(col)
126 }
127 }
128 }
129
130 pub fn debug_id(&self) -> Option<DebugId> {
132 match self {
133 DecodedMap::Regular(sm) => sm.get_debug_id(),
134 DecodedMap::Index(smi) => smi.debug_id(),
135 DecodedMap::Hermes(smh) => smh.get_debug_id(),
136 }
137 }
138
139 pub fn set_debug_id(&mut self, debug_id: Option<DebugId>) {
141 match self {
142 DecodedMap::Regular(sm) => sm.set_debug_id(debug_id),
143 DecodedMap::Index(smi) => smi.set_debug_id(debug_id),
144 DecodedMap::Hermes(smh) => smh.set_debug_id(debug_id),
145 }
146 }
147}
148
149#[derive(PartialEq, Eq, Copy, Clone, Debug)]
156pub struct RawToken {
157 pub dst_line: u32,
159 pub dst_col: u32,
161 pub src_line: u32,
163 pub src_col: u32,
165 pub src_id: u32,
167 pub name_id: u32,
169
170 pub is_range: bool,
174}
175
176#[derive(Copy, Clone)]
178pub struct Token<'a> {
179 raw: &'a RawToken,
180 pub(crate) sm: &'a SourceMap,
181 pub(crate) idx: usize,
182 offset: u32,
183}
184
185impl<'a> Token<'a> {
186 pub fn sourcemap(&self) -> &'a SourceMap {
188 self.sm
189 }
190}
191
192impl PartialEq for Token<'_> {
193 fn eq(&self, other: &Token<'_>) -> bool {
194 self.raw == other.raw
195 }
196}
197
198impl Eq for Token<'_> {}
199
200impl PartialOrd for Token<'_> {
201 fn partial_cmp(&self, other: &Token<'_>) -> Option<Ordering> {
202 Some(self.cmp(other))
203 }
204}
205
206impl Ord for Token<'_> {
207 fn cmp(&self, other: &Token<'_>) -> Ordering {
208 macro_rules! try_cmp {
209 ($a:expr, $b:expr) => {
210 match $a.cmp(&$b) {
211 Ordering::Equal => {}
212 x => {
213 return x;
214 }
215 }
216 };
217 }
218 try_cmp!(self.get_dst_line(), other.get_dst_line());
219 try_cmp!(self.get_dst_col(), other.get_dst_col());
220 try_cmp!(self.get_source(), other.get_source());
221 try_cmp!(self.get_src_line(), other.get_src_line());
222 try_cmp!(self.get_src_col(), other.get_src_col());
223 try_cmp!(self.get_name(), other.get_name());
224 try_cmp!(self.is_range(), other.is_range());
225
226 Ordering::Equal
227 }
228}
229
230impl<'a> Token<'a> {
231 pub fn get_dst_line(&self) -> u32 {
233 self.raw.dst_line
234 }
235
236 pub fn get_dst_col(&self) -> u32 {
238 self.raw.dst_col
239 }
240
241 pub fn get_dst(&self) -> (u32, u32) {
243 (self.get_dst_line(), self.get_dst_col())
244 }
245
246 pub fn get_src_line(&self) -> u32 {
248 self.raw.src_line
249 }
250
251 pub fn get_src_col(&self) -> u32 {
253 self.raw.src_col.saturating_add(self.offset)
254 }
255
256 pub fn get_src(&self) -> (u32, u32) {
258 (self.get_src_line(), self.get_src_col())
259 }
260
261 pub fn get_src_id(&self) -> u32 {
263 self.raw.src_id
264 }
265
266 pub fn get_source(&self) -> Option<&'a str> {
268 if self.raw.src_id == !0 {
269 None
270 } else {
271 self.sm.get_source(self.raw.src_id)
272 }
273 }
274
275 pub fn has_source(&self) -> bool {
277 self.raw.src_id != !0
278 }
279
280 pub fn get_name(&self) -> Option<&'a str> {
282 if self.raw.name_id == !0 {
283 None
284 } else {
285 self.sm.get_name(self.raw.name_id)
286 }
287 }
288
289 pub fn has_name(&self) -> bool {
291 self.get_name().is_some()
292 }
293
294 pub fn get_name_id(&self) -> u32 {
296 self.raw.name_id
297 }
298
299 pub fn to_tuple(&self) -> (&'a str, u32, u32, Option<&'a str>) {
302 (
303 self.get_source().unwrap_or(""),
304 self.get_src_line(),
305 self.get_src_col(),
306 self.get_name(),
307 )
308 }
309
310 pub fn get_raw_token(&self) -> RawToken {
312 *self.raw
313 }
314
315 pub fn get_source_view(&self) -> Option<&SourceView> {
317 self.sm.get_source_view(self.get_src_id())
318 }
319
320 pub fn is_range(&self) -> bool {
324 self.raw.is_range
325 }
326}
327
328pub struct TokenIter<'a> {
330 i: &'a SourceMap,
331 next_idx: usize,
332}
333
334impl TokenIter<'_> {
335 pub fn seek(&mut self, line: u32, col: u32) -> bool {
336 let token = self.i.lookup_token(line, col);
337 match token {
338 Some(token) => {
339 self.next_idx = token.idx + 1;
340 true
341 }
342 None => false,
343 }
344 }
345}
346
347impl<'a> Iterator for TokenIter<'a> {
348 type Item = Token<'a>;
349
350 fn next(&mut self) -> Option<Token<'a>> {
351 self.i.get_token(self.next_idx).inspect(|_| {
352 self.next_idx += 1;
353 })
354 }
355}
356
357pub struct SourceIter<'a> {
359 i: &'a SourceMap,
360 next_idx: u32,
361}
362
363impl<'a> Iterator for SourceIter<'a> {
364 type Item = &'a str;
365
366 fn next(&mut self) -> Option<&'a str> {
367 self.i.get_source(self.next_idx).inspect(|_| {
368 self.next_idx += 1;
369 })
370 }
371}
372
373pub struct SourceContentsIter<'a> {
375 i: &'a SourceMap,
376 next_idx: u32,
377}
378
379impl<'a> Iterator for SourceContentsIter<'a> {
380 type Item = Option<&'a str>;
381
382 fn next(&mut self) -> Option<Option<&'a str>> {
383 if self.next_idx >= self.i.get_source_count() {
384 None
385 } else {
386 let rv = Some(self.i.get_source_contents(self.next_idx));
387 self.next_idx += 1;
388 rv
389 }
390 }
391}
392
393pub struct NameIter<'a> {
395 i: &'a SourceMap,
396 next_idx: u32,
397}
398
399impl<'a> Iterator for NameIter<'a> {
400 type Item = &'a str;
401
402 fn next(&mut self) -> Option<&'a str> {
403 self.i.get_name(self.next_idx).inspect(|_| {
404 self.next_idx += 1;
405 })
406 }
407}
408
409impl fmt::Debug for Token<'_> {
410 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
411 write!(f, "<Token {self:#}>")
412 }
413}
414
415impl fmt::Display for Token<'_> {
416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 write!(
418 f,
419 "{}:{}:{}{}",
420 self.get_source().unwrap_or("<unknown>"),
421 self.get_src_line(),
422 self.get_src_col(),
423 self.get_name()
424 .map(|x| format!(" name={x}"))
425 .unwrap_or_default()
426 )?;
427 if f.alternate() {
428 write!(
429 f,
430 " ({}:{}){}",
431 self.get_dst_line(),
432 self.get_dst_col(),
433 if self.is_range() { " (range)" } else { "" }
434 )?;
435 }
436 Ok(())
437 }
438}
439
440#[derive(Debug, Clone, PartialEq)]
442pub struct SourceMapSection {
443 offset: (u32, u32),
444 url: Option<String>,
445 map: Option<Box<DecodedMap>>,
446}
447
448pub struct SourceMapSectionIter<'a> {
450 i: &'a SourceMapIndex,
451 next_idx: u32,
452}
453
454impl<'a> Iterator for SourceMapSectionIter<'a> {
455 type Item = &'a SourceMapSection;
456
457 fn next(&mut self) -> Option<&'a SourceMapSection> {
458 self.i.get_section(self.next_idx).inspect(|_| {
459 self.next_idx += 1;
460 })
461 }
462}
463
464#[derive(Debug, Clone, PartialEq)]
466pub struct SourceMapIndex {
467 file: Option<String>,
468 sections: Vec<SourceMapSection>,
469 x_facebook_offsets: Option<Vec<Option<u32>>>,
470 x_metro_module_paths: Option<Vec<String>>,
471 debug_id: Option<DebugId>,
472}
473
474#[derive(Clone, Debug, PartialEq)]
480pub struct SourceMap {
481 pub(crate) file: Option<Arc<str>>,
482 pub(crate) tokens: Vec<RawToken>,
483 pub(crate) names: Vec<Arc<str>>,
484 pub(crate) source_root: Option<Arc<str>>,
485 pub(crate) sources: Vec<Arc<str>>,
486 pub(crate) sources_prefixed: Option<Vec<Arc<str>>>,
487 pub(crate) sources_content: Vec<Option<SourceView>>,
488 pub(crate) ignore_list: BTreeSet<u32>,
489 pub(crate) debug_id: Option<DebugId>,
490}
491
492impl SourceMap {
493 pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMap> {
514 match decode(rdr)? {
515 DecodedMap::Regular(sm) => Ok(sm),
516 _ => Err(Error::IncompatibleSourceMap),
517 }
518 }
519
520 pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
541 encode(self, w)
542 }
543
544 pub fn to_data_url(&self) -> Result<String> {
558 let mut buf = vec![];
559 encode(self, &mut buf)?;
560 let b64 = base64_simd::STANDARD.encode_to_string(&buf);
561 Ok(format!(
562 "data:application/json;charset=utf-8;base64,{}",
563 b64
564 ))
565 }
566
567 pub fn from_slice(slice: &[u8]) -> Result<SourceMap> {
583 match decode_slice(slice)? {
584 DecodedMap::Regular(sm) => Ok(sm),
585 _ => Err(Error::IncompatibleSourceMap),
586 }
587 }
588
589 pub fn new(
598 file: Option<Arc<str>>,
599 mut tokens: Vec<RawToken>,
600 names: Vec<Arc<str>>,
601 sources: Vec<Arc<str>>,
602 sources_content: Option<Vec<Option<Arc<str>>>>,
603 ) -> SourceMap {
604 tokens.sort_unstable_by_key(|t| (t.dst_line, t.dst_col));
605 SourceMap {
606 file,
607 tokens,
608 names,
609 source_root: None,
610 sources,
611 sources_prefixed: None,
612 sources_content: sources_content
613 .unwrap_or_default()
614 .into_iter()
615 .map(|opt| opt.map(SourceView::new))
616 .collect(),
617 ignore_list: BTreeSet::default(),
618 debug_id: None,
619 }
620 }
621
622 pub fn get_debug_id(&self) -> Option<DebugId> {
624 self.debug_id
625 }
626
627 pub fn set_debug_id(&mut self, debug_id: Option<DebugId>) {
629 self.debug_id = debug_id
630 }
631
632 pub fn get_file(&self) -> Option<&str> {
634 self.file.as_deref()
635 }
636
637 pub fn set_file<T: Into<Arc<str>>>(&mut self, value: Option<T>) {
639 self.file = value.map(Into::into);
640 }
641
642 pub fn get_source_root(&self) -> Option<&str> {
644 self.source_root.as_deref()
645 }
646
647 fn prefix_source(source_root: &str, source: &str) -> Arc<str> {
648 let source_root = source_root.strip_suffix('/').unwrap_or(source_root);
649 let is_valid = !source.is_empty()
650 && (source.starts_with('/')
651 || source.starts_with("http:")
652 || source.starts_with("https:"));
653
654 if is_valid {
655 source.into()
656 } else {
657 format!("{source_root}/{source}").into()
658 }
659 }
660
661 pub fn set_source_root<T: Into<Arc<str>>>(&mut self, value: Option<T>) {
663 self.source_root = value.map(Into::into);
664
665 match self.source_root.as_deref().filter(|rs| !rs.is_empty()) {
666 Some(source_root) => {
667 let sources_prefixed = self
668 .sources
669 .iter()
670 .map(|source| Self::prefix_source(source_root, source))
671 .collect();
672 self.sources_prefixed = Some(sources_prefixed)
673 }
674 None => self.sources_prefixed = None,
675 }
676 }
677
678 pub fn add_to_ignore_list(&mut self, src_id: u32) {
679 self.ignore_list.insert(src_id);
680 }
681
682 pub fn ignore_list(&self) -> impl Iterator<Item = &u32> {
683 self.ignore_list.iter()
684 }
685
686 pub fn get_token(&self, idx: usize) -> Option<Token<'_>> {
688 self.tokens.get(idx).map(|raw| Token {
689 raw,
690 sm: self,
691 idx,
692 offset: 0,
693 })
694 }
695
696 pub fn get_token_count(&self) -> u32 {
698 self.tokens.len() as u32
699 }
700
701 pub fn tokens(&self) -> TokenIter<'_> {
703 TokenIter {
704 i: self,
705 next_idx: 0,
706 }
707 }
708
709 pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
711 let (idx, raw) =
712 greatest_lower_bound(&self.tokens, &(line, col), |t| (t.dst_line, t.dst_col))?;
713
714 let mut token = Token {
715 raw,
716 sm: self,
717 idx,
718 offset: 0,
719 };
720
721 if token.is_range() {
722 token.offset = col - token.get_dst_col();
723 }
724
725 Some(token)
726 }
727
728 pub fn get_original_function_name(
737 &self,
738 line: u32,
739 col: u32,
740 minified_name: &str,
741 sv: &SourceView,
742 ) -> Option<&str> {
743 self.lookup_token(line, col)
744 .and_then(|token| sv.get_original_function_name(token, minified_name))
745 }
746
747 pub fn get_source_count(&self) -> u32 {
749 self.sources.len() as u32
750 }
751
752 pub fn get_source(&self, idx: u32) -> Option<&str> {
754 let sources = self.sources_prefixed.as_deref().unwrap_or(&self.sources);
755 sources.get(idx as usize).map(|x| &x[..])
756 }
757
758 pub fn set_source(&mut self, idx: u32, value: &str) {
763 self.sources[idx as usize] = value.into();
764
765 if let Some(sources_prefixed) = self.sources_prefixed.as_mut() {
766 sources_prefixed[idx as usize] =
768 Self::prefix_source(self.source_root.as_deref().unwrap(), value);
769 }
770 }
771
772 pub fn sources(&self) -> SourceIter<'_> {
774 SourceIter {
775 i: self,
776 next_idx: 0,
777 }
778 }
779
780 pub fn get_source_view(&self, idx: u32) -> Option<&SourceView> {
782 self.sources_content
783 .get(idx as usize)
784 .and_then(Option::as_ref)
785 }
786
787 pub fn get_source_contents(&self, idx: u32) -> Option<&str> {
789 self.sources_content
790 .get(idx as usize)
791 .and_then(Option::as_ref)
792 .map(SourceView::source)
793 }
794
795 pub fn set_source_contents(&mut self, idx: u32, value: Option<&str>) {
797 if self.sources_content.len() != self.sources.len() {
798 self.sources_content.resize(self.sources.len(), None);
799 }
800 self.sources_content[idx as usize] = value.map(|x| SourceView::from_string(x.to_string()));
801 }
802
803 pub fn source_contents(&self) -> SourceContentsIter<'_> {
805 SourceContentsIter {
806 i: self,
807 next_idx: 0,
808 }
809 }
810
811 pub fn names(&self) -> NameIter<'_> {
813 NameIter {
814 i: self,
815 next_idx: 0,
816 }
817 }
818
819 pub fn get_name_count(&self) -> u32 {
821 self.names.len() as u32
822 }
823
824 pub fn has_names(&self) -> bool {
826 !self.names.is_empty()
827 }
828
829 pub fn get_name(&self, idx: u32) -> Option<&str> {
831 self.names.get(idx as usize).map(|x| &x[..])
832 }
833
834 pub fn remove_names(&mut self) {
836 self.names.clear();
837 }
838
839 pub fn rewrite(self, options: &RewriteOptions<'_>) -> Result<SourceMap> {
861 Ok(self.rewrite_with_mapping(options)?.0)
862 }
863
864 pub(crate) fn rewrite_with_mapping(
866 self,
867 options: &RewriteOptions<'_>,
868 ) -> Result<(SourceMap, Vec<u32>)> {
869 let mut builder = SourceMapBuilder::new(self.get_file());
870 builder.set_debug_id(self.debug_id);
871
872 for token in self.tokens() {
873 let raw = builder.add_token(&token, options.with_names);
874 if raw.src_id != !0
875 && options.with_source_contents
876 && !builder.has_source_contents(raw.src_id)
877 {
878 builder
879 .set_source_contents(raw.src_id, self.get_source_contents(token.get_src_id()));
880 }
881 }
882
883 #[cfg(any(unix, windows, target_os = "redox"))]
884 {
885 if options.load_local_source_contents {
886 builder.load_local_source_contents(options.base_path)?;
887 }
888 }
889
890 let mut prefixes = vec![];
891 let mut need_common_prefix = false;
892 for &prefix in options.strip_prefixes.iter() {
893 if prefix == "~" {
894 need_common_prefix = true;
895 } else {
896 prefixes.push(prefix.to_string());
897 }
898 }
899 if need_common_prefix {
900 if let Some(prefix) = find_common_prefix(self.sources.iter().map(AsRef::as_ref)) {
901 prefixes.push(prefix);
902 }
903 }
904 if !prefixes.is_empty() {
905 builder.strip_prefixes(&prefixes);
906 }
907
908 let mapping = builder.take_mapping();
909
910 let sm = builder.into_sourcemap();
911
912 Ok((sm, mapping))
913 }
914
915 pub fn adjust_mappings(&mut self, adjustment: &Self) {
931 #[derive(Debug, Clone, Copy)]
960 struct Range {
961 start: (u32, u32),
962 end: (u32, u32),
963 value: RawToken,
964 }
965
966 fn create_ranges(
968 mut tokens: Vec<RawToken>,
969 key: fn(&RawToken) -> (u32, u32),
970 ) -> Vec<Range> {
971 tokens.sort_unstable_by_key(key);
972
973 let mut token_iter = tokens.into_iter().peekable();
974 let mut ranges = Vec::new();
975
976 while let Some(t) = token_iter.next() {
977 let start = key(&t);
978 let next_start = token_iter.peek().map_or((u32::MAX, u32::MAX), key);
979 let end = std::cmp::min(next_start, (start.0, u32::MAX));
981 ranges.push(Range {
982 start,
983 end,
984 value: t,
985 });
986 }
987
988 ranges
989 }
990
991 let self_tokens = std::mem::take(&mut self.tokens);
997 let original_ranges = create_ranges(self_tokens, |t| (t.dst_line, t.dst_col));
998 let adjustment_ranges =
999 create_ranges(adjustment.tokens.clone(), |t| (t.src_line, t.src_col));
1000
1001 let mut original_ranges_iter = original_ranges.iter();
1002
1003 let mut original_range = match original_ranges_iter.next() {
1004 Some(r) => r,
1005 None => return,
1006 };
1007
1008 'outer: for &adjustment_range in &adjustment_ranges {
1011 let (line_diff, col_diff) = (
1014 adjustment_range.value.dst_line as i32 - adjustment_range.value.src_line as i32,
1015 adjustment_range.value.dst_col as i32 - adjustment_range.value.src_col as i32,
1016 );
1017
1018 while original_range.end <= adjustment_range.start {
1020 match original_ranges_iter.next() {
1021 Some(r) => original_range = r,
1022 None => break 'outer,
1023 }
1024 }
1025
1026 while original_range.start < adjustment_range.end {
1030 let (dst_line, dst_col) =
1032 std::cmp::max(original_range.start, adjustment_range.start);
1033 let mut token = RawToken {
1034 dst_line,
1035 dst_col,
1036 ..original_range.value
1037 };
1038
1039 token.dst_line = (token.dst_line as i32 + line_diff) as u32;
1040 token.dst_col = (token.dst_col as i32 + col_diff) as u32;
1041
1042 self.tokens.push(token);
1043
1044 if original_range.end >= adjustment_range.end {
1045 break;
1048 } else {
1049 match original_ranges_iter.next() {
1051 Some(r) => original_range = r,
1052 None => break 'outer,
1053 }
1054 }
1055 }
1056 }
1057
1058 self.tokens
1059 .sort_unstable_by_key(|t| (t.dst_line, t.dst_col));
1060 }
1061}
1062
1063impl SourceMapIndex {
1064 pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMapIndex> {
1069 match decode(rdr)? {
1070 DecodedMap::Index(smi) => Ok(smi),
1071 _ => Err(Error::IncompatibleSourceMap),
1072 }
1073 }
1074
1075 pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
1077 encode(self, w)
1078 }
1079
1080 pub fn from_slice(slice: &[u8]) -> Result<SourceMapIndex> {
1085 match decode_slice(slice)? {
1086 DecodedMap::Index(smi) => Ok(smi),
1087 _ => Err(Error::IncompatibleSourceMap),
1088 }
1089 }
1090
1091 pub fn new(file: Option<String>, sections: Vec<SourceMapSection>) -> SourceMapIndex {
1096 SourceMapIndex {
1097 file,
1098 sections,
1099 x_facebook_offsets: None,
1100 x_metro_module_paths: None,
1101 debug_id: None,
1102 }
1103 }
1104
1105 pub fn new_ram_bundle_compatible(
1113 file: Option<String>,
1114 sections: Vec<SourceMapSection>,
1115 x_facebook_offsets: Option<Vec<Option<u32>>>,
1116 x_metro_module_paths: Option<Vec<String>>,
1117 ) -> SourceMapIndex {
1118 SourceMapIndex {
1119 file,
1120 sections,
1121 x_facebook_offsets,
1122 x_metro_module_paths,
1123 debug_id: None,
1124 }
1125 }
1126
1127 pub(crate) fn debug_id(&self) -> Option<DebugId> {
1129 self.debug_id
1130 }
1131
1132 fn set_debug_id(&mut self, debug_id: Option<DebugId>) {
1133 self.debug_id = debug_id;
1134 }
1135
1136 pub(crate) fn with_debug_id(mut self, debug_id: Option<DebugId>) -> Self {
1138 self.set_debug_id(debug_id);
1139 self
1140 }
1141
1142 pub fn get_file(&self) -> Option<&str> {
1144 self.file.as_ref().map(|x| &x[..])
1145 }
1146
1147 pub fn set_file(&mut self, value: Option<&str>) {
1149 self.file = value.map(str::to_owned);
1150 }
1151
1152 pub fn get_section_count(&self) -> u32 {
1154 self.sections.len() as u32
1155 }
1156
1157 pub fn get_section(&self, idx: u32) -> Option<&SourceMapSection> {
1159 self.sections.get(idx as usize)
1160 }
1161
1162 pub fn get_section_mut(&mut self, idx: u32) -> Option<&mut SourceMapSection> {
1164 self.sections.get_mut(idx as usize)
1165 }
1166
1167 pub fn sections(&self) -> SourceMapSectionIter<'_> {
1169 SourceMapSectionIter {
1170 i: self,
1171 next_idx: 0,
1172 }
1173 }
1174
1175 pub fn get_original_function_name(
1184 &self,
1185 line: u32,
1186 col: u32,
1187 minified_name: &str,
1188 sv: &SourceView,
1189 ) -> Option<&str> {
1190 self.lookup_token(line, col)
1191 .and_then(|token| sv.get_original_function_name(token, minified_name))
1192 }
1193
1194 pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
1200 let (_section_idx, section) =
1201 greatest_lower_bound(&self.sections, &(line, col), SourceMapSection::get_offset)?;
1202 let map = section.get_sourcemap()?;
1203 let (off_line, off_col) = section.get_offset();
1204 map.lookup_token(
1205 line - off_line,
1206 if line == off_line { col - off_col } else { col },
1207 )
1208 }
1209
1210 pub fn flatten(&self) -> Result<SourceMap> {
1213 let mut builder = SourceMapBuilder::new(self.get_file());
1214
1215 for section in self.sections() {
1216 let (off_line, off_col) = section.get_offset();
1217 let map = match section.get_sourcemap() {
1218 Some(map) => match map {
1219 DecodedMap::Regular(sm) => Cow::Borrowed(sm),
1220 DecodedMap::Index(idx) => Cow::Owned(idx.flatten()?),
1221 DecodedMap::Hermes(smh) => Cow::Borrowed(&smh.sm),
1222 },
1223 None => {
1224 return Err(Error::CannotFlatten(format!(
1225 "Section has an unresolved \
1226 sourcemap: {}",
1227 section.get_url().unwrap_or("<unknown url>")
1228 )));
1229 }
1230 };
1231
1232 let mut src_id_map = FxHashMap::<u32, u32>::default();
1233
1234 for (original_id, (source, contents)) in
1235 map.sources().zip(map.source_contents()).enumerate()
1236 {
1237 let src_id = builder.add_source(source);
1238
1239 src_id_map.insert(original_id as u32, src_id);
1240
1241 if let Some(contents) = contents {
1242 builder.set_source_contents(src_id, Some(contents));
1243 }
1244 }
1245
1246 let mut name_id_map = FxHashMap::<u32, u32>::default();
1247
1248 for (original_id, name) in map.names().enumerate() {
1249 let name_id = builder.add_name(name);
1250 name_id_map.insert(original_id as u32, name_id);
1251 }
1252
1253 for token in map.tokens() {
1254 let dst_col = if token.get_dst_line() == 0 {
1255 token.get_dst_col() + off_col
1256 } else {
1257 token.get_dst_col()
1258 };
1259
1260 let original_src_id = token.raw.src_id;
1262 let src_id = if original_src_id == !0 {
1263 None
1264 } else {
1265 src_id_map.get(&original_src_id).copied()
1266 };
1267
1268 let original_name_id = token.raw.name_id;
1269 let name_id = if original_name_id == !0 {
1270 None
1271 } else {
1272 name_id_map.get(&original_name_id).copied()
1273 };
1274
1275 let raw = builder.add_raw(
1276 token.get_dst_line() + off_line,
1277 dst_col,
1278 token.get_src_line(),
1279 token.get_src_col(),
1280 src_id,
1281 name_id,
1282 token.is_range(),
1283 );
1284
1285 if map.ignore_list.contains(&token.get_src_id()) {
1286 builder.add_to_ignore_list(raw.src_id);
1287 }
1288 }
1289 }
1290
1291 Ok(builder.into_sourcemap())
1292 }
1293
1294 pub fn flatten_and_rewrite(self, options: &RewriteOptions<'_>) -> Result<SourceMap> {
1298 self.flatten()?.rewrite(options)
1299 }
1300
1301 pub fn is_for_ram_bundle(&self) -> bool {
1303 self.x_facebook_offsets.is_some() && self.x_metro_module_paths.is_some()
1304 }
1305
1306 pub fn x_facebook_offsets(&self) -> Option<&[Option<u32>]> {
1308 self.x_facebook_offsets.as_ref().map(|x| &x[..])
1309 }
1310
1311 pub fn x_metro_module_paths(&self) -> Option<&[String]> {
1313 self.x_metro_module_paths.as_ref().map(|x| &x[..])
1314 }
1315
1316 pub fn adjust_sections_offset_rows(&mut self, amount: u32) -> bool {
1322 let adjusted_rows: Vec<_> = self
1323 .sections
1324 .iter()
1325 .filter_map(|section| section.offset.0.checked_add(amount))
1327 .collect();
1328
1329 if adjusted_rows.len() != self.sections.len() {
1330 return false;
1332 }
1333
1334 for (section, adjustment) in self.sections.iter_mut().zip(adjusted_rows) {
1335 section.offset.0 = adjustment;
1336 }
1337
1338 true
1339 }
1340}
1341
1342impl SourceMapSection {
1343 pub fn new(
1349 offset: (u32, u32),
1350 url: Option<String>,
1351 map: Option<DecodedMap>,
1352 ) -> SourceMapSection {
1353 SourceMapSection {
1354 offset,
1355 url,
1356 map: map.map(Box::new),
1357 }
1358 }
1359
1360 pub fn get_offset_line(&self) -> u32 {
1362 self.offset.0
1363 }
1364
1365 pub fn get_offset_col(&self) -> u32 {
1367 self.offset.1
1368 }
1369
1370 pub fn get_offset(&self) -> (u32, u32) {
1372 self.offset
1373 }
1374
1375 pub fn get_url(&self) -> Option<&str> {
1377 self.url.as_deref()
1378 }
1379
1380 pub fn set_url(&mut self, value: Option<&str>) {
1382 self.url = value.map(str::to_owned);
1383 }
1384
1385 pub fn get_sourcemap(&self) -> Option<&DecodedMap> {
1387 self.map.as_ref().map(Box::as_ref)
1388 }
1389
1390 pub fn get_sourcemap_mut(&mut self) -> Option<&mut DecodedMap> {
1392 self.map.as_mut().map(Box::as_mut)
1393 }
1394
1395 pub fn set_sourcemap(&mut self, sm: Option<DecodedMap>) {
1397 self.map = sm.map(Box::new);
1398 }
1399}
1400
1401#[cfg(test)]
1402mod tests {
1403 use std::collections::BTreeSet;
1404
1405 use super::{DecodedMap, RewriteOptions, SourceMap, SourceMapIndex, SourceMapSection};
1406 use debugid::DebugId;
1407
1408 #[test]
1409 fn test_rewrite_debugid() {
1410 let input: &[_] = br#"{
1411 "version":3,
1412 "sources":["coolstuff.js"],
1413 "names":["x","alert"],
1414 "mappings":"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM",
1415 "debug_id":"00000000-0000-0000-0000-000000000000"
1416 }"#;
1417
1418 let sm = SourceMap::from_slice(input).unwrap();
1419
1420 assert_eq!(sm.debug_id, Some(DebugId::default()));
1421
1422 let new_sm = sm
1423 .rewrite(&RewriteOptions {
1424 with_names: false,
1425 ..Default::default()
1426 })
1427 .unwrap();
1428
1429 assert_eq!(new_sm.debug_id, Some(DebugId::default()));
1430 }
1431
1432 #[test]
1433 fn test_debugid_alias() {
1434 let input: &[_] = br#"{
1435 "version":3,
1436 "sources":["coolstuff.js"],
1437 "names":["x","alert"],
1438 "mappings":"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM",
1439 "debug_id":"00000000-0000-0000-0000-000000000000",
1440 "debugId": "11111111-1111-1111-1111-111111111111"
1441 }"#;
1442
1443 let sm = SourceMap::from_slice(input).unwrap();
1444
1445 assert_eq!(sm.debug_id, Some(DebugId::default()));
1446 }
1447
1448 #[test]
1449 fn test_adjust_mappings_injection() {
1450 for bundler in ["esbuild", "rollup", "vite", "webpack", "rspack"] {
1465 let original_map_file = std::fs::File::open(format!(
1466 "tests/fixtures/adjust_mappings/{bundler}.bundle.js.map"
1467 ))
1468 .unwrap();
1469
1470 let injected_map_file = std::fs::File::open(format!(
1471 "tests/fixtures/adjust_mappings/{bundler}-injected.bundle.js.map"
1472 ))
1473 .unwrap();
1474
1475 let composed_map_file = std::fs::File::open(format!(
1476 "tests/fixtures/adjust_mappings/{bundler}-composed.bundle.js.map"
1477 ))
1478 .unwrap();
1479
1480 let mut original_map = SourceMap::from_reader(original_map_file).unwrap();
1481 let injected_map = SourceMap::from_reader(injected_map_file).unwrap();
1482 let composed_map = SourceMap::from_reader(composed_map_file).unwrap();
1483 original_map.adjust_mappings(&injected_map);
1484
1485 assert_eq!(
1486 original_map.tokens, composed_map.tokens,
1487 "bundler = {bundler}"
1488 );
1489 }
1490 }
1491
1492 #[test]
1493 fn test_roundtrip() {
1494 let sm = br#"{
1495 "version": 3,
1496 "file": "foo.js",
1497 "sources": [
1498 "./bar.js",
1499 "./baz.js"
1500 ],
1501 "sourceRoot": "webpack:///",
1502 "sourcesContent": [null, null],
1503 "names": [],
1504 "mappings": ""
1505 }"#;
1506
1507 let sm = SourceMap::from_slice(sm).unwrap();
1508 let mut out = Vec::new();
1509 sm.to_writer(&mut out).unwrap();
1510
1511 let sm_new = SourceMap::from_slice(&out).unwrap();
1512 assert_eq!(sm_new.sources, sm.sources);
1513 }
1514
1515 #[test]
1516 fn test_sourcemap_index_default_debug_id() {
1517 let sm = SourceMapIndex::new(None, vec![]);
1518 assert!(sm.debug_id().is_none());
1519 }
1520
1521 #[test]
1522 fn test_sourcemap_index_debug_id() {
1523 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
1524
1525 let sm = SourceMapIndex::new(None, vec![])
1526 .with_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")));
1527
1528 assert_eq!(
1529 sm.debug_id(),
1530 Some(DEBUG_ID.parse().expect("valid debug id"))
1531 );
1532 }
1533
1534 #[test]
1535 fn test_decoded_map_regular_debug_id() {
1536 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
1537
1538 let mut decoded_map = DecodedMap::Regular(SourceMap {
1539 file: None,
1540 tokens: vec![],
1541 names: vec![],
1542 source_root: None,
1543 sources: vec![],
1544 sources_prefixed: None,
1545 sources_content: vec![],
1546 ignore_list: BTreeSet::new(),
1547 debug_id: None,
1548 });
1549
1550 assert!(decoded_map.debug_id().is_none());
1551
1552 decoded_map.set_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")));
1553
1554 assert_eq!(
1555 decoded_map,
1556 DecodedMap::Regular(SourceMap {
1557 file: None,
1558 tokens: vec![],
1559 names: vec![],
1560 source_root: None,
1561 sources: vec![],
1562 sources_prefixed: None,
1563 sources_content: vec![],
1564 ignore_list: BTreeSet::new(),
1565 debug_id: Some(DEBUG_ID.parse().expect("valid debug id")),
1566 })
1567 );
1568
1569 assert_eq!(
1570 decoded_map.debug_id(),
1571 Some(DEBUG_ID.parse().expect("valid debug id"))
1572 );
1573 }
1574
1575 #[test]
1576 fn test_decoded_map_index_debug_id() {
1577 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
1578
1579 let mut decoded_map = DecodedMap::Index(SourceMapIndex {
1580 file: None,
1581 sections: vec![],
1582 x_facebook_offsets: None,
1583 x_metro_module_paths: None,
1584 debug_id: None,
1585 });
1586
1587 assert!(decoded_map.debug_id().is_none());
1588
1589 decoded_map.set_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")));
1590
1591 assert_eq!(
1592 decoded_map,
1593 DecodedMap::Index(SourceMapIndex {
1594 file: None,
1595 sections: vec![],
1596 x_facebook_offsets: None,
1597 x_metro_module_paths: None,
1598 debug_id: Some(DEBUG_ID.parse().expect("valid debug id")),
1599 })
1600 );
1601
1602 assert_eq!(
1603 decoded_map.debug_id(),
1604 Some(DEBUG_ID.parse().expect("valid debug id"))
1605 );
1606 }
1607
1608 #[test]
1609 fn test_adjust_sections_offset_rows_basic() {
1610 let mut smi = SourceMapIndex::new(
1612 Some("test.js".to_string()),
1613 vec![
1614 SourceMapSection::new((0, 0), None, None),
1615 SourceMapSection::new((10, 0), None, None),
1616 ],
1617 );
1618
1619 assert!(smi.adjust_sections_offset_rows(1));
1621
1622 assert_eq!(
1624 smi,
1625 SourceMapIndex::new(
1626 Some("test.js".to_string()),
1627 vec![
1628 SourceMapSection::new((1, 0), None, None),
1629 SourceMapSection::new((11, 0), None, None),
1630 ],
1631 )
1632 );
1633 }
1634
1635 #[test]
1636 fn test_adjust_sections_offset_rows_zero() {
1637 let mut smi = SourceMapIndex::new(
1639 Some("test.js".to_string()),
1640 vec![
1641 SourceMapSection::new((0, 0), None, None),
1642 SourceMapSection::new((10, 0), None, None),
1643 ],
1644 );
1645
1646 assert!(smi.adjust_sections_offset_rows(0));
1648
1649 assert_eq!(
1651 smi,
1652 SourceMapIndex::new(
1653 Some("test.js".to_string()),
1654 vec![
1655 SourceMapSection::new((0, 0), None, None),
1656 SourceMapSection::new((10, 0), None, None),
1657 ],
1658 )
1659 );
1660 }
1661
1662 #[test]
1663 fn test_adjust_sections_offset_rows_multiple_sections() {
1664 let mut smi = SourceMapIndex::new(
1666 Some("test.js".to_string()),
1667 vec![
1668 SourceMapSection::new((0, 0), None, None),
1669 SourceMapSection::new((10, 0), None, None),
1670 SourceMapSection::new((20, 10), None, None),
1671 SourceMapSection::new((30, 40), None, None),
1672 ],
1673 );
1674
1675 assert!(smi.adjust_sections_offset_rows(1));
1677
1678 assert_eq!(
1680 smi,
1681 SourceMapIndex::new(
1682 Some("test.js".to_string()),
1683 vec![
1684 SourceMapSection::new((1, 0), None, None),
1685 SourceMapSection::new((11, 0), None, None),
1686 SourceMapSection::new((21, 10), None, None),
1687 SourceMapSection::new((31, 40), None, None),
1688 ],
1689 )
1690 );
1691 }
1692
1693 #[test]
1694 fn test_adjust_sections_offset_rows_overflow() {
1695 let mut smi = SourceMapIndex::new(
1697 Some("test.js".to_string()),
1698 vec![
1699 SourceMapSection::new((0, 0), None, None),
1700 SourceMapSection::new((u32::MAX, 0), None, None),
1701 ],
1702 );
1703
1704 let original_smi = smi.clone();
1706
1707 assert!(!smi.adjust_sections_offset_rows(1));
1709
1710 assert_eq!(smi, original_smi);
1712 }
1713
1714 #[test]
1715 fn test_adjust_sections_offset_rows_partial_overflow() {
1716 let mut smi = SourceMapIndex::new(
1718 Some("test.js".to_string()),
1719 vec![
1720 SourceMapSection::new((0, 0), None, None),
1721 SourceMapSection::new((10, 0), None, None),
1722 SourceMapSection::new((20, 0), None, None),
1723 SourceMapSection::new((u32::MAX, 0), None, None),
1724 ],
1725 );
1726
1727 let original_smi = smi.clone();
1729
1730 assert!(!smi.adjust_sections_offset_rows(1));
1732
1733 assert_eq!(smi, original_smi);
1735 }
1736
1737 #[test]
1738 fn test_adjust_sections_offset_rows_large_amount() {
1739 let mut smi = SourceMapIndex::new(
1741 Some("test.js".to_string()),
1742 vec![
1743 SourceMapSection::new((0, 0), None, None),
1744 SourceMapSection::new((10, 0), None, None),
1745 ],
1746 );
1747
1748 assert!(smi.adjust_sections_offset_rows(1_000_000));
1749
1750 assert_eq!(
1752 smi,
1753 SourceMapIndex::new(
1754 Some("test.js".to_string()),
1755 vec![
1756 SourceMapSection::new((1_000_000, 0), None, None),
1757 SourceMapSection::new((1_000_010, 0), None, None),
1758 ],
1759 )
1760 );
1761 }
1762
1763 #[test]
1764 fn adjust_sections_offset_rows_large_amount_overflow() {
1765 let mut smi = SourceMapIndex::new(
1767 Some("test.js".to_string()),
1768 vec![
1769 SourceMapSection::new((0, 0), None, None),
1770 SourceMapSection::new((10, 0), None, None),
1771 ],
1772 );
1773
1774 let original_smi = smi.clone();
1776
1777 assert!(!smi.adjust_sections_offset_rows(u32::MAX));
1779
1780 assert_eq!(smi, original_smi);
1782 }
1783
1784 #[test]
1785 fn adjust_sections_offset_rows_no_sections() {
1786 let mut smi = SourceMapIndex::new(Some("test.js".to_string()), vec![]);
1788
1789 assert!(smi.adjust_sections_offset_rows(1));
1791
1792 assert_eq!(
1794 smi,
1795 SourceMapIndex::new(Some("test.js".to_string()), vec![])
1796 );
1797 }
1798
1799 mod prop {
1800 use magic_string::MagicString;
1819 use proptest::prelude::*;
1820
1821 use crate::SourceMap;
1822
1823 #[derive(Debug, Clone)]
1825 enum FirstEdit {
1826 Insert(u32, String),
1828 Delete(i64, i64),
1830 }
1831
1832 impl FirstEdit {
1833 fn apply(&self, line: usize, ms: &mut MagicString) {
1835 let line_offset = line * 11;
1837 match self {
1838 FirstEdit::Insert(col, s) => {
1839 ms.append_left(line_offset as u32 + *col, s).unwrap();
1840 }
1841 FirstEdit::Delete(start, end) => {
1842 ms.remove(line_offset as i64 + *start, line_offset as i64 + *end)
1843 .unwrap();
1844 }
1845 }
1846 }
1847 }
1848
1849 fn nth_line_start_end(n: usize, s: &str) -> (usize, usize) {
1852 let line = s.lines().nth(n).unwrap();
1853 let start = line.as_ptr() as usize - s.as_ptr() as usize;
1854 let end = if n == 9 {
1856 start + line.len()
1857 } else {
1858 start + line.len() + 1
1859 };
1860 (start, end)
1861 }
1862
1863 #[derive(Debug, Clone)]
1865 enum SecondEdit {
1866 Prepend(String),
1868 Append(String),
1870 Insert(usize, String),
1872 Delete(usize),
1874 }
1875
1876 impl SecondEdit {
1877 fn apply(&self, orig: &str, ms: &mut MagicString) {
1882 match self {
1883 SecondEdit::Prepend(s) => {
1884 ms.prepend(s).unwrap();
1885 }
1886 SecondEdit::Append(s) => {
1887 ms.append(s).unwrap();
1888 }
1889 SecondEdit::Insert(line, s) => {
1890 let (start, _) = nth_line_start_end(*line, orig);
1891 ms.prepend_left(start as u32, s).unwrap();
1892 }
1893 SecondEdit::Delete(line) => {
1894 let (start, end) = nth_line_start_end(*line, orig);
1895 ms.remove(start as i64, end as i64).unwrap();
1896 }
1897 }
1898 }
1899 }
1900
1901 fn starting_string() -> impl Strategy<Value = String> {
1903 (vec!["[a-z]{10}"; 10]).prop_map(|v| v.join("\n"))
1904 }
1905
1906 fn first_edit() -> impl Strategy<Value = FirstEdit> {
1908 prop_oneof![
1909 (1u32..9, "[a-z]{5}").prop_map(|(c, s)| FirstEdit::Insert(c, s)),
1910 (1i64..10)
1911 .prop_flat_map(|end| (0..end, Just(end)))
1912 .prop_map(|(a, b)| FirstEdit::Delete(a, b))
1913 ]
1914 }
1915
1916 fn first_edit_sequence() -> impl Strategy<Value = Vec<FirstEdit>> {
1920 let mut vec = Vec::with_capacity(10);
1921
1922 for _ in 0..10 {
1923 vec.push(first_edit())
1924 }
1925
1926 vec
1927 }
1928
1929 fn second_edit_sequence() -> impl Strategy<Value = Vec<SecondEdit>> {
1934 let edits = (0..10)
1935 .map(|i| {
1936 prop_oneof![
1937 "[a-z\n]{12}".prop_map(SecondEdit::Prepend),
1938 "[a-z\n]{12}".prop_map(SecondEdit::Append),
1939 "[a-z\n]{11}\n".prop_map(move |s| SecondEdit::Insert(i, s)),
1940 Just(SecondEdit::Delete(i)),
1941 ]
1942 })
1943 .collect::<Vec<_>>();
1944
1945 edits.prop_shuffle()
1946 }
1947
1948 proptest! {
1949 #[test]
1950 fn test_composition_identity(
1951 input in starting_string(),
1952 first_edits in first_edit_sequence(),
1953 second_edits in second_edit_sequence(),
1954 ) {
1955
1956 let mut ms1 = MagicString::new(&input);
1959
1960 for (line, first_edit) in first_edits.iter().enumerate() {
1961 first_edit.apply(line, &mut ms1);
1962 }
1963
1964 let first_map = ms1.generate_map(Default::default()).unwrap().to_string().unwrap();
1965 let mut first_map = SourceMap::from_slice(first_map.as_bytes()).unwrap();
1966
1967 let transformed_input = ms1.to_string();
1968
1969 let mut ms2 = MagicString::new(&transformed_input);
1970
1971 for second_edit in second_edits.iter() {
1972 second_edit.apply(&transformed_input, &mut ms2);
1973 }
1974
1975 let output_1 = ms2.to_string();
1976
1977 let second_map = ms2.generate_map(Default::default()).unwrap().to_string().unwrap();
1978 let second_map = SourceMap::from_slice(second_map.as_bytes()).unwrap();
1979
1980 let mut ms3 = MagicString::new(&input);
1983
1984 for (line, first_edit) in first_edits.iter().enumerate() {
1985 first_edit.apply(line, &mut ms3);
1986 }
1987
1988 for second_edit in second_edits.iter() {
1989 second_edit.apply(&input, &mut ms3);
1990 }
1991
1992 let output_2 = ms3.to_string();
1993
1994 let third_map = ms3.generate_map(Default::default()).unwrap().to_string().unwrap();
1995 let third_map = SourceMap::from_slice(third_map.as_bytes()).unwrap();
1996
1997 assert_eq!(output_1, output_2);
1999
2000 first_map.adjust_mappings(&second_map);
2001
2002 assert_eq!(first_map.tokens, third_map.tokens);
2003 }
2004 }
2005 }
2006}