1use rustc_hash::FxHashMap;
28use srcmap_codec::{vlq_encode_unchecked, vlq_encode_unsigned};
29
30#[inline]
32fn is_sorted_by_position(mappings: &[Mapping]) -> bool {
33 mappings.windows(2).all(|w| {
34 (w[0].generated_line, w[0].generated_column) <= (w[1].generated_line, w[1].generated_column)
35 })
36}
37use srcmap_scopes::ScopeInfo;
38
39use std::io;
40
41#[derive(Debug, Clone)]
50pub struct SourceMapParts {
51 pub file: Option<String>,
53 pub mappings: String,
55 pub sources: Vec<String>,
57 pub names: Vec<String>,
59 pub sources_content: Vec<Option<String>>,
61 pub ignore_list: Vec<u32>,
63 pub debug_id: Option<String>,
65 pub source_root: Option<String>,
67 pub range_mappings: Option<String>,
69}
70
71#[derive(Debug, Clone)]
76pub struct Mapping {
77 pub generated_line: u32,
79 pub generated_column: u32,
81 pub source: Option<u32>,
83 pub original_line: u32,
85 pub original_column: u32,
87 pub name: Option<u32>,
89 pub is_range_mapping: bool,
91}
92
93#[derive(Debug)]
109pub struct SourceMapGenerator {
110 file: Option<String>,
111 source_root: Option<String>,
112 sources: Vec<String>,
113 sources_content: Vec<Option<String>>,
114 names: Vec<String>,
115 mappings: Vec<Mapping>,
116 ignore_list: Vec<u32>,
117 debug_id: Option<String>,
118 scopes: Option<ScopeInfo>,
119 assume_sorted: bool,
120
121 source_map: FxHashMap<String, u32>,
123 name_map: FxHashMap<String, u32>,
124}
125
126impl SourceMapGenerator {
127 pub fn new(file: Option<String>) -> Self {
129 Self {
130 file,
131 source_root: None,
132 sources: Vec::new(),
133 sources_content: Vec::new(),
134 names: Vec::new(),
135 mappings: Vec::new(),
136 ignore_list: Vec::new(),
137 debug_id: None,
138 scopes: None,
139 assume_sorted: false,
140 source_map: FxHashMap::default(),
141 name_map: FxHashMap::default(),
142 }
143 }
144
145 pub fn with_capacity(file: Option<String>, mapping_capacity: usize) -> Self {
150 Self {
151 file,
152 source_root: None,
153 sources: Vec::new(),
154 sources_content: Vec::new(),
155 names: Vec::new(),
156 mappings: Vec::with_capacity(mapping_capacity),
157 ignore_list: Vec::new(),
158 debug_id: None,
159 scopes: None,
160 assume_sorted: false,
161 source_map: FxHashMap::default(),
162 name_map: FxHashMap::default(),
163 }
164 }
165
166 pub fn set_source_root(&mut self, root: impl Into<String>) {
168 self.source_root = Some(root.into());
169 }
170
171 pub fn set_debug_id(&mut self, id: impl Into<String>) {
173 self.debug_id = Some(id.into());
174 }
175
176 pub fn set_scopes(&mut self, scopes: ScopeInfo) {
178 self.scopes = Some(scopes);
179 }
180
181 pub fn set_assume_sorted(&mut self, sorted: bool) {
189 self.assume_sorted = sorted;
190 }
191
192 #[inline]
194 pub fn add_source(&mut self, source: &str) -> u32 {
195 if let Some(&idx) = self.source_map.get(source) {
196 return idx;
197 }
198 let idx = self.sources.len() as u32;
199 self.sources.push(source.to_string());
200 self.sources_content.push(None);
201 self.source_map.insert(source.to_string(), idx);
202 idx
203 }
204
205 pub fn set_source_content(&mut self, source_idx: u32, content: impl Into<String>) {
207 if (source_idx as usize) < self.sources_content.len() {
208 self.sources_content[source_idx as usize] = Some(content.into());
209 }
210 }
211
212 #[inline]
214 pub fn add_name(&mut self, name: &str) -> u32 {
215 if let Some(&idx) = self.name_map.get(name) {
216 return idx;
217 }
218 let idx = self.names.len() as u32;
219 self.names.push(name.to_string());
220 self.name_map.insert(name.to_string(), idx);
221 idx
222 }
223
224 pub fn add_to_ignore_list(&mut self, source_idx: u32) {
226 if !self.ignore_list.contains(&source_idx) {
227 self.ignore_list.push(source_idx);
228 }
229 }
230
231 pub fn add_generated_mapping(&mut self, generated_line: u32, generated_column: u32) {
233 self.mappings.push(Mapping {
234 generated_line,
235 generated_column,
236 source: None,
237 original_line: 0,
238 original_column: 0,
239 name: None,
240 is_range_mapping: false,
241 });
242 }
243
244 pub fn add_mapping(
246 &mut self,
247 generated_line: u32,
248 generated_column: u32,
249 source: u32,
250 original_line: u32,
251 original_column: u32,
252 ) {
253 self.mappings.push(Mapping {
254 generated_line,
255 generated_column,
256 source: Some(source),
257 original_line,
258 original_column,
259 name: None,
260 is_range_mapping: false,
261 });
262 }
263
264 pub fn add_named_mapping(
266 &mut self,
267 generated_line: u32,
268 generated_column: u32,
269 source: u32,
270 original_line: u32,
271 original_column: u32,
272 name: u32,
273 ) {
274 self.mappings.push(Mapping {
275 generated_line,
276 generated_column,
277 source: Some(source),
278 original_line,
279 original_column,
280 name: Some(name),
281 is_range_mapping: false,
282 });
283 }
284
285 pub fn add_range_mapping(
291 &mut self,
292 generated_line: u32,
293 generated_column: u32,
294 source: u32,
295 original_line: u32,
296 original_column: u32,
297 ) {
298 self.mappings.push(Mapping {
299 generated_line,
300 generated_column,
301 source: Some(source),
302 original_line,
303 original_column,
304 name: None,
305 is_range_mapping: true,
306 });
307 }
308
309 pub fn add_named_range_mapping(
311 &mut self,
312 generated_line: u32,
313 generated_column: u32,
314 source: u32,
315 original_line: u32,
316 original_column: u32,
317 name: u32,
318 ) {
319 self.mappings.push(Mapping {
320 generated_line,
321 generated_column,
322 source: Some(source),
323 original_line,
324 original_column,
325 name: Some(name),
326 is_range_mapping: true,
327 });
328 }
329
330 pub fn maybe_add_mapping(
336 &mut self,
337 generated_line: u32,
338 generated_column: u32,
339 source: u32,
340 original_line: u32,
341 original_column: u32,
342 ) -> bool {
343 if let Some(last) = self.mappings.last()
344 && last.generated_line == generated_line
345 && last.source == Some(source)
346 && last.original_line == original_line
347 && last.original_column == original_column
348 {
349 return false;
350 }
351 self.add_mapping(generated_line, generated_column, source, original_line, original_column);
352 true
353 }
354
355 fn encode_mappings_into(&self, out: &mut Vec<u8>) {
358 if self.mappings.is_empty() {
359 return;
360 }
361
362 if self.assume_sorted || is_sorted_by_position(&self.mappings) {
365 #[cfg(feature = "parallel")]
366 if self.mappings.len() >= 4096 {
367 let refs: Vec<&Mapping> = self.mappings.iter().collect();
368 let encoded = Self::encode_parallel_impl(&refs);
369 out.extend_from_slice(encoded.as_bytes());
370 return;
371 }
372 Self::encode_sequential_into(&self.mappings, out);
373 return;
374 }
375
376 let mut sorted: Vec<&Mapping> = self.mappings.iter().collect();
378 sorted.sort_unstable_by(|a, b| {
379 a.generated_line
380 .cmp(&b.generated_line)
381 .then(a.generated_column.cmp(&b.generated_column))
382 });
383
384 #[cfg(feature = "parallel")]
385 if sorted.len() >= 4096 {
386 let encoded = Self::encode_parallel_impl(&sorted);
387 out.extend_from_slice(encoded.as_bytes());
388 return;
389 }
390
391 Self::encode_sequential_into(&sorted, out);
392 }
393
394 #[allow(dead_code, reason = "used by tests and the parallel encoding path")]
396 fn encode_mappings(&self) -> String {
397 if self.mappings.is_empty() {
398 return String::new();
399 }
400
401 if self.assume_sorted || is_sorted_by_position(&self.mappings) {
404 #[cfg(feature = "parallel")]
405 if self.mappings.len() >= 4096 {
406 let refs: Vec<&Mapping> = self.mappings.iter().collect();
407 return Self::encode_parallel_impl(&refs);
408 }
409 return Self::encode_sequential_impl(&self.mappings);
410 }
411
412 let mut sorted: Vec<&Mapping> = self.mappings.iter().collect();
414 sorted.sort_unstable_by(|a, b| {
415 a.generated_line
416 .cmp(&b.generated_line)
417 .then(a.generated_column.cmp(&b.generated_column))
418 });
419
420 #[cfg(feature = "parallel")]
421 if sorted.len() >= 4096 {
422 return Self::encode_parallel_impl(&sorted);
423 }
424
425 Self::encode_sequential_impl(&sorted)
426 }
427
428 #[inline]
433 fn encode_sequential_into<T: std::borrow::Borrow<Mapping>>(sorted: &[T], out: &mut Vec<u8>) {
434 let max_line = sorted.last().map_or(0, |m| m.borrow().generated_line as usize);
436 out.reserve(sorted.len() * 36 + max_line + 1);
437
438 let mut prev_gen_col: i64 = 0;
439 let mut prev_source: i64 = 0;
440 let mut prev_orig_line: i64 = 0;
441 let mut prev_orig_col: i64 = 0;
442 let mut prev_name: i64 = 0;
443 let mut prev_gen_line: u32 = 0;
444 let mut first_in_line = true;
445
446 for item in sorted {
447 let m = item.borrow();
448 while prev_gen_line < m.generated_line {
449 out.push(b';');
450 prev_gen_line += 1;
451 prev_gen_col = 0;
452 first_in_line = true;
453 }
454
455 if !first_in_line {
456 out.push(b',');
457 }
458 first_in_line = false;
459
460 unsafe {
465 vlq_encode_unchecked(out, m.generated_column as i64 - prev_gen_col);
466 prev_gen_col = m.generated_column as i64;
467
468 if let Some(source) = m.source {
469 vlq_encode_unchecked(out, source as i64 - prev_source);
470 prev_source = source as i64;
471
472 vlq_encode_unchecked(out, m.original_line as i64 - prev_orig_line);
473 prev_orig_line = m.original_line as i64;
474
475 vlq_encode_unchecked(out, m.original_column as i64 - prev_orig_col);
476 prev_orig_col = m.original_column as i64;
477
478 if let Some(name) = m.name {
479 vlq_encode_unchecked(out, name as i64 - prev_name);
480 prev_name = name as i64;
481 }
482 }
483 }
484 }
485 }
486
487 #[inline]
488 #[allow(dead_code, reason = "used by tests and the parallel encoding path")]
489 fn encode_sequential_impl<T: std::borrow::Borrow<Mapping>>(sorted: &[T]) -> String {
490 let max_line = sorted.last().map_or(0, |m| m.borrow().generated_line as usize);
491 let mut out: Vec<u8> = Vec::with_capacity(sorted.len() * 36 + max_line + 1);
492 Self::encode_sequential_into(sorted, &mut out);
493
494 debug_assert!(out.is_ascii());
495 unsafe { String::from_utf8_unchecked(out) }
498 }
499
500 #[cfg(feature = "parallel")]
501 fn encode_parallel_impl(sorted: &[&Mapping]) -> String {
502 use rayon::prelude::*;
503
504 let max_line = sorted
505 .last()
506 .expect("encode_parallel_impl requires non-empty sorted slice")
507 .generated_line as usize;
508
509 let mut line_ranges: Vec<(usize, usize)> = vec![(0, 0); max_line + 1];
511 let mut i = 0;
512 while i < sorted.len() {
513 let line = sorted[i].generated_line as usize;
514 let start = i;
515 while i < sorted.len() && sorted[i].generated_line as usize == line {
516 i += 1;
517 }
518 line_ranges[line] = (start, i);
519 }
520
521 let mut states: Vec<(i64, i64, i64, i64)> = Vec::with_capacity(max_line + 1);
523 let mut prev_source: i64 = 0;
524 let mut prev_orig_line: i64 = 0;
525 let mut prev_orig_col: i64 = 0;
526 let mut prev_name: i64 = 0;
527
528 for &(start, end) in &line_ranges {
529 states.push((prev_source, prev_orig_line, prev_orig_col, prev_name));
530 for m in &sorted[start..end] {
531 if let Some(source) = m.source {
532 prev_source = source as i64;
533 prev_orig_line = m.original_line as i64;
534 prev_orig_col = m.original_column as i64;
535 if let Some(name) = m.name {
536 prev_name = name as i64;
537 }
538 }
539 }
540 }
541
542 let encoded_lines: Vec<Vec<u8>> = line_ranges
544 .par_iter()
545 .zip(states.par_iter())
546 .map(|(&(start, end), &(s, ol, oc, n))| {
547 if start == end {
548 return Vec::new();
549 }
550 encode_mapping_slice(&sorted[start..end], s, ol, oc, n)
551 })
552 .collect();
553
554 let total_len = encoded_lines.iter().map(|l| l.len()).sum::<usize>() + max_line;
556 let mut out: Vec<u8> = Vec::with_capacity(total_len);
557 for (i, bytes) in encoded_lines.iter().enumerate() {
558 if i > 0 {
559 out.push(b';');
560 }
561 out.extend_from_slice(bytes);
562 }
563
564 debug_assert!(out.is_ascii());
567 unsafe { String::from_utf8_unchecked(out) }
568 }
569
570 fn encode_range_mappings(&self) -> Option<String> {
573 if !self.mappings.iter().any(|m| m.is_range_mapping) {
574 return None;
575 }
576
577 let ordered: Vec<&Mapping> = if self.assume_sorted || is_sorted_by_position(&self.mappings)
578 {
579 self.mappings.iter().collect()
580 } else {
581 let mut sorted: Vec<&Mapping> = self.mappings.iter().collect();
582 sorted.sort_unstable_by(|a, b| {
583 a.generated_line
584 .cmp(&b.generated_line)
585 .then(a.generated_column.cmp(&b.generated_column))
586 });
587 sorted
588 };
589
590 let max_line = ordered.last().map_or(0, |m| m.generated_line);
591 let mut out: Vec<u8> = Vec::new();
592 let mut ordered_idx = 0;
593
594 for line in 0..=max_line {
595 if line > 0 {
596 out.push(b';');
597 }
598 let mut prev_offset: u64 = 0;
599 let mut first_on_line = true;
600 let mut line_local_idx: u64 = 0;
601
602 while ordered_idx < ordered.len() && ordered[ordered_idx].generated_line == line {
603 if ordered[ordered_idx].is_range_mapping {
604 if !first_on_line {
605 out.push(b',');
606 }
607 first_on_line = false;
608 let delta = line_local_idx - prev_offset;
609 vlq_encode_unsigned(&mut out, delta);
610 prev_offset = line_local_idx;
611 }
612 line_local_idx += 1;
613 ordered_idx += 1;
614 }
615 }
616
617 while out.last() == Some(&b';') {
618 out.pop();
619 }
620
621 if out.is_empty() {
622 return None;
623 }
624
625 debug_assert!(out.is_ascii());
626 Some(unsafe { String::from_utf8_unchecked(out) })
629 }
630
631 pub fn to_json(&self) -> String {
633 use std::io::Write;
634
635 let (scopes_str, names_for_json) = if let Some(ref scopes_info) = self.scopes {
637 let mut names = self.names.clone();
638 let s = srcmap_scopes::encode_scopes(scopes_info, &mut names);
639 (Some(s), std::borrow::Cow::Owned(names))
640 } else {
641 (None, std::borrow::Cow::Borrowed(&self.names))
642 };
643
644 let sources_size: usize = self.sources.iter().map(|s| s.len() + 4).sum();
646 let names_size: usize = names_for_json.iter().map(|n| n.len() + 4).sum();
647 let content_size: usize =
648 self.sources_content.iter().map(|c| c.as_ref().map_or(5, |s| s.len() + 4)).sum();
649 let mappings_estimate = self.mappings.len() * 6;
651 let capacity = 100 + sources_size + names_size + mappings_estimate + content_size;
652
653 let mut json: Vec<u8> = Vec::with_capacity(capacity);
654 json.extend_from_slice(br#"{"version":3"#);
655
656 if let Some(ref file) = self.file {
657 json.extend_from_slice(br#","file":"#);
658 json_quote_into(&mut json, file);
659 }
660
661 if let Some(ref root) = self.source_root {
662 json.extend_from_slice(br#","sourceRoot":"#);
663 json_quote_into(&mut json, root);
664 }
665
666 json.extend_from_slice(br#","sources":["#);
668 for (i, s) in self.sources.iter().enumerate() {
669 if i > 0 {
670 json.push(b',');
671 }
672 json_quote_into(&mut json, s);
673 }
674 json.push(b']');
675
676 if self.sources_content.iter().any(|c| c.is_some()) {
678 json.extend_from_slice(br#","sourcesContent":["#);
679
680 #[cfg(feature = "parallel")]
681 {
682 use rayon::prelude::*;
683
684 let total_content: usize =
685 self.sources_content.iter().map(|c| c.as_ref().map_or(0, |s| s.len())).sum();
686
687 if self.sources_content.len() >= 8 && total_content >= 8192 {
688 let quoted: Vec<String> = self
689 .sources_content
690 .par_iter()
691 .map(|c| match c {
692 Some(content) => json_quote(content),
693 None => "null".to_string(),
694 })
695 .collect();
696 for (i, q) in quoted.iter().enumerate() {
697 if i > 0 {
698 json.push(b',');
699 }
700 json.extend_from_slice(q.as_bytes());
701 }
702 } else {
703 for (i, c) in self.sources_content.iter().enumerate() {
704 if i > 0 {
705 json.push(b',');
706 }
707 match c {
708 Some(content) => json_quote_into(&mut json, content),
709 None => json.extend_from_slice(b"null"),
710 }
711 }
712 }
713 }
714
715 #[cfg(not(feature = "parallel"))]
716 for (i, c) in self.sources_content.iter().enumerate() {
717 if i > 0 {
718 json.push(b',');
719 }
720 match c {
721 Some(content) => json_quote_into(&mut json, content),
722 None => json.extend_from_slice(b"null"),
723 }
724 }
725
726 json.push(b']');
727 }
728
729 json.extend_from_slice(br#","names":["#);
731 for (i, n) in names_for_json.iter().enumerate() {
732 if i > 0 {
733 json.push(b',');
734 }
735 json_quote_into(&mut json, n);
736 }
737 json.push(b']');
738
739 json.extend_from_slice(br#","mappings":""#);
742 self.encode_mappings_into(&mut json);
743 json.push(b'"');
744
745 if !self.ignore_list.is_empty() {
747 json.extend_from_slice(br#","ignoreList":["#);
748 for (i, &idx) in self.ignore_list.iter().enumerate() {
749 if i > 0 {
750 json.push(b',');
751 }
752 let _ = write!(json, "{idx}");
753 }
754 json.push(b']');
755 }
756
757 if let Some(ref range_mappings) = self.encode_range_mappings() {
759 json.extend_from_slice(br#","rangeMappings":""#);
760 json.extend_from_slice(range_mappings.as_bytes());
761 json.push(b'"');
762 }
763
764 if let Some(ref id) = self.debug_id {
766 json.extend_from_slice(br#","debugId":"#);
767 json_quote_into(&mut json, id);
768 }
769
770 if let Some(ref s) = scopes_str {
772 json.extend_from_slice(br#","scopes":"#);
773 json_quote_into(&mut json, s);
774 }
775
776 json.push(b'}');
777
778 unsafe { String::from_utf8_unchecked(json) }
781 }
782
783 pub fn mapping_count(&self) -> usize {
785 self.mappings.len()
786 }
787
788 pub fn to_decoded_map(&self) -> srcmap_sourcemap::SourceMap {
793 let convert_mapping = |m: &Mapping| srcmap_sourcemap::Mapping {
794 generated_line: m.generated_line,
795 generated_column: m.generated_column,
796 source: m.source.unwrap_or(u32::MAX),
797 original_line: m.original_line,
798 original_column: m.original_column,
799 name: m.name.unwrap_or(u32::MAX),
800 is_range_mapping: m.is_range_mapping,
801 };
802
803 let sm_mappings: Vec<srcmap_sourcemap::Mapping> =
804 if self.assume_sorted || is_sorted_by_position(&self.mappings) {
805 self.mappings.iter().map(convert_mapping).collect()
807 } else {
808 let mut sorted: Vec<&Mapping> = self.mappings.iter().collect();
810 sorted.sort_unstable_by(|a, b| {
811 a.generated_line
812 .cmp(&b.generated_line)
813 .then(a.generated_column.cmp(&b.generated_column))
814 });
815 sorted.iter().map(|m| convert_mapping(m)).collect()
816 };
817
818 let sources_content: Vec<Option<String>> = self.sources_content.clone();
820
821 let sources: Vec<String> = match &self.source_root {
823 Some(root) if !root.is_empty() => {
824 self.sources.iter().map(|s| format!("{root}{s}")).collect()
825 }
826 _ => self.sources.clone(),
827 };
828
829 srcmap_sourcemap::SourceMap::from_parts(
830 self.file.clone(),
831 self.source_root.clone(),
832 sources,
833 sources_content,
834 self.names.clone(),
835 sm_mappings,
836 self.ignore_list.clone(),
837 self.debug_id.clone(),
838 None, )
840 }
841
842 pub fn into_parts(self) -> SourceMapParts {
848 let mappings = self.encode_mappings();
849 let range_mappings = self.encode_range_mappings();
850
851 SourceMapParts {
852 file: self.file,
853 mappings,
854 sources: self.sources,
855 names: self.names,
856 sources_content: self.sources_content,
857 ignore_list: self.ignore_list,
858 debug_id: self.debug_id,
859 source_root: self.source_root,
860 range_mappings,
861 }
862 }
863
864 pub fn to_writer(&self, writer: &mut impl io::Write) -> io::Result<()> {
869 let (scopes_str, names_for_json) = if let Some(ref scopes_info) = self.scopes {
871 let mut names = self.names.clone();
872 let s = srcmap_scopes::encode_scopes(scopes_info, &mut names);
873 (Some(s), std::borrow::Cow::Owned(names))
874 } else {
875 (None, std::borrow::Cow::Borrowed(&self.names))
876 };
877
878 writer.write_all(br#"{"version":3"#)?;
879
880 if let Some(ref file) = self.file {
881 writer.write_all(br#","file":"#)?;
882 write_json_quoted(writer, file)?;
883 }
884
885 if let Some(ref root) = self.source_root {
886 writer.write_all(br#","sourceRoot":"#)?;
887 write_json_quoted(writer, root)?;
888 }
889
890 writer.write_all(br#","sources":["#)?;
892 for (i, s) in self.sources.iter().enumerate() {
893 if i > 0 {
894 writer.write_all(b",")?;
895 }
896 write_json_quoted(writer, s)?;
897 }
898 writer.write_all(b"]")?;
899
900 if self.sources_content.iter().any(|c| c.is_some()) {
902 writer.write_all(br#","sourcesContent":["#)?;
903 for (i, c) in self.sources_content.iter().enumerate() {
904 if i > 0 {
905 writer.write_all(b",")?;
906 }
907 match c {
908 Some(content) => write_json_quoted(writer, content)?,
909 None => writer.write_all(b"null")?,
910 }
911 }
912 writer.write_all(b"]")?;
913 }
914
915 writer.write_all(br#","names":["#)?;
917 for (i, n) in names_for_json.iter().enumerate() {
918 if i > 0 {
919 writer.write_all(b",")?;
920 }
921 write_json_quoted(writer, n)?;
922 }
923 writer.write_all(b"]")?;
924
925 writer.write_all(br#","mappings":""#)?;
927 let mut vlq_buf: Vec<u8> = Vec::new();
928 self.encode_mappings_into(&mut vlq_buf);
929 writer.write_all(&vlq_buf)?;
930 writer.write_all(b"\"")?;
931
932 if !self.ignore_list.is_empty() {
934 writer.write_all(br#","ignoreList":["#)?;
935 for (i, &idx) in self.ignore_list.iter().enumerate() {
936 if i > 0 {
937 writer.write_all(b",")?;
938 }
939 write!(writer, "{idx}")?;
940 }
941 writer.write_all(b"]")?;
942 }
943
944 if let Some(ref range_mappings) = self.encode_range_mappings() {
946 writer.write_all(br#","rangeMappings":""#)?;
947 writer.write_all(range_mappings.as_bytes())?;
948 writer.write_all(b"\"")?;
949 }
950
951 if let Some(ref id) = self.debug_id {
953 writer.write_all(br#","debugId":"#)?;
954 write_json_quoted(writer, id)?;
955 }
956
957 if let Some(ref s) = scopes_str {
959 writer.write_all(br#","scopes":"#)?;
960 write_json_quoted(writer, s)?;
961 }
962
963 writer.write_all(b"}")?;
964 Ok(())
965 }
966}
967
968#[derive(Debug)]
994pub struct StreamingGenerator {
995 file: Option<String>,
996 source_root: Option<String>,
997 sources: Vec<String>,
998 sources_content: Vec<Option<String>>,
999 names: Vec<String>,
1000 ignore_list: Vec<u32>,
1001 debug_id: Option<String>,
1002
1003 source_map: FxHashMap<String, u32>,
1005 name_map: FxHashMap<String, u32>,
1006
1007 vlq_out: Vec<u8>,
1009 prev_gen_line: u32,
1010 prev_gen_col: i64,
1011 prev_source: i64,
1012 prev_orig_line: i64,
1013 prev_orig_col: i64,
1014 prev_name: i64,
1015 first_in_line: bool,
1016 mapping_count: usize,
1017
1018 line_local_index: u32,
1020 range_entries: Vec<(u32, u32)>,
1021}
1022
1023impl StreamingGenerator {
1024 pub fn new(file: Option<String>) -> Self {
1026 Self {
1027 file,
1028 source_root: None,
1029 sources: Vec::new(),
1030 sources_content: Vec::new(),
1031 names: Vec::new(),
1032 ignore_list: Vec::new(),
1033 debug_id: None,
1034 source_map: FxHashMap::default(),
1035 name_map: FxHashMap::default(),
1036 vlq_out: Vec::with_capacity(1024),
1037 prev_gen_line: 0,
1038 prev_gen_col: 0,
1039 prev_source: 0,
1040 prev_orig_line: 0,
1041 prev_orig_col: 0,
1042 prev_name: 0,
1043 first_in_line: true,
1044 mapping_count: 0,
1045 line_local_index: 0,
1046 range_entries: Vec::new(),
1047 }
1048 }
1049
1050 pub fn with_capacity(file: Option<String>, vlq_capacity: usize) -> Self {
1052 Self {
1053 file,
1054 source_root: None,
1055 sources: Vec::new(),
1056 sources_content: Vec::new(),
1057 names: Vec::new(),
1058 ignore_list: Vec::new(),
1059 debug_id: None,
1060 source_map: FxHashMap::default(),
1061 name_map: FxHashMap::default(),
1062 vlq_out: Vec::with_capacity(vlq_capacity),
1063 prev_gen_line: 0,
1064 prev_gen_col: 0,
1065 prev_source: 0,
1066 prev_orig_line: 0,
1067 prev_orig_col: 0,
1068 prev_name: 0,
1069 first_in_line: true,
1070 mapping_count: 0,
1071 line_local_index: 0,
1072 range_entries: Vec::new(),
1073 }
1074 }
1075
1076 pub fn set_source_root(&mut self, root: impl Into<String>) {
1078 self.source_root = Some(root.into());
1079 }
1080
1081 pub fn set_debug_id(&mut self, id: impl Into<String>) {
1083 self.debug_id = Some(id.into());
1084 }
1085
1086 #[inline]
1088 pub fn add_source(&mut self, source: &str) -> u32 {
1089 if let Some(&idx) = self.source_map.get(source) {
1090 return idx;
1091 }
1092 let idx = self.sources.len() as u32;
1093 self.sources.push(source.to_string());
1094 self.sources_content.push(None);
1095 self.source_map.insert(source.to_string(), idx);
1096 idx
1097 }
1098
1099 pub fn set_source_content(&mut self, source_idx: u32, content: impl Into<String>) {
1101 if (source_idx as usize) < self.sources_content.len() {
1102 self.sources_content[source_idx as usize] = Some(content.into());
1103 }
1104 }
1105
1106 #[inline]
1108 pub fn add_name(&mut self, name: &str) -> u32 {
1109 if let Some(&idx) = self.name_map.get(name) {
1110 return idx;
1111 }
1112 let idx = self.names.len() as u32;
1113 self.names.push(name.to_string());
1114 self.name_map.insert(name.to_string(), idx);
1115 idx
1116 }
1117
1118 pub fn add_to_ignore_list(&mut self, source_idx: u32) {
1120 if !self.ignore_list.contains(&source_idx) {
1121 self.ignore_list.push(source_idx);
1122 }
1123 }
1124
1125 #[inline]
1129 pub fn add_generated_mapping(&mut self, generated_line: u32, generated_column: u32) {
1130 self.advance_to_line(generated_line);
1131
1132 self.vlq_out.reserve(8);
1134
1135 if !self.first_in_line {
1136 self.vlq_out.push(b',');
1137 }
1138 self.first_in_line = false;
1139
1140 unsafe {
1142 vlq_encode_unchecked(&mut self.vlq_out, generated_column as i64 - self.prev_gen_col);
1143 }
1144 self.prev_gen_col = generated_column as i64;
1145 self.line_local_index += 1;
1146 self.mapping_count += 1;
1147 }
1148
1149 #[inline]
1153 pub fn add_mapping(
1154 &mut self,
1155 generated_line: u32,
1156 generated_column: u32,
1157 source: u32,
1158 original_line: u32,
1159 original_column: u32,
1160 ) {
1161 self.advance_to_line(generated_line);
1162
1163 self.vlq_out.reserve(29);
1165
1166 if !self.first_in_line {
1167 self.vlq_out.push(b',');
1168 }
1169 self.first_in_line = false;
1170
1171 unsafe {
1173 vlq_encode_unchecked(&mut self.vlq_out, generated_column as i64 - self.prev_gen_col);
1174 self.prev_gen_col = generated_column as i64;
1175
1176 vlq_encode_unchecked(&mut self.vlq_out, source as i64 - self.prev_source);
1177 self.prev_source = source as i64;
1178
1179 vlq_encode_unchecked(&mut self.vlq_out, original_line as i64 - self.prev_orig_line);
1180 self.prev_orig_line = original_line as i64;
1181
1182 vlq_encode_unchecked(&mut self.vlq_out, original_column as i64 - self.prev_orig_col);
1183 self.prev_orig_col = original_column as i64;
1184 }
1185
1186 self.line_local_index += 1;
1187 self.mapping_count += 1;
1188 }
1189
1190 #[inline]
1195 pub fn add_range_mapping(
1196 &mut self,
1197 generated_line: u32,
1198 generated_column: u32,
1199 source: u32,
1200 original_line: u32,
1201 original_column: u32,
1202 ) {
1203 self.advance_to_line(generated_line);
1204 self.range_entries.push((self.prev_gen_line, self.line_local_index));
1205
1206 self.vlq_out.reserve(29);
1208
1209 if !self.first_in_line {
1210 self.vlq_out.push(b',');
1211 }
1212 self.first_in_line = false;
1213
1214 unsafe {
1216 vlq_encode_unchecked(&mut self.vlq_out, generated_column as i64 - self.prev_gen_col);
1217 self.prev_gen_col = generated_column as i64;
1218
1219 vlq_encode_unchecked(&mut self.vlq_out, source as i64 - self.prev_source);
1220 self.prev_source = source as i64;
1221
1222 vlq_encode_unchecked(&mut self.vlq_out, original_line as i64 - self.prev_orig_line);
1223 self.prev_orig_line = original_line as i64;
1224
1225 vlq_encode_unchecked(&mut self.vlq_out, original_column as i64 - self.prev_orig_col);
1226 self.prev_orig_col = original_column as i64;
1227 }
1228
1229 self.line_local_index += 1;
1230 self.mapping_count += 1;
1231 }
1232
1233 #[inline]
1237 pub fn add_named_mapping(
1238 &mut self,
1239 generated_line: u32,
1240 generated_column: u32,
1241 source: u32,
1242 original_line: u32,
1243 original_column: u32,
1244 name: u32,
1245 ) {
1246 self.advance_to_line(generated_line);
1247
1248 self.vlq_out.reserve(36);
1250
1251 if !self.first_in_line {
1252 self.vlq_out.push(b',');
1253 }
1254 self.first_in_line = false;
1255
1256 unsafe {
1258 vlq_encode_unchecked(&mut self.vlq_out, generated_column as i64 - self.prev_gen_col);
1259 self.prev_gen_col = generated_column as i64;
1260
1261 vlq_encode_unchecked(&mut self.vlq_out, source as i64 - self.prev_source);
1262 self.prev_source = source as i64;
1263
1264 vlq_encode_unchecked(&mut self.vlq_out, original_line as i64 - self.prev_orig_line);
1265 self.prev_orig_line = original_line as i64;
1266
1267 vlq_encode_unchecked(&mut self.vlq_out, original_column as i64 - self.prev_orig_col);
1268 self.prev_orig_col = original_column as i64;
1269
1270 vlq_encode_unchecked(&mut self.vlq_out, name as i64 - self.prev_name);
1271 self.prev_name = name as i64;
1272 }
1273
1274 self.line_local_index += 1;
1275 self.mapping_count += 1;
1276 }
1277
1278 #[inline]
1284 pub fn add_named_range_mapping(
1285 &mut self,
1286 generated_line: u32,
1287 generated_column: u32,
1288 source: u32,
1289 original_line: u32,
1290 original_column: u32,
1291 name: u32,
1292 ) {
1293 self.advance_to_line(generated_line);
1294 self.range_entries.push((self.prev_gen_line, self.line_local_index));
1295
1296 self.vlq_out.reserve(36);
1298
1299 if !self.first_in_line {
1300 self.vlq_out.push(b',');
1301 }
1302 self.first_in_line = false;
1303
1304 unsafe {
1306 vlq_encode_unchecked(&mut self.vlq_out, generated_column as i64 - self.prev_gen_col);
1307 self.prev_gen_col = generated_column as i64;
1308
1309 vlq_encode_unchecked(&mut self.vlq_out, source as i64 - self.prev_source);
1310 self.prev_source = source as i64;
1311
1312 vlq_encode_unchecked(&mut self.vlq_out, original_line as i64 - self.prev_orig_line);
1313 self.prev_orig_line = original_line as i64;
1314
1315 vlq_encode_unchecked(&mut self.vlq_out, original_column as i64 - self.prev_orig_col);
1316 self.prev_orig_col = original_column as i64;
1317
1318 vlq_encode_unchecked(&mut self.vlq_out, name as i64 - self.prev_name);
1319 self.prev_name = name as i64;
1320 }
1321
1322 self.line_local_index += 1;
1323 self.mapping_count += 1;
1324 }
1325
1326 pub fn mapping_count(&self) -> usize {
1328 self.mapping_count
1329 }
1330
1331 #[inline]
1333 fn advance_to_line(&mut self, generated_line: u32) {
1334 while self.prev_gen_line < generated_line {
1335 self.vlq_out.push(b';');
1336 self.prev_gen_line += 1;
1337 self.prev_gen_col = 0;
1338 self.first_in_line = true;
1339 self.line_local_index = 0;
1340 }
1341 }
1342
1343 pub fn to_json(&self) -> String {
1345 use std::io::Write;
1346
1347 let vlq = self.vlq_str();
1348
1349 let sources_size: usize = self.sources.iter().map(|s| s.len() + 4).sum();
1351 let names_size: usize = self.names.iter().map(|n| n.len() + 4).sum();
1352 let content_size: usize =
1353 self.sources_content.iter().map(|c| c.as_ref().map_or(5, |s| s.len() + 4)).sum();
1354 let capacity = 100 + sources_size + names_size + vlq.len() + content_size;
1355
1356 let mut json: Vec<u8> = Vec::with_capacity(capacity);
1357 json.extend_from_slice(br#"{"version":3"#);
1358
1359 if let Some(ref file) = self.file {
1360 json.extend_from_slice(br#","file":"#);
1361 json_quote_into(&mut json, file);
1362 }
1363
1364 if let Some(ref root) = self.source_root {
1365 json.extend_from_slice(br#","sourceRoot":"#);
1366 json_quote_into(&mut json, root);
1367 }
1368
1369 json.extend_from_slice(br#","sources":["#);
1370 for (i, s) in self.sources.iter().enumerate() {
1371 if i > 0 {
1372 json.push(b',');
1373 }
1374 json_quote_into(&mut json, s);
1375 }
1376 json.push(b']');
1377
1378 if self.sources_content.iter().any(|c| c.is_some()) {
1379 json.extend_from_slice(br#","sourcesContent":["#);
1380 for (i, c) in self.sources_content.iter().enumerate() {
1381 if i > 0 {
1382 json.push(b',');
1383 }
1384 match c {
1385 Some(content) => json_quote_into(&mut json, content),
1386 None => json.extend_from_slice(b"null"),
1387 }
1388 }
1389 json.push(b']');
1390 }
1391
1392 json.extend_from_slice(br#","names":["#);
1393 for (i, n) in self.names.iter().enumerate() {
1394 if i > 0 {
1395 json.push(b',');
1396 }
1397 json_quote_into(&mut json, n);
1398 }
1399 json.push(b']');
1400
1401 json.extend_from_slice(br#","mappings":""#);
1403 json.extend_from_slice(vlq.as_bytes());
1404 json.push(b'"');
1405
1406 if !self.ignore_list.is_empty() {
1407 json.extend_from_slice(br#","ignoreList":["#);
1408 for (i, &idx) in self.ignore_list.iter().enumerate() {
1409 if i > 0 {
1410 json.push(b',');
1411 }
1412 let _ = write!(json, "{idx}");
1413 }
1414 json.push(b']');
1415 }
1416
1417 if let Some(ref range_mappings) = self.encode_range_mappings() {
1418 json.extend_from_slice(br#","rangeMappings":""#);
1419 json.extend_from_slice(range_mappings.as_bytes());
1420 json.push(b'"');
1421 }
1422
1423 if let Some(ref id) = self.debug_id {
1424 json.extend_from_slice(br#","debugId":"#);
1425 json_quote_into(&mut json, id);
1426 }
1427
1428 json.push(b'}');
1429
1430 unsafe { String::from_utf8_unchecked(json) }
1433 }
1434
1435 pub fn to_decoded_map(
1447 &self,
1448 ) -> Result<srcmap_sourcemap::SourceMap, srcmap_sourcemap::ParseError> {
1449 let vlq = self.vlq_str();
1450 let range_mappings = self.encode_range_mappings();
1451
1452 let sources: Vec<String> = match &self.source_root {
1453 Some(root) if !root.is_empty() => {
1454 self.sources.iter().map(|s| format!("{root}{s}")).collect()
1455 }
1456 _ => self.sources.clone(),
1457 };
1458
1459 srcmap_sourcemap::SourceMap::from_vlq_with_range_mappings(
1460 vlq,
1461 sources,
1462 self.names.clone(),
1463 self.file.clone(),
1464 self.source_root.clone(),
1465 self.sources_content.clone(),
1466 self.ignore_list.clone(),
1467 self.debug_id.clone(),
1468 range_mappings.as_deref(),
1469 )
1470 }
1471
1472 fn encode_range_mappings(&self) -> Option<String> {
1475 if self.range_entries.is_empty() {
1476 return None;
1477 }
1478
1479 let max_line = self.range_entries.last().map_or(0, |&(line, _)| line);
1480 let mut out: Vec<u8> = Vec::new();
1481 let mut entry_idx = 0;
1482
1483 for line in 0..=max_line {
1484 if line > 0 {
1485 out.push(b';');
1486 }
1487 let mut prev_offset: u64 = 0;
1488 let mut first_on_line = true;
1489
1490 while entry_idx < self.range_entries.len() && self.range_entries[entry_idx].0 == line {
1491 if !first_on_line {
1492 out.push(b',');
1493 }
1494 first_on_line = false;
1495 let local_idx = self.range_entries[entry_idx].1 as u64;
1496 let delta = local_idx - prev_offset;
1497 vlq_encode_unsigned(&mut out, delta);
1498 prev_offset = local_idx;
1499 entry_idx += 1;
1500 }
1501 }
1502
1503 while out.last() == Some(&b';') {
1504 out.pop();
1505 }
1506
1507 if out.is_empty() {
1508 return None;
1509 }
1510
1511 Some(unsafe { String::from_utf8_unchecked(out) })
1513 }
1514
1515 fn vlq_str(&self) -> &str {
1517 let end = self.vlq_out.iter().rposition(|&b| b != b';').map_or(0, |i| i + 1);
1518 unsafe { std::str::from_utf8_unchecked(&self.vlq_out[..end]) }
1520 }
1521
1522 pub fn into_parts(self) -> SourceMapParts {
1528 let range_mappings = self.encode_range_mappings();
1529 let end = self.vlq_out.iter().rposition(|&b| b != b';').map_or(0, |i| i + 1);
1531 let mappings = unsafe { String::from_utf8_unchecked(self.vlq_out[..end].to_vec()) };
1533
1534 SourceMapParts {
1535 file: self.file,
1536 mappings,
1537 sources: self.sources,
1538 names: self.names,
1539 sources_content: self.sources_content,
1540 ignore_list: self.ignore_list,
1541 debug_id: self.debug_id,
1542 source_root: self.source_root,
1543 range_mappings,
1544 }
1545 }
1546
1547 pub fn to_writer(&self, writer: &mut impl io::Write) -> io::Result<()> {
1552 let vlq = self.vlq_str();
1553
1554 writer.write_all(br#"{"version":3"#)?;
1555
1556 if let Some(ref file) = self.file {
1557 writer.write_all(br#","file":"#)?;
1558 write_json_quoted(writer, file)?;
1559 }
1560
1561 if let Some(ref root) = self.source_root {
1562 writer.write_all(br#","sourceRoot":"#)?;
1563 write_json_quoted(writer, root)?;
1564 }
1565
1566 writer.write_all(br#","sources":["#)?;
1568 for (i, s) in self.sources.iter().enumerate() {
1569 if i > 0 {
1570 writer.write_all(b",")?;
1571 }
1572 write_json_quoted(writer, s)?;
1573 }
1574 writer.write_all(b"]")?;
1575
1576 if self.sources_content.iter().any(|c| c.is_some()) {
1578 writer.write_all(br#","sourcesContent":["#)?;
1579 for (i, c) in self.sources_content.iter().enumerate() {
1580 if i > 0 {
1581 writer.write_all(b",")?;
1582 }
1583 match c {
1584 Some(content) => write_json_quoted(writer, content)?,
1585 None => writer.write_all(b"null")?,
1586 }
1587 }
1588 writer.write_all(b"]")?;
1589 }
1590
1591 writer.write_all(br#","names":["#)?;
1593 for (i, n) in self.names.iter().enumerate() {
1594 if i > 0 {
1595 writer.write_all(b",")?;
1596 }
1597 write_json_quoted(writer, n)?;
1598 }
1599 writer.write_all(b"]")?;
1600
1601 writer.write_all(br#","mappings":""#)?;
1603 writer.write_all(vlq.as_bytes())?;
1604 writer.write_all(b"\"")?;
1605
1606 if !self.ignore_list.is_empty() {
1608 writer.write_all(br#","ignoreList":["#)?;
1609 for (i, &idx) in self.ignore_list.iter().enumerate() {
1610 if i > 0 {
1611 writer.write_all(b",")?;
1612 }
1613 write!(writer, "{idx}")?;
1614 }
1615 writer.write_all(b"]")?;
1616 }
1617
1618 if let Some(ref range_mappings) = self.encode_range_mappings() {
1620 writer.write_all(br#","rangeMappings":""#)?;
1621 writer.write_all(range_mappings.as_bytes())?;
1622 writer.write_all(b"\"")?;
1623 }
1624
1625 if let Some(ref id) = self.debug_id {
1627 writer.write_all(br#","debugId":"#)?;
1628 write_json_quoted(writer, id)?;
1629 }
1630
1631 writer.write_all(b"}")?;
1632 Ok(())
1633 }
1634}
1635
1636#[cfg(feature = "parallel")]
1641fn encode_mapping_slice(
1642 mappings: &[&Mapping],
1643 init_source: i64,
1644 init_orig_line: i64,
1645 init_orig_col: i64,
1646 init_name: i64,
1647) -> Vec<u8> {
1648 let mut buf = Vec::with_capacity(mappings.len() * 36);
1650 let mut prev_gen_col: i64 = 0;
1651 let mut prev_source = init_source;
1652 let mut prev_orig_line = init_orig_line;
1653 let mut prev_orig_col = init_orig_col;
1654 let mut prev_name = init_name;
1655 let mut first = true;
1656
1657 for m in mappings {
1658 if !first {
1659 buf.push(b',');
1660 }
1661 first = false;
1662
1663 unsafe {
1666 vlq_encode_unchecked(&mut buf, m.generated_column as i64 - prev_gen_col);
1667 prev_gen_col = m.generated_column as i64;
1668
1669 if let Some(source) = m.source {
1670 vlq_encode_unchecked(&mut buf, source as i64 - prev_source);
1671 prev_source = source as i64;
1672
1673 vlq_encode_unchecked(&mut buf, m.original_line as i64 - prev_orig_line);
1674 prev_orig_line = m.original_line as i64;
1675
1676 vlq_encode_unchecked(&mut buf, m.original_column as i64 - prev_orig_col);
1677 prev_orig_col = m.original_column as i64;
1678
1679 if let Some(name) = m.name {
1680 vlq_encode_unchecked(&mut buf, name as i64 - prev_name);
1681 prev_name = name as i64;
1682 }
1683 }
1684 }
1685 }
1686
1687 buf
1688}
1689
1690fn json_quote_into(out: &mut Vec<u8>, s: &str) {
1692 let bytes = s.as_bytes();
1693 out.push(b'"');
1694
1695 let mut start = 0;
1696 for (i, &b) in bytes.iter().enumerate() {
1697 let escape: &[u8] = match b {
1698 b'"' => b"\\\"",
1699 b'\\' => b"\\\\",
1700 b'\n' => b"\\n",
1701 b'\r' => b"\\r",
1702 b'\t' => b"\\t",
1703 0x00..=0x1f => {
1704 out.extend_from_slice(&bytes[start..i]);
1705 let hex = b"0123456789abcdef";
1706 out.extend_from_slice(&[
1707 b'\\',
1708 b'u',
1709 b'0',
1710 b'0',
1711 hex[(b >> 4) as usize],
1712 hex[(b & 0xf) as usize],
1713 ]);
1714 start = i + 1;
1715 continue;
1716 }
1717 _ => continue,
1718 };
1719 out.extend_from_slice(&bytes[start..i]);
1720 out.extend_from_slice(escape);
1721 start = i + 1;
1722 }
1723
1724 out.extend_from_slice(&bytes[start..]);
1725 out.push(b'"');
1726}
1727
1728#[cfg(feature = "parallel")]
1730fn json_quote(s: &str) -> String {
1731 let mut out = Vec::with_capacity(s.len() + 2);
1732 json_quote_into(&mut out, s);
1733 unsafe { String::from_utf8_unchecked(out) }
1735}
1736
1737fn write_json_quoted(writer: &mut impl io::Write, s: &str) -> io::Result<()> {
1739 let mut buf = Vec::with_capacity(s.len() + 2);
1740 json_quote_into(&mut buf, s);
1741 writer.write_all(&buf)
1742}
1743
1744#[cfg(test)]
1747mod tests {
1748 use super::*;
1749
1750 #[test]
1751 fn empty_generator() {
1752 let builder = SourceMapGenerator::new(None);
1753 let json = builder.to_json();
1754 assert!(json.contains(r#""version":3"#));
1755 assert!(json.contains(r#""mappings":"""#));
1756 }
1757
1758 #[test]
1759 fn simple_mapping() {
1760 let mut builder = SourceMapGenerator::new(Some("output.js".to_string()));
1761 let src = builder.add_source("input.js");
1762 builder.add_mapping(0, 0, src, 0, 0);
1763
1764 let json = builder.to_json();
1765 assert!(json.contains(r#""file":"output.js""#));
1766 assert!(json.contains(r#""sources":["input.js"]"#));
1767
1768 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1770 let loc = sm.original_position_for(0, 0).unwrap();
1771 assert_eq!(sm.source(loc.source), "input.js");
1772 assert_eq!(loc.line, 0);
1773 assert_eq!(loc.column, 0);
1774 }
1775
1776 #[test]
1777 fn mapping_with_name() {
1778 let mut builder = SourceMapGenerator::new(None);
1779 let src = builder.add_source("input.js");
1780 let name = builder.add_name("myFunction");
1781 builder.add_named_mapping(0, 0, src, 0, 0, name);
1782
1783 let json = builder.to_json();
1784 assert!(json.contains(r#""names":["myFunction"]"#));
1785
1786 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1787 let loc = sm.original_position_for(0, 0).unwrap();
1788 assert_eq!(loc.name, Some(0));
1789 assert_eq!(sm.name(0), "myFunction");
1790 }
1791
1792 #[test]
1793 fn multiple_lines() {
1794 let mut builder = SourceMapGenerator::new(None);
1795 let src = builder.add_source("input.js");
1796 builder.add_mapping(0, 0, src, 0, 0);
1797 builder.add_mapping(1, 4, src, 1, 2);
1798 builder.add_mapping(2, 0, src, 2, 0);
1799
1800 let json = builder.to_json();
1801 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1802 assert_eq!(sm.line_count(), 3);
1803
1804 let loc = sm.original_position_for(1, 4).unwrap();
1805 assert_eq!(loc.line, 1);
1806 assert_eq!(loc.column, 2);
1807 }
1808
1809 #[test]
1810 fn multiple_sources() {
1811 let mut builder = SourceMapGenerator::new(None);
1812 let a = builder.add_source("a.js");
1813 let b = builder.add_source("b.js");
1814 builder.add_mapping(0, 0, a, 0, 0);
1815 builder.add_mapping(1, 0, b, 0, 0);
1816
1817 let json = builder.to_json();
1818 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1819
1820 let loc0 = sm.original_position_for(0, 0).unwrap();
1821 let loc1 = sm.original_position_for(1, 0).unwrap();
1822 assert_eq!(sm.source(loc0.source), "a.js");
1823 assert_eq!(sm.source(loc1.source), "b.js");
1824 }
1825
1826 #[test]
1827 fn source_content() {
1828 let mut builder = SourceMapGenerator::new(None);
1829 let src = builder.add_source("input.js");
1830 builder.set_source_content(src, "var x = 1;".to_string());
1831 builder.add_mapping(0, 0, src, 0, 0);
1832
1833 let json = builder.to_json();
1834 assert!(json.contains(r#""sourcesContent":["var x = 1;"]"#));
1835
1836 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1837 assert_eq!(sm.sources_content[0], Some("var x = 1;".to_string()));
1838 }
1839
1840 #[test]
1841 fn source_root() {
1842 let mut builder = SourceMapGenerator::new(None);
1843 builder.set_source_root("src/".to_string());
1844 let src = builder.add_source("input.js");
1845 builder.add_mapping(0, 0, src, 0, 0);
1846
1847 let json = builder.to_json();
1848 assert!(json.contains(r#""sourceRoot":"src/""#));
1849 }
1850
1851 #[test]
1852 fn ignore_list() {
1853 let mut builder = SourceMapGenerator::new(None);
1854 let _app = builder.add_source("app.js");
1855 let lib = builder.add_source("node_modules/lib.js");
1856 builder.add_to_ignore_list(lib);
1857 builder.add_mapping(0, 0, lib, 0, 0);
1858
1859 let json = builder.to_json();
1860 assert!(json.contains(r#""ignoreList":[1]"#));
1861 }
1862
1863 #[test]
1864 fn generated_only_mapping() {
1865 let mut builder = SourceMapGenerator::new(None);
1866 builder.add_generated_mapping(0, 0);
1867
1868 let json = builder.to_json();
1869 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1870 assert!(sm.original_position_for(0, 0).is_none());
1872 }
1873
1874 #[test]
1875 fn dedup_sources_and_names() {
1876 let mut builder = SourceMapGenerator::new(None);
1877 let s1 = builder.add_source("input.js");
1878 let s2 = builder.add_source("input.js"); assert_eq!(s1, s2);
1880
1881 let n1 = builder.add_name("foo");
1882 let n2 = builder.add_name("foo"); assert_eq!(n1, n2);
1884
1885 assert_eq!(builder.sources.len(), 1);
1886 assert_eq!(builder.names.len(), 1);
1887 }
1888
1889 #[test]
1890 fn large_roundtrip() {
1891 let mut builder = SourceMapGenerator::new(Some("bundle.js".to_string()));
1892
1893 for i in 0..5 {
1894 builder.add_source(&format!("src/file{i}.js"));
1895 }
1896 for i in 0..10 {
1897 builder.add_name(&format!("var{i}"));
1898 }
1899
1900 for line in 0..100u32 {
1902 for col in 0..10u32 {
1903 let src = (line * 10 + col) % 5;
1904 let name = if col % 3 == 0 { Some(col % 10) } else { None };
1905
1906 match name {
1907 Some(n) => builder.add_named_mapping(line, col * 10, src, line, col * 5, n),
1908 None => builder.add_mapping(line, col * 10, src, line, col * 5),
1909 }
1910 }
1911 }
1912
1913 let json = builder.to_json();
1914 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1915
1916 assert_eq!(sm.mapping_count(), 1000);
1917 assert_eq!(sm.line_count(), 100);
1918
1919 let loc = sm.original_position_for(50, 30).unwrap();
1921 assert_eq!(loc.line, 50);
1922 assert_eq!(loc.column, 15);
1923 }
1924
1925 #[test]
1926 fn json_escaping() {
1927 let mut builder = SourceMapGenerator::new(None);
1928 let src = builder.add_source("path/with\"quotes.js");
1929 builder.set_source_content(src, "line1\nline2\ttab".to_string());
1930 builder.add_mapping(0, 0, src, 0, 0);
1931
1932 let json = builder.to_json();
1933 let _: serde_json::Value = serde_json::from_str(&json).unwrap();
1935 }
1936
1937 #[test]
1938 fn maybe_add_mapping_skips_redundant() {
1939 let mut builder = SourceMapGenerator::new(None);
1940 let src = builder.add_source("input.js");
1941
1942 assert!(builder.maybe_add_mapping(0, 0, src, 10, 0));
1944 assert!(!builder.maybe_add_mapping(0, 5, src, 10, 0));
1946 assert!(builder.maybe_add_mapping(0, 10, src, 11, 0));
1948 assert!(builder.maybe_add_mapping(1, 0, src, 11, 0));
1950
1951 assert_eq!(builder.mapping_count(), 3);
1952
1953 let json = builder.to_json();
1954 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1955 assert_eq!(sm.mapping_count(), 3);
1956 }
1957
1958 #[test]
1959 fn maybe_add_mapping_different_source() {
1960 let mut builder = SourceMapGenerator::new(None);
1961 let a = builder.add_source("a.js");
1962 let b = builder.add_source("b.js");
1963
1964 assert!(builder.maybe_add_mapping(0, 0, a, 0, 0));
1965 assert!(builder.maybe_add_mapping(0, 5, b, 0, 0));
1967
1968 assert_eq!(builder.mapping_count(), 2);
1969 }
1970
1971 #[test]
1972 fn to_decoded_map_basic() {
1973 let mut builder = SourceMapGenerator::new(Some("output.js".to_string()));
1974 let src = builder.add_source("input.js");
1975 builder.add_mapping(0, 0, src, 0, 0);
1976 builder.add_mapping(1, 4, src, 1, 2);
1977
1978 let sm = builder.to_decoded_map();
1979 assert_eq!(sm.mapping_count(), 2);
1980 assert_eq!(sm.line_count(), 2);
1981
1982 let loc = sm.original_position_for(0, 0).unwrap();
1983 assert_eq!(sm.source(loc.source), "input.js");
1984 assert_eq!(loc.line, 0);
1985 assert_eq!(loc.column, 0);
1986
1987 let loc = sm.original_position_for(1, 4).unwrap();
1988 assert_eq!(loc.line, 1);
1989 assert_eq!(loc.column, 2);
1990 }
1991
1992 #[test]
1993 fn to_decoded_map_with_names() {
1994 let mut builder = SourceMapGenerator::new(None);
1995 let src = builder.add_source("input.js");
1996 let name = builder.add_name("myFunction");
1997 builder.add_named_mapping(0, 0, src, 0, 0, name);
1998
1999 let sm = builder.to_decoded_map();
2000 let loc = sm.original_position_for(0, 0).unwrap();
2001 assert_eq!(loc.name, Some(0));
2002 assert_eq!(sm.name(0), "myFunction");
2003 }
2004
2005 #[test]
2006 fn to_decoded_map_matches_json_roundtrip() {
2007 let mut builder = SourceMapGenerator::new(Some("bundle.js".to_string()));
2008 for i in 0..5 {
2009 builder.add_source(&format!("src/file{i}.js"));
2010 }
2011 for i in 0..10 {
2012 builder.add_name(&format!("var{i}"));
2013 }
2014
2015 for line in 0..50u32 {
2016 for col in 0..10u32 {
2017 let src = (line * 10 + col) % 5;
2018 let name = if col % 3 == 0 { Some(col % 10) } else { None };
2019 match name {
2020 Some(n) => builder.add_named_mapping(line, col * 10, src, line, col * 5, n),
2021 None => builder.add_mapping(line, col * 10, src, line, col * 5),
2022 }
2023 }
2024 }
2025
2026 let sm_decoded = builder.to_decoded_map();
2028 let json = builder.to_json();
2029 let sm_json = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2030
2031 assert_eq!(sm_decoded.mapping_count(), sm_json.mapping_count());
2032 assert_eq!(sm_decoded.line_count(), sm_json.line_count());
2033
2034 for m in sm_json.all_mappings() {
2036 let a = sm_json.original_position_for(m.generated_line, m.generated_column);
2037 let b = sm_decoded.original_position_for(m.generated_line, m.generated_column);
2038 match (a, b) {
2039 (Some(a), Some(b)) => {
2040 assert_eq!(
2041 a.source, b.source,
2042 "source mismatch at ({}, {})",
2043 m.generated_line, m.generated_column
2044 );
2045 assert_eq!(
2046 a.line, b.line,
2047 "line mismatch at ({}, {})",
2048 m.generated_line, m.generated_column
2049 );
2050 assert_eq!(
2051 a.column, b.column,
2052 "column mismatch at ({}, {})",
2053 m.generated_line, m.generated_column
2054 );
2055 assert_eq!(
2056 a.name, b.name,
2057 "name mismatch at ({}, {})",
2058 m.generated_line, m.generated_column
2059 );
2060 }
2061 (None, None) => {}
2062 _ => panic!("lookup mismatch at ({}, {})", m.generated_line, m.generated_column),
2063 }
2064 }
2065 }
2066
2067 #[test]
2068 fn to_decoded_map_empty() {
2069 let builder = SourceMapGenerator::new(None);
2070 let sm = builder.to_decoded_map();
2071 assert_eq!(sm.mapping_count(), 0);
2072 assert_eq!(sm.line_count(), 0);
2073 }
2074
2075 #[test]
2076 fn to_decoded_map_generated_only() {
2077 let mut builder = SourceMapGenerator::new(None);
2078 builder.add_generated_mapping(0, 0);
2079
2080 let sm = builder.to_decoded_map();
2081 assert_eq!(sm.mapping_count(), 1);
2082 assert!(sm.original_position_for(0, 0).is_none());
2084 }
2085
2086 #[test]
2087 fn to_decoded_map_multiple_sources() {
2088 let mut builder = SourceMapGenerator::new(None);
2089 let a = builder.add_source("a.js");
2090 let b = builder.add_source("b.js");
2091 builder.add_mapping(0, 0, a, 0, 0);
2092 builder.add_mapping(1, 0, b, 0, 0);
2093
2094 let sm = builder.to_decoded_map();
2095 let loc0 = sm.original_position_for(0, 0).unwrap();
2096 let loc1 = sm.original_position_for(1, 0).unwrap();
2097 assert_eq!(sm.source(loc0.source), "a.js");
2098 assert_eq!(sm.source(loc1.source), "b.js");
2099 }
2100
2101 #[test]
2102 fn to_decoded_map_with_source_content() {
2103 let mut builder = SourceMapGenerator::new(None);
2104 let src = builder.add_source("input.js");
2105 builder.set_source_content(src, "var x = 1;".to_string());
2106 builder.add_mapping(0, 0, src, 0, 0);
2107
2108 let sm = builder.to_decoded_map();
2109 assert_eq!(sm.sources_content[0], Some("var x = 1;".to_string()));
2110 }
2111
2112 #[test]
2113 fn to_decoded_map_reverse_lookup() {
2114 let mut builder = SourceMapGenerator::new(None);
2115 let src = builder.add_source("input.js");
2116 builder.add_mapping(0, 0, src, 10, 5);
2117
2118 let sm = builder.to_decoded_map();
2119 let loc = sm.generated_position_for("input.js", 10, 5).unwrap();
2120 assert_eq!(loc.line, 0);
2121 assert_eq!(loc.column, 0);
2122 }
2123
2124 #[test]
2125 fn to_decoded_map_sparse_lines() {
2126 let mut builder = SourceMapGenerator::new(None);
2127 let src = builder.add_source("input.js");
2128 builder.add_mapping(0, 0, src, 0, 0);
2129 builder.add_mapping(5, 0, src, 5, 0);
2130
2131 let sm = builder.to_decoded_map();
2132 assert_eq!(sm.line_count(), 6);
2133 assert!(sm.original_position_for(0, 0).is_some());
2134 assert!(sm.original_position_for(2, 0).is_none());
2135 assert!(sm.original_position_for(5, 0).is_some());
2136 }
2137
2138 #[test]
2139 fn empty_lines_between_mappings() {
2140 let mut builder = SourceMapGenerator::new(None);
2141 let src = builder.add_source("input.js");
2142 builder.add_mapping(0, 0, src, 0, 0);
2143 builder.add_mapping(5, 0, src, 5, 0);
2145
2146 let json = builder.to_json();
2147 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2148
2149 assert!(sm.original_position_for(0, 0).is_some());
2151 assert!(sm.original_position_for(2, 0).is_none());
2153 assert!(sm.original_position_for(5, 0).is_some());
2155 }
2156
2157 #[test]
2158 fn debug_id() {
2159 let mut builder = SourceMapGenerator::new(None);
2160 builder.set_debug_id("85314830-023f-4cf1-a267-535f4e37bb17".to_string());
2161 let src = builder.add_source("input.js");
2162 builder.add_mapping(0, 0, src, 0, 0);
2163
2164 let json = builder.to_json();
2165 assert!(json.contains(r#""debugId":"85314830-023f-4cf1-a267-535f4e37bb17""#));
2166
2167 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2168 assert_eq!(sm.debug_id.as_deref(), Some("85314830-023f-4cf1-a267-535f4e37bb17"));
2169 }
2170
2171 #[test]
2172 fn scopes_roundtrip() {
2173 use srcmap_scopes::{Binding, GeneratedRange, OriginalScope, Position, ScopeInfo};
2174
2175 let mut builder = SourceMapGenerator::new(Some("bundle.js".to_string()));
2176 let src = builder.add_source("input.js");
2177 builder.set_source_content(
2178 src,
2179 "function hello(name) {\n return name;\n}\nhello('world');".to_string(),
2180 );
2181 let name_hello = builder.add_name("hello");
2182 builder.add_named_mapping(0, 0, src, 0, 0, name_hello);
2183 builder.add_mapping(1, 0, src, 1, 0);
2184
2185 builder.set_scopes(ScopeInfo {
2187 scopes: vec![Some(OriginalScope {
2188 start: Position { line: 0, column: 0 },
2189 end: Position { line: 3, column: 14 },
2190 name: None,
2191 kind: Some("global".to_string()),
2192 is_stack_frame: false,
2193 variables: vec!["hello".to_string()],
2194 children: vec![OriginalScope {
2195 start: Position { line: 0, column: 9 },
2196 end: Position { line: 2, column: 1 },
2197 name: Some("hello".to_string()),
2198 kind: Some("function".to_string()),
2199 is_stack_frame: true,
2200 variables: vec!["name".to_string()],
2201 children: vec![],
2202 }],
2203 })],
2204 ranges: vec![GeneratedRange {
2205 start: Position { line: 0, column: 0 },
2206 end: Position { line: 3, column: 14 },
2207 is_stack_frame: false,
2208 is_hidden: false,
2209 definition: Some(0),
2210 call_site: None,
2211 bindings: vec![Binding::Expression("hello".to_string())],
2212 children: vec![GeneratedRange {
2213 start: Position { line: 0, column: 9 },
2214 end: Position { line: 2, column: 1 },
2215 is_stack_frame: true,
2216 is_hidden: false,
2217 definition: Some(1),
2218 call_site: None,
2219 bindings: vec![Binding::Expression("name".to_string())],
2220 children: vec![],
2221 }],
2222 }],
2223 });
2224
2225 let json = builder.to_json();
2226
2227 assert!(json.contains(r#""scopes":"#));
2229
2230 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2232 assert!(sm.scopes.is_some());
2233
2234 let scopes_info = sm.scopes.unwrap();
2235
2236 assert_eq!(scopes_info.scopes.len(), 1);
2238 let root_scope = scopes_info.scopes[0].as_ref().unwrap();
2239 assert_eq!(root_scope.kind.as_deref(), Some("global"));
2240 assert_eq!(root_scope.variables, vec!["hello"]);
2241 assert_eq!(root_scope.children.len(), 1);
2242
2243 let fn_scope = &root_scope.children[0];
2244 assert_eq!(fn_scope.name.as_deref(), Some("hello"));
2245 assert_eq!(fn_scope.kind.as_deref(), Some("function"));
2246 assert!(fn_scope.is_stack_frame);
2247 assert_eq!(fn_scope.variables, vec!["name"]);
2248
2249 assert_eq!(scopes_info.ranges.len(), 1);
2251 let outer = &scopes_info.ranges[0];
2252 assert_eq!(outer.definition, Some(0));
2253 assert_eq!(outer.bindings, vec![Binding::Expression("hello".to_string())]);
2254 assert_eq!(outer.children.len(), 1);
2255
2256 let inner = &outer.children[0];
2257 assert_eq!(inner.definition, Some(1));
2258 assert!(inner.is_stack_frame);
2259 assert_eq!(inner.bindings, vec![Binding::Expression("name".to_string())]);
2260 }
2261
2262 #[test]
2263 fn scopes_with_inlining_roundtrip() {
2264 use srcmap_scopes::{
2265 Binding, CallSite, GeneratedRange, OriginalScope, Position, ScopeInfo,
2266 };
2267
2268 let mut builder = SourceMapGenerator::new(None);
2269 let src = builder.add_source("input.js");
2270 builder.add_mapping(0, 0, src, 0, 0);
2271
2272 builder.set_scopes(ScopeInfo {
2273 scopes: vec![Some(OriginalScope {
2274 start: Position { line: 0, column: 0 },
2275 end: Position { line: 10, column: 0 },
2276 name: None,
2277 kind: None,
2278 is_stack_frame: false,
2279 variables: vec!["x".to_string()],
2280 children: vec![OriginalScope {
2281 start: Position { line: 1, column: 0 },
2282 end: Position { line: 4, column: 1 },
2283 name: Some("greet".to_string()),
2284 kind: Some("function".to_string()),
2285 is_stack_frame: true,
2286 variables: vec!["msg".to_string()],
2287 children: vec![],
2288 }],
2289 })],
2290 ranges: vec![GeneratedRange {
2291 start: Position { line: 0, column: 0 },
2292 end: Position { line: 10, column: 0 },
2293 is_stack_frame: false,
2294 is_hidden: false,
2295 definition: Some(0),
2296 call_site: None,
2297 bindings: vec![Binding::Expression("_x".to_string())],
2298 children: vec![GeneratedRange {
2299 start: Position { line: 6, column: 0 },
2300 end: Position { line: 8, column: 0 },
2301 is_stack_frame: true,
2302 is_hidden: false,
2303 definition: Some(1),
2304 call_site: Some(CallSite { source_index: 0, line: 8, column: 0 }),
2305 bindings: vec![Binding::Expression("\"Hello\"".to_string())],
2306 children: vec![],
2307 }],
2308 }],
2309 });
2310
2311 let json = builder.to_json();
2312 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2313 let info = sm.scopes.unwrap();
2314
2315 let inlined = &info.ranges[0].children[0];
2317 assert_eq!(inlined.call_site, Some(CallSite { source_index: 0, line: 8, column: 0 }));
2318 assert_eq!(inlined.bindings, vec![Binding::Expression("\"Hello\"".to_string())]);
2319 }
2320
2321 #[test]
2322 fn set_source_content_out_of_bounds() {
2323 let mut builder = SourceMapGenerator::new(None);
2324 builder.set_source_content(0, "content".to_string());
2326 let json = builder.to_json();
2328 assert!(!json.contains("content"));
2329 }
2330
2331 #[test]
2332 fn add_to_ignore_list_dedup() {
2333 let mut builder = SourceMapGenerator::new(None);
2334 let idx = builder.add_source("vendor.js");
2335 builder.add_to_ignore_list(idx);
2336 builder.add_to_ignore_list(idx); let json = builder.to_json();
2338 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2340 assert_eq!(sm.ignore_list, vec![0]);
2341 }
2342
2343 #[test]
2344 fn to_decoded_map_with_source_root() {
2345 let mut builder = SourceMapGenerator::new(None);
2346 builder.set_source_root("src/".to_string());
2347 let src = builder.add_source("app.ts");
2348 builder.add_mapping(0, 0, src, 0, 0);
2349 let sm = builder.to_decoded_map();
2350 assert_eq!(sm.sources, vec!["src/app.ts"]);
2352 }
2353
2354 #[test]
2355 fn json_escaping_special_chars() {
2356 let mut builder = SourceMapGenerator::new(None);
2357 let src = builder.add_source("a.js");
2358 builder.set_source_content(src, "line1\nline2\r\ttab\\\"\x01end".to_string());
2360 builder.add_mapping(0, 0, src, 0, 0);
2361 let json = builder.to_json();
2362 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2364 assert_eq!(sm.sources_content, vec![Some("line1\nline2\r\ttab\\\"\x01end".to_string())]);
2365 }
2366
2367 #[test]
2368 fn json_escaping_in_names() {
2369 let mut builder = SourceMapGenerator::new(None);
2370 let src = builder.add_source("a.js");
2371 let name = builder.add_name("func\"with\\special");
2372 builder.add_named_mapping(0, 0, src, 0, 0, name);
2373 let json = builder.to_json();
2374 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2375 assert_eq!(sm.names[0], "func\"with\\special");
2376 }
2377
2378 #[test]
2379 fn json_escaping_in_sources() {
2380 let mut builder = SourceMapGenerator::new(None);
2381 let src = builder.add_source("path/with\"quotes.js");
2382 builder.add_mapping(0, 0, src, 0, 0);
2383 let json = builder.to_json();
2384 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2385 assert_eq!(sm.sources[0], "path/with\"quotes.js");
2386 }
2387
2388 #[cfg(feature = "parallel")]
2389 mod parallel_tests {
2390 use super::*;
2391
2392 fn build_large_generator(lines: u32, cols_per_line: u32) -> SourceMapGenerator {
2393 let mut builder = SourceMapGenerator::new(Some("bundle.js".to_string()));
2394 for i in 0..10 {
2395 let src = builder.add_source(&format!("src/file{i}.js"));
2396 builder.set_source_content(
2397 src,
2398 format!("// source file {i}\n{}", "x = 1;\n".repeat(100)),
2399 );
2400 }
2401 for i in 0..20 {
2402 builder.add_name(&format!("var{i}"));
2403 }
2404
2405 for line in 0..lines {
2406 for col in 0..cols_per_line {
2407 let src = (line * cols_per_line + col) % 10;
2408 let name = if col % 3 == 0 { Some(col % 20) } else { None };
2409 match name {
2410 Some(n) => builder.add_named_mapping(line, col * 10, src, line, col * 5, n),
2411 None => builder.add_mapping(line, col * 10, src, line, col * 5),
2412 }
2413 }
2414 }
2415 builder
2416 }
2417
2418 #[test]
2419 fn parallel_large_roundtrip() {
2420 let builder = build_large_generator(500, 20);
2421 let json = builder.to_json();
2422 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2423 assert_eq!(sm.mapping_count(), 10000);
2424 assert_eq!(sm.line_count(), 500);
2425
2426 let loc = sm.original_position_for(250, 50).unwrap();
2428 assert_eq!(loc.line, 250);
2429 assert_eq!(loc.column, 25);
2430 }
2431
2432 #[test]
2433 fn parallel_matches_sequential() {
2434 let builder = build_large_generator(500, 20);
2435
2436 let mut sorted: Vec<&Mapping> = builder.mappings.iter().collect();
2438 sorted.sort_unstable_by(|a, b| {
2439 a.generated_line
2440 .cmp(&b.generated_line)
2441 .then(a.generated_column.cmp(&b.generated_column))
2442 });
2443
2444 let sequential = SourceMapGenerator::encode_sequential_impl(&sorted);
2445 let parallel = SourceMapGenerator::encode_parallel_impl(&sorted);
2446 assert_eq!(sequential, parallel);
2447 }
2448
2449 #[test]
2450 fn parallel_with_sparse_lines() {
2451 let mut builder = SourceMapGenerator::new(None);
2452 let src = builder.add_source("input.js");
2453
2454 for i in 0..50 {
2456 let line = i * 100;
2457 for col in 0..100u32 {
2458 builder.add_mapping(line, col * 10, src, line, col * 5);
2459 }
2460 }
2461
2462 let json = builder.to_json();
2463 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2464 assert_eq!(sm.mapping_count(), 5000);
2465
2466 assert!(sm.original_position_for(50, 0).is_none());
2468 let loc = sm.original_position_for(200, 50).unwrap();
2470 assert_eq!(loc.line, 200);
2471 assert_eq!(loc.column, 25);
2472 }
2473 }
2474
2475 #[test]
2478 fn streaming_basic() {
2479 let mut sg = StreamingGenerator::new(Some("out.js".to_string()));
2480 let src = sg.add_source("input.js");
2481 sg.add_mapping(0, 0, src, 0, 0);
2482 sg.add_mapping(1, 0, src, 1, 0);
2483
2484 let json = sg.to_json();
2485 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2486 assert_eq!(sm.sources, vec!["input.js"]);
2487 assert_eq!(sm.mapping_count(), 2);
2488
2489 let loc0 = sm.original_position_for(0, 0).unwrap();
2490 assert_eq!(sm.source(loc0.source), "input.js");
2491 assert_eq!(loc0.line, 0);
2492
2493 let loc1 = sm.original_position_for(1, 0).unwrap();
2494 assert_eq!(loc1.line, 1);
2495 }
2496
2497 #[test]
2498 fn streaming_with_names() {
2499 let mut sg = StreamingGenerator::new(None);
2500 let src = sg.add_source("a.js");
2501 let name = sg.add_name("foo");
2502 sg.add_named_mapping(0, 0, src, 0, 0, name);
2503
2504 let sm = srcmap_sourcemap::SourceMap::from_json(&sg.to_json()).unwrap();
2505 let loc = sm.original_position_for(0, 0).unwrap();
2506 assert_eq!(loc.name, Some(0));
2507 assert_eq!(sm.name(0), "foo");
2508 }
2509
2510 #[test]
2511 fn streaming_generated_only() {
2512 let mut sg = StreamingGenerator::new(None);
2513 let src = sg.add_source("a.js");
2514 sg.add_generated_mapping(0, 0);
2515 sg.add_mapping(0, 5, src, 0, 0);
2516
2517 let sm = srcmap_sourcemap::SourceMap::from_json(&sg.to_json()).unwrap();
2518 assert_eq!(sm.mapping_count(), 2);
2519 assert!(sm.original_position_for(0, 0).is_none());
2520 assert!(sm.original_position_for(0, 5).is_some());
2521 }
2522
2523 #[test]
2524 fn streaming_matches_regular_generator() {
2525 let mut regular = SourceMapGenerator::new(Some("out.js".to_string()));
2526 let mut streaming = StreamingGenerator::new(Some("out.js".to_string()));
2527
2528 let src_r = regular.add_source("a.js");
2529 let src_s = streaming.add_source("a.js");
2530
2531 let name_r = regular.add_name("hello");
2532 let name_s = streaming.add_name("hello");
2533
2534 regular.set_source_content(src_r, "var hello;".to_string());
2535 streaming.set_source_content(src_s, "var hello;".to_string());
2536
2537 regular.add_named_mapping(0, 0, src_r, 0, 0, name_r);
2538 streaming.add_named_mapping(0, 0, src_s, 0, 0, name_s);
2539
2540 regular.add_mapping(0, 10, src_r, 0, 4);
2541 streaming.add_mapping(0, 10, src_s, 0, 4);
2542
2543 regular.add_mapping(1, 0, src_r, 1, 0);
2544 streaming.add_mapping(1, 0, src_s, 1, 0);
2545
2546 let sm_r = srcmap_sourcemap::SourceMap::from_json(®ular.to_json()).unwrap();
2547 let sm_s = srcmap_sourcemap::SourceMap::from_json(&streaming.to_json()).unwrap();
2548
2549 assert_eq!(sm_r.mapping_count(), sm_s.mapping_count());
2550 assert_eq!(sm_r.sources, sm_s.sources);
2551 assert_eq!(sm_r.names, sm_s.names);
2552 assert_eq!(sm_r.sources_content, sm_s.sources_content);
2553
2554 for (a, b) in sm_r.all_mappings().iter().zip(sm_s.all_mappings().iter()) {
2555 assert_eq!(a.generated_line, b.generated_line);
2556 assert_eq!(a.generated_column, b.generated_column);
2557 assert_eq!(a.source, b.source);
2558 assert_eq!(a.original_line, b.original_line);
2559 assert_eq!(a.original_column, b.original_column);
2560 assert_eq!(a.name, b.name);
2561 }
2562 }
2563
2564 #[test]
2565 fn streaming_to_decoded_map() {
2566 let mut sg = StreamingGenerator::new(None);
2567 let src = sg.add_source("test.js");
2568 sg.add_mapping(0, 0, src, 0, 0);
2569 sg.add_mapping(2, 5, src, 1, 3);
2570
2571 let sm = sg.to_decoded_map().unwrap();
2572 assert_eq!(sm.mapping_count(), 2);
2573 assert_eq!(sm.sources, vec!["test.js"]);
2574
2575 let loc = sm.original_position_for(2, 5).unwrap();
2576 assert_eq!(loc.line, 1);
2577 assert_eq!(loc.column, 3);
2578 }
2579
2580 #[test]
2581 fn streaming_source_dedup() {
2582 let mut sg = StreamingGenerator::new(None);
2583 let src1 = sg.add_source("a.js");
2584 let src2 = sg.add_source("a.js");
2585 assert_eq!(src1, src2);
2586 assert_eq!(sg.sources.len(), 1);
2587 }
2588
2589 #[test]
2590 fn streaming_ignore_list() {
2591 let mut sg = StreamingGenerator::new(None);
2592 let src = sg.add_source("vendor.js");
2593 sg.add_to_ignore_list(src);
2594 sg.add_mapping(0, 0, src, 0, 0);
2595
2596 let sm = srcmap_sourcemap::SourceMap::from_json(&sg.to_json()).unwrap();
2597 assert_eq!(sm.ignore_list, vec![0]);
2598 }
2599
2600 #[test]
2601 fn streaming_empty() {
2602 let sg = StreamingGenerator::new(None);
2603 let json = sg.to_json();
2604 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2605 assert_eq!(sm.mapping_count(), 0);
2606 }
2607
2608 #[test]
2609 fn streaming_sparse_lines() {
2610 let mut sg = StreamingGenerator::new(None);
2611 let src = sg.add_source("a.js");
2612 sg.add_mapping(0, 0, src, 0, 0);
2613 sg.add_mapping(5, 0, src, 5, 0);
2614
2615 let sm = srcmap_sourcemap::SourceMap::from_json(&sg.to_json()).unwrap();
2616 assert_eq!(sm.mapping_count(), 2);
2617 assert!(sm.original_position_for(0, 0).is_some());
2618 assert!(sm.original_position_for(5, 0).is_some());
2619 }
2620
2621 #[test]
2624 fn range_mapping_basic() {
2625 let mut builder = SourceMapGenerator::new(None);
2626 let src = builder.add_source("input.js");
2627 builder.add_range_mapping(0, 0, src, 0, 0);
2628 builder.add_mapping(0, 5, src, 0, 10);
2629
2630 let json = builder.to_json();
2631 assert!(json.contains(r#""rangeMappings":"A""#));
2632 }
2633
2634 #[test]
2635 fn range_mapping_multiple_on_line() {
2636 let mut builder = SourceMapGenerator::new(None);
2637 let src = builder.add_source("input.js");
2638 builder.add_range_mapping(0, 0, src, 0, 0);
2639 builder.add_mapping(0, 5, src, 0, 10);
2640 builder.add_range_mapping(0, 10, src, 0, 20);
2641
2642 let json = builder.to_json();
2643 assert!(json.contains(r#""rangeMappings":"A,C""#));
2644 }
2645
2646 #[test]
2647 fn range_mapping_multi_line() {
2648 let mut builder = SourceMapGenerator::new(None);
2649 let src = builder.add_source("input.js");
2650 builder.add_range_mapping(0, 0, src, 0, 0);
2651 builder.add_range_mapping(1, 0, src, 1, 0);
2652
2653 let json = builder.to_json();
2654 assert!(json.contains(r#""rangeMappings":"A;A""#));
2655 }
2656
2657 #[test]
2658 fn no_range_mappings_omits_field() {
2659 let mut builder = SourceMapGenerator::new(None);
2660 let src = builder.add_source("input.js");
2661 builder.add_mapping(0, 0, src, 0, 0);
2662
2663 let json = builder.to_json();
2664 assert!(!json.contains("rangeMappings"));
2665 }
2666
2667 #[test]
2668 fn named_range_mapping() {
2669 let mut builder = SourceMapGenerator::new(None);
2670 let src = builder.add_source("input.js");
2671 let name = builder.add_name("foo");
2672 builder.add_named_range_mapping(0, 0, src, 0, 0, name);
2673
2674 let json = builder.to_json();
2675 assert!(json.contains(r#""rangeMappings":"A""#));
2676 }
2677
2678 #[test]
2679 fn to_decoded_map_preserves_range_mappings() {
2680 let mut builder = SourceMapGenerator::new(None);
2681 let src = builder.add_source("input.js");
2682 builder.add_range_mapping(0, 0, src, 0, 0);
2683 builder.add_mapping(0, 5, src, 0, 10);
2684
2685 let sm = builder.to_decoded_map();
2686 assert!(sm.has_range_mappings());
2687 let mappings = sm.all_mappings();
2688 assert!(mappings[0].is_range_mapping);
2689 assert!(!mappings[1].is_range_mapping);
2690 }
2691
2692 #[test]
2695 fn streaming_range_mapping_basic() {
2696 let mut sg = StreamingGenerator::new(None);
2697 let src = sg.add_source("input.js");
2698 sg.add_range_mapping(0, 0, src, 0, 0);
2699 sg.add_mapping(0, 5, src, 0, 10);
2700
2701 let json = sg.to_json();
2702 assert!(json.contains(r#""rangeMappings":"A""#));
2703 }
2704
2705 #[test]
2706 fn streaming_range_mapping_roundtrip() {
2707 let mut sg = StreamingGenerator::new(None);
2708 let src = sg.add_source("input.js");
2709 sg.add_range_mapping(0, 0, src, 0, 0);
2710 sg.add_mapping(0, 5, src, 0, 10);
2711
2712 let sm = sg.to_decoded_map().unwrap();
2713 assert!(sm.has_range_mappings());
2714 let mappings = sm.all_mappings();
2715 assert!(mappings[0].is_range_mapping);
2716 assert!(!mappings[1].is_range_mapping);
2717 }
2718
2719 #[test]
2720 fn streaming_range_and_named_range() {
2721 let mut sg = StreamingGenerator::new(None);
2722 let src = sg.add_source("input.js");
2723 let name = sg.add_name("foo");
2724 sg.add_range_mapping(0, 0, src, 0, 0);
2725 sg.add_named_range_mapping(0, 10, src, 0, 5, name);
2726
2727 let json = sg.to_json();
2728 assert!(json.contains(r#""rangeMappings":"A,B""#));
2729
2730 let sm = sg.to_decoded_map().unwrap();
2731 assert!(sm.has_range_mappings());
2732 let mappings = sm.all_mappings();
2733 assert!(mappings[0].is_range_mapping);
2734 assert!(mappings[1].is_range_mapping);
2735 }
2736
2737 #[test]
2738 fn streaming_range_mapping_matches_regular() {
2739 let mut regular = SourceMapGenerator::new(None);
2740 let mut streaming = StreamingGenerator::new(None);
2741
2742 let src_r = regular.add_source("input.js");
2743 let src_s = streaming.add_source("input.js");
2744
2745 regular.add_range_mapping(0, 0, src_r, 0, 0);
2746 streaming.add_range_mapping(0, 0, src_s, 0, 0);
2747
2748 regular.add_mapping(0, 5, src_r, 0, 10);
2749 streaming.add_mapping(0, 5, src_s, 0, 10);
2750
2751 regular.add_range_mapping(0, 10, src_r, 0, 20);
2752 streaming.add_range_mapping(0, 10, src_s, 0, 20);
2753
2754 regular.add_range_mapping(1, 0, src_r, 1, 0);
2755 streaming.add_range_mapping(1, 0, src_s, 1, 0);
2756
2757 let json_r = regular.to_json();
2758 let json_s = streaming.to_json();
2759
2760 let sm_r = srcmap_sourcemap::SourceMap::from_json(&json_r).unwrap();
2761 let sm_s = srcmap_sourcemap::SourceMap::from_json(&json_s).unwrap();
2762
2763 assert_eq!(sm_r.mapping_count(), sm_s.mapping_count());
2764
2765 for (a, b) in sm_r.all_mappings().iter().zip(sm_s.all_mappings().iter()) {
2766 assert_eq!(a.generated_line, b.generated_line);
2767 assert_eq!(a.generated_column, b.generated_column);
2768 assert_eq!(a.source, b.source);
2769 assert_eq!(a.original_line, b.original_line);
2770 assert_eq!(a.original_column, b.original_column);
2771 assert_eq!(a.name, b.name);
2772 assert_eq!(a.is_range_mapping, b.is_range_mapping);
2773 }
2774 }
2775
2776 #[test]
2779 fn into_parts_basic() {
2780 let mut builder = SourceMapGenerator::new(Some("output.js".to_string()));
2781 let src = builder.add_source("input.js");
2782 builder.set_source_content(src, "var x = 1;".to_string());
2783 let name = builder.add_name("x");
2784 builder.add_named_mapping(0, 0, src, 0, 4, name);
2785 builder.add_mapping(1, 0, src, 1, 0);
2786 builder.set_debug_id("test-id");
2787
2788 let json = builder.to_json();
2789 let sm_json = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2790
2791 let mut builder2 = SourceMapGenerator::new(Some("output.js".to_string()));
2793 let src2 = builder2.add_source("input.js");
2794 builder2.set_source_content(src2, "var x = 1;".to_string());
2795 let name2 = builder2.add_name("x");
2796 builder2.add_named_mapping(0, 0, src2, 0, 4, name2);
2797 builder2.add_mapping(1, 0, src2, 1, 0);
2798 builder2.set_debug_id("test-id");
2799
2800 let parts = builder2.into_parts();
2801 assert_eq!(parts.file, Some("output.js".to_string()));
2802 assert_eq!(parts.sources, vec!["input.js"]);
2803 assert_eq!(parts.names, vec!["x"]);
2804 assert_eq!(parts.sources_content, vec![Some("var x = 1;".to_string())]);
2805 assert_eq!(parts.debug_id, Some("test-id".to_string()));
2806 assert!(!parts.mappings.is_empty());
2807
2808 let sm_parts = srcmap_sourcemap::SourceMap::from_vlq(
2810 &parts.mappings,
2811 parts.sources,
2812 parts.names,
2813 parts.file,
2814 parts.source_root,
2815 parts.sources_content,
2816 parts.ignore_list,
2817 parts.debug_id,
2818 )
2819 .unwrap();
2820 assert_eq!(sm_parts.mapping_count(), sm_json.mapping_count());
2821 }
2822
2823 #[test]
2824 fn into_parts_empty() {
2825 let builder = SourceMapGenerator::new(None);
2826 let parts = builder.into_parts();
2827 assert_eq!(parts.file, None);
2828 assert!(parts.mappings.is_empty());
2829 assert!(parts.sources.is_empty());
2830 assert!(parts.names.is_empty());
2831 }
2832
2833 #[test]
2834 fn into_parts_with_ignore_list() {
2835 let mut builder = SourceMapGenerator::new(None);
2836 let src = builder.add_source("vendor.js");
2837 builder.add_to_ignore_list(src);
2838 builder.add_mapping(0, 0, src, 0, 0);
2839
2840 let parts = builder.into_parts();
2841 assert_eq!(parts.ignore_list, vec![0]);
2842 }
2843
2844 #[test]
2845 fn into_parts_with_range_mappings() {
2846 let mut builder = SourceMapGenerator::new(None);
2847 let src = builder.add_source("input.js");
2848 builder.add_range_mapping(0, 0, src, 0, 0);
2849 builder.add_mapping(0, 5, src, 0, 10);
2850
2851 let parts = builder.into_parts();
2852 assert!(parts.range_mappings.is_some());
2853 }
2854
2855 #[test]
2856 fn streaming_into_parts() {
2857 let mut sg = StreamingGenerator::new(Some("out.js".to_string()));
2858 let src = sg.add_source("input.js");
2859 sg.set_source_content(src, "var x = 1;".to_string());
2860 let name = sg.add_name("x");
2861 sg.add_named_mapping(0, 0, src, 0, 4, name);
2862 sg.add_mapping(1, 0, src, 1, 0);
2863
2864 let parts = sg.into_parts();
2865 assert_eq!(parts.file, Some("out.js".to_string()));
2866 assert_eq!(parts.sources, vec!["input.js"]);
2867 assert_eq!(parts.names, vec!["x"]);
2868 assert!(!parts.mappings.is_empty());
2869 }
2870
2871 #[test]
2874 fn to_writer_matches_to_json() {
2875 let mut builder = SourceMapGenerator::new(Some("output.js".to_string()));
2876 let src = builder.add_source("input.js");
2877 builder.set_source_content(src, "var x = 1;".to_string());
2878 let name = builder.add_name("x");
2879 builder.add_named_mapping(0, 0, src, 0, 4, name);
2880 builder.add_mapping(1, 0, src, 1, 0);
2881
2882 let json = builder.to_json();
2883 let mut buf = Vec::new();
2884 builder.to_writer(&mut buf).unwrap();
2885 let writer_output = String::from_utf8(buf).unwrap();
2886
2887 assert_eq!(json, writer_output);
2888 }
2889
2890 #[test]
2891 fn to_writer_empty() {
2892 let builder = SourceMapGenerator::new(None);
2893 let mut buf = Vec::new();
2894 builder.to_writer(&mut buf).unwrap();
2895 let output = String::from_utf8(buf).unwrap();
2896 assert!(output.contains(r#""version":3"#));
2897 assert!(output.contains(r#""mappings":"""#));
2898 }
2899
2900 #[test]
2901 fn to_writer_with_all_fields() {
2902 let mut builder = SourceMapGenerator::new(Some("bundle.js".to_string()));
2903 builder.set_source_root("src/");
2904 builder.set_debug_id("test-uuid");
2905 let src = builder.add_source("app.ts");
2906 builder.set_source_content(src, "const x = 1;".to_string());
2907 builder.add_to_ignore_list(src);
2908 let name = builder.add_name("x");
2909 builder.add_named_mapping(0, 0, src, 0, 6, name);
2910
2911 let json = builder.to_json();
2912 let mut buf = Vec::new();
2913 builder.to_writer(&mut buf).unwrap();
2914 let writer_output = String::from_utf8(buf).unwrap();
2915
2916 assert_eq!(json, writer_output);
2917
2918 let sm = srcmap_sourcemap::SourceMap::from_json(&writer_output).unwrap();
2920 assert_eq!(sm.source(0), "src/app.ts");
2921 assert_eq!(sm.name(0), "x");
2922 }
2923
2924 #[test]
2925 fn streaming_to_writer_matches_to_json() {
2926 let mut sg = StreamingGenerator::new(Some("out.js".to_string()));
2927 let src = sg.add_source("input.js");
2928 sg.set_source_content(src, "var x = 1;".to_string());
2929 let name = sg.add_name("x");
2930 sg.add_named_mapping(0, 0, src, 0, 4, name);
2931 sg.add_mapping(1, 0, src, 1, 0);
2932
2933 let json = sg.to_json();
2934 let mut buf = Vec::new();
2935 sg.to_writer(&mut buf).unwrap();
2936 let writer_output = String::from_utf8(buf).unwrap();
2937
2938 assert_eq!(json, writer_output);
2939 }
2940}