Skip to main content

parcel_sourcemap/
lib.rs

1#![deny(clippy::all)]
2// Retain the imported public std types and algorithms. These exact legacy source
3// diagnostics are incompatible with Vize's config/current Clippy; first-party
4// policy and every other upstream deny remain unchanged.
5#![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        // TODO: Create new public function that validates if source and name exist?
80        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                // Write a ';' for each line between this and last line, way more efficient than storing empty lines or looping...
177                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                // Source should only be written if there is any
199                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    // Write the sourcemap instance to a buffer
351    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    // Create a sourcemap instance from a buffer
357    pub fn from_buffer(project_root: &str, buf: &[u8]) -> Result<SourceMap, SourceMapError> {
358        // TODO: see if we can use the archived data directly rather than deserializing at all...
359        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                    // First is a generated column that is always present.
557                    read_relative_vlq(&mut generated_column, &mut input)?;
558
559                    // Read source, original line, and original column if the
560                    // mapping has them.
561                    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}