1#![deny(clippy::all)]
2#![allow(
6 clippy::disallowed_macros,
7 clippy::disallowed_types,
8 clippy::manual_repeat_n,
9 clippy::needless_return,
10 clippy::single_match,
11 clippy::suspicious_to_owned,
12 clippy::unnecessary_cast,
13 clippy::unnecessary_map_or,
14 clippy::unnecessary_sort_by,
15 clippy::unused_enumerate_index
16)]
17
18pub mod mapping;
19pub mod mapping_line;
20pub mod sourcemap_error;
21pub mod utils;
22mod vlq_utils;
23
24use crate::utils::make_relative_path;
25#[cfg(feature = "json")]
26use data_url::DataUrl;
27pub use mapping::{Mapping, OriginalLocation};
28use mapping_line::MappingLine;
29pub use sourcemap_error::{SourceMapError, SourceMapErrorType};
30#[cfg(feature = "json")]
31use std::borrow::Cow;
32use std::io;
33
34use rkyv::{util::AlignedVec, Archive, Deserialize, Serialize};
35
36use vlq_utils::{is_mapping_separator, read_relative_vlq};
37
38#[derive(Archive, Serialize, Deserialize, Debug, Default, Clone)]
39pub struct SourceMapInner {
40 pub sources: Vec<String>,
41 pub sources_content: Vec<String>,
42 pub names: Vec<String>,
43 pub mapping_lines: Vec<MappingLine>,
44}
45
46#[derive(Debug, Clone)]
47pub struct SourceMap {
48 pub project_root: String,
49 inner: SourceMapInner,
50}
51
52impl SourceMap {
53 pub fn new(project_root: &str) -> Self {
54 Self {
55 project_root: String::from(project_root),
56 inner: SourceMapInner::default(),
57 }
58 }
59
60 fn ensure_lines(&mut self, generated_line: usize) {
61 let mut line = self.inner.mapping_lines.len();
62 if line <= generated_line {
63 self.inner
64 .mapping_lines
65 .reserve(generated_line - self.inner.mapping_lines.len() + 1);
66 while line <= generated_line {
67 self.inner.mapping_lines.push(MappingLine::new());
68 line += 1;
69 }
70 }
71 }
72
73 pub fn add_mapping(
74 &mut self,
75 generated_line: u32,
76 generated_column: u32,
77 original: Option<OriginalLocation>,
78 ) {
79 self.ensure_lines(generated_line as usize);
81 self.inner.mapping_lines[generated_line as usize].add_mapping(generated_column, original);
82 }
83
84 pub fn add_mapping_with_offset(
85 &mut self,
86 mapping: Mapping,
87 line_offset: i64,
88 column_offset: i64,
89 ) -> Result<(), SourceMapError> {
90 let (generated_line, generated_line_overflowed) =
91 (mapping.generated_line as i64).overflowing_add(line_offset);
92 if generated_line_overflowed || generated_line > (u32::MAX as i64) {
93 return Err(SourceMapError::new_with_reason(
94 SourceMapErrorType::UnexpectedlyBigNumber,
95 "mapping.generated_line + line_offset",
96 ));
97 }
98
99 if generated_line < 0 {
100 return Err(SourceMapError::new_with_reason(
101 SourceMapErrorType::UnexpectedNegativeNumber,
102 "mapping.generated_line + line_offset",
103 ));
104 }
105
106 let (generated_column, generated_column_overflowed) =
107 (mapping.generated_column as i64).overflowing_add(column_offset);
108 if generated_column_overflowed || generated_column > (u32::MAX as i64) {
109 return Err(SourceMapError::new_with_reason(
110 SourceMapErrorType::UnexpectedlyBigNumber,
111 "mapping.generated_column + column_offset",
112 ));
113 }
114
115 if generated_column < 0 {
116 return Err(SourceMapError::new_with_reason(
117 SourceMapErrorType::UnexpectedNegativeNumber,
118 "mapping.generated_column + column_offset",
119 ));
120 }
121
122 self.add_mapping(
123 generated_line as u32,
124 generated_column as u32,
125 mapping.original,
126 );
127 Ok(())
128 }
129
130 pub fn find_closest_mapping(
131 &mut self,
132 generated_line: u32,
133 generated_column: u32,
134 ) -> Option<Mapping> {
135 if let Some(line) = self.inner.mapping_lines.get_mut(generated_line as usize) {
136 if let Some(line_mapping) = line.find_closest_mapping(generated_column) {
137 return Some(Mapping {
138 generated_line,
139 generated_column: line_mapping.generated_column,
140 original: line_mapping.original,
141 });
142 }
143 }
144
145 None
146 }
147
148 pub fn get_mappings(&self) -> Vec<Mapping> {
149 let mut mappings = Vec::new();
150 for (generated_line, mapping_line) in self.inner.mapping_lines.iter().enumerate() {
151 for mapping in mapping_line.mappings.iter() {
152 mappings.push(Mapping {
153 generated_line: generated_line as u32,
154 generated_column: mapping.generated_column,
155 original: mapping.original,
156 });
157 }
158 }
159 mappings
160 }
161
162 pub fn write_vlq<W>(&mut self, output: &mut W) -> Result<(), SourceMapError>
163 where
164 W: io::Write,
165 {
166 let mut last_generated_line: u32 = 0;
167 let mut previous_source: i64 = 0;
168 let mut previous_original_line: i64 = 0;
169 let mut previous_original_column: i64 = 0;
170 let mut previous_name: i64 = 0;
171
172 for (generated_line, line_content) in self.inner.mapping_lines.iter_mut().enumerate() {
173 let mut previous_generated_column: u32 = 0;
174 let cloned_generated_line = generated_line as u32;
175 if cloned_generated_line > 0 {
176 output.write_all(
178 &b";".repeat((cloned_generated_line - last_generated_line) as usize),
179 )?;
180 }
181
182 line_content.ensure_sorted();
183
184 let mut is_first_mapping: bool = true;
185 for mapping in &line_content.mappings {
186 let generated_column = mapping.generated_column;
187 let original_location_option = &mapping.original;
188 if !is_first_mapping {
189 output.write_all(b",")?;
190 }
191
192 vlq::encode(
193 (generated_column - previous_generated_column) as i64,
194 output,
195 )?;
196 previous_generated_column = generated_column;
197
198 if let Some(original) = &original_location_option {
200 let original_source = original.source as i64;
201 vlq::encode(original_source - previous_source, output)?;
202 previous_source = original_source;
203
204 let original_line = original.original_line as i64;
205 vlq::encode((original_line - previous_original_line) as i64, output)?;
206 previous_original_line = original_line;
207
208 let original_column = original.original_column as i64;
209 vlq::encode(original_column - previous_original_column, output)?;
210 previous_original_column = original_column;
211
212 if let Some(name) = original.name {
213 let original_name = name as i64;
214 vlq::encode(original_name - previous_name, output)?;
215 previous_name = original_name;
216 }
217 }
218
219 is_first_mapping = false;
220 }
221
222 last_generated_line = cloned_generated_line;
223 }
224
225 Ok(())
226 }
227
228 pub fn add_source(&mut self, source: &str) -> u32 {
229 let relative_source = make_relative_path(self.project_root.as_str(), source);
230 match self
231 .inner
232 .sources
233 .iter()
234 .position(|s| relative_source.eq(s))
235 {
236 Some(i) => i as u32,
237 None => {
238 self.inner.sources.push(relative_source);
239 (self.inner.sources.len() - 1) as u32
240 }
241 }
242 }
243
244 pub fn add_sources<I: AsRef<str>>(&mut self, sources: Vec<I>) -> Vec<u32> {
245 self.inner.sources.reserve(sources.len());
246 let mut result_vec = Vec::with_capacity(sources.len());
247 for s in sources.iter() {
248 result_vec.push(self.add_source(s.as_ref()));
249 }
250 result_vec
251 }
252
253 pub fn get_source_index(&self, source: &str) -> Result<Option<u32>, SourceMapError> {
254 let normalized_source = make_relative_path(self.project_root.as_str(), source);
255 Ok(self
256 .inner
257 .sources
258 .iter()
259 .position(|s| normalized_source.eq(s))
260 .map(|v| v as u32))
261 }
262
263 pub fn get_source(&self, index: u32) -> Result<&str, SourceMapError> {
264 self.inner
265 .sources
266 .get(index as usize)
267 .map(|v| v.as_str())
268 .ok_or_else(|| SourceMapError::new(SourceMapErrorType::SourceOutOfRange))
269 }
270
271 pub fn get_sources(&self) -> &Vec<String> {
272 &self.inner.sources
273 }
274
275 pub fn add_name(&mut self, name: &str) -> u32 {
276 return match self.inner.names.iter().position(|s| name.eq(s)) {
277 Some(i) => i as u32,
278 None => {
279 self.inner.names.push(String::from(name));
280 (self.inner.names.len() - 1) as u32
281 }
282 };
283 }
284
285 pub fn add_names<I: AsRef<str>>(&mut self, names: Vec<I>) -> Vec<u32> {
286 self.inner.names.reserve(names.len());
287 return names.iter().map(|n| self.add_name(n.as_ref())).collect();
288 }
289
290 pub fn get_name_index(&self, name: &str) -> Option<u32> {
291 self.inner
292 .names
293 .iter()
294 .position(|n| name.eq(n))
295 .map(|v| v as u32)
296 }
297
298 pub fn get_name(&self, index: u32) -> Result<&str, SourceMapError> {
299 self.inner
300 .names
301 .get(index as usize)
302 .map(|v| v.as_str())
303 .ok_or_else(|| SourceMapError::new(SourceMapErrorType::NameOutOfRange))
304 }
305
306 pub fn get_names(&self) -> &Vec<String> {
307 &self.inner.names
308 }
309
310 pub fn set_source_content(
311 &mut self,
312 source_index: usize,
313 source_content: &str,
314 ) -> Result<(), SourceMapError> {
315 if self.inner.sources.is_empty() || source_index > self.inner.sources.len() - 1 {
316 return Err(SourceMapError::new(SourceMapErrorType::SourceOutOfRange));
317 }
318
319 let sources_content_len = self.inner.sources_content.len();
320 if sources_content_len > source_index {
321 self.inner.sources_content[source_index] = String::from(source_content);
322 } else {
323 self.inner
324 .sources_content
325 .reserve((source_index + 1) - sources_content_len);
326 let items_to_add = source_index - sources_content_len;
327 for _n in 0..items_to_add {
328 self.inner.sources_content.push(String::from(""));
329 }
330 self.inner
331 .sources_content
332 .push(String::from(source_content));
333 }
334
335 Ok(())
336 }
337
338 pub fn get_source_content(&self, index: u32) -> Result<&str, SourceMapError> {
339 self.inner
340 .sources_content
341 .get(index as usize)
342 .map(|v| v.as_str())
343 .ok_or_else(|| SourceMapError::new(SourceMapErrorType::SourceOutOfRange))
344 }
345
346 pub fn get_sources_content(&self) -> &Vec<String> {
347 &self.inner.sources_content
348 }
349
350 pub fn to_buffer(&self, output: &mut AlignedVec) -> Result<(), SourceMapError> {
352 *output = rkyv::to_bytes::<rkyv::rancor::Error>(&self.inner)?;
353 Ok(())
354 }
355
356 pub fn from_buffer(project_root: &str, buf: &[u8]) -> Result<SourceMap, SourceMapError> {
358 let inner = rkyv::from_bytes::<SourceMapInner, rkyv::rancor::Error>(buf)?;
360 Ok(SourceMap {
361 project_root: String::from(project_root),
362 inner,
363 })
364 }
365
366 pub fn add_sourcemap(
367 &mut self,
368 sourcemap: &mut SourceMap,
369 line_offset: i64,
370 ) -> Result<(), SourceMapError> {
371 self.inner.sources.reserve(sourcemap.inner.sources.len());
372 let mut source_indexes = Vec::with_capacity(sourcemap.inner.sources.len());
373 let sources = std::mem::take(&mut sourcemap.inner.sources);
374 for s in sources.iter() {
375 source_indexes.push(self.add_source(s));
376 }
377
378 self.inner.names.reserve(sourcemap.inner.names.len());
379 let mut names_indexes = Vec::with_capacity(sourcemap.inner.names.len());
380 let names = std::mem::take(&mut sourcemap.inner.names);
381 for n in names.iter() {
382 names_indexes.push(self.add_name(n));
383 }
384
385 self.inner
386 .sources_content
387 .reserve(sourcemap.inner.sources_content.len());
388 let sources_content = std::mem::take(&mut sourcemap.inner.sources_content);
389 for (i, source_content_str) in sources_content.iter().enumerate() {
390 if let Some(source_index) = source_indexes.get(i) {
391 self.set_source_content(*source_index as usize, source_content_str)?;
392 }
393 }
394
395 let mapping_lines = std::mem::take(&mut sourcemap.inner.mapping_lines);
396 for (line, mapping_line) in mapping_lines.into_iter().enumerate() {
397 let generated_line = (line as i64) + line_offset;
398 if generated_line >= 0 {
399 let mut line = mapping_line;
400 for mapping in line.mappings.iter_mut() {
401 match &mut mapping.original {
402 Some(original_mapping_location) => {
403 original_mapping_location.source = match source_indexes
404 .get(original_mapping_location.source as usize)
405 {
406 Some(new_source_index) => *new_source_index,
407 None => {
408 return Err(SourceMapError::new(
409 SourceMapErrorType::SourceOutOfRange,
410 ));
411 }
412 };
413
414 original_mapping_location.name = match original_mapping_location.name {
415 Some(name_index) => match names_indexes.get(name_index as usize) {
416 Some(new_name_index) => Some(*new_name_index),
417 None => {
418 return Err(SourceMapError::new(
419 SourceMapErrorType::NameOutOfRange,
420 ));
421 }
422 },
423 None => None,
424 };
425 }
426 None => {}
427 }
428 }
429
430 self.ensure_lines(generated_line as usize);
431 self.inner.mapping_lines[generated_line as usize] = line;
432 }
433 }
434
435 Ok(())
436 }
437
438 pub fn extends(&mut self, original_sourcemap: &mut SourceMap) -> Result<(), SourceMapError> {
439 self.inner
440 .sources
441 .reserve(original_sourcemap.inner.sources.len());
442 let mut source_indexes = Vec::with_capacity(original_sourcemap.inner.sources.len());
443 for s in original_sourcemap.inner.sources.iter() {
444 source_indexes.push(self.add_source(s));
445 }
446
447 self.inner
448 .names
449 .reserve(original_sourcemap.inner.names.len());
450 let mut names_indexes = Vec::with_capacity(original_sourcemap.inner.names.len());
451 for n in original_sourcemap.inner.names.iter() {
452 names_indexes.push(self.add_name(n));
453 }
454
455 self.inner
456 .sources_content
457 .reserve(original_sourcemap.inner.sources_content.len());
458 for (i, source_content_str) in original_sourcemap.inner.sources_content.iter().enumerate() {
459 if let Some(source_index) = source_indexes.get(i) {
460 self.set_source_content(*source_index as usize, source_content_str)?;
461 }
462 }
463
464 for (_generated_line, line_content) in self.inner.mapping_lines.iter_mut().enumerate() {
465 for mapping in line_content.mappings.iter_mut() {
466 let original_location_option = &mut mapping.original;
467 if let Some(original_location) = original_location_option {
468 let found_mapping = original_sourcemap.find_closest_mapping(
469 original_location.original_line,
470 original_location.original_column,
471 );
472 match found_mapping {
473 Some(original_mapping) => match original_mapping.original {
474 Some(original_mapping_location) => {
475 *original_location_option = Some(OriginalLocation::new(
476 original_mapping_location.original_line,
477 original_mapping_location.original_column,
478 match source_indexes
479 .get(original_mapping_location.source as usize)
480 {
481 Some(new_source_index) => *new_source_index,
482 None => {
483 return Err(SourceMapError::new(
484 SourceMapErrorType::SourceOutOfRange,
485 ));
486 }
487 },
488 match original_mapping_location.name {
489 Some(name_index) => {
490 match names_indexes.get(name_index as usize) {
491 Some(new_name_index) => Some(*new_name_index),
492 None => {
493 return Err(SourceMapError::new(
494 SourceMapErrorType::NameOutOfRange,
495 ));
496 }
497 }
498 }
499 None => None,
500 },
501 ));
502 }
503 None => {
504 *original_location_option = None;
505 }
506 },
507 None => {
508 *original_location_option = None;
509 }
510 }
511 }
512 }
513 }
514
515 Ok(())
516 }
517
518 pub fn add_vlq_map<I: AsRef<str>>(
519 &mut self,
520 input: &[u8],
521 sources: Vec<I>,
522 sources_content: Vec<I>,
523 names: Vec<I>,
524 line_offset: i64,
525 column_offset: i64,
526 ) -> Result<(), SourceMapError> {
527 let mut generated_line: i64 = line_offset;
528 let mut generated_column: i64 = column_offset;
529 let mut original_line = 0;
530 let mut original_column = 0;
531 let mut source = 0;
532 let mut name = 0;
533
534 let source_indexes: Vec<u32> = self.add_sources(sources);
535 let name_indexes: Vec<u32> = self.add_names(names);
536
537 self.inner.sources_content.reserve(sources_content.len());
538 for (i, source_content) in sources_content.iter().enumerate() {
539 if let Some(source_index) = source_indexes.get(i) {
540 self.set_source_content(*source_index as usize, source_content.as_ref())?;
541 }
542 }
543
544 let mut input = input.iter().cloned().peekable();
545 while let Some(byte) = input.peek().cloned() {
546 match byte {
547 b';' => {
548 generated_line += 1;
549 generated_column = column_offset;
550 input.next().unwrap();
551 }
552 b',' => {
553 input.next().unwrap();
554 }
555 _ => {
556 read_relative_vlq(&mut generated_column, &mut input)?;
558
559 let original = if input.peek().cloned().map_or(true, is_mapping_separator) {
562 None
563 } else {
564 read_relative_vlq(&mut source, &mut input)?;
565 read_relative_vlq(&mut original_line, &mut input)?;
566 read_relative_vlq(&mut original_column, &mut input)?;
567 Some(OriginalLocation::new(
568 original_line as u32,
569 original_column as u32,
570 match source_indexes.get(source as usize) {
571 Some(v) => *v,
572 None => {
573 return Err(SourceMapError::new(
574 SourceMapErrorType::SourceOutOfRange,
575 ));
576 }
577 },
578 if input.peek().cloned().map_or(true, is_mapping_separator) {
579 None
580 } else {
581 read_relative_vlq(&mut name, &mut input)?;
582 Some(match name_indexes.get(name as usize) {
583 Some(v) => *v,
584 None => {
585 return Err(SourceMapError::new(
586 SourceMapErrorType::NameOutOfRange,
587 ));
588 }
589 })
590 },
591 ))
592 };
593
594 if generated_line >= 0 {
595 self.add_mapping(generated_line as u32, generated_column as u32, original);
596 }
597 }
598 }
599 }
600
601 Ok(())
602 }
603
604 pub fn offset_columns(
605 &mut self,
606 generated_line: u32,
607 generated_column: u32,
608 generated_column_offset: i64,
609 ) -> Result<(), SourceMapError> {
610 match self.inner.mapping_lines.get_mut(generated_line as usize) {
611 Some(line) => line.offset_columns(generated_column, generated_column_offset),
612 None => Ok(()),
613 }
614 }
615
616 pub fn offset_lines(
617 &mut self,
618 generated_line: u32,
619 generated_line_offset: i64,
620 ) -> Result<(), SourceMapError> {
621 if generated_line_offset == 0 || self.inner.mapping_lines.is_empty() {
622 return Ok(());
623 }
624
625 let (start_line, overflowed) =
626 (generated_line as i64).overflowing_add(generated_line_offset);
627 if overflowed || start_line > (u32::MAX as i64) {
628 return Err(SourceMapError::new_with_reason(
629 SourceMapErrorType::UnexpectedNegativeNumber,
630 "column + column_offset cannot be negative",
631 ));
632 }
633
634 let line = generated_line as usize;
635 let abs_offset = generated_line_offset.unsigned_abs() as usize;
636 if generated_line_offset > 0 {
637 if line > self.inner.mapping_lines.len() {
638 self.ensure_lines(line + abs_offset);
639 } else {
640 self.inner
641 .mapping_lines
642 .splice(line..line, (0..abs_offset).map(|_| MappingLine::new()));
643 }
644 } else {
645 self.inner.mapping_lines.drain(line - abs_offset..line);
646 }
647
648 Ok(())
649 }
650
651 pub fn add_empty_map(
652 &mut self,
653 source: &str,
654 source_content: &str,
655 line_offset: i64,
656 ) -> Result<(), SourceMapError> {
657 let source_index = self.add_source(source);
658 self.set_source_content(source_index as usize, source_content)?;
659
660 for (line_count, _line) in source_content.lines().enumerate() {
661 let generated_line = (line_count as i64) + line_offset;
662 if generated_line >= 0 {
663 self.add_mapping(
664 generated_line as u32,
665 0,
666 Some(OriginalLocation::new(
667 line_count as u32,
668 0,
669 source_index,
670 None,
671 )),
672 )
673 }
674 }
675
676 Ok(())
677 }
678
679 #[cfg(feature = "json")]
680 pub fn from_json(project_root: &str, input: &str) -> Result<Self, SourceMapError> {
681 #[derive(serde::Deserialize)]
682 #[serde(rename_all = "camelCase")]
683 struct JSONSourceMap<'a> {
684 mappings: &'a str,
685 #[serde(borrow)]
686 sources: Vec<Cow<'a, str>>,
687 #[serde(borrow, default)]
688 sources_content: Vec<Option<Cow<'a, str>>>,
689 names: Vec<Cow<'a, str>>,
690 }
691
692 let json: JSONSourceMap = serde_json::from_str(input)?;
693 let mut sources_content = Vec::with_capacity(json.sources.len());
694 for i in 0..json.sources.len() {
695 sources_content.push(if let Some(Some(content)) = json.sources_content.get(i) {
696 content.to_owned()
697 } else {
698 "".into()
699 });
700 }
701
702 let mut sm = Self::new(project_root);
703 sm.add_vlq_map(
704 json.mappings.as_bytes(),
705 json.sources,
706 sources_content,
707 json.names,
708 0,
709 0,
710 )?;
711 Ok(sm)
712 }
713
714 #[cfg(feature = "json")]
715 pub fn to_json(&mut self, source_root: Option<&str>) -> Result<String, SourceMapError> {
716 let mut vlq_output: Vec<u8> = Vec::new();
717 self.write_vlq(&mut vlq_output)?;
718
719 #[derive(serde::Serialize)]
720 #[serde(rename_all = "camelCase")]
721 struct JSONSourceMap<'a> {
722 version: u8,
723 source_root: Option<&'a str>,
724 mappings: &'a str,
725 sources: &'a Vec<String>,
726 sources_content: &'a Vec<String>,
727 names: &'a Vec<String>,
728 }
729
730 let sm = JSONSourceMap {
731 version: 3,
732 source_root,
733 mappings: unsafe { std::str::from_utf8_unchecked(&vlq_output) },
734 sources: self.get_sources(),
735 sources_content: self.get_sources_content(),
736 names: self.get_names(),
737 };
738
739 Ok(serde_json::to_string(&sm)?)
740 }
741
742 #[cfg(feature = "json")]
743 pub fn from_data_url(project_root: &str, data_url: &str) -> Result<Self, SourceMapError> {
744 let url = DataUrl::process(data_url)?;
745 let mime = url.mime_type();
746 if mime.type_ != "application" || mime.subtype != "json" {
747 return Err(SourceMapError::new(SourceMapErrorType::DataUrlError));
748 }
749
750 let (data, _) = url
751 .decode_to_vec()
752 .map_err(|_| SourceMapError::new(SourceMapErrorType::DataUrlError))?;
753 let input = unsafe { std::str::from_utf8_unchecked(data.as_slice()) };
754
755 Self::from_json(project_root, input)
756 }
757
758 #[cfg(feature = "json")]
759 pub fn to_data_url(&mut self, source_root: Option<&str>) -> Result<String, SourceMapError> {
760 let buf = self.to_json(source_root)?;
761 let b64 = base64_simd::Base64::STANDARD.encode_to_boxed_str(buf.as_bytes());
762 Ok(format!(
763 "data:application/json;charset=utf-8;base64,{}",
764 b64
765 ))
766 }
767}
768
769#[allow(non_fmt_panics)]
770#[test]
771fn test_buffers() {
772 let map = SourceMap::new("/");
773 let mut output = AlignedVec::new();
774 match map.to_buffer(&mut output) {
775 Ok(_) => {}
776 Err(err) => panic!(err),
777 }
778 match SourceMap::from_buffer("/", &output) {
779 Ok(map) => {
780 println!("{:?}", map)
781 }
782 Err(err) => panic!(err),
783 }
784}
785
786#[cfg(feature = "json")]
787#[test]
788fn test_to_json() {
789 let mut map = SourceMap::new("/");
790 map.add_mapping(1, 1, None);
791 let json = map.to_json(Some("/")).unwrap();
792 assert_eq!(
793 json,
794 r#"{"version":3,"sourceRoot":"/","mappings":";C","sources":[],"sourcesContent":[],"names":[]}"#
795 );
796}
797
798#[cfg(feature = "json")]
799#[test]
800fn test_from_json() {
801 let map = SourceMap::from_json("/", r#"{"version":3,"sourceRoot":"/","mappings":";C","sources":[],"sourcesContent":[],"names":[]}"#).unwrap();
802 let mappings = map.get_mappings();
803 assert_eq!(
804 mappings,
805 vec![Mapping {
806 generated_line: 1,
807 generated_column: 1,
808 original: None
809 }]
810 );
811}
812
813#[cfg(feature = "json")]
814#[test]
815fn test_from_json_no_sources_content() {
816 let map = SourceMap::from_json(
817 "/",
818 r#"{"version":3,"sourceRoot":"/","mappings":";C","sources":["file.js"],"names":[]}"#,
819 )
820 .unwrap();
821 assert_eq!(map.get_sources_content(), &vec![""]);
822 let map = SourceMap::from_json(
823 "/",
824 r#"{"version":3,"sourceRoot":"/","mappings":";C","sources":["file.js"],"sourcesContent":[null],"names":[]}"#,
825 )
826 .unwrap();
827 assert_eq!(map.get_sources_content(), &vec![""]);
828}
829
830#[cfg(feature = "json")]
831#[test]
832fn test_to_data_url() {
833 let mut map = SourceMap::new("/");
834 map.add_mapping(1, 1, None);
835 let url = map.to_data_url(Some("/")).unwrap();
836 println!("{}", url);
837 assert_eq!(
838 url,
839 r#"data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VSb290IjoiLyIsIm1hcHBpbmdzIjoiO0MiLCJzb3VyY2VzIjpbXSwic291cmNlc0NvbnRlbnQiOltdLCJuYW1lcyI6W119"#
840 );
841}
842
843#[cfg(feature = "json")]
844#[test]
845fn test_from_data_url() {
846 let map = SourceMap::from_data_url("/", r#"data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VSb290IjoiLyIsIm1hcHBpbmdzIjoiO0MiLCJzb3VyY2VzIjpbXSwic291cmNlc0NvbnRlbnQiOltdLCJuYW1lcyI6W119"#).unwrap();
847 let mappings = map.get_mappings();
848 assert_eq!(
849 mappings,
850 vec![Mapping {
851 generated_line: 1,
852 generated_column: 1,
853 original: None
854 }]
855 );
856}