1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::collections::BTreeSet;
4use std::fmt;
5use std::io::{Read, Write};
6use std::path::Path;
7
8use crate::builder::SourceMapBuilder;
9use crate::decoder::{decode, decode_slice};
10use crate::encoder::encode;
11use crate::errors::{Error, Result};
12use crate::hermes::SourceMapHermes;
13use crate::sourceview::SourceView;
14use crate::utils::{find_common_prefix, greatest_lower_bound};
15
16use bytes_str::BytesStr;
17use debugid::DebugId;
18use rustc_hash::FxHashSet;
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<&BytesStr> {
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 {
251 self.raw.src_line
252 }
253
254 pub fn get_src_col(&self) -> u32 {
259 self.raw.src_col.saturating_add(self.offset)
260 }
261
262 pub fn get_src(&self) -> (u32, u32) {
264 (self.get_src_line(), self.get_src_col())
265 }
266
267 pub fn get_src_id(&self) -> u32 {
269 self.raw.src_id
270 }
271
272 pub fn get_source(&self) -> Option<&'a BytesStr> {
274 if self.raw.src_id == !0 {
275 None
276 } else {
277 self.sm.get_source(self.raw.src_id)
278 }
279 }
280
281 pub fn has_source(&self) -> bool {
283 self.raw.src_id != !0
284 }
285
286 pub fn get_name(&self) -> Option<&'a BytesStr> {
288 if self.raw.name_id == !0 {
289 None
290 } else {
291 self.sm.get_name(self.raw.name_id)
292 }
293 }
294
295 pub fn has_name(&self) -> bool {
297 self.get_name().is_some()
298 }
299
300 pub fn get_name_id(&self) -> u32 {
302 self.raw.name_id
303 }
304
305 pub fn to_tuple(&self) -> (&'a str, u32, u32, Option<&'a str>) {
308 (
309 self.get_source().map(|v| &**v).unwrap_or(""),
310 self.get_src_line(),
311 self.get_src_col(),
312 self.get_name().map(|v| &**v),
313 )
314 }
315
316 pub fn get_raw_token(&self) -> RawToken {
318 *self.raw
319 }
320
321 pub fn get_source_view(&self) -> Option<&SourceView> {
323 self.sm.get_source_view(self.get_src_id())
324 }
325
326 pub fn is_range(&self) -> bool {
330 self.raw.is_range
331 }
332}
333
334pub struct TokenIter<'a> {
336 i: &'a SourceMap,
337 next_idx: usize,
338}
339
340impl TokenIter<'_> {
341 pub fn seek(&mut self, line: u32, col: u32) -> bool {
342 let token = self.i.lookup_token(line, col);
343 match token {
344 Some(token) => {
345 self.next_idx = token.idx + 1;
346 true
347 }
348 None => false,
349 }
350 }
351}
352
353impl<'a> Iterator for TokenIter<'a> {
354 type Item = Token<'a>;
355
356 fn next(&mut self) -> Option<Token<'a>> {
357 self.i.get_token(self.next_idx).inspect(|_| {
358 self.next_idx += 1;
359 })
360 }
361}
362
363pub struct SourceIter<'a> {
365 i: &'a SourceMap,
366 next_idx: u32,
367}
368
369impl<'a> Iterator for SourceIter<'a> {
370 type Item = &'a BytesStr;
371
372 fn next(&mut self) -> Option<&'a BytesStr> {
373 self.i.get_source(self.next_idx).inspect(|_| {
374 self.next_idx += 1;
375 })
376 }
377}
378
379pub struct SourceContentsIter<'a> {
381 i: &'a SourceMap,
382 next_idx: u32,
383}
384
385impl<'a> Iterator for SourceContentsIter<'a> {
386 type Item = Option<&'a BytesStr>;
387
388 fn next(&mut self) -> Option<Option<&'a BytesStr>> {
389 if self.next_idx >= self.i.get_source_count() {
390 None
391 } else {
392 let rv = Some(self.i.get_source_contents(self.next_idx));
393 self.next_idx += 1;
394 rv
395 }
396 }
397}
398
399pub struct NameIter<'a> {
401 i: &'a SourceMap,
402 next_idx: u32,
403}
404
405impl<'a> Iterator for NameIter<'a> {
406 type Item = &'a BytesStr;
407
408 fn next(&mut self) -> Option<&'a BytesStr> {
409 self.i.get_name(self.next_idx).inspect(|_| {
410 self.next_idx += 1;
411 })
412 }
413}
414
415impl fmt::Debug for Token<'_> {
416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 write!(f, "<Token {self:#}>")
418 }
419}
420
421impl fmt::Display for Token<'_> {
422 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423 write!(
424 f,
425 "{}:{}:{}{}",
426 self.get_source().map(|v| &**v).unwrap_or("<unknown>"),
427 self.get_src_line(),
428 self.get_src_col(),
429 self.get_name()
430 .map(|x| format!(" name={x}"))
431 .unwrap_or_default()
432 )?;
433 if f.alternate() {
434 write!(
435 f,
436 " ({}:{}){}",
437 self.get_dst_line(),
438 self.get_dst_col(),
439 if self.is_range() { " (range)" } else { "" }
440 )?;
441 }
442 Ok(())
443 }
444}
445
446#[derive(Debug, Clone, PartialEq)]
448pub struct SourceMapSection {
449 offset: (u32, u32),
450 url: Option<String>,
451 map: Option<Box<DecodedMap>>,
452}
453
454pub struct SourceMapSectionIter<'a> {
456 i: &'a SourceMapIndex,
457 next_idx: u32,
458}
459
460impl<'a> Iterator for SourceMapSectionIter<'a> {
461 type Item = &'a SourceMapSection;
462
463 fn next(&mut self) -> Option<&'a SourceMapSection> {
464 self.i.get_section(self.next_idx).inspect(|_| {
465 self.next_idx += 1;
466 })
467 }
468}
469
470#[derive(Debug, Clone, PartialEq)]
472pub struct SourceMapIndex {
473 file: Option<BytesStr>,
474 sections: Vec<SourceMapSection>,
475 x_facebook_offsets: Option<Vec<Option<u32>>>,
476 x_metro_module_paths: Option<Vec<String>>,
477 debug_id: Option<DebugId>,
478}
479
480#[derive(Clone, Debug, PartialEq)]
486pub struct SourceMap {
487 pub(crate) file: Option<BytesStr>,
488 pub(crate) tokens: Vec<RawToken>,
489 pub(crate) names: Vec<BytesStr>,
490 pub(crate) source_root: Option<BytesStr>,
491 pub(crate) sources: Vec<BytesStr>,
492 pub(crate) sources_prefixed: Option<Vec<BytesStr>>,
493 pub(crate) sources_content: Vec<Option<SourceView>>,
494 pub(crate) ignore_list: BTreeSet<u32>,
495 pub(crate) debug_id: Option<DebugId>,
496}
497
498impl SourceMap {
499 pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMap> {
520 match decode(rdr)? {
521 DecodedMap::Regular(sm) => Ok(sm),
522 _ => Err(Error::IncompatibleSourceMap),
523 }
524 }
525
526 pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
547 encode(self, w)
548 }
549
550 pub fn to_data_url(&self) -> Result<String> {
564 let mut buf = vec![];
565 encode(self, &mut buf)?;
566 let b64 = base64_simd::STANDARD.encode_to_string(&buf);
567 Ok(format!("data:application/json;charset=utf-8;base64,{b64}"))
568 }
569
570 pub fn from_slice(slice: &[u8]) -> Result<SourceMap> {
586 match decode_slice(slice)? {
587 DecodedMap::Regular(sm) => Ok(sm),
588 _ => Err(Error::IncompatibleSourceMap),
589 }
590 }
591
592 pub fn new(
601 file: Option<BytesStr>,
602 mut tokens: Vec<RawToken>,
603 names: Vec<BytesStr>,
604 sources: Vec<BytesStr>,
605 sources_content: Option<Vec<Option<BytesStr>>>,
606 ) -> SourceMap {
607 tokens.sort_unstable_by_key(|t| (t.dst_line, t.dst_col));
608 SourceMap {
609 file,
610 tokens,
611 names,
612 source_root: None,
613 sources,
614 sources_prefixed: None,
615 sources_content: sources_content
616 .unwrap_or_default()
617 .into_iter()
618 .map(|opt| opt.map(SourceView::new))
619 .collect(),
620 ignore_list: BTreeSet::default(),
621 debug_id: None,
622 }
623 }
624
625 pub fn get_debug_id(&self) -> Option<DebugId> {
627 self.debug_id
628 }
629
630 pub fn set_debug_id(&mut self, debug_id: Option<DebugId>) {
632 self.debug_id = debug_id
633 }
634
635 pub fn get_file(&self) -> Option<&BytesStr> {
637 self.file.as_ref()
638 }
639
640 pub fn set_file<T: Into<BytesStr>>(&mut self, value: Option<T>) {
642 self.file = value.map(Into::into);
643 }
644
645 pub fn get_source_root(&self) -> Option<&BytesStr> {
647 self.source_root.as_ref()
648 }
649
650 fn prefix_source(source_root: &BytesStr, source: &BytesStr) -> BytesStr {
651 let source_root = source_root.strip_suffix('/').unwrap_or(source_root);
652 let is_valid = !source.is_empty()
653 && (source.starts_with('/')
654 || source.starts_with("http:")
655 || source.starts_with("https:"));
656
657 if is_valid {
658 source.clone()
659 } else {
660 format!("{source_root}/{source}").into()
661 }
662 }
663
664 pub fn set_source_root<T: Into<BytesStr>>(&mut self, value: Option<T>) {
666 self.source_root = value.map(Into::into);
667
668 match self.source_root.as_ref().filter(|rs| !rs.is_empty()) {
669 Some(source_root) => {
670 let sources_prefixed = self
671 .sources
672 .iter()
673 .map(|source| Self::prefix_source(source_root, source))
674 .collect();
675 self.sources_prefixed = Some(sources_prefixed)
676 }
677 None => self.sources_prefixed = None,
678 }
679 }
680
681 pub fn add_to_ignore_list(&mut self, src_id: u32) {
682 self.ignore_list.insert(src_id);
683 }
684
685 pub fn ignore_list(&self) -> impl Iterator<Item = &u32> {
686 self.ignore_list.iter()
687 }
688
689 pub fn get_token(&self, idx: usize) -> Option<Token<'_>> {
691 self.tokens.get(idx).map(|raw| Token {
692 raw,
693 sm: self,
694 idx,
695 offset: 0,
696 })
697 }
698
699 pub fn get_token_count(&self) -> u32 {
701 self.tokens.len() as u32
702 }
703
704 pub fn tokens(&self) -> TokenIter<'_> {
706 TokenIter {
707 i: self,
708 next_idx: 0,
709 }
710 }
711
712 pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
714 let (idx, raw) =
715 greatest_lower_bound(&self.tokens, &(line, col), |t| (t.dst_line, t.dst_col))?;
716
717 let mut token = Token {
718 raw,
719 sm: self,
720 idx,
721 offset: 0,
722 };
723
724 if token.is_range() {
725 token.offset = col - token.get_dst_col();
726 }
727
728 Some(token)
729 }
730
731 pub fn get_original_function_name(
740 &self,
741 line: u32,
742 col: u32,
743 minified_name: &str,
744 sv: &SourceView,
745 ) -> Option<&BytesStr> {
746 self.lookup_token(line, col)
747 .and_then(|token| sv.get_original_function_name(token, minified_name))
748 }
749
750 pub fn get_source_count(&self) -> u32 {
752 self.sources.len() as u32
753 }
754
755 pub fn get_source(&self, idx: u32) -> Option<&BytesStr> {
757 let sources = self.sources_prefixed.as_deref().unwrap_or(&self.sources);
758 sources.get(idx as usize)
759 }
760
761 pub fn set_source(&mut self, idx: u32, value: BytesStr) {
766 self.sources[idx as usize] = value.clone();
767
768 if let Some(sources_prefixed) = self.sources_prefixed.as_mut() {
769 sources_prefixed[idx as usize] =
771 Self::prefix_source(self.source_root.as_ref().unwrap(), &value);
772 }
773 }
774
775 pub fn sources(&self) -> SourceIter<'_> {
777 SourceIter {
778 i: self,
779 next_idx: 0,
780 }
781 }
782
783 pub fn get_source_view(&self, idx: u32) -> Option<&SourceView> {
785 self.sources_content
786 .get(idx as usize)
787 .and_then(Option::as_ref)
788 }
789
790 pub fn get_source_contents(&self, idx: u32) -> Option<&BytesStr> {
792 self.sources_content
793 .get(idx as usize)
794 .and_then(Option::as_ref)
795 .map(SourceView::source)
796 }
797
798 pub fn set_source_contents(&mut self, idx: u32, value: Option<BytesStr>) {
800 if self.sources_content.len() != self.sources.len() {
801 self.sources_content.resize(self.sources.len(), None);
802 }
803 self.sources_content[idx as usize] = value.map(SourceView::from_string);
804 }
805
806 pub fn source_contents(&self) -> SourceContentsIter<'_> {
808 SourceContentsIter {
809 i: self,
810 next_idx: 0,
811 }
812 }
813
814 pub fn names(&self) -> NameIter<'_> {
816 NameIter {
817 i: self,
818 next_idx: 0,
819 }
820 }
821
822 pub fn get_name_count(&self) -> u32 {
824 self.names.len() as u32
825 }
826
827 pub fn has_names(&self) -> bool {
829 !self.names.is_empty()
830 }
831
832 pub fn get_name(&self, idx: u32) -> Option<&BytesStr> {
834 self.names.get(idx as usize)
835 }
836
837 pub fn remove_names(&mut self) {
839 self.names.clear();
840 }
841
842 pub fn rewrite(self, options: &RewriteOptions<'_>) -> Result<SourceMap> {
864 Ok(self.rewrite_with_mapping(options)?.0)
865 }
866
867 pub(crate) fn rewrite_with_mapping(
869 self,
870 options: &RewriteOptions<'_>,
871 ) -> Result<(SourceMap, Vec<u32>)> {
872 let mut builder = SourceMapBuilder::new(self.get_file().cloned());
873 builder.set_debug_id(self.debug_id);
874
875 for token in self.tokens() {
876 let raw = builder.add_token(&token, options.with_names);
877 if raw.src_id != !0
878 && options.with_source_contents
879 && !builder.has_source_contents(raw.src_id)
880 {
881 builder.set_source_contents(
882 raw.src_id,
883 self.get_source_contents(token.get_src_id()).cloned(),
884 );
885 }
886 }
887
888 #[cfg(any(unix, windows, target_os = "redox"))]
889 {
890 if options.load_local_source_contents {
891 builder.load_local_source_contents(options.base_path)?;
892 }
893 }
894
895 let mut prefixes = vec![];
896 let mut need_common_prefix = false;
897 for &prefix in options.strip_prefixes.iter() {
898 if prefix == "~" {
899 need_common_prefix = true;
900 } else {
901 prefixes.push(prefix.to_string());
902 }
903 }
904 if need_common_prefix {
905 if let Some(prefix) = find_common_prefix(self.sources.iter().map(AsRef::as_ref)) {
906 prefixes.push(prefix);
907 }
908 }
909 if !prefixes.is_empty() {
910 builder.strip_prefixes(&prefixes);
911 }
912
913 let mapping = builder.take_mapping();
914
915 let sm = builder.into_sourcemap();
916
917 Ok((sm, mapping))
918 }
919
920 pub fn adjust_mappings(&mut self, adjustment: &Self) {
936 self.tokens = adjust_mappings(
937 std::mem::take(&mut self.tokens),
938 Cow::Borrowed(&adjustment.tokens),
939 );
940 }
941
942 pub fn adjust_mappings_from_multiple(self, adjustments: Vec<crate::lazy::SourceMap>) -> Self {
947 adjust_mappings_from_multiple(self, adjustments)
948 }
949}
950
951pub(crate) fn adjust_mappings(
952 mut self_tokens: Vec<RawToken>,
953 adjustments: Cow<[RawToken]>,
954) -> Vec<RawToken> {
955 #[derive(Debug, Clone, Copy)]
984 struct Range<'a> {
985 start: (u32, u32),
986 end: (u32, u32),
987 value: &'a RawToken,
988 }
989
990 #[allow(clippy::ptr_arg)]
992 fn create_ranges(tokens: &mut [RawToken], key: fn(&RawToken) -> (u32, u32)) -> Vec<Range<'_>> {
993 tokens.sort_unstable_by_key(key);
994
995 let mut token_iter = tokens.iter().peekable();
996 let mut ranges = Vec::new();
997
998 while let Some(t) = token_iter.next() {
999 let start = key(t);
1000 let next_start = token_iter.peek().map_or((u32::MAX, u32::MAX), |t| key(t));
1001 let end = std::cmp::min(next_start, (start.0, u32::MAX));
1003 ranges.push(Range {
1004 start,
1005 end,
1006 value: t,
1007 });
1008 }
1009
1010 ranges
1011 }
1012
1013 let mut new_tokens = Vec::with_capacity(self_tokens.len());
1014
1015 let original_ranges = create_ranges(&mut self_tokens, |t| (t.dst_line, t.dst_col));
1021 let mut adjustment_tokens = adjustments.into_owned();
1022 let adjustment_ranges = create_ranges(&mut adjustment_tokens, |t| (t.src_line, t.src_col));
1023
1024 let mut original_ranges_iter = original_ranges.iter();
1025
1026 let mut original_range = match original_ranges_iter.next() {
1027 Some(r) => r,
1028 None => return self_tokens,
1029 };
1030
1031 'outer: for &adjustment_range in &adjustment_ranges {
1034 let (line_diff, col_diff) = (
1037 adjustment_range.value.dst_line as i32 - adjustment_range.value.src_line as i32,
1038 adjustment_range.value.dst_col as i32 - adjustment_range.value.src_col as i32,
1039 );
1040
1041 while original_range.end <= adjustment_range.start {
1043 match original_ranges_iter.next() {
1044 Some(r) => original_range = r,
1045 None => break 'outer,
1046 }
1047 }
1048
1049 while original_range.start < adjustment_range.end {
1053 let (dst_line, dst_col) = std::cmp::max(original_range.start, adjustment_range.start);
1055 let mut token = RawToken {
1056 dst_line,
1057 dst_col,
1058 ..*original_range.value
1059 };
1060
1061 token.dst_line = (token.dst_line as i32 + line_diff) as u32;
1062 token.dst_col = (token.dst_col as i32 + col_diff) as u32;
1063
1064 new_tokens.push(token);
1065
1066 if original_range.end >= adjustment_range.end {
1067 break;
1070 } else {
1071 match original_ranges_iter.next() {
1073 Some(r) => original_range = r,
1074 None => break 'outer,
1075 }
1076 }
1077 }
1078 }
1079
1080 new_tokens.sort_unstable_by_key(|t| (t.dst_line, t.dst_col));
1081
1082 new_tokens
1083}
1084
1085pub fn adjust_mappings_from_multiple(
1086 mut this: SourceMap,
1087 mut input_maps: Vec<crate::lazy::SourceMap>,
1088) -> SourceMap {
1089 #[derive(Debug, Clone, Copy)]
1092 struct Range<'a> {
1093 start: (u32, u32),
1094 end: (u32, u32),
1095 value: &'a RawToken,
1096 map_idx: u32,
1097 }
1098
1099 #[allow(clippy::ptr_arg)]
1101 fn create_ranges(
1102 tokens: &mut [(u32, RawToken)],
1103 key: fn(&RawToken) -> (u32, u32),
1104 ) -> Vec<Range<'_>> {
1105 tokens.sort_unstable_by_key(|(_, t)| key(t));
1106
1107 let mut token_iter = tokens.iter().peekable();
1108 let mut ranges = Vec::new();
1109
1110 while let Some((map_idx, t)) = token_iter.next() {
1111 let start = key(t);
1112 let next_start = token_iter
1113 .peek()
1114 .map_or((u32::MAX, u32::MAX), |(_, t)| key(t));
1115 let end = std::cmp::min(next_start, (start.0, u32::MAX));
1117 ranges.push(Range {
1118 start,
1119 end,
1120 value: t,
1121 map_idx: *map_idx,
1122 });
1123 }
1124
1125 ranges
1126 }
1127
1128 let mut input_tokens = input_maps
1134 .iter_mut()
1135 .enumerate()
1136 .flat_map(|(i, map)| {
1137 std::mem::take(&mut map.tokens)
1138 .into_iter()
1139 .map(move |t| ((i + 1) as u32, t))
1140 })
1141 .collect::<Vec<_>>();
1142 let input_ranges = create_ranges(&mut input_tokens[..], |t| (t.dst_line, t.dst_col));
1143 let mut self_tokens = std::mem::take(&mut this.tokens)
1144 .into_iter()
1145 .map(|t| (0u32, t))
1146 .collect::<Vec<_>>();
1147 let self_ranges = create_ranges(&mut self_tokens[..], |t| (t.src_line, t.src_col));
1148
1149 let mut input_ranges_iter = input_ranges.iter();
1150 let mut input_range = match input_ranges_iter.next() {
1151 Some(r) => Some(r),
1152 None => return this,
1153 };
1154
1155 let covered_input_files = input_maps
1156 .iter_mut()
1157 .flat_map(|m| m.file().cloned())
1158 .collect::<FxHashSet<_>>();
1159
1160 let mut new_map = SourceMapBuilder::new(None);
1161 let mut add_mapping = |input_maps: &mut Vec<crate::lazy::SourceMap<'_>>,
1162 map_idx: u32,
1163 dst_line: u32,
1164 dst_col: u32,
1165 src_line: u32,
1166 src_col: u32,
1167 src_id: u32,
1168 name_id: u32,
1169 is_range: bool| {
1170 let (src_id, name) = if map_idx == 0 {
1171 let src = this.get_source(src_id).cloned();
1172 (
1173 src.map(|src| {
1174 let new_src_id = new_map.add_source(src);
1175 new_map
1176 .set_source_contents(new_src_id, this.get_source_contents(src_id).cloned());
1177 new_src_id
1178 }),
1179 this.get_name(name_id).cloned(),
1180 )
1181 } else {
1182 let this = &mut input_maps[(map_idx - 1) as usize];
1183 let src = this.get_source(src_id).cloned();
1184 (
1185 src.map(|src| {
1186 let new_src_id = new_map.add_source(src);
1187 new_map
1188 .set_source_contents(new_src_id, this.get_source_contents(src_id).cloned());
1189 new_src_id
1190 }),
1191 this.get_name(name_id).cloned(),
1192 )
1193 };
1194 let name_id = name.map(|name| new_map.add_name(name));
1195 new_map.add_raw(
1196 dst_line, dst_col, src_line, src_col, src_id, name_id, is_range,
1197 );
1198 };
1199
1200 for &self_range in &self_ranges {
1203 let (line_diff, col_diff) = (
1206 self_range.value.dst_line as i32 - self_range.value.src_line as i32,
1207 self_range.value.dst_col as i32 - self_range.value.src_col as i32,
1208 );
1209
1210 while input_range.is_some_and(|input_range| input_range.end <= self_range.start) {
1212 input_range = input_ranges_iter.next();
1213 }
1214 if input_range.is_none_or(|input_range| {
1217 self_range.start >= input_range.end
1218 || this.get_source(self_range.value.src_id).is_none_or(|src| {
1219 Some(src) != input_maps[(input_range.map_idx - 1) as usize].file()
1220 })
1221 }) {
1222 if this
1225 .get_source(self_range.value.src_id)
1226 .is_none_or(|f| !covered_input_files.contains(f))
1227 {
1228 add_mapping(
1229 &mut input_maps,
1230 0,
1231 self_range.value.dst_line,
1232 self_range.value.dst_col,
1233 self_range.value.src_line,
1234 self_range.value.src_col,
1235 self_range.value.src_id,
1236 self_range.value.name_id,
1237 self_range.value.is_range,
1238 );
1239 }
1240 } else {
1241 let mut input_range_value = input_range.unwrap();
1242 while input_range_value.start < self_range.end {
1244 let (dst_line, dst_col) = std::cmp::max(input_range_value.start, self_range.start);
1246 add_mapping(
1247 &mut input_maps,
1248 input_range_value.map_idx,
1249 (dst_line as i32 + line_diff) as u32,
1250 (dst_col as i32 + col_diff) as u32,
1251 input_range_value.value.src_line,
1252 input_range_value.value.src_col,
1253 input_range_value.value.src_id,
1254 input_range_value.value.name_id,
1255 input_range_value.value.is_range,
1256 );
1257
1258 if input_range_value.end >= self_range.end {
1259 break;
1262 } else {
1263 match input_ranges_iter.next() {
1265 Some(r) => {
1266 input_range_value = r;
1267 input_range = Some(r);
1268 }
1269 None => {
1270 input_range = None;
1271 break;
1272 }
1273 }
1274 }
1275 }
1276 }
1277 }
1278
1279 let mut new_map = new_map.into_sourcemap();
1280
1281 new_map
1282 .tokens
1283 .sort_unstable_by_key(|t| (t.dst_line, t.dst_col));
1284
1285 new_map
1286}
1287
1288impl SourceMapIndex {
1289 pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMapIndex> {
1294 match decode(rdr)? {
1295 DecodedMap::Index(smi) => Ok(smi),
1296 _ => Err(Error::IncompatibleSourceMap),
1297 }
1298 }
1299
1300 pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
1302 encode(self, w)
1303 }
1304
1305 pub fn from_slice(slice: &[u8]) -> Result<SourceMapIndex> {
1310 match decode_slice(slice)? {
1311 DecodedMap::Index(smi) => Ok(smi),
1312 _ => Err(Error::IncompatibleSourceMap),
1313 }
1314 }
1315
1316 pub fn new(file: Option<BytesStr>, sections: Vec<SourceMapSection>) -> SourceMapIndex {
1321 SourceMapIndex {
1322 file,
1323 sections,
1324 x_facebook_offsets: None,
1325 x_metro_module_paths: None,
1326 debug_id: None,
1327 }
1328 }
1329
1330 pub fn new_ram_bundle_compatible(
1338 file: Option<BytesStr>,
1339 sections: Vec<SourceMapSection>,
1340 x_facebook_offsets: Option<Vec<Option<u32>>>,
1341 x_metro_module_paths: Option<Vec<String>>,
1342 ) -> SourceMapIndex {
1343 SourceMapIndex {
1344 file,
1345 sections,
1346 x_facebook_offsets,
1347 x_metro_module_paths,
1348 debug_id: None,
1349 }
1350 }
1351
1352 pub(crate) fn debug_id(&self) -> Option<DebugId> {
1354 self.debug_id
1355 }
1356
1357 fn set_debug_id(&mut self, debug_id: Option<DebugId>) {
1358 self.debug_id = debug_id;
1359 }
1360
1361 pub(crate) fn with_debug_id(mut self, debug_id: Option<DebugId>) -> Self {
1363 self.set_debug_id(debug_id);
1364 self
1365 }
1366
1367 pub fn get_file(&self) -> Option<&BytesStr> {
1369 self.file.as_ref()
1370 }
1371
1372 pub fn set_file(&mut self, value: Option<BytesStr>) {
1374 self.file = value;
1375 }
1376
1377 pub fn get_section_count(&self) -> u32 {
1379 self.sections.len() as u32
1380 }
1381
1382 pub fn get_section(&self, idx: u32) -> Option<&SourceMapSection> {
1384 self.sections.get(idx as usize)
1385 }
1386
1387 pub fn get_section_mut(&mut self, idx: u32) -> Option<&mut SourceMapSection> {
1389 self.sections.get_mut(idx as usize)
1390 }
1391
1392 pub fn sections(&self) -> SourceMapSectionIter<'_> {
1394 SourceMapSectionIter {
1395 i: self,
1396 next_idx: 0,
1397 }
1398 }
1399
1400 pub fn get_original_function_name(
1409 &self,
1410 line: u32,
1411 col: u32,
1412 minified_name: &str,
1413 sv: &SourceView,
1414 ) -> Option<&BytesStr> {
1415 self.lookup_token(line, col)
1416 .and_then(|token| sv.get_original_function_name(token, minified_name))
1417 }
1418
1419 pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
1425 let (_section_idx, section) =
1426 greatest_lower_bound(&self.sections, &(line, col), SourceMapSection::get_offset)?;
1427 let map = section.get_sourcemap()?;
1428 let (off_line, off_col) = section.get_offset();
1429 map.lookup_token(
1430 line - off_line,
1431 if line == off_line { col - off_col } else { col },
1432 )
1433 }
1434
1435 pub fn flatten(&self) -> Result<SourceMap> {
1438 let mut builder = SourceMapBuilder::new(self.get_file().cloned());
1439
1440 for section in self.sections() {
1441 let (off_line, off_col) = section.get_offset();
1442 let map = match section.get_sourcemap() {
1443 Some(map) => match map {
1444 DecodedMap::Regular(sm) => Cow::Borrowed(sm),
1445 DecodedMap::Index(idx) => Cow::Owned(idx.flatten()?),
1446 DecodedMap::Hermes(smh) => Cow::Borrowed(&smh.sm),
1447 },
1448 None => {
1449 return Err(Error::CannotFlatten(format!(
1450 "Section has an unresolved \
1451 sourcemap: {}",
1452 section.get_url().unwrap_or("<unknown url>")
1453 )));
1454 }
1455 };
1456
1457 let mut src_id_map = Vec::<u32>::with_capacity(map.sources().count());
1458
1459 for (original_id, (source, contents)) in
1460 map.sources().zip(map.source_contents()).enumerate()
1461 {
1462 debug_assert_eq!(original_id, src_id_map.len());
1463 let src_id = builder.add_source(source.clone());
1464
1465 src_id_map.push(src_id);
1466
1467 if let Some(contents) = contents {
1468 builder.set_source_contents(src_id, Some(contents.clone()));
1469 }
1470 }
1471
1472 let mut name_id_map = Vec::<u32>::with_capacity(map.names().count());
1473
1474 for (original_id, name) in map.names().enumerate() {
1475 debug_assert_eq!(original_id, name_id_map.len());
1476 let name_id = builder.add_name(name.clone());
1477 name_id_map.push(name_id);
1478 }
1479
1480 for token in map.tokens() {
1481 let dst_col = if token.get_dst_line() == 0 {
1482 token.get_dst_col() + off_col
1483 } else {
1484 token.get_dst_col()
1485 };
1486
1487 let original_src_id = token.raw.src_id;
1489 let src_id = if original_src_id == !0 {
1490 None
1491 } else {
1492 src_id_map.get(original_src_id as usize).copied()
1493 };
1494
1495 let original_name_id = token.raw.name_id;
1496 let name_id = if original_name_id == !0 {
1497 None
1498 } else {
1499 name_id_map.get(original_name_id as usize).copied()
1500 };
1501
1502 let raw = builder.add_raw(
1503 token.get_dst_line() + off_line,
1504 dst_col,
1505 token.get_src_line(),
1506 token.get_src_col(),
1507 src_id,
1508 name_id,
1509 token.is_range(),
1510 );
1511
1512 if map.ignore_list.contains(&token.get_src_id()) {
1513 builder.add_to_ignore_list(raw.src_id);
1514 }
1515 }
1516 }
1517
1518 Ok(builder.into_sourcemap())
1519 }
1520
1521 pub fn flatten_and_rewrite(self, options: &RewriteOptions<'_>) -> Result<SourceMap> {
1525 self.flatten()?.rewrite(options)
1526 }
1527
1528 pub fn is_for_ram_bundle(&self) -> bool {
1530 self.x_facebook_offsets.is_some() && self.x_metro_module_paths.is_some()
1531 }
1532
1533 pub fn x_facebook_offsets(&self) -> Option<&[Option<u32>]> {
1535 self.x_facebook_offsets.as_ref().map(|x| &x[..])
1536 }
1537
1538 pub fn x_metro_module_paths(&self) -> Option<&[String]> {
1540 self.x_metro_module_paths.as_ref().map(|x| &x[..])
1541 }
1542
1543 pub fn adjust_sections_offset_rows(&mut self, amount: u32) -> bool {
1549 let adjusted_rows: Vec<_> = self
1550 .sections
1551 .iter()
1552 .filter_map(|section| section.offset.0.checked_add(amount))
1554 .collect();
1555
1556 if adjusted_rows.len() != self.sections.len() {
1557 return false;
1559 }
1560
1561 for (section, adjustment) in self.sections.iter_mut().zip(adjusted_rows) {
1562 section.offset.0 = adjustment;
1563 }
1564
1565 true
1566 }
1567}
1568
1569impl SourceMapSection {
1570 pub fn new(
1576 offset: (u32, u32),
1577 url: Option<String>,
1578 map: Option<DecodedMap>,
1579 ) -> SourceMapSection {
1580 SourceMapSection {
1581 offset,
1582 url,
1583 map: map.map(Box::new),
1584 }
1585 }
1586
1587 pub fn get_offset_line(&self) -> u32 {
1589 self.offset.0
1590 }
1591
1592 pub fn get_offset_col(&self) -> u32 {
1594 self.offset.1
1595 }
1596
1597 pub fn get_offset(&self) -> (u32, u32) {
1599 self.offset
1600 }
1601
1602 pub fn get_url(&self) -> Option<&str> {
1604 self.url.as_deref()
1605 }
1606
1607 pub fn set_url(&mut self, value: Option<&str>) {
1609 self.url = value.map(str::to_owned);
1610 }
1611
1612 pub fn get_sourcemap(&self) -> Option<&DecodedMap> {
1614 self.map.as_ref().map(Box::as_ref)
1615 }
1616
1617 pub fn get_sourcemap_mut(&mut self) -> Option<&mut DecodedMap> {
1619 self.map.as_mut().map(Box::as_mut)
1620 }
1621
1622 pub fn set_sourcemap(&mut self, sm: Option<DecodedMap>) {
1624 self.map = sm.map(Box::new);
1625 }
1626}
1627
1628#[cfg(test)]
1629mod tests {
1630 use std::collections::BTreeSet;
1631
1632 use crate::lazy::MaybeRawValue;
1633
1634 use super::{DecodedMap, RewriteOptions, SourceMap, SourceMapIndex, SourceMapSection};
1635 use debugid::DebugId;
1636
1637 #[test]
1638 fn test_rewrite_debugid() {
1639 let input: &[_] = br#"{
1640 "version":3,
1641 "sources":["coolstuff.js"],
1642 "names":["x","alert"],
1643 "mappings":"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM",
1644 "debug_id":"00000000-0000-0000-0000-000000000000"
1645 }"#;
1646
1647 let sm = SourceMap::from_slice(input).unwrap();
1648
1649 assert_eq!(sm.debug_id, Some(DebugId::default()));
1650
1651 let new_sm = sm
1652 .rewrite(&RewriteOptions {
1653 with_names: false,
1654 ..Default::default()
1655 })
1656 .unwrap();
1657
1658 assert_eq!(new_sm.debug_id, Some(DebugId::default()));
1659 }
1660
1661 #[test]
1662 fn test_debugid_alias() {
1663 let input: &[_] = br#"{
1664 "version":3,
1665 "sources":["coolstuff.js"],
1666 "names":["x","alert"],
1667 "mappings":"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM",
1668 "debug_id":"00000000-0000-0000-0000-000000000000",
1669 "debugId": "11111111-1111-1111-1111-111111111111"
1670 }"#;
1671
1672 let sm = SourceMap::from_slice(input).unwrap();
1673
1674 assert_eq!(sm.debug_id, Some(DebugId::default()));
1675 }
1676
1677 #[test]
1678 fn test_adjust_mappings_injection() {
1679 for bundler in ["esbuild", "rollup", "vite", "webpack", "rspack"] {
1694 let original_map_file = std::fs::File::open(format!(
1695 "tests/fixtures/adjust_mappings/{bundler}.bundle.js.map"
1696 ))
1697 .unwrap();
1698
1699 let injected_map_file = std::fs::File::open(format!(
1700 "tests/fixtures/adjust_mappings/{bundler}-injected.bundle.js.map"
1701 ))
1702 .unwrap();
1703
1704 let composed_map_file = std::fs::File::open(format!(
1705 "tests/fixtures/adjust_mappings/{bundler}-composed.bundle.js.map"
1706 ))
1707 .unwrap();
1708
1709 let mut original_map = SourceMap::from_reader(original_map_file).unwrap();
1710 let injected_map = SourceMap::from_reader(injected_map_file).unwrap();
1711 let composed_map = SourceMap::from_reader(composed_map_file).unwrap();
1712 original_map.adjust_mappings(&injected_map);
1713
1714 assert_eq!(
1715 original_map.tokens, composed_map.tokens,
1716 "bundler = {bundler}"
1717 );
1718 }
1719 }
1720
1721 #[test]
1722 fn adjust_mappings_from_multiple() {
1723 let original_map_file = std::fs::read_to_string(
1724 "tests/fixtures/adjust_mappings_from_multiple/sourcemapped.js.map",
1725 )
1726 .unwrap();
1727
1728 let bundled_map_file =
1729 std::fs::read_to_string("tests/fixtures/adjust_mappings_from_multiple/bundle.js.map")
1730 .unwrap();
1731
1732 let mut original_map = crate::lazy::decode(original_map_file.as_bytes())
1733 .unwrap()
1734 .into_source_map()
1735 .unwrap();
1736 original_map.file = Some(MaybeRawValue::Data("turbopack:///[project]/turbopack/crates/turbopack-tests/tests/snapshot/source_maps/input-source-map-merged/input/sourcemapped.js".into()));
1737
1738 let bundled_map = match crate::decode(bundled_map_file.as_bytes()).unwrap() {
1739 DecodedMap::Regular(source_map) => source_map,
1740 DecodedMap::Index(source_map_index) => source_map_index.flatten().unwrap(),
1741 DecodedMap::Hermes(_) => unimplemented!(),
1742 };
1743 let bundled_map = bundled_map.adjust_mappings_from_multiple(vec![original_map]);
1745
1746 let mut result = vec![];
1747 bundled_map.to_writer(&mut result).unwrap();
1748 let result = String::from_utf8(result).unwrap();
1749 let bundled_map_file =
1752 std::fs::read_to_string("tests/fixtures/adjust_mappings_from_multiple/merged.js.map")
1753 .unwrap();
1754
1755 assert_eq!(bundled_map_file, result)
1756 }
1757
1758 #[test]
1759 fn test_roundtrip() {
1760 let sm = br#"{
1761 "version": 3,
1762 "file": "foo.js",
1763 "sources": [
1764 "./bar.js",
1765 "./baz.js"
1766 ],
1767 "sourceRoot": "webpack:///",
1768 "sourcesContent": [null, null],
1769 "names": [],
1770 "mappings": ""
1771 }"#;
1772
1773 let sm = SourceMap::from_slice(sm).unwrap();
1774 let mut out = Vec::new();
1775 sm.to_writer(&mut out).unwrap();
1776
1777 let sm_new = SourceMap::from_slice(&out).unwrap();
1778 assert_eq!(sm_new.sources, sm.sources);
1779 }
1780
1781 #[test]
1782 fn test_sourcemap_index_default_debug_id() {
1783 let sm = SourceMapIndex::new(None, vec![]);
1784 assert!(sm.debug_id().is_none());
1785 }
1786
1787 #[test]
1788 fn test_sourcemap_index_debug_id() {
1789 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
1790
1791 let sm = SourceMapIndex::new(None, vec![])
1792 .with_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")));
1793
1794 assert_eq!(
1795 sm.debug_id(),
1796 Some(DEBUG_ID.parse().expect("valid debug id"))
1797 );
1798 }
1799
1800 #[test]
1801 fn test_decoded_map_regular_debug_id() {
1802 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
1803
1804 let mut decoded_map = DecodedMap::Regular(SourceMap {
1805 file: None,
1806 tokens: vec![],
1807 names: vec![],
1808 source_root: None,
1809 sources: vec![],
1810 sources_prefixed: None,
1811 sources_content: vec![],
1812 ignore_list: BTreeSet::new(),
1813 debug_id: None,
1814 });
1815
1816 assert!(decoded_map.debug_id().is_none());
1817
1818 decoded_map.set_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")));
1819
1820 assert_eq!(
1821 decoded_map,
1822 DecodedMap::Regular(SourceMap {
1823 file: None,
1824 tokens: vec![],
1825 names: vec![],
1826 source_root: None,
1827 sources: vec![],
1828 sources_prefixed: None,
1829 sources_content: vec![],
1830 ignore_list: BTreeSet::new(),
1831 debug_id: Some(DEBUG_ID.parse().expect("valid debug id")),
1832 })
1833 );
1834
1835 assert_eq!(
1836 decoded_map.debug_id(),
1837 Some(DEBUG_ID.parse().expect("valid debug id"))
1838 );
1839 }
1840
1841 #[test]
1842 fn test_decoded_map_index_debug_id() {
1843 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
1844
1845 let mut decoded_map = DecodedMap::Index(SourceMapIndex {
1846 file: None,
1847 sections: vec![],
1848 x_facebook_offsets: None,
1849 x_metro_module_paths: None,
1850 debug_id: None,
1851 });
1852
1853 assert!(decoded_map.debug_id().is_none());
1854
1855 decoded_map.set_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")));
1856
1857 assert_eq!(
1858 decoded_map,
1859 DecodedMap::Index(SourceMapIndex {
1860 file: None,
1861 sections: vec![],
1862 x_facebook_offsets: None,
1863 x_metro_module_paths: None,
1864 debug_id: Some(DEBUG_ID.parse().expect("valid debug id")),
1865 })
1866 );
1867
1868 assert_eq!(
1869 decoded_map.debug_id(),
1870 Some(DEBUG_ID.parse().expect("valid debug id"))
1871 );
1872 }
1873
1874 #[test]
1875 fn test_adjust_sections_offset_rows_basic() {
1876 let mut smi = SourceMapIndex::new(
1878 Some("test.js".into()),
1879 vec![
1880 SourceMapSection::new((0, 0), None, None),
1881 SourceMapSection::new((10, 0), None, None),
1882 ],
1883 );
1884
1885 assert!(smi.adjust_sections_offset_rows(1));
1887
1888 assert_eq!(
1890 smi,
1891 SourceMapIndex::new(
1892 Some("test.js".into()),
1893 vec![
1894 SourceMapSection::new((1, 0), None, None),
1895 SourceMapSection::new((11, 0), None, None),
1896 ],
1897 )
1898 );
1899 }
1900
1901 #[test]
1902 fn test_adjust_sections_offset_rows_zero() {
1903 let mut smi = SourceMapIndex::new(
1905 Some("test.js".into()),
1906 vec![
1907 SourceMapSection::new((0, 0), None, None),
1908 SourceMapSection::new((10, 0), None, None),
1909 ],
1910 );
1911
1912 assert!(smi.adjust_sections_offset_rows(0));
1914
1915 assert_eq!(
1917 smi,
1918 SourceMapIndex::new(
1919 Some("test.js".into()),
1920 vec![
1921 SourceMapSection::new((0, 0), None, None),
1922 SourceMapSection::new((10, 0), None, None),
1923 ],
1924 )
1925 );
1926 }
1927
1928 #[test]
1929 fn test_adjust_sections_offset_rows_multiple_sections() {
1930 let mut smi = SourceMapIndex::new(
1932 Some("test.js".into()),
1933 vec![
1934 SourceMapSection::new((0, 0), None, None),
1935 SourceMapSection::new((10, 0), None, None),
1936 SourceMapSection::new((20, 10), None, None),
1937 SourceMapSection::new((30, 40), None, None),
1938 ],
1939 );
1940
1941 assert!(smi.adjust_sections_offset_rows(1));
1943
1944 assert_eq!(
1946 smi,
1947 SourceMapIndex::new(
1948 Some("test.js".into()),
1949 vec![
1950 SourceMapSection::new((1, 0), None, None),
1951 SourceMapSection::new((11, 0), None, None),
1952 SourceMapSection::new((21, 10), None, None),
1953 SourceMapSection::new((31, 40), None, None),
1954 ],
1955 )
1956 );
1957 }
1958
1959 #[test]
1960 fn test_adjust_sections_offset_rows_overflow() {
1961 let mut smi = SourceMapIndex::new(
1963 Some("test.js".into()),
1964 vec![
1965 SourceMapSection::new((0, 0), None, None),
1966 SourceMapSection::new((u32::MAX, 0), None, None),
1967 ],
1968 );
1969
1970 let original_smi = smi.clone();
1972
1973 assert!(!smi.adjust_sections_offset_rows(1));
1975
1976 assert_eq!(smi, original_smi);
1978 }
1979
1980 #[test]
1981 fn test_adjust_sections_offset_rows_partial_overflow() {
1982 let mut smi = SourceMapIndex::new(
1984 Some("test.js".into()),
1985 vec![
1986 SourceMapSection::new((0, 0), None, None),
1987 SourceMapSection::new((10, 0), None, None),
1988 SourceMapSection::new((20, 0), None, None),
1989 SourceMapSection::new((u32::MAX, 0), None, None),
1990 ],
1991 );
1992
1993 let original_smi = smi.clone();
1995
1996 assert!(!smi.adjust_sections_offset_rows(1));
1998
1999 assert_eq!(smi, original_smi);
2001 }
2002
2003 #[test]
2004 fn test_adjust_sections_offset_rows_large_amount() {
2005 let mut smi = SourceMapIndex::new(
2007 Some("test.js".into()),
2008 vec![
2009 SourceMapSection::new((0, 0), None, None),
2010 SourceMapSection::new((10, 0), None, None),
2011 ],
2012 );
2013
2014 assert!(smi.adjust_sections_offset_rows(1_000_000));
2015
2016 assert_eq!(
2018 smi,
2019 SourceMapIndex::new(
2020 Some("test.js".into()),
2021 vec![
2022 SourceMapSection::new((1_000_000, 0), None, None),
2023 SourceMapSection::new((1_000_010, 0), None, None),
2024 ],
2025 )
2026 );
2027 }
2028
2029 #[test]
2030 fn adjust_sections_offset_rows_large_amount_overflow() {
2031 let mut smi = SourceMapIndex::new(
2033 Some("test.js".into()),
2034 vec![
2035 SourceMapSection::new((0, 0), None, None),
2036 SourceMapSection::new((10, 0), None, None),
2037 ],
2038 );
2039
2040 let original_smi = smi.clone();
2042
2043 assert!(!smi.adjust_sections_offset_rows(u32::MAX));
2045
2046 assert_eq!(smi, original_smi);
2048 }
2049
2050 #[test]
2051 fn adjust_sections_offset_rows_no_sections() {
2052 let mut smi = SourceMapIndex::new(Some("test.js".into()), vec![]);
2054
2055 assert!(smi.adjust_sections_offset_rows(1));
2057
2058 assert_eq!(smi, SourceMapIndex::new(Some("test.js".into()), vec![]));
2060 }
2061
2062 mod prop {
2063 use magic_string::MagicString;
2082 use proptest::prelude::*;
2083
2084 use crate::SourceMap;
2085
2086 #[derive(Debug, Clone)]
2088 enum FirstEdit {
2089 Insert(u32, String),
2091 Delete(i64, i64),
2093 }
2094
2095 impl FirstEdit {
2096 fn apply(&self, line: usize, ms: &mut MagicString) {
2098 let line_offset = line * 11;
2100 match self {
2101 FirstEdit::Insert(col, s) => {
2102 ms.append_left(line_offset as u32 + *col, s).unwrap();
2103 }
2104 FirstEdit::Delete(start, end) => {
2105 ms.remove(line_offset as i64 + *start, line_offset as i64 + *end)
2106 .unwrap();
2107 }
2108 }
2109 }
2110 }
2111
2112 fn nth_line_start_end(n: usize, s: &str) -> (usize, usize) {
2115 let line = s.lines().nth(n).unwrap();
2116 let start = line.as_ptr() as usize - s.as_ptr() as usize;
2117 let end = if n == 9 {
2119 start + line.len()
2120 } else {
2121 start + line.len() + 1
2122 };
2123 (start, end)
2124 }
2125
2126 #[derive(Debug, Clone)]
2128 enum SecondEdit {
2129 Prepend(String),
2131 Append(String),
2133 Insert(usize, String),
2135 Delete(usize),
2137 }
2138
2139 impl SecondEdit {
2140 fn apply(&self, orig: &str, ms: &mut MagicString) {
2145 match self {
2146 SecondEdit::Prepend(s) => {
2147 ms.prepend(s).unwrap();
2148 }
2149 SecondEdit::Append(s) => {
2150 ms.append(s).unwrap();
2151 }
2152 SecondEdit::Insert(line, s) => {
2153 let (start, _) = nth_line_start_end(*line, orig);
2154 ms.prepend_left(start as u32, s).unwrap();
2155 }
2156 SecondEdit::Delete(line) => {
2157 let (start, end) = nth_line_start_end(*line, orig);
2158 ms.remove(start as i64, end as i64).unwrap();
2159 }
2160 }
2161 }
2162 }
2163
2164 fn starting_string() -> impl Strategy<Value = String> {
2166 (vec!["[a-z]{10}"; 10]).prop_map(|v| v.join("\n"))
2167 }
2168
2169 fn first_edit() -> impl Strategy<Value = FirstEdit> {
2171 prop_oneof![
2172 (1u32..9, "[a-z]{5}").prop_map(|(c, s)| FirstEdit::Insert(c, s)),
2173 (1i64..10)
2174 .prop_flat_map(|end| (0..end, Just(end)))
2175 .prop_map(|(a, b)| FirstEdit::Delete(a, b))
2176 ]
2177 }
2178
2179 fn first_edit_sequence() -> impl Strategy<Value = Vec<FirstEdit>> {
2183 let mut vec = Vec::with_capacity(10);
2184
2185 for _ in 0..10 {
2186 vec.push(first_edit())
2187 }
2188
2189 vec
2190 }
2191
2192 fn second_edit_sequence() -> impl Strategy<Value = Vec<SecondEdit>> {
2197 let edits = (0..10)
2198 .map(|i| {
2199 prop_oneof![
2200 "[a-z\n]{12}".prop_map(SecondEdit::Prepend),
2201 "[a-z\n]{12}".prop_map(SecondEdit::Append),
2202 "[a-z\n]{11}\n".prop_map(move |s| SecondEdit::Insert(i, s)),
2203 Just(SecondEdit::Delete(i)),
2204 ]
2205 })
2206 .collect::<Vec<_>>();
2207
2208 edits.prop_shuffle()
2209 }
2210
2211 proptest! {
2212 #[test]
2213 fn test_composition_identity(
2214 input in starting_string(),
2215 first_edits in first_edit_sequence(),
2216 second_edits in second_edit_sequence(),
2217 ) {
2218
2219 let mut ms1 = MagicString::new(&input);
2222
2223 for (line, first_edit) in first_edits.iter().enumerate() {
2224 first_edit.apply(line, &mut ms1);
2225 }
2226
2227 let first_map = ms1.generate_map(Default::default()).unwrap().to_string().unwrap();
2228 let mut first_map = SourceMap::from_slice(first_map.as_bytes()).unwrap();
2229
2230 let transformed_input = ms1.to_string();
2231
2232 let mut ms2 = MagicString::new(&transformed_input);
2233
2234 for second_edit in second_edits.iter() {
2235 second_edit.apply(&transformed_input, &mut ms2);
2236 }
2237
2238 let output_1 = ms2.to_string();
2239
2240 let second_map = ms2.generate_map(Default::default()).unwrap().to_string().unwrap();
2241 let second_map = SourceMap::from_slice(second_map.as_bytes()).unwrap();
2242
2243 let mut ms3 = MagicString::new(&input);
2246
2247 for (line, first_edit) in first_edits.iter().enumerate() {
2248 first_edit.apply(line, &mut ms3);
2249 }
2250
2251 for second_edit in second_edits.iter() {
2252 second_edit.apply(&input, &mut ms3);
2253 }
2254
2255 let output_2 = ms3.to_string();
2256
2257 let third_map = ms3.generate_map(Default::default()).unwrap().to_string().unwrap();
2258 let third_map = SourceMap::from_slice(third_map.as_bytes()).unwrap();
2259
2260 assert_eq!(output_1, output_2);
2262
2263 first_map.adjust_mappings(&second_map);
2264
2265 assert_eq!(first_map.tokens, third_map.tokens);
2266 }
2267 }
2268 }
2269}