1use rustc_hash::FxHashMap;
28use srcmap_codec::{vlq_encode, vlq_encode_unchecked, vlq_encode_unsigned};
29use srcmap_scopes::ScopeInfo;
30
31#[derive(Debug, Clone)]
38pub struct Mapping {
39 pub generated_line: u32,
41 pub generated_column: u32,
43 pub source: Option<u32>,
45 pub original_line: u32,
47 pub original_column: u32,
49 pub name: Option<u32>,
51 pub is_range_mapping: bool,
53}
54
55#[derive(Debug)]
71pub struct SourceMapGenerator {
72 file: Option<String>,
73 source_root: Option<String>,
74 sources: Vec<String>,
75 sources_content: Vec<Option<String>>,
76 names: Vec<String>,
77 mappings: Vec<Mapping>,
78 ignore_list: Vec<u32>,
79 debug_id: Option<String>,
80 scopes: Option<ScopeInfo>,
81 assume_sorted: bool,
82
83 source_map: FxHashMap<String, u32>,
85 name_map: FxHashMap<String, u32>,
86}
87
88impl SourceMapGenerator {
89 pub fn new(file: Option<String>) -> Self {
91 Self {
92 file,
93 source_root: None,
94 sources: Vec::new(),
95 sources_content: Vec::new(),
96 names: Vec::new(),
97 mappings: Vec::new(),
98 ignore_list: Vec::new(),
99 debug_id: None,
100 scopes: None,
101 assume_sorted: false,
102 source_map: FxHashMap::default(),
103 name_map: FxHashMap::default(),
104 }
105 }
106
107 pub fn set_source_root(&mut self, root: impl Into<String>) {
109 self.source_root = Some(root.into());
110 }
111
112 pub fn set_debug_id(&mut self, id: impl Into<String>) {
114 self.debug_id = Some(id.into());
115 }
116
117 pub fn set_scopes(&mut self, scopes: ScopeInfo) {
119 self.scopes = Some(scopes);
120 }
121
122 pub fn set_assume_sorted(&mut self, sorted: bool) {
130 self.assume_sorted = sorted;
131 }
132
133 #[inline]
135 pub fn add_source(&mut self, source: &str) -> u32 {
136 if let Some(&idx) = self.source_map.get(source) {
137 return idx;
138 }
139 let idx = self.sources.len() as u32;
140 self.sources.push(source.to_string());
141 self.sources_content.push(None);
142 self.source_map.insert(source.to_string(), idx);
143 idx
144 }
145
146 pub fn set_source_content(&mut self, source_idx: u32, content: impl Into<String>) {
148 if (source_idx as usize) < self.sources_content.len() {
149 self.sources_content[source_idx as usize] = Some(content.into());
150 }
151 }
152
153 #[inline]
155 pub fn add_name(&mut self, name: &str) -> u32 {
156 if let Some(&idx) = self.name_map.get(name) {
157 return idx;
158 }
159 let idx = self.names.len() as u32;
160 self.names.push(name.to_string());
161 self.name_map.insert(name.to_string(), idx);
162 idx
163 }
164
165 pub fn add_to_ignore_list(&mut self, source_idx: u32) {
167 if !self.ignore_list.contains(&source_idx) {
168 self.ignore_list.push(source_idx);
169 }
170 }
171
172 pub fn add_generated_mapping(&mut self, generated_line: u32, generated_column: u32) {
174 self.mappings.push(Mapping {
175 generated_line,
176 generated_column,
177 source: None,
178 original_line: 0,
179 original_column: 0,
180 name: None,
181 is_range_mapping: false,
182 });
183 }
184
185 pub fn add_mapping(
187 &mut self,
188 generated_line: u32,
189 generated_column: u32,
190 source: u32,
191 original_line: u32,
192 original_column: u32,
193 ) {
194 self.mappings.push(Mapping {
195 generated_line,
196 generated_column,
197 source: Some(source),
198 original_line,
199 original_column,
200 name: None,
201 is_range_mapping: false,
202 });
203 }
204
205 pub fn add_named_mapping(
207 &mut self,
208 generated_line: u32,
209 generated_column: u32,
210 source: u32,
211 original_line: u32,
212 original_column: u32,
213 name: u32,
214 ) {
215 self.mappings.push(Mapping {
216 generated_line,
217 generated_column,
218 source: Some(source),
219 original_line,
220 original_column,
221 name: Some(name),
222 is_range_mapping: false,
223 });
224 }
225
226 pub fn add_range_mapping(
232 &mut self,
233 generated_line: u32,
234 generated_column: u32,
235 source: u32,
236 original_line: u32,
237 original_column: u32,
238 ) {
239 self.mappings.push(Mapping {
240 generated_line,
241 generated_column,
242 source: Some(source),
243 original_line,
244 original_column,
245 name: None,
246 is_range_mapping: true,
247 });
248 }
249
250 pub fn add_named_range_mapping(
252 &mut self,
253 generated_line: u32,
254 generated_column: u32,
255 source: u32,
256 original_line: u32,
257 original_column: u32,
258 name: u32,
259 ) {
260 self.mappings.push(Mapping {
261 generated_line,
262 generated_column,
263 source: Some(source),
264 original_line,
265 original_column,
266 name: Some(name),
267 is_range_mapping: true,
268 });
269 }
270
271 pub fn maybe_add_mapping(
277 &mut self,
278 generated_line: u32,
279 generated_column: u32,
280 source: u32,
281 original_line: u32,
282 original_column: u32,
283 ) -> bool {
284 if let Some(last) = self.mappings.last()
285 && last.generated_line == generated_line
286 && last.source == Some(source)
287 && last.original_line == original_line
288 && last.original_column == original_column
289 {
290 return false;
291 }
292 self.add_mapping(
293 generated_line,
294 generated_column,
295 source,
296 original_line,
297 original_column,
298 );
299 true
300 }
301
302 fn encode_mappings(&self) -> String {
304 if self.mappings.is_empty() {
305 return String::new();
306 }
307
308 if self.assume_sorted {
309 return Self::encode_sequential_impl(&self.mappings);
310 }
311
312 let mut sorted: Vec<&Mapping> = self.mappings.iter().collect();
314 sorted.sort_unstable_by(|a, b| {
315 a.generated_line
316 .cmp(&b.generated_line)
317 .then(a.generated_column.cmp(&b.generated_column))
318 });
319
320 #[cfg(feature = "parallel")]
321 if sorted.len() >= 4096 {
322 return Self::encode_parallel_impl(&sorted);
323 }
324
325 Self::encode_sequential_impl(&sorted)
326 }
327
328 #[inline]
329 fn encode_sequential_impl<T: std::borrow::Borrow<Mapping>>(sorted: &[T]) -> String {
330 let mut out: Vec<u8> = Vec::with_capacity(sorted.len() * 36);
332
333 let mut prev_gen_col: i64 = 0;
334 let mut prev_source: i64 = 0;
335 let mut prev_orig_line: i64 = 0;
336 let mut prev_orig_col: i64 = 0;
337 let mut prev_name: i64 = 0;
338 let mut prev_gen_line: u32 = 0;
339 let mut first_in_line = true;
340
341 for item in sorted {
342 let m = item.borrow();
343 while prev_gen_line < m.generated_line {
344 out.push(b';');
345 prev_gen_line += 1;
346 prev_gen_col = 0;
347 first_in_line = true;
348 }
349
350 if !first_in_line {
351 out.push(b',');
352 }
353 first_in_line = false;
354
355 unsafe {
358 vlq_encode_unchecked(&mut out, m.generated_column as i64 - prev_gen_col);
359 prev_gen_col = m.generated_column as i64;
360
361 if let Some(source) = m.source {
362 vlq_encode_unchecked(&mut out, source as i64 - prev_source);
363 prev_source = source as i64;
364
365 vlq_encode_unchecked(&mut out, m.original_line as i64 - prev_orig_line);
366 prev_orig_line = m.original_line as i64;
367
368 vlq_encode_unchecked(&mut out, m.original_column as i64 - prev_orig_col);
369 prev_orig_col = m.original_column as i64;
370
371 if let Some(name) = m.name {
372 vlq_encode_unchecked(&mut out, name as i64 - prev_name);
373 prev_name = name as i64;
374 }
375 }
376 }
377 }
378
379 debug_assert!(out.is_ascii());
382 unsafe { String::from_utf8_unchecked(out) }
383 }
384
385 #[cfg(feature = "parallel")]
386 fn encode_parallel_impl(sorted: &[&Mapping]) -> String {
387 use rayon::prelude::*;
388
389 let max_line = sorted
390 .last()
391 .expect("encode_parallel_impl requires non-empty sorted slice")
392 .generated_line as usize;
393
394 let mut line_ranges: Vec<(usize, usize)> = vec![(0, 0); max_line + 1];
396 let mut i = 0;
397 while i < sorted.len() {
398 let line = sorted[i].generated_line as usize;
399 let start = i;
400 while i < sorted.len() && sorted[i].generated_line as usize == line {
401 i += 1;
402 }
403 line_ranges[line] = (start, i);
404 }
405
406 let mut states: Vec<(i64, i64, i64, i64)> = Vec::with_capacity(max_line + 1);
408 let mut prev_source: i64 = 0;
409 let mut prev_orig_line: i64 = 0;
410 let mut prev_orig_col: i64 = 0;
411 let mut prev_name: i64 = 0;
412
413 for &(start, end) in &line_ranges {
414 states.push((prev_source, prev_orig_line, prev_orig_col, prev_name));
415 for m in &sorted[start..end] {
416 if let Some(source) = m.source {
417 prev_source = source as i64;
418 prev_orig_line = m.original_line as i64;
419 prev_orig_col = m.original_column as i64;
420 if let Some(name) = m.name {
421 prev_name = name as i64;
422 }
423 }
424 }
425 }
426
427 let encoded_lines: Vec<Vec<u8>> = line_ranges
429 .par_iter()
430 .zip(states.par_iter())
431 .map(|(&(start, end), &(s, ol, oc, n))| {
432 if start == end {
433 return Vec::new();
434 }
435 encode_mapping_slice(&sorted[start..end], s, ol, oc, n)
436 })
437 .collect();
438
439 let total_len = encoded_lines.iter().map(|l| l.len()).sum::<usize>() + max_line;
441 let mut out: Vec<u8> = Vec::with_capacity(total_len);
442 for (i, bytes) in encoded_lines.iter().enumerate() {
443 if i > 0 {
444 out.push(b';');
445 }
446 out.extend_from_slice(bytes);
447 }
448
449 debug_assert!(out.is_ascii());
452 unsafe { String::from_utf8_unchecked(out) }
453 }
454
455 fn encode_range_mappings(&self) -> Option<String> {
458 if !self.mappings.iter().any(|m| m.is_range_mapping) {
459 return None;
460 }
461
462 let ordered: Vec<&Mapping> = if self.assume_sorted {
463 self.mappings.iter().collect()
464 } else {
465 let mut sorted: Vec<&Mapping> = self.mappings.iter().collect();
466 sorted.sort_unstable_by(|a, b| {
467 a.generated_line
468 .cmp(&b.generated_line)
469 .then(a.generated_column.cmp(&b.generated_column))
470 });
471 sorted
472 };
473
474 let max_line = ordered.last().map_or(0, |m| m.generated_line);
475 let mut out: Vec<u8> = Vec::new();
476 let mut ordered_idx = 0;
477
478 for line in 0..=max_line {
479 if line > 0 {
480 out.push(b';');
481 }
482 let mut prev_offset: u64 = 0;
483 let mut first_on_line = true;
484 let mut line_local_idx: u64 = 0;
485
486 while ordered_idx < ordered.len() && ordered[ordered_idx].generated_line == line {
487 if ordered[ordered_idx].is_range_mapping {
488 if !first_on_line {
489 out.push(b',');
490 }
491 first_on_line = false;
492 let delta = line_local_idx - prev_offset;
493 vlq_encode_unsigned(&mut out, delta);
494 prev_offset = line_local_idx;
495 }
496 line_local_idx += 1;
497 ordered_idx += 1;
498 }
499 }
500
501 while out.last() == Some(&b';') {
502 out.pop();
503 }
504
505 if out.is_empty() {
506 return None;
507 }
508
509 debug_assert!(out.is_ascii());
512 Some(unsafe { String::from_utf8_unchecked(out) })
513 }
514
515 pub fn to_json(&self) -> String {
517 use std::io::Write;
518
519 let mappings = self.encode_mappings();
520
521 let (scopes_str, names_for_json) = if let Some(ref scopes_info) = self.scopes {
523 let mut names = self.names.clone();
524 let s = srcmap_scopes::encode_scopes(scopes_info, &mut names);
525 (Some(s), std::borrow::Cow::Owned(names))
526 } else {
527 (None, std::borrow::Cow::Borrowed(&self.names))
528 };
529
530 let sources_size: usize = self.sources.iter().map(|s| s.len() + 4).sum();
532 let names_size: usize = names_for_json.iter().map(|n| n.len() + 4).sum();
533 let content_size: usize = self
534 .sources_content
535 .iter()
536 .map(|c| c.as_ref().map_or(5, |s| s.len() + 4))
537 .sum();
538 let capacity = 100 + sources_size + names_size + mappings.len() + content_size;
539
540 let mut json: Vec<u8> = Vec::with_capacity(capacity);
541 json.extend_from_slice(br#"{"version":3"#);
542
543 if let Some(ref file) = self.file {
544 json.extend_from_slice(br#","file":"#);
545 json_quote_into(&mut json, file);
546 }
547
548 if let Some(ref root) = self.source_root {
549 json.extend_from_slice(br#","sourceRoot":"#);
550 json_quote_into(&mut json, root);
551 }
552
553 json.extend_from_slice(br#","sources":["#);
555 for (i, s) in self.sources.iter().enumerate() {
556 if i > 0 {
557 json.push(b',');
558 }
559 json_quote_into(&mut json, s);
560 }
561 json.push(b']');
562
563 if self.sources_content.iter().any(|c| c.is_some()) {
565 json.extend_from_slice(br#","sourcesContent":["#);
566
567 #[cfg(feature = "parallel")]
568 {
569 use rayon::prelude::*;
570
571 let total_content: usize = self
572 .sources_content
573 .iter()
574 .map(|c| c.as_ref().map_or(0, |s| s.len()))
575 .sum();
576
577 if self.sources_content.len() >= 8 && total_content >= 8192 {
578 let quoted: Vec<String> = self
579 .sources_content
580 .par_iter()
581 .map(|c| match c {
582 Some(content) => json_quote(content),
583 None => "null".to_string(),
584 })
585 .collect();
586 for (i, q) in quoted.iter().enumerate() {
587 if i > 0 {
588 json.push(b',');
589 }
590 json.extend_from_slice(q.as_bytes());
591 }
592 } else {
593 for (i, c) in self.sources_content.iter().enumerate() {
594 if i > 0 {
595 json.push(b',');
596 }
597 match c {
598 Some(content) => json_quote_into(&mut json, content),
599 None => json.extend_from_slice(b"null"),
600 }
601 }
602 }
603 }
604
605 #[cfg(not(feature = "parallel"))]
606 for (i, c) in self.sources_content.iter().enumerate() {
607 if i > 0 {
608 json.push(b',');
609 }
610 match c {
611 Some(content) => json_quote_into(&mut json, content),
612 None => json.extend_from_slice(b"null"),
613 }
614 }
615
616 json.push(b']');
617 }
618
619 json.extend_from_slice(br#","names":["#);
621 for (i, n) in names_for_json.iter().enumerate() {
622 if i > 0 {
623 json.push(b',');
624 }
625 json_quote_into(&mut json, n);
626 }
627 json.push(b']');
628
629 json.extend_from_slice(br#","mappings":""#);
631 json.extend_from_slice(mappings.as_bytes());
632 json.push(b'"');
633
634 if !self.ignore_list.is_empty() {
636 json.extend_from_slice(br#","ignoreList":["#);
637 for (i, &idx) in self.ignore_list.iter().enumerate() {
638 if i > 0 {
639 json.push(b',');
640 }
641 let _ = write!(json, "{idx}");
642 }
643 json.push(b']');
644 }
645
646 if let Some(ref range_mappings) = self.encode_range_mappings() {
648 json.extend_from_slice(br#","rangeMappings":""#);
649 json.extend_from_slice(range_mappings.as_bytes());
650 json.push(b'"');
651 }
652
653 if let Some(ref id) = self.debug_id {
655 json.extend_from_slice(br#","debugId":"#);
656 json_quote_into(&mut json, id);
657 }
658
659 if let Some(ref s) = scopes_str {
661 json.extend_from_slice(br#","scopes":"#);
662 json_quote_into(&mut json, s);
663 }
664
665 json.push(b'}');
666
667 unsafe { String::from_utf8_unchecked(json) }
670 }
671
672 pub fn mapping_count(&self) -> usize {
674 self.mappings.len()
675 }
676
677 pub fn to_decoded_map(&self) -> srcmap_sourcemap::SourceMap {
682 let sm_mappings: Vec<srcmap_sourcemap::Mapping> = if self.assume_sorted {
683 self.mappings
685 .iter()
686 .map(|m| srcmap_sourcemap::Mapping {
687 generated_line: m.generated_line,
688 generated_column: m.generated_column,
689 source: m.source.unwrap_or(u32::MAX),
690 original_line: m.original_line,
691 original_column: m.original_column,
692 name: m.name.unwrap_or(u32::MAX),
693 is_range_mapping: m.is_range_mapping,
694 })
695 .collect()
696 } else {
697 let mut sorted: Vec<&Mapping> = self.mappings.iter().collect();
699 sorted.sort_unstable_by(|a, b| {
700 a.generated_line
701 .cmp(&b.generated_line)
702 .then(a.generated_column.cmp(&b.generated_column))
703 });
704 sorted
705 .iter()
706 .map(|m| srcmap_sourcemap::Mapping {
707 generated_line: m.generated_line,
708 generated_column: m.generated_column,
709 source: m.source.unwrap_or(u32::MAX),
710 original_line: m.original_line,
711 original_column: m.original_column,
712 name: m.name.unwrap_or(u32::MAX),
713 is_range_mapping: m.is_range_mapping,
714 })
715 .collect()
716 };
717
718 let sources_content: Vec<Option<String>> = self.sources_content.clone();
720
721 let sources: Vec<String> = match &self.source_root {
723 Some(root) if !root.is_empty() => {
724 self.sources.iter().map(|s| format!("{root}{s}")).collect()
725 }
726 _ => self.sources.clone(),
727 };
728
729 srcmap_sourcemap::SourceMap::from_parts(
730 self.file.clone(),
731 self.source_root.clone(),
732 sources,
733 sources_content,
734 self.names.clone(),
735 sm_mappings,
736 self.ignore_list.clone(),
737 self.debug_id.clone(),
738 None, )
740 }
741}
742
743#[derive(Debug)]
769pub struct StreamingGenerator {
770 file: Option<String>,
771 source_root: Option<String>,
772 sources: Vec<String>,
773 sources_content: Vec<Option<String>>,
774 names: Vec<String>,
775 ignore_list: Vec<u32>,
776 debug_id: Option<String>,
777
778 source_map: FxHashMap<String, u32>,
780 name_map: FxHashMap<String, u32>,
781
782 vlq_out: Vec<u8>,
784 prev_gen_line: u32,
785 prev_gen_col: i64,
786 prev_source: i64,
787 prev_orig_line: i64,
788 prev_orig_col: i64,
789 prev_name: i64,
790 first_in_line: bool,
791 mapping_count: usize,
792
793 line_local_index: u32,
795 range_entries: Vec<(u32, u32)>,
796}
797
798impl StreamingGenerator {
799 pub fn new(file: Option<String>) -> Self {
801 Self {
802 file,
803 source_root: None,
804 sources: Vec::new(),
805 sources_content: Vec::new(),
806 names: Vec::new(),
807 ignore_list: Vec::new(),
808 debug_id: None,
809 source_map: FxHashMap::default(),
810 name_map: FxHashMap::default(),
811 vlq_out: Vec::with_capacity(1024),
812 prev_gen_line: 0,
813 prev_gen_col: 0,
814 prev_source: 0,
815 prev_orig_line: 0,
816 prev_orig_col: 0,
817 prev_name: 0,
818 first_in_line: true,
819 mapping_count: 0,
820 line_local_index: 0,
821 range_entries: Vec::new(),
822 }
823 }
824
825 pub fn set_source_root(&mut self, root: impl Into<String>) {
827 self.source_root = Some(root.into());
828 }
829
830 pub fn set_debug_id(&mut self, id: impl Into<String>) {
832 self.debug_id = Some(id.into());
833 }
834
835 #[inline]
837 pub fn add_source(&mut self, source: &str) -> u32 {
838 if let Some(&idx) = self.source_map.get(source) {
839 return idx;
840 }
841 let idx = self.sources.len() as u32;
842 self.sources.push(source.to_string());
843 self.sources_content.push(None);
844 self.source_map.insert(source.to_string(), idx);
845 idx
846 }
847
848 pub fn set_source_content(&mut self, source_idx: u32, content: impl Into<String>) {
850 if (source_idx as usize) < self.sources_content.len() {
851 self.sources_content[source_idx as usize] = Some(content.into());
852 }
853 }
854
855 #[inline]
857 pub fn add_name(&mut self, name: &str) -> u32 {
858 if let Some(&idx) = self.name_map.get(name) {
859 return idx;
860 }
861 let idx = self.names.len() as u32;
862 self.names.push(name.to_string());
863 self.name_map.insert(name.to_string(), idx);
864 idx
865 }
866
867 pub fn add_to_ignore_list(&mut self, source_idx: u32) {
869 if !self.ignore_list.contains(&source_idx) {
870 self.ignore_list.push(source_idx);
871 }
872 }
873
874 #[inline]
878 pub fn add_generated_mapping(&mut self, generated_line: u32, generated_column: u32) {
879 self.advance_to_line(generated_line);
880
881 if !self.first_in_line {
882 self.vlq_out.push(b',');
883 }
884 self.first_in_line = false;
885
886 vlq_encode(
887 &mut self.vlq_out,
888 generated_column as i64 - self.prev_gen_col,
889 );
890 self.prev_gen_col = generated_column as i64;
891 self.line_local_index += 1;
892 self.mapping_count += 1;
893 }
894
895 #[inline]
899 pub fn add_mapping(
900 &mut self,
901 generated_line: u32,
902 generated_column: u32,
903 source: u32,
904 original_line: u32,
905 original_column: u32,
906 ) {
907 self.advance_to_line(generated_line);
908
909 if !self.first_in_line {
910 self.vlq_out.push(b',');
911 }
912 self.first_in_line = false;
913
914 vlq_encode(
915 &mut self.vlq_out,
916 generated_column as i64 - self.prev_gen_col,
917 );
918 self.prev_gen_col = generated_column as i64;
919
920 vlq_encode(&mut self.vlq_out, source as i64 - self.prev_source);
921 self.prev_source = source as i64;
922
923 vlq_encode(
924 &mut self.vlq_out,
925 original_line as i64 - self.prev_orig_line,
926 );
927 self.prev_orig_line = original_line as i64;
928
929 vlq_encode(
930 &mut self.vlq_out,
931 original_column as i64 - self.prev_orig_col,
932 );
933 self.prev_orig_col = original_column as i64;
934
935 self.line_local_index += 1;
936 self.mapping_count += 1;
937 }
938
939 #[inline]
944 pub fn add_range_mapping(
945 &mut self,
946 generated_line: u32,
947 generated_column: u32,
948 source: u32,
949 original_line: u32,
950 original_column: u32,
951 ) {
952 self.advance_to_line(generated_line);
953 self.range_entries
954 .push((self.prev_gen_line, self.line_local_index));
955
956 if !self.first_in_line {
957 self.vlq_out.push(b',');
958 }
959 self.first_in_line = false;
960
961 vlq_encode(
962 &mut self.vlq_out,
963 generated_column as i64 - self.prev_gen_col,
964 );
965 self.prev_gen_col = generated_column as i64;
966
967 vlq_encode(&mut self.vlq_out, source as i64 - self.prev_source);
968 self.prev_source = source as i64;
969
970 vlq_encode(
971 &mut self.vlq_out,
972 original_line as i64 - self.prev_orig_line,
973 );
974 self.prev_orig_line = original_line as i64;
975
976 vlq_encode(
977 &mut self.vlq_out,
978 original_column as i64 - self.prev_orig_col,
979 );
980 self.prev_orig_col = original_column as i64;
981
982 self.line_local_index += 1;
983 self.mapping_count += 1;
984 }
985
986 #[inline]
990 pub fn add_named_mapping(
991 &mut self,
992 generated_line: u32,
993 generated_column: u32,
994 source: u32,
995 original_line: u32,
996 original_column: u32,
997 name: u32,
998 ) {
999 self.advance_to_line(generated_line);
1000
1001 if !self.first_in_line {
1002 self.vlq_out.push(b',');
1003 }
1004 self.first_in_line = false;
1005
1006 vlq_encode(
1007 &mut self.vlq_out,
1008 generated_column as i64 - self.prev_gen_col,
1009 );
1010 self.prev_gen_col = generated_column as i64;
1011
1012 vlq_encode(&mut self.vlq_out, source as i64 - self.prev_source);
1013 self.prev_source = source as i64;
1014
1015 vlq_encode(
1016 &mut self.vlq_out,
1017 original_line as i64 - self.prev_orig_line,
1018 );
1019 self.prev_orig_line = original_line as i64;
1020
1021 vlq_encode(
1022 &mut self.vlq_out,
1023 original_column as i64 - self.prev_orig_col,
1024 );
1025 self.prev_orig_col = original_column as i64;
1026
1027 vlq_encode(&mut self.vlq_out, name as i64 - self.prev_name);
1028 self.prev_name = name as i64;
1029
1030 self.line_local_index += 1;
1031 self.mapping_count += 1;
1032 }
1033
1034 #[inline]
1040 pub fn add_named_range_mapping(
1041 &mut self,
1042 generated_line: u32,
1043 generated_column: u32,
1044 source: u32,
1045 original_line: u32,
1046 original_column: u32,
1047 name: u32,
1048 ) {
1049 self.advance_to_line(generated_line);
1050 self.range_entries
1051 .push((self.prev_gen_line, self.line_local_index));
1052
1053 if !self.first_in_line {
1054 self.vlq_out.push(b',');
1055 }
1056 self.first_in_line = false;
1057
1058 vlq_encode(
1059 &mut self.vlq_out,
1060 generated_column as i64 - self.prev_gen_col,
1061 );
1062 self.prev_gen_col = generated_column as i64;
1063
1064 vlq_encode(&mut self.vlq_out, source as i64 - self.prev_source);
1065 self.prev_source = source as i64;
1066
1067 vlq_encode(
1068 &mut self.vlq_out,
1069 original_line as i64 - self.prev_orig_line,
1070 );
1071 self.prev_orig_line = original_line as i64;
1072
1073 vlq_encode(
1074 &mut self.vlq_out,
1075 original_column as i64 - self.prev_orig_col,
1076 );
1077 self.prev_orig_col = original_column as i64;
1078
1079 vlq_encode(&mut self.vlq_out, name as i64 - self.prev_name);
1080 self.prev_name = name as i64;
1081
1082 self.line_local_index += 1;
1083 self.mapping_count += 1;
1084 }
1085
1086 pub fn mapping_count(&self) -> usize {
1088 self.mapping_count
1089 }
1090
1091 #[inline]
1093 fn advance_to_line(&mut self, generated_line: u32) {
1094 while self.prev_gen_line < generated_line {
1095 self.vlq_out.push(b';');
1096 self.prev_gen_line += 1;
1097 self.prev_gen_col = 0;
1098 self.first_in_line = true;
1099 self.line_local_index = 0;
1100 }
1101 }
1102
1103 pub fn to_json(&self) -> String {
1105 use std::io::Write;
1106
1107 let vlq = self.vlq_string();
1108
1109 let sources_size: usize = self.sources.iter().map(|s| s.len() + 4).sum();
1111 let names_size: usize = self.names.iter().map(|n| n.len() + 4).sum();
1112 let content_size: usize = self
1113 .sources_content
1114 .iter()
1115 .map(|c| c.as_ref().map_or(5, |s| s.len() + 4))
1116 .sum();
1117 let capacity = 100 + sources_size + names_size + vlq.len() + content_size;
1118
1119 let mut json: Vec<u8> = Vec::with_capacity(capacity);
1120 json.extend_from_slice(br#"{"version":3"#);
1121
1122 if let Some(ref file) = self.file {
1123 json.extend_from_slice(br#","file":"#);
1124 json_quote_into(&mut json, file);
1125 }
1126
1127 if let Some(ref root) = self.source_root {
1128 json.extend_from_slice(br#","sourceRoot":"#);
1129 json_quote_into(&mut json, root);
1130 }
1131
1132 json.extend_from_slice(br#","sources":["#);
1133 for (i, s) in self.sources.iter().enumerate() {
1134 if i > 0 {
1135 json.push(b',');
1136 }
1137 json_quote_into(&mut json, s);
1138 }
1139 json.push(b']');
1140
1141 if self.sources_content.iter().any(|c| c.is_some()) {
1142 json.extend_from_slice(br#","sourcesContent":["#);
1143 for (i, c) in self.sources_content.iter().enumerate() {
1144 if i > 0 {
1145 json.push(b',');
1146 }
1147 match c {
1148 Some(content) => json_quote_into(&mut json, content),
1149 None => json.extend_from_slice(b"null"),
1150 }
1151 }
1152 json.push(b']');
1153 }
1154
1155 json.extend_from_slice(br#","names":["#);
1156 for (i, n) in self.names.iter().enumerate() {
1157 if i > 0 {
1158 json.push(b',');
1159 }
1160 json_quote_into(&mut json, n);
1161 }
1162 json.push(b']');
1163
1164 json.extend_from_slice(br#","mappings":""#);
1166 json.extend_from_slice(vlq.as_bytes());
1167 json.push(b'"');
1168
1169 if !self.ignore_list.is_empty() {
1170 json.extend_from_slice(br#","ignoreList":["#);
1171 for (i, &idx) in self.ignore_list.iter().enumerate() {
1172 if i > 0 {
1173 json.push(b',');
1174 }
1175 let _ = write!(json, "{idx}");
1176 }
1177 json.push(b']');
1178 }
1179
1180 if let Some(ref range_mappings) = self.encode_range_mappings() {
1181 json.extend_from_slice(br#","rangeMappings":""#);
1182 json.extend_from_slice(range_mappings.as_bytes());
1183 json.push(b'"');
1184 }
1185
1186 if let Some(ref id) = self.debug_id {
1187 json.extend_from_slice(br#","debugId":"#);
1188 json_quote_into(&mut json, id);
1189 }
1190
1191 json.push(b'}');
1192
1193 unsafe { String::from_utf8_unchecked(json) }
1196 }
1197
1198 pub fn to_decoded_map(
1210 &self,
1211 ) -> Result<srcmap_sourcemap::SourceMap, srcmap_sourcemap::ParseError> {
1212 let vlq = self.vlq_string();
1213 let range_mappings = self.encode_range_mappings();
1214
1215 let sources: Vec<String> = match &self.source_root {
1216 Some(root) if !root.is_empty() => {
1217 self.sources.iter().map(|s| format!("{root}{s}")).collect()
1218 }
1219 _ => self.sources.clone(),
1220 };
1221
1222 srcmap_sourcemap::SourceMap::from_vlq_with_range_mappings(
1223 &vlq,
1224 sources,
1225 self.names.clone(),
1226 self.file.clone(),
1227 self.source_root.clone(),
1228 self.sources_content.clone(),
1229 self.ignore_list.clone(),
1230 self.debug_id.clone(),
1231 range_mappings.as_deref(),
1232 )
1233 }
1234
1235 fn encode_range_mappings(&self) -> Option<String> {
1238 if self.range_entries.is_empty() {
1239 return None;
1240 }
1241
1242 let max_line = self.range_entries.last().map_or(0, |&(line, _)| line);
1243 let mut out: Vec<u8> = Vec::new();
1244 let mut entry_idx = 0;
1245
1246 for line in 0..=max_line {
1247 if line > 0 {
1248 out.push(b';');
1249 }
1250 let mut prev_offset: u64 = 0;
1251 let mut first_on_line = true;
1252
1253 while entry_idx < self.range_entries.len() && self.range_entries[entry_idx].0 == line {
1254 if !first_on_line {
1255 out.push(b',');
1256 }
1257 first_on_line = false;
1258 let local_idx = self.range_entries[entry_idx].1 as u64;
1259 let delta = local_idx - prev_offset;
1260 vlq_encode_unsigned(&mut out, delta);
1261 prev_offset = local_idx;
1262 entry_idx += 1;
1263 }
1264 }
1265
1266 while out.last() == Some(&b';') {
1267 out.pop();
1268 }
1269
1270 if out.is_empty() {
1271 return None;
1272 }
1273
1274 Some(unsafe { String::from_utf8_unchecked(out) })
1276 }
1277
1278 fn vlq_string(&self) -> String {
1280 let end = self
1281 .vlq_out
1282 .iter()
1283 .rposition(|&b| b != b';')
1284 .map_or(0, |i| i + 1);
1285 unsafe { String::from_utf8_unchecked(self.vlq_out[..end].to_vec()) }
1287 }
1288}
1289
1290#[cfg(feature = "parallel")]
1295fn encode_mapping_slice(
1296 mappings: &[&Mapping],
1297 init_source: i64,
1298 init_orig_line: i64,
1299 init_orig_col: i64,
1300 init_name: i64,
1301) -> Vec<u8> {
1302 let mut buf = Vec::with_capacity(mappings.len() * 6);
1303 let mut prev_gen_col: i64 = 0;
1304 let mut prev_source = init_source;
1305 let mut prev_orig_line = init_orig_line;
1306 let mut prev_orig_col = init_orig_col;
1307 let mut prev_name = init_name;
1308 let mut first = true;
1309
1310 for m in mappings {
1311 if !first {
1312 buf.push(b',');
1313 }
1314 first = false;
1315
1316 vlq_encode(&mut buf, m.generated_column as i64 - prev_gen_col);
1317 prev_gen_col = m.generated_column as i64;
1318
1319 if let Some(source) = m.source {
1320 vlq_encode(&mut buf, source as i64 - prev_source);
1321 prev_source = source as i64;
1322
1323 vlq_encode(&mut buf, m.original_line as i64 - prev_orig_line);
1324 prev_orig_line = m.original_line as i64;
1325
1326 vlq_encode(&mut buf, m.original_column as i64 - prev_orig_col);
1327 prev_orig_col = m.original_column as i64;
1328
1329 if let Some(name) = m.name {
1330 vlq_encode(&mut buf, name as i64 - prev_name);
1331 prev_name = name as i64;
1332 }
1333 }
1334 }
1335
1336 buf
1337}
1338
1339fn json_quote_into(out: &mut Vec<u8>, s: &str) {
1341 let bytes = s.as_bytes();
1342 out.push(b'"');
1343
1344 let mut start = 0;
1345 for (i, &b) in bytes.iter().enumerate() {
1346 let escape: &[u8] = match b {
1347 b'"' => b"\\\"",
1348 b'\\' => b"\\\\",
1349 b'\n' => b"\\n",
1350 b'\r' => b"\\r",
1351 b'\t' => b"\\t",
1352 0x00..=0x1f => {
1353 out.extend_from_slice(&bytes[start..i]);
1354 let hex = b"0123456789abcdef";
1355 out.extend_from_slice(&[
1356 b'\\',
1357 b'u',
1358 b'0',
1359 b'0',
1360 hex[(b >> 4) as usize],
1361 hex[(b & 0xf) as usize],
1362 ]);
1363 start = i + 1;
1364 continue;
1365 }
1366 _ => continue,
1367 };
1368 out.extend_from_slice(&bytes[start..i]);
1369 out.extend_from_slice(escape);
1370 start = i + 1;
1371 }
1372
1373 out.extend_from_slice(&bytes[start..]);
1374 out.push(b'"');
1375}
1376
1377#[cfg(feature = "parallel")]
1379fn json_quote(s: &str) -> String {
1380 let mut out = Vec::with_capacity(s.len() + 2);
1381 json_quote_into(&mut out, s);
1382 unsafe { String::from_utf8_unchecked(out) }
1384}
1385
1386#[cfg(test)]
1389mod tests {
1390 use super::*;
1391
1392 #[test]
1393 fn empty_generator() {
1394 let builder = SourceMapGenerator::new(None);
1395 let json = builder.to_json();
1396 assert!(json.contains(r#""version":3"#));
1397 assert!(json.contains(r#""mappings":"""#));
1398 }
1399
1400 #[test]
1401 fn simple_mapping() {
1402 let mut builder = SourceMapGenerator::new(Some("output.js".to_string()));
1403 let src = builder.add_source("input.js");
1404 builder.add_mapping(0, 0, src, 0, 0);
1405
1406 let json = builder.to_json();
1407 assert!(json.contains(r#""file":"output.js""#));
1408 assert!(json.contains(r#""sources":["input.js"]"#));
1409
1410 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1412 let loc = sm.original_position_for(0, 0).unwrap();
1413 assert_eq!(sm.source(loc.source), "input.js");
1414 assert_eq!(loc.line, 0);
1415 assert_eq!(loc.column, 0);
1416 }
1417
1418 #[test]
1419 fn mapping_with_name() {
1420 let mut builder = SourceMapGenerator::new(None);
1421 let src = builder.add_source("input.js");
1422 let name = builder.add_name("myFunction");
1423 builder.add_named_mapping(0, 0, src, 0, 0, name);
1424
1425 let json = builder.to_json();
1426 assert!(json.contains(r#""names":["myFunction"]"#));
1427
1428 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1429 let loc = sm.original_position_for(0, 0).unwrap();
1430 assert_eq!(loc.name, Some(0));
1431 assert_eq!(sm.name(0), "myFunction");
1432 }
1433
1434 #[test]
1435 fn multiple_lines() {
1436 let mut builder = SourceMapGenerator::new(None);
1437 let src = builder.add_source("input.js");
1438 builder.add_mapping(0, 0, src, 0, 0);
1439 builder.add_mapping(1, 4, src, 1, 2);
1440 builder.add_mapping(2, 0, src, 2, 0);
1441
1442 let json = builder.to_json();
1443 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1444 assert_eq!(sm.line_count(), 3);
1445
1446 let loc = sm.original_position_for(1, 4).unwrap();
1447 assert_eq!(loc.line, 1);
1448 assert_eq!(loc.column, 2);
1449 }
1450
1451 #[test]
1452 fn multiple_sources() {
1453 let mut builder = SourceMapGenerator::new(None);
1454 let a = builder.add_source("a.js");
1455 let b = builder.add_source("b.js");
1456 builder.add_mapping(0, 0, a, 0, 0);
1457 builder.add_mapping(1, 0, b, 0, 0);
1458
1459 let json = builder.to_json();
1460 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1461
1462 let loc0 = sm.original_position_for(0, 0).unwrap();
1463 let loc1 = sm.original_position_for(1, 0).unwrap();
1464 assert_eq!(sm.source(loc0.source), "a.js");
1465 assert_eq!(sm.source(loc1.source), "b.js");
1466 }
1467
1468 #[test]
1469 fn source_content() {
1470 let mut builder = SourceMapGenerator::new(None);
1471 let src = builder.add_source("input.js");
1472 builder.set_source_content(src, "var x = 1;".to_string());
1473 builder.add_mapping(0, 0, src, 0, 0);
1474
1475 let json = builder.to_json();
1476 assert!(json.contains(r#""sourcesContent":["var x = 1;"]"#));
1477
1478 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1479 assert_eq!(sm.sources_content[0], Some("var x = 1;".to_string()));
1480 }
1481
1482 #[test]
1483 fn source_root() {
1484 let mut builder = SourceMapGenerator::new(None);
1485 builder.set_source_root("src/".to_string());
1486 let src = builder.add_source("input.js");
1487 builder.add_mapping(0, 0, src, 0, 0);
1488
1489 let json = builder.to_json();
1490 assert!(json.contains(r#""sourceRoot":"src/""#));
1491 }
1492
1493 #[test]
1494 fn ignore_list() {
1495 let mut builder = SourceMapGenerator::new(None);
1496 let _app = builder.add_source("app.js");
1497 let lib = builder.add_source("node_modules/lib.js");
1498 builder.add_to_ignore_list(lib);
1499 builder.add_mapping(0, 0, lib, 0, 0);
1500
1501 let json = builder.to_json();
1502 assert!(json.contains(r#""ignoreList":[1]"#));
1503 }
1504
1505 #[test]
1506 fn generated_only_mapping() {
1507 let mut builder = SourceMapGenerator::new(None);
1508 builder.add_generated_mapping(0, 0);
1509
1510 let json = builder.to_json();
1511 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1512 assert!(sm.original_position_for(0, 0).is_none());
1514 }
1515
1516 #[test]
1517 fn dedup_sources_and_names() {
1518 let mut builder = SourceMapGenerator::new(None);
1519 let s1 = builder.add_source("input.js");
1520 let s2 = builder.add_source("input.js"); assert_eq!(s1, s2);
1522
1523 let n1 = builder.add_name("foo");
1524 let n2 = builder.add_name("foo"); assert_eq!(n1, n2);
1526
1527 assert_eq!(builder.sources.len(), 1);
1528 assert_eq!(builder.names.len(), 1);
1529 }
1530
1531 #[test]
1532 fn large_roundtrip() {
1533 let mut builder = SourceMapGenerator::new(Some("bundle.js".to_string()));
1534
1535 for i in 0..5 {
1536 builder.add_source(&format!("src/file{i}.js"));
1537 }
1538 for i in 0..10 {
1539 builder.add_name(&format!("var{i}"));
1540 }
1541
1542 for line in 0..100u32 {
1544 for col in 0..10u32 {
1545 let src = (line * 10 + col) % 5;
1546 let name = if col % 3 == 0 { Some(col % 10) } else { None };
1547
1548 match name {
1549 Some(n) => builder.add_named_mapping(line, col * 10, src, line, col * 5, n),
1550 None => builder.add_mapping(line, col * 10, src, line, col * 5),
1551 }
1552 }
1553 }
1554
1555 let json = builder.to_json();
1556 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1557
1558 assert_eq!(sm.mapping_count(), 1000);
1559 assert_eq!(sm.line_count(), 100);
1560
1561 let loc = sm.original_position_for(50, 30).unwrap();
1563 assert_eq!(loc.line, 50);
1564 assert_eq!(loc.column, 15);
1565 }
1566
1567 #[test]
1568 fn json_escaping() {
1569 let mut builder = SourceMapGenerator::new(None);
1570 let src = builder.add_source("path/with\"quotes.js");
1571 builder.set_source_content(src, "line1\nline2\ttab".to_string());
1572 builder.add_mapping(0, 0, src, 0, 0);
1573
1574 let json = builder.to_json();
1575 let _: serde_json::Value = serde_json::from_str(&json).unwrap();
1577 }
1578
1579 #[test]
1580 fn maybe_add_mapping_skips_redundant() {
1581 let mut builder = SourceMapGenerator::new(None);
1582 let src = builder.add_source("input.js");
1583
1584 assert!(builder.maybe_add_mapping(0, 0, src, 10, 0));
1586 assert!(!builder.maybe_add_mapping(0, 5, src, 10, 0));
1588 assert!(builder.maybe_add_mapping(0, 10, src, 11, 0));
1590 assert!(builder.maybe_add_mapping(1, 0, src, 11, 0));
1592
1593 assert_eq!(builder.mapping_count(), 3);
1594
1595 let json = builder.to_json();
1596 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1597 assert_eq!(sm.mapping_count(), 3);
1598 }
1599
1600 #[test]
1601 fn maybe_add_mapping_different_source() {
1602 let mut builder = SourceMapGenerator::new(None);
1603 let a = builder.add_source("a.js");
1604 let b = builder.add_source("b.js");
1605
1606 assert!(builder.maybe_add_mapping(0, 0, a, 0, 0));
1607 assert!(builder.maybe_add_mapping(0, 5, b, 0, 0));
1609
1610 assert_eq!(builder.mapping_count(), 2);
1611 }
1612
1613 #[test]
1614 fn to_decoded_map_basic() {
1615 let mut builder = SourceMapGenerator::new(Some("output.js".to_string()));
1616 let src = builder.add_source("input.js");
1617 builder.add_mapping(0, 0, src, 0, 0);
1618 builder.add_mapping(1, 4, src, 1, 2);
1619
1620 let sm = builder.to_decoded_map();
1621 assert_eq!(sm.mapping_count(), 2);
1622 assert_eq!(sm.line_count(), 2);
1623
1624 let loc = sm.original_position_for(0, 0).unwrap();
1625 assert_eq!(sm.source(loc.source), "input.js");
1626 assert_eq!(loc.line, 0);
1627 assert_eq!(loc.column, 0);
1628
1629 let loc = sm.original_position_for(1, 4).unwrap();
1630 assert_eq!(loc.line, 1);
1631 assert_eq!(loc.column, 2);
1632 }
1633
1634 #[test]
1635 fn to_decoded_map_with_names() {
1636 let mut builder = SourceMapGenerator::new(None);
1637 let src = builder.add_source("input.js");
1638 let name = builder.add_name("myFunction");
1639 builder.add_named_mapping(0, 0, src, 0, 0, name);
1640
1641 let sm = builder.to_decoded_map();
1642 let loc = sm.original_position_for(0, 0).unwrap();
1643 assert_eq!(loc.name, Some(0));
1644 assert_eq!(sm.name(0), "myFunction");
1645 }
1646
1647 #[test]
1648 fn to_decoded_map_matches_json_roundtrip() {
1649 let mut builder = SourceMapGenerator::new(Some("bundle.js".to_string()));
1650 for i in 0..5 {
1651 builder.add_source(&format!("src/file{i}.js"));
1652 }
1653 for i in 0..10 {
1654 builder.add_name(&format!("var{i}"));
1655 }
1656
1657 for line in 0..50u32 {
1658 for col in 0..10u32 {
1659 let src = (line * 10 + col) % 5;
1660 let name = if col % 3 == 0 { Some(col % 10) } else { None };
1661 match name {
1662 Some(n) => builder.add_named_mapping(line, col * 10, src, line, col * 5, n),
1663 None => builder.add_mapping(line, col * 10, src, line, col * 5),
1664 }
1665 }
1666 }
1667
1668 let sm_decoded = builder.to_decoded_map();
1670 let json = builder.to_json();
1671 let sm_json = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1672
1673 assert_eq!(sm_decoded.mapping_count(), sm_json.mapping_count());
1674 assert_eq!(sm_decoded.line_count(), sm_json.line_count());
1675
1676 for m in sm_json.all_mappings() {
1678 let a = sm_json.original_position_for(m.generated_line, m.generated_column);
1679 let b = sm_decoded.original_position_for(m.generated_line, m.generated_column);
1680 match (a, b) {
1681 (Some(a), Some(b)) => {
1682 assert_eq!(
1683 a.source, b.source,
1684 "source mismatch at ({}, {})",
1685 m.generated_line, m.generated_column
1686 );
1687 assert_eq!(
1688 a.line, b.line,
1689 "line mismatch at ({}, {})",
1690 m.generated_line, m.generated_column
1691 );
1692 assert_eq!(
1693 a.column, b.column,
1694 "column mismatch at ({}, {})",
1695 m.generated_line, m.generated_column
1696 );
1697 assert_eq!(
1698 a.name, b.name,
1699 "name mismatch at ({}, {})",
1700 m.generated_line, m.generated_column
1701 );
1702 }
1703 (None, None) => {}
1704 _ => panic!(
1705 "lookup mismatch at ({}, {})",
1706 m.generated_line, m.generated_column
1707 ),
1708 }
1709 }
1710 }
1711
1712 #[test]
1713 fn to_decoded_map_empty() {
1714 let builder = SourceMapGenerator::new(None);
1715 let sm = builder.to_decoded_map();
1716 assert_eq!(sm.mapping_count(), 0);
1717 assert_eq!(sm.line_count(), 0);
1718 }
1719
1720 #[test]
1721 fn to_decoded_map_generated_only() {
1722 let mut builder = SourceMapGenerator::new(None);
1723 builder.add_generated_mapping(0, 0);
1724
1725 let sm = builder.to_decoded_map();
1726 assert_eq!(sm.mapping_count(), 1);
1727 assert!(sm.original_position_for(0, 0).is_none());
1729 }
1730
1731 #[test]
1732 fn to_decoded_map_multiple_sources() {
1733 let mut builder = SourceMapGenerator::new(None);
1734 let a = builder.add_source("a.js");
1735 let b = builder.add_source("b.js");
1736 builder.add_mapping(0, 0, a, 0, 0);
1737 builder.add_mapping(1, 0, b, 0, 0);
1738
1739 let sm = builder.to_decoded_map();
1740 let loc0 = sm.original_position_for(0, 0).unwrap();
1741 let loc1 = sm.original_position_for(1, 0).unwrap();
1742 assert_eq!(sm.source(loc0.source), "a.js");
1743 assert_eq!(sm.source(loc1.source), "b.js");
1744 }
1745
1746 #[test]
1747 fn to_decoded_map_with_source_content() {
1748 let mut builder = SourceMapGenerator::new(None);
1749 let src = builder.add_source("input.js");
1750 builder.set_source_content(src, "var x = 1;".to_string());
1751 builder.add_mapping(0, 0, src, 0, 0);
1752
1753 let sm = builder.to_decoded_map();
1754 assert_eq!(sm.sources_content[0], Some("var x = 1;".to_string()));
1755 }
1756
1757 #[test]
1758 fn to_decoded_map_reverse_lookup() {
1759 let mut builder = SourceMapGenerator::new(None);
1760 let src = builder.add_source("input.js");
1761 builder.add_mapping(0, 0, src, 10, 5);
1762
1763 let sm = builder.to_decoded_map();
1764 let loc = sm.generated_position_for("input.js", 10, 5).unwrap();
1765 assert_eq!(loc.line, 0);
1766 assert_eq!(loc.column, 0);
1767 }
1768
1769 #[test]
1770 fn to_decoded_map_sparse_lines() {
1771 let mut builder = SourceMapGenerator::new(None);
1772 let src = builder.add_source("input.js");
1773 builder.add_mapping(0, 0, src, 0, 0);
1774 builder.add_mapping(5, 0, src, 5, 0);
1775
1776 let sm = builder.to_decoded_map();
1777 assert_eq!(sm.line_count(), 6);
1778 assert!(sm.original_position_for(0, 0).is_some());
1779 assert!(sm.original_position_for(2, 0).is_none());
1780 assert!(sm.original_position_for(5, 0).is_some());
1781 }
1782
1783 #[test]
1784 fn empty_lines_between_mappings() {
1785 let mut builder = SourceMapGenerator::new(None);
1786 let src = builder.add_source("input.js");
1787 builder.add_mapping(0, 0, src, 0, 0);
1788 builder.add_mapping(5, 0, src, 5, 0);
1790
1791 let json = builder.to_json();
1792 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1793
1794 assert!(sm.original_position_for(0, 0).is_some());
1796 assert!(sm.original_position_for(2, 0).is_none());
1798 assert!(sm.original_position_for(5, 0).is_some());
1800 }
1801
1802 #[test]
1803 fn debug_id() {
1804 let mut builder = SourceMapGenerator::new(None);
1805 builder.set_debug_id("85314830-023f-4cf1-a267-535f4e37bb17".to_string());
1806 let src = builder.add_source("input.js");
1807 builder.add_mapping(0, 0, src, 0, 0);
1808
1809 let json = builder.to_json();
1810 assert!(json.contains(r#""debugId":"85314830-023f-4cf1-a267-535f4e37bb17""#));
1811
1812 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1813 assert_eq!(
1814 sm.debug_id.as_deref(),
1815 Some("85314830-023f-4cf1-a267-535f4e37bb17")
1816 );
1817 }
1818
1819 #[test]
1820 fn scopes_roundtrip() {
1821 use srcmap_scopes::{Binding, GeneratedRange, OriginalScope, Position, ScopeInfo};
1822
1823 let mut builder = SourceMapGenerator::new(Some("bundle.js".to_string()));
1824 let src = builder.add_source("input.js");
1825 builder.set_source_content(
1826 src,
1827 "function hello(name) {\n return name;\n}\nhello('world');".to_string(),
1828 );
1829 let name_hello = builder.add_name("hello");
1830 builder.add_named_mapping(0, 0, src, 0, 0, name_hello);
1831 builder.add_mapping(1, 0, src, 1, 0);
1832
1833 builder.set_scopes(ScopeInfo {
1835 scopes: vec![Some(OriginalScope {
1836 start: Position { line: 0, column: 0 },
1837 end: Position {
1838 line: 3,
1839 column: 14,
1840 },
1841 name: None,
1842 kind: Some("global".to_string()),
1843 is_stack_frame: false,
1844 variables: vec!["hello".to_string()],
1845 children: vec![OriginalScope {
1846 start: Position { line: 0, column: 9 },
1847 end: Position { line: 2, column: 1 },
1848 name: Some("hello".to_string()),
1849 kind: Some("function".to_string()),
1850 is_stack_frame: true,
1851 variables: vec!["name".to_string()],
1852 children: vec![],
1853 }],
1854 })],
1855 ranges: vec![GeneratedRange {
1856 start: Position { line: 0, column: 0 },
1857 end: Position {
1858 line: 3,
1859 column: 14,
1860 },
1861 is_stack_frame: false,
1862 is_hidden: false,
1863 definition: Some(0),
1864 call_site: None,
1865 bindings: vec![Binding::Expression("hello".to_string())],
1866 children: vec![GeneratedRange {
1867 start: Position { line: 0, column: 9 },
1868 end: Position { line: 2, column: 1 },
1869 is_stack_frame: true,
1870 is_hidden: false,
1871 definition: Some(1),
1872 call_site: None,
1873 bindings: vec![Binding::Expression("name".to_string())],
1874 children: vec![],
1875 }],
1876 }],
1877 });
1878
1879 let json = builder.to_json();
1880
1881 assert!(json.contains(r#""scopes":"#));
1883
1884 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1886 assert!(sm.scopes.is_some());
1887
1888 let scopes_info = sm.scopes.unwrap();
1889
1890 assert_eq!(scopes_info.scopes.len(), 1);
1892 let root_scope = scopes_info.scopes[0].as_ref().unwrap();
1893 assert_eq!(root_scope.kind.as_deref(), Some("global"));
1894 assert_eq!(root_scope.variables, vec!["hello"]);
1895 assert_eq!(root_scope.children.len(), 1);
1896
1897 let fn_scope = &root_scope.children[0];
1898 assert_eq!(fn_scope.name.as_deref(), Some("hello"));
1899 assert_eq!(fn_scope.kind.as_deref(), Some("function"));
1900 assert!(fn_scope.is_stack_frame);
1901 assert_eq!(fn_scope.variables, vec!["name"]);
1902
1903 assert_eq!(scopes_info.ranges.len(), 1);
1905 let outer = &scopes_info.ranges[0];
1906 assert_eq!(outer.definition, Some(0));
1907 assert_eq!(
1908 outer.bindings,
1909 vec![Binding::Expression("hello".to_string())]
1910 );
1911 assert_eq!(outer.children.len(), 1);
1912
1913 let inner = &outer.children[0];
1914 assert_eq!(inner.definition, Some(1));
1915 assert!(inner.is_stack_frame);
1916 assert_eq!(
1917 inner.bindings,
1918 vec![Binding::Expression("name".to_string())]
1919 );
1920 }
1921
1922 #[test]
1923 fn scopes_with_inlining_roundtrip() {
1924 use srcmap_scopes::{
1925 Binding, CallSite, GeneratedRange, OriginalScope, Position, ScopeInfo,
1926 };
1927
1928 let mut builder = SourceMapGenerator::new(None);
1929 let src = builder.add_source("input.js");
1930 builder.add_mapping(0, 0, src, 0, 0);
1931
1932 builder.set_scopes(ScopeInfo {
1933 scopes: vec![Some(OriginalScope {
1934 start: Position { line: 0, column: 0 },
1935 end: Position {
1936 line: 10,
1937 column: 0,
1938 },
1939 name: None,
1940 kind: None,
1941 is_stack_frame: false,
1942 variables: vec!["x".to_string()],
1943 children: vec![OriginalScope {
1944 start: Position { line: 1, column: 0 },
1945 end: Position { line: 4, column: 1 },
1946 name: Some("greet".to_string()),
1947 kind: Some("function".to_string()),
1948 is_stack_frame: true,
1949 variables: vec!["msg".to_string()],
1950 children: vec![],
1951 }],
1952 })],
1953 ranges: vec![GeneratedRange {
1954 start: Position { line: 0, column: 0 },
1955 end: Position {
1956 line: 10,
1957 column: 0,
1958 },
1959 is_stack_frame: false,
1960 is_hidden: false,
1961 definition: Some(0),
1962 call_site: None,
1963 bindings: vec![Binding::Expression("_x".to_string())],
1964 children: vec![GeneratedRange {
1965 start: Position { line: 6, column: 0 },
1966 end: Position { line: 8, column: 0 },
1967 is_stack_frame: true,
1968 is_hidden: false,
1969 definition: Some(1),
1970 call_site: Some(CallSite {
1971 source_index: 0,
1972 line: 8,
1973 column: 0,
1974 }),
1975 bindings: vec![Binding::Expression("\"Hello\"".to_string())],
1976 children: vec![],
1977 }],
1978 }],
1979 });
1980
1981 let json = builder.to_json();
1982 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
1983 let info = sm.scopes.unwrap();
1984
1985 let inlined = &info.ranges[0].children[0];
1987 assert_eq!(
1988 inlined.call_site,
1989 Some(CallSite {
1990 source_index: 0,
1991 line: 8,
1992 column: 0,
1993 })
1994 );
1995 assert_eq!(
1996 inlined.bindings,
1997 vec![Binding::Expression("\"Hello\"".to_string())]
1998 );
1999 }
2000
2001 #[test]
2002 fn set_source_content_out_of_bounds() {
2003 let mut builder = SourceMapGenerator::new(None);
2004 builder.set_source_content(0, "content".to_string());
2006 let json = builder.to_json();
2008 assert!(!json.contains("content"));
2009 }
2010
2011 #[test]
2012 fn add_to_ignore_list_dedup() {
2013 let mut builder = SourceMapGenerator::new(None);
2014 let idx = builder.add_source("vendor.js");
2015 builder.add_to_ignore_list(idx);
2016 builder.add_to_ignore_list(idx); let json = builder.to_json();
2018 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2020 assert_eq!(sm.ignore_list, vec![0]);
2021 }
2022
2023 #[test]
2024 fn to_decoded_map_with_source_root() {
2025 let mut builder = SourceMapGenerator::new(None);
2026 builder.set_source_root("src/".to_string());
2027 let src = builder.add_source("app.ts");
2028 builder.add_mapping(0, 0, src, 0, 0);
2029 let sm = builder.to_decoded_map();
2030 assert_eq!(sm.sources, vec!["src/app.ts"]);
2032 }
2033
2034 #[test]
2035 fn json_escaping_special_chars() {
2036 let mut builder = SourceMapGenerator::new(None);
2037 let src = builder.add_source("a.js");
2038 builder.set_source_content(src, "line1\nline2\r\ttab\\\"\x01end".to_string());
2040 builder.add_mapping(0, 0, src, 0, 0);
2041 let json = builder.to_json();
2042 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2044 assert_eq!(
2045 sm.sources_content,
2046 vec![Some("line1\nline2\r\ttab\\\"\x01end".to_string())]
2047 );
2048 }
2049
2050 #[test]
2051 fn json_escaping_in_names() {
2052 let mut builder = SourceMapGenerator::new(None);
2053 let src = builder.add_source("a.js");
2054 let name = builder.add_name("func\"with\\special");
2055 builder.add_named_mapping(0, 0, src, 0, 0, name);
2056 let json = builder.to_json();
2057 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2058 assert_eq!(sm.names[0], "func\"with\\special");
2059 }
2060
2061 #[test]
2062 fn json_escaping_in_sources() {
2063 let mut builder = SourceMapGenerator::new(None);
2064 let src = builder.add_source("path/with\"quotes.js");
2065 builder.add_mapping(0, 0, src, 0, 0);
2066 let json = builder.to_json();
2067 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2068 assert_eq!(sm.sources[0], "path/with\"quotes.js");
2069 }
2070
2071 #[cfg(feature = "parallel")]
2072 mod parallel_tests {
2073 use super::*;
2074
2075 fn build_large_generator(lines: u32, cols_per_line: u32) -> SourceMapGenerator {
2076 let mut builder = SourceMapGenerator::new(Some("bundle.js".to_string()));
2077 for i in 0..10 {
2078 let src = builder.add_source(&format!("src/file{i}.js"));
2079 builder.set_source_content(
2080 src,
2081 format!("// source file {i}\n{}", "x = 1;\n".repeat(100)),
2082 );
2083 }
2084 for i in 0..20 {
2085 builder.add_name(&format!("var{i}"));
2086 }
2087
2088 for line in 0..lines {
2089 for col in 0..cols_per_line {
2090 let src = (line * cols_per_line + col) % 10;
2091 let name = if col % 3 == 0 { Some(col % 20) } else { None };
2092 match name {
2093 Some(n) => builder.add_named_mapping(line, col * 10, src, line, col * 5, n),
2094 None => builder.add_mapping(line, col * 10, src, line, col * 5),
2095 }
2096 }
2097 }
2098 builder
2099 }
2100
2101 #[test]
2102 fn parallel_large_roundtrip() {
2103 let builder = build_large_generator(500, 20);
2104 let json = builder.to_json();
2105 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2106 assert_eq!(sm.mapping_count(), 10000);
2107 assert_eq!(sm.line_count(), 500);
2108
2109 let loc = sm.original_position_for(250, 50).unwrap();
2111 assert_eq!(loc.line, 250);
2112 assert_eq!(loc.column, 25);
2113 }
2114
2115 #[test]
2116 fn parallel_matches_sequential() {
2117 let builder = build_large_generator(500, 20);
2118
2119 let mut sorted: Vec<&Mapping> = builder.mappings.iter().collect();
2121 sorted.sort_unstable_by(|a, b| {
2122 a.generated_line
2123 .cmp(&b.generated_line)
2124 .then(a.generated_column.cmp(&b.generated_column))
2125 });
2126
2127 let sequential = SourceMapGenerator::encode_sequential_impl(&sorted);
2128 let parallel = SourceMapGenerator::encode_parallel_impl(&sorted);
2129 assert_eq!(sequential, parallel);
2130 }
2131
2132 #[test]
2133 fn parallel_with_sparse_lines() {
2134 let mut builder = SourceMapGenerator::new(None);
2135 let src = builder.add_source("input.js");
2136
2137 for i in 0..50 {
2139 let line = i * 100;
2140 for col in 0..100u32 {
2141 builder.add_mapping(line, col * 10, src, line, col * 5);
2142 }
2143 }
2144
2145 let json = builder.to_json();
2146 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2147 assert_eq!(sm.mapping_count(), 5000);
2148
2149 assert!(sm.original_position_for(50, 0).is_none());
2151 let loc = sm.original_position_for(200, 50).unwrap();
2153 assert_eq!(loc.line, 200);
2154 assert_eq!(loc.column, 25);
2155 }
2156 }
2157
2158 #[test]
2161 fn streaming_basic() {
2162 let mut sg = StreamingGenerator::new(Some("out.js".to_string()));
2163 let src = sg.add_source("input.js");
2164 sg.add_mapping(0, 0, src, 0, 0);
2165 sg.add_mapping(1, 0, src, 1, 0);
2166
2167 let json = sg.to_json();
2168 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2169 assert_eq!(sm.sources, vec!["input.js"]);
2170 assert_eq!(sm.mapping_count(), 2);
2171
2172 let loc0 = sm.original_position_for(0, 0).unwrap();
2173 assert_eq!(sm.source(loc0.source), "input.js");
2174 assert_eq!(loc0.line, 0);
2175
2176 let loc1 = sm.original_position_for(1, 0).unwrap();
2177 assert_eq!(loc1.line, 1);
2178 }
2179
2180 #[test]
2181 fn streaming_with_names() {
2182 let mut sg = StreamingGenerator::new(None);
2183 let src = sg.add_source("a.js");
2184 let name = sg.add_name("foo");
2185 sg.add_named_mapping(0, 0, src, 0, 0, name);
2186
2187 let sm = srcmap_sourcemap::SourceMap::from_json(&sg.to_json()).unwrap();
2188 let loc = sm.original_position_for(0, 0).unwrap();
2189 assert_eq!(loc.name, Some(0));
2190 assert_eq!(sm.name(0), "foo");
2191 }
2192
2193 #[test]
2194 fn streaming_generated_only() {
2195 let mut sg = StreamingGenerator::new(None);
2196 let src = sg.add_source("a.js");
2197 sg.add_generated_mapping(0, 0);
2198 sg.add_mapping(0, 5, src, 0, 0);
2199
2200 let sm = srcmap_sourcemap::SourceMap::from_json(&sg.to_json()).unwrap();
2201 assert_eq!(sm.mapping_count(), 2);
2202 assert!(sm.original_position_for(0, 0).is_none());
2203 assert!(sm.original_position_for(0, 5).is_some());
2204 }
2205
2206 #[test]
2207 fn streaming_matches_regular_generator() {
2208 let mut regular = SourceMapGenerator::new(Some("out.js".to_string()));
2209 let mut streaming = StreamingGenerator::new(Some("out.js".to_string()));
2210
2211 let src_r = regular.add_source("a.js");
2212 let src_s = streaming.add_source("a.js");
2213
2214 let name_r = regular.add_name("hello");
2215 let name_s = streaming.add_name("hello");
2216
2217 regular.set_source_content(src_r, "var hello;".to_string());
2218 streaming.set_source_content(src_s, "var hello;".to_string());
2219
2220 regular.add_named_mapping(0, 0, src_r, 0, 0, name_r);
2221 streaming.add_named_mapping(0, 0, src_s, 0, 0, name_s);
2222
2223 regular.add_mapping(0, 10, src_r, 0, 4);
2224 streaming.add_mapping(0, 10, src_s, 0, 4);
2225
2226 regular.add_mapping(1, 0, src_r, 1, 0);
2227 streaming.add_mapping(1, 0, src_s, 1, 0);
2228
2229 let sm_r = srcmap_sourcemap::SourceMap::from_json(®ular.to_json()).unwrap();
2230 let sm_s = srcmap_sourcemap::SourceMap::from_json(&streaming.to_json()).unwrap();
2231
2232 assert_eq!(sm_r.mapping_count(), sm_s.mapping_count());
2233 assert_eq!(sm_r.sources, sm_s.sources);
2234 assert_eq!(sm_r.names, sm_s.names);
2235 assert_eq!(sm_r.sources_content, sm_s.sources_content);
2236
2237 for (a, b) in sm_r.all_mappings().iter().zip(sm_s.all_mappings().iter()) {
2238 assert_eq!(a.generated_line, b.generated_line);
2239 assert_eq!(a.generated_column, b.generated_column);
2240 assert_eq!(a.source, b.source);
2241 assert_eq!(a.original_line, b.original_line);
2242 assert_eq!(a.original_column, b.original_column);
2243 assert_eq!(a.name, b.name);
2244 }
2245 }
2246
2247 #[test]
2248 fn streaming_to_decoded_map() {
2249 let mut sg = StreamingGenerator::new(None);
2250 let src = sg.add_source("test.js");
2251 sg.add_mapping(0, 0, src, 0, 0);
2252 sg.add_mapping(2, 5, src, 1, 3);
2253
2254 let sm = sg.to_decoded_map().unwrap();
2255 assert_eq!(sm.mapping_count(), 2);
2256 assert_eq!(sm.sources, vec!["test.js"]);
2257
2258 let loc = sm.original_position_for(2, 5).unwrap();
2259 assert_eq!(loc.line, 1);
2260 assert_eq!(loc.column, 3);
2261 }
2262
2263 #[test]
2264 fn streaming_source_dedup() {
2265 let mut sg = StreamingGenerator::new(None);
2266 let src1 = sg.add_source("a.js");
2267 let src2 = sg.add_source("a.js");
2268 assert_eq!(src1, src2);
2269 assert_eq!(sg.sources.len(), 1);
2270 }
2271
2272 #[test]
2273 fn streaming_ignore_list() {
2274 let mut sg = StreamingGenerator::new(None);
2275 let src = sg.add_source("vendor.js");
2276 sg.add_to_ignore_list(src);
2277 sg.add_mapping(0, 0, src, 0, 0);
2278
2279 let sm = srcmap_sourcemap::SourceMap::from_json(&sg.to_json()).unwrap();
2280 assert_eq!(sm.ignore_list, vec![0]);
2281 }
2282
2283 #[test]
2284 fn streaming_empty() {
2285 let sg = StreamingGenerator::new(None);
2286 let json = sg.to_json();
2287 let sm = srcmap_sourcemap::SourceMap::from_json(&json).unwrap();
2288 assert_eq!(sm.mapping_count(), 0);
2289 }
2290
2291 #[test]
2292 fn streaming_sparse_lines() {
2293 let mut sg = StreamingGenerator::new(None);
2294 let src = sg.add_source("a.js");
2295 sg.add_mapping(0, 0, src, 0, 0);
2296 sg.add_mapping(5, 0, src, 5, 0);
2297
2298 let sm = srcmap_sourcemap::SourceMap::from_json(&sg.to_json()).unwrap();
2299 assert_eq!(sm.mapping_count(), 2);
2300 assert!(sm.original_position_for(0, 0).is_some());
2301 assert!(sm.original_position_for(5, 0).is_some());
2302 }
2303
2304 #[test]
2307 fn range_mapping_basic() {
2308 let mut builder = SourceMapGenerator::new(None);
2309 let src = builder.add_source("input.js");
2310 builder.add_range_mapping(0, 0, src, 0, 0);
2311 builder.add_mapping(0, 5, src, 0, 10);
2312
2313 let json = builder.to_json();
2314 assert!(json.contains(r#""rangeMappings":"A""#));
2315 }
2316
2317 #[test]
2318 fn range_mapping_multiple_on_line() {
2319 let mut builder = SourceMapGenerator::new(None);
2320 let src = builder.add_source("input.js");
2321 builder.add_range_mapping(0, 0, src, 0, 0);
2322 builder.add_mapping(0, 5, src, 0, 10);
2323 builder.add_range_mapping(0, 10, src, 0, 20);
2324
2325 let json = builder.to_json();
2326 assert!(json.contains(r#""rangeMappings":"A,C""#));
2327 }
2328
2329 #[test]
2330 fn range_mapping_multi_line() {
2331 let mut builder = SourceMapGenerator::new(None);
2332 let src = builder.add_source("input.js");
2333 builder.add_range_mapping(0, 0, src, 0, 0);
2334 builder.add_range_mapping(1, 0, src, 1, 0);
2335
2336 let json = builder.to_json();
2337 assert!(json.contains(r#""rangeMappings":"A;A""#));
2338 }
2339
2340 #[test]
2341 fn no_range_mappings_omits_field() {
2342 let mut builder = SourceMapGenerator::new(None);
2343 let src = builder.add_source("input.js");
2344 builder.add_mapping(0, 0, src, 0, 0);
2345
2346 let json = builder.to_json();
2347 assert!(!json.contains("rangeMappings"));
2348 }
2349
2350 #[test]
2351 fn named_range_mapping() {
2352 let mut builder = SourceMapGenerator::new(None);
2353 let src = builder.add_source("input.js");
2354 let name = builder.add_name("foo");
2355 builder.add_named_range_mapping(0, 0, src, 0, 0, name);
2356
2357 let json = builder.to_json();
2358 assert!(json.contains(r#""rangeMappings":"A""#));
2359 }
2360
2361 #[test]
2362 fn to_decoded_map_preserves_range_mappings() {
2363 let mut builder = SourceMapGenerator::new(None);
2364 let src = builder.add_source("input.js");
2365 builder.add_range_mapping(0, 0, src, 0, 0);
2366 builder.add_mapping(0, 5, src, 0, 10);
2367
2368 let sm = builder.to_decoded_map();
2369 assert!(sm.has_range_mappings());
2370 let mappings = sm.all_mappings();
2371 assert!(mappings[0].is_range_mapping);
2372 assert!(!mappings[1].is_range_mapping);
2373 }
2374
2375 #[test]
2378 fn streaming_range_mapping_basic() {
2379 let mut sg = StreamingGenerator::new(None);
2380 let src = sg.add_source("input.js");
2381 sg.add_range_mapping(0, 0, src, 0, 0);
2382 sg.add_mapping(0, 5, src, 0, 10);
2383
2384 let json = sg.to_json();
2385 assert!(json.contains(r#""rangeMappings":"A""#));
2386 }
2387
2388 #[test]
2389 fn streaming_range_mapping_roundtrip() {
2390 let mut sg = StreamingGenerator::new(None);
2391 let src = sg.add_source("input.js");
2392 sg.add_range_mapping(0, 0, src, 0, 0);
2393 sg.add_mapping(0, 5, src, 0, 10);
2394
2395 let sm = sg.to_decoded_map().unwrap();
2396 assert!(sm.has_range_mappings());
2397 let mappings = sm.all_mappings();
2398 assert!(mappings[0].is_range_mapping);
2399 assert!(!mappings[1].is_range_mapping);
2400 }
2401
2402 #[test]
2403 fn streaming_range_and_named_range() {
2404 let mut sg = StreamingGenerator::new(None);
2405 let src = sg.add_source("input.js");
2406 let name = sg.add_name("foo");
2407 sg.add_range_mapping(0, 0, src, 0, 0);
2408 sg.add_named_range_mapping(0, 10, src, 0, 5, name);
2409
2410 let json = sg.to_json();
2411 assert!(json.contains(r#""rangeMappings":"A,B""#));
2412
2413 let sm = sg.to_decoded_map().unwrap();
2414 assert!(sm.has_range_mappings());
2415 let mappings = sm.all_mappings();
2416 assert!(mappings[0].is_range_mapping);
2417 assert!(mappings[1].is_range_mapping);
2418 }
2419
2420 #[test]
2421 fn streaming_range_mapping_matches_regular() {
2422 let mut regular = SourceMapGenerator::new(None);
2423 let mut streaming = StreamingGenerator::new(None);
2424
2425 let src_r = regular.add_source("input.js");
2426 let src_s = streaming.add_source("input.js");
2427
2428 regular.add_range_mapping(0, 0, src_r, 0, 0);
2429 streaming.add_range_mapping(0, 0, src_s, 0, 0);
2430
2431 regular.add_mapping(0, 5, src_r, 0, 10);
2432 streaming.add_mapping(0, 5, src_s, 0, 10);
2433
2434 regular.add_range_mapping(0, 10, src_r, 0, 20);
2435 streaming.add_range_mapping(0, 10, src_s, 0, 20);
2436
2437 regular.add_range_mapping(1, 0, src_r, 1, 0);
2438 streaming.add_range_mapping(1, 0, src_s, 1, 0);
2439
2440 let json_r = regular.to_json();
2441 let json_s = streaming.to_json();
2442
2443 let sm_r = srcmap_sourcemap::SourceMap::from_json(&json_r).unwrap();
2444 let sm_s = srcmap_sourcemap::SourceMap::from_json(&json_s).unwrap();
2445
2446 assert_eq!(sm_r.mapping_count(), sm_s.mapping_count());
2447
2448 for (a, b) in sm_r.all_mappings().iter().zip(sm_s.all_mappings().iter()) {
2449 assert_eq!(a.generated_line, b.generated_line);
2450 assert_eq!(a.generated_column, b.generated_column);
2451 assert_eq!(a.source, b.source);
2452 assert_eq!(a.original_line, b.original_line);
2453 assert_eq!(a.original_column, b.original_column);
2454 assert_eq!(a.name, b.name);
2455 assert_eq!(a.is_range_mapping, b.is_range_mapping);
2456 }
2457 }
2458}