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