speedy_parcel_sourcemap/
lib.rs

1#![deny(clippy::all)]
2
3pub mod mapping;
4pub mod mapping_line;
5pub mod sourcemap_error;
6pub mod utils;
7mod vlq_utils;
8
9use crate::utils::make_relative_path;
10pub use mapping::{Mapping, OriginalLocation};
11use mapping_line::MappingLine;
12pub use sourcemap_error::{SourceMapError, SourceMapErrorType};
13use std::io;
14
15use rkyv::{
16    archived_root,
17    de::deserializers::AllocDeserializer,
18    ser::{serializers::AlignedSerializer, Serializer},
19    AlignedVec, Archive, Deserialize, Serialize,
20};
21
22use vlq_utils::{is_mapping_separator, read_relative_vlq};
23
24#[derive(Archive, Serialize, Deserialize, Debug, Default, Clone)]
25pub struct SourceMapInner {
26    pub sources: Vec<String>,
27    pub sources_content: Vec<String>,
28    pub names: Vec<String>,
29    pub mapping_lines: Vec<MappingLine>,
30}
31
32#[derive(Debug, Clone)]
33pub struct SourceMap {
34    pub project_root: String,
35    inner: SourceMapInner,
36}
37
38impl SourceMap {
39    pub fn new(project_root: &str) -> Self {
40        Self {
41            project_root: String::from(project_root),
42            inner: SourceMapInner::default(),
43        }
44    }
45
46    fn ensure_lines(&mut self, generated_line: usize) {
47        let mut line = self.inner.mapping_lines.len();
48        if line <= generated_line {
49            self.inner
50                .mapping_lines
51                .reserve(generated_line - self.inner.mapping_lines.len() + 1);
52            while line <= generated_line {
53                self.inner.mapping_lines.push(MappingLine::new());
54                line += 1;
55            }
56        }
57    }
58
59    pub fn add_mapping(
60        &mut self,
61        generated_line: u32,
62        generated_column: u32,
63        original: Option<OriginalLocation>,
64    ) {
65        // TODO: Create new public function that validates if source and name exist?
66        self.ensure_lines(generated_line as usize);
67        self.inner.mapping_lines[generated_line as usize].add_mapping(generated_column, original);
68    }
69
70    pub fn add_mapping_with_offset(
71        &mut self,
72        mapping: Mapping,
73        line_offset: i64,
74        column_offset: i64,
75    ) -> Result<(), SourceMapError> {
76        let (generated_line, generated_line_overflowed) =
77            (mapping.generated_line as i64).overflowing_add(line_offset);
78        if generated_line_overflowed || generated_line > (u32::MAX as i64) {
79            return Err(SourceMapError::new_with_reason(
80                SourceMapErrorType::UnexpectedlyBigNumber,
81                "mapping.generated_line + line_offset",
82            ));
83        }
84
85        if generated_line < 0 {
86            return Err(SourceMapError::new_with_reason(
87                SourceMapErrorType::UnexpectedNegativeNumber,
88                "mapping.generated_line + line_offset",
89            ));
90        }
91
92        let (generated_column, generated_column_overflowed) =
93            (mapping.generated_column as i64).overflowing_add(column_offset);
94        if generated_column_overflowed || generated_column > (u32::MAX as i64) {
95            return Err(SourceMapError::new_with_reason(
96                SourceMapErrorType::UnexpectedlyBigNumber,
97                "mapping.generated_column + column_offset",
98            ));
99        }
100
101        if generated_column < 0 {
102            return Err(SourceMapError::new_with_reason(
103                SourceMapErrorType::UnexpectedNegativeNumber,
104                "mapping.generated_column + column_offset",
105            ));
106        }
107
108        self.add_mapping(
109            generated_line as u32,
110            generated_column as u32,
111            mapping.original,
112        );
113        Ok(())
114    }
115
116    pub fn find_closest_mapping(
117        &mut self,
118        generated_line: u32,
119        generated_column: u32,
120    ) -> Option<Mapping> {
121        if let Some(line) = self.inner.mapping_lines.get_mut(generated_line as usize) {
122            if let Some(line_mapping) = line.find_closest_mapping(generated_column) {
123                return Some(Mapping {
124                    generated_line,
125                    generated_column: line_mapping.generated_column,
126                    original: line_mapping.original,
127                });
128            }
129        }
130
131        None
132    }
133
134    pub fn get_mappings(&self) -> Vec<Mapping> {
135        let mut mappings = Vec::new();
136        for (generated_line, mapping_line) in self.inner.mapping_lines.iter().enumerate() {
137            for mapping in mapping_line.mappings.iter() {
138                mappings.push(Mapping {
139                    generated_line: generated_line as u32,
140                    generated_column: mapping.generated_column,
141                    original: mapping.original,
142                });
143            }
144        }
145        mappings
146    }
147
148    pub fn write_vlq<W>(&mut self, output: &mut W) -> Result<(), SourceMapError>
149    where
150        W: io::Write,
151    {
152        let mut last_generated_line: u32 = 0;
153        let mut previous_source: i64 = 0;
154        let mut previous_original_line: i64 = 0;
155        let mut previous_original_column: i64 = 0;
156        let mut previous_name: i64 = 0;
157
158        for (generated_line, line_content) in self.inner.mapping_lines.iter_mut().enumerate() {
159            let mut previous_generated_column: u32 = 0;
160            let cloned_generated_line = generated_line as u32;
161            if cloned_generated_line > 0 {
162                // Write a ';' for each line between this and last line, way more efficient than storing empty lines or looping...
163                output.write_all(
164                    &b";".repeat((cloned_generated_line - last_generated_line) as usize),
165                )?;
166            }
167
168            line_content.ensure_sorted();
169
170            let mut is_first_mapping: bool = true;
171            for mapping in &line_content.mappings {
172                let generated_column = mapping.generated_column;
173                let original_location_option = &mapping.original;
174                if !is_first_mapping {
175                    output.write_all(b",")?;
176                }
177
178                vlq::encode(
179                    (generated_column - previous_generated_column) as i64,
180                    output,
181                )?;
182                previous_generated_column = generated_column;
183
184                // Source should only be written if there is any
185                if let Some(original) = &original_location_option {
186                    let original_source = original.source as i64;
187                    vlq::encode(original_source - previous_source, output)?;
188                    previous_source = original_source;
189
190                    let original_line = original.original_line as i64;
191                    vlq::encode((original_line - previous_original_line) as i64, output)?;
192                    previous_original_line = original_line;
193
194                    let original_column = original.original_column as i64;
195                    vlq::encode(original_column - previous_original_column, output)?;
196                    previous_original_column = original_column;
197
198                    if let Some(name) = original.name {
199                        let original_name = name as i64;
200                        vlq::encode(original_name - previous_name, output)?;
201                        previous_name = original_name;
202                    }
203                }
204
205                is_first_mapping = false;
206            }
207
208            last_generated_line = cloned_generated_line;
209        }
210
211        Ok(())
212    }
213
214    pub fn add_source(&mut self, source: &str) -> u32 {
215        let relative_source = make_relative_path(self.project_root.as_str(), source);
216        match self
217            .inner
218            .sources
219            .iter()
220            .position(|s| relative_source.eq(s))
221        {
222            Some(i) => i as u32,
223            None => {
224                self.inner.sources.push(relative_source);
225                (self.inner.sources.len() - 1) as u32
226            }
227        }
228    }
229
230    pub fn add_sources(&mut self, sources: Vec<&str>) -> Vec<u32> {
231        self.inner.sources.reserve(sources.len());
232        let mut result_vec = Vec::with_capacity(sources.len());
233        for s in sources.iter() {
234            result_vec.push(self.add_source(s));
235        }
236        result_vec
237    }
238
239    pub fn get_source_index(&self, source: &str) -> Result<Option<u32>, SourceMapError> {
240        let normalized_source = make_relative_path(self.project_root.as_str(), source);
241        Ok(self
242            .inner
243            .sources
244            .iter()
245            .position(|s| normalized_source.eq(s))
246            .map(|v| v as u32))
247    }
248
249    pub fn get_source(&self, index: u32) -> Result<&str, SourceMapError> {
250        self.inner
251            .sources
252            .get(index as usize)
253            .map(|v| v.as_str())
254            .ok_or_else(|| SourceMapError::new(SourceMapErrorType::SourceOutOfRange))
255    }
256
257    pub fn get_sources(&self) -> &Vec<String> {
258        &self.inner.sources
259    }
260
261    pub fn add_name(&mut self, name: &str) -> u32 {
262        return match self.inner.names.iter().position(|s| name.eq(s)) {
263            Some(i) => i as u32,
264            None => {
265                self.inner.names.push(String::from(name));
266                (self.inner.names.len() - 1) as u32
267            }
268        };
269    }
270
271    pub fn add_names(&mut self, names: Vec<&str>) -> Vec<u32> {
272        self.inner.names.reserve(names.len());
273        return names.iter().map(|n| self.add_name(n)).collect();
274    }
275
276    pub fn get_name_index(&self, name: &str) -> Option<u32> {
277        self.inner
278            .names
279            .iter()
280            .position(|n| name.eq(n))
281            .map(|v| v as u32)
282    }
283
284    pub fn get_name(&self, index: u32) -> Result<&str, SourceMapError> {
285        self.inner
286            .names
287            .get(index as usize)
288            .map(|v| v.as_str())
289            .ok_or_else(|| SourceMapError::new(SourceMapErrorType::NameOutOfRange))
290    }
291
292    pub fn get_names(&self) -> &Vec<String> {
293        &self.inner.names
294    }
295
296    pub fn set_source_content(
297        &mut self,
298        source_index: usize,
299        source_content: &str,
300    ) -> Result<(), SourceMapError> {
301        if self.inner.sources.is_empty() || source_index > self.inner.sources.len() - 1 {
302            return Err(SourceMapError::new(SourceMapErrorType::SourceOutOfRange));
303        }
304
305        let sources_content_len = self.inner.sources_content.len();
306        if sources_content_len > source_index {
307            self.inner.sources_content[source_index] = String::from(source_content);
308        } else {
309            self.inner
310                .sources_content
311                .reserve((source_index + 1) - sources_content_len);
312            let items_to_add = source_index - sources_content_len;
313            for _n in 0..items_to_add {
314                self.inner.sources_content.push(String::from(""));
315            }
316            self.inner
317                .sources_content
318                .push(String::from(source_content));
319        }
320
321        Ok(())
322    }
323
324    pub fn get_source_content(&self, index: u32) -> Result<&str, SourceMapError> {
325        self.inner
326            .sources_content
327            .get(index as usize)
328            .map(|v| v.as_str())
329            .ok_or_else(|| SourceMapError::new(SourceMapErrorType::SourceOutOfRange))
330    }
331
332    pub fn get_sources_content(&self) -> &Vec<String> {
333        &self.inner.sources_content
334    }
335
336    // Write the sourcemap instance to a buffer
337    pub fn to_buffer(&self, output: &mut AlignedVec) -> Result<(), SourceMapError> {
338        output.clear();
339        let mut serializer = AlignedSerializer::new(output);
340        serializer.serialize_value(&self.inner)?;
341        Ok(())
342    }
343
344    // Create a sourcemap instance from a buffer
345    pub fn from_buffer(project_root: &str, buf: &[u8]) -> Result<SourceMap, SourceMapError> {
346        let archived = unsafe { archived_root::<SourceMapInner>(buf) };
347        // TODO: see if we can use the archived data directly rather than deserializing at all...
348        let mut deserializer = AllocDeserializer;
349        let inner = archived.deserialize(&mut deserializer)?;
350        Ok(SourceMap {
351            project_root: String::from(project_root),
352            inner,
353        })
354    }
355
356    pub fn add_sourcemap(
357        &mut self,
358        sourcemap: &mut SourceMap,
359        line_offset: i64,
360    ) -> Result<(), SourceMapError> {
361        self.inner.sources.reserve(sourcemap.inner.sources.len());
362        let mut source_indexes = Vec::with_capacity(sourcemap.inner.sources.len());
363        let sources = std::mem::take(&mut sourcemap.inner.sources);
364        for s in sources.iter() {
365            source_indexes.push(self.add_source(s));
366        }
367
368        self.inner.names.reserve(sourcemap.inner.names.len());
369        let mut names_indexes = Vec::with_capacity(sourcemap.inner.names.len());
370        let names = std::mem::take(&mut sourcemap.inner.names);
371        for n in names.iter() {
372            names_indexes.push(self.add_name(n));
373        }
374
375        self.inner
376            .sources_content
377            .reserve(sourcemap.inner.sources_content.len());
378        let sources_content = std::mem::take(&mut sourcemap.inner.sources_content);
379        for (i, source_content_str) in sources_content.iter().enumerate() {
380            if let Some(source_index) = source_indexes.get(i) {
381                self.set_source_content(*source_index as usize, source_content_str)?;
382            }
383        }
384
385        let mapping_lines = std::mem::take(&mut sourcemap.inner.mapping_lines);
386        for (line, mapping_line) in mapping_lines.into_iter().enumerate() {
387            let generated_line = (line as i64) + line_offset;
388            if generated_line >= 0 {
389                let mut line = mapping_line;
390                for mapping in line.mappings.iter_mut() {
391                    match &mut mapping.original {
392                        Some(original_mapping_location) => {
393                            original_mapping_location.source = match source_indexes
394                                .get(original_mapping_location.source as usize)
395                            {
396                                Some(new_source_index) => *new_source_index,
397                                None => {
398                                    return Err(SourceMapError::new(
399                                        SourceMapErrorType::SourceOutOfRange,
400                                    ));
401                                }
402                            };
403
404                            original_mapping_location.name = match original_mapping_location.name {
405                                Some(name_index) => match names_indexes.get(name_index as usize) {
406                                    Some(new_name_index) => Some(*new_name_index),
407                                    None => {
408                                        return Err(SourceMapError::new(
409                                            SourceMapErrorType::NameOutOfRange,
410                                        ));
411                                    }
412                                },
413                                None => None,
414                            };
415                        }
416                        None => {}
417                    }
418                }
419
420                self.ensure_lines(generated_line as usize);
421                self.inner.mapping_lines[generated_line as usize] = line;
422            }
423        }
424
425        Ok(())
426    }
427
428    pub fn extends(&mut self, original_sourcemap: &mut SourceMap) -> Result<(), SourceMapError> {
429        self.inner
430            .sources
431            .reserve(original_sourcemap.inner.sources.len());
432        let mut source_indexes = Vec::with_capacity(original_sourcemap.inner.sources.len());
433        for s in original_sourcemap.inner.sources.iter() {
434            source_indexes.push(self.add_source(s));
435        }
436
437        self.inner
438            .names
439            .reserve(original_sourcemap.inner.names.len());
440        let mut names_indexes = Vec::with_capacity(original_sourcemap.inner.names.len());
441        for n in original_sourcemap.inner.names.iter() {
442            names_indexes.push(self.add_name(n));
443        }
444
445        self.inner
446            .sources_content
447            .reserve(original_sourcemap.inner.sources_content.len());
448        for (i, source_content_str) in original_sourcemap.inner.sources_content.iter().enumerate() {
449            if let Some(source_index) = source_indexes.get(i) {
450                self.set_source_content(*source_index as usize, source_content_str)?;
451            }
452        }
453
454        for (_generated_line, line_content) in self.inner.mapping_lines.iter_mut().enumerate() {
455            for mapping in line_content.mappings.iter_mut() {
456                let original_location_option = &mut mapping.original;
457                if let Some(original_location) = original_location_option {
458                    let found_mapping = original_sourcemap.find_closest_mapping(
459                        original_location.original_line,
460                        original_location.original_column,
461                    );
462                    match found_mapping {
463                        Some(original_mapping) => match original_mapping.original {
464                            Some(original_mapping_location) => {
465                                *original_location_option = Some(OriginalLocation::new(
466                                    original_mapping_location.original_line,
467                                    original_mapping_location.original_column,
468                                    match source_indexes
469                                        .get(original_mapping_location.source as usize)
470                                    {
471                                        Some(new_source_index) => *new_source_index,
472                                        None => {
473                                            return Err(SourceMapError::new(
474                                                SourceMapErrorType::SourceOutOfRange,
475                                            ));
476                                        }
477                                    },
478                                    match original_mapping_location.name {
479                                        Some(name_index) => {
480                                            match names_indexes.get(name_index as usize) {
481                                                Some(new_name_index) => Some(*new_name_index),
482                                                None => {
483                                                    return Err(SourceMapError::new(
484                                                        SourceMapErrorType::NameOutOfRange,
485                                                    ));
486                                                }
487                                            }
488                                        }
489                                        None => None,
490                                    },
491                                ));
492                            }
493                            None => {
494                                *original_location_option = None;
495                            }
496                        },
497                        None => {
498                            *original_location_option = None;
499                        }
500                    }
501                }
502            }
503        }
504
505        Ok(())
506    }
507
508    pub fn add_vlq_map(
509        &mut self,
510        input: &[u8],
511        sources: Vec<&str>,
512        sources_content: Vec<&str>,
513        names: Vec<&str>,
514        line_offset: i64,
515        column_offset: i64,
516    ) -> Result<(), SourceMapError> {
517        let mut generated_line: i64 = line_offset;
518        let mut generated_column: i64 = column_offset;
519        let mut original_line = 0;
520        let mut original_column = 0;
521        let mut source = 0;
522        let mut name = 0;
523
524        let source_indexes: Vec<u32> = self.add_sources(sources);
525        let name_indexes: Vec<u32> = self.add_names(names);
526
527        self.inner.sources_content.reserve(sources_content.len());
528        for (i, source_content) in sources_content.iter().enumerate() {
529            if let Some(source_index) = source_indexes.get(i) {
530                self.set_source_content(*source_index as usize, source_content)?;
531            }
532        }
533
534        let mut input = input.iter().cloned().peekable();
535        while let Some(byte) = input.peek().cloned() {
536            match byte {
537                b';' => {
538                    generated_line += 1;
539                    generated_column = column_offset;
540                    input.next().unwrap();
541                }
542                b',' => {
543                    input.next().unwrap();
544                }
545                _ => {
546                    // First is a generated column that is always present.
547                    read_relative_vlq(&mut generated_column, &mut input)?;
548
549                    // Read source, original line, and original column if the
550                    // mapping has them.
551                    let original = if input.peek().cloned().map_or(true, is_mapping_separator) {
552                        None
553                    } else {
554                        read_relative_vlq(&mut source, &mut input)?;
555                        read_relative_vlq(&mut original_line, &mut input)?;
556                        read_relative_vlq(&mut original_column, &mut input)?;
557                        Some(OriginalLocation::new(
558                            original_line as u32,
559                            original_column as u32,
560                            match source_indexes.get(source as usize) {
561                                Some(v) => *v,
562                                None => {
563                                    return Err(SourceMapError::new(
564                                        SourceMapErrorType::SourceOutOfRange,
565                                    ));
566                                }
567                            },
568                            if input.peek().cloned().map_or(true, is_mapping_separator) {
569                                None
570                            } else {
571                                read_relative_vlq(&mut name, &mut input)?;
572                                Some(match name_indexes.get(name as usize) {
573                                    Some(v) => *v,
574                                    None => {
575                                        return Err(SourceMapError::new(
576                                            SourceMapErrorType::NameOutOfRange,
577                                        ));
578                                    }
579                                })
580                            },
581                        ))
582                    };
583
584                    if generated_line >= 0 {
585                        self.add_mapping(generated_line as u32, generated_column as u32, original);
586                    }
587                }
588            }
589        }
590
591        Ok(())
592    }
593
594    pub fn offset_columns(
595        &mut self,
596        generated_line: u32,
597        generated_column: u32,
598        generated_column_offset: i64,
599    ) -> Result<(), SourceMapError> {
600        match self.inner.mapping_lines.get_mut(generated_line as usize) {
601            Some(line) => line.offset_columns(generated_column, generated_column_offset),
602            None => Ok(()),
603        }
604    }
605
606    pub fn offset_lines(
607        &mut self,
608        generated_line: u32,
609        generated_line_offset: i64,
610    ) -> Result<(), SourceMapError> {
611        if generated_line_offset == 0 || self.inner.mapping_lines.is_empty() {
612            return Ok(());
613        }
614
615        let (start_line, overflowed) =
616            (generated_line as i64).overflowing_add(generated_line_offset);
617        if overflowed || start_line > (u32::MAX as i64) {
618            return Err(SourceMapError::new_with_reason(
619                SourceMapErrorType::UnexpectedNegativeNumber,
620                "column + column_offset cannot be negative",
621            ));
622        }
623
624        let line = generated_line as usize;
625        let abs_offset = generated_line_offset.abs() as usize;
626        if generated_line_offset > 0 {
627            if line > self.inner.mapping_lines.len() {
628                self.ensure_lines(line + abs_offset);
629            } else {
630                self.inner
631                    .mapping_lines
632                    .splice(line..line, (0..abs_offset).map(|_| MappingLine::new()));
633            }
634        } else {
635            self.inner.mapping_lines.drain(line - abs_offset..line);
636        }
637
638        Ok(())
639    }
640
641    pub fn add_empty_map(
642        &mut self,
643        source: &str,
644        source_content: &str,
645        line_offset: i64,
646    ) -> Result<(), SourceMapError> {
647        let source_index = self.add_source(source);
648        self.set_source_content(source_index as usize, source_content)?;
649
650        for (line_count, _line) in source_content.lines().enumerate() {
651            let generated_line = (line_count as i64) + line_offset;
652            if generated_line >= 0 {
653                self.add_mapping(
654                    generated_line as u32,
655                    0,
656                    Some(OriginalLocation::new(
657                        line_count as u32,
658                        0,
659                        source_index,
660                        None,
661                    )),
662                )
663            }
664        }
665
666        Ok(())
667    }
668}
669
670#[allow(non_fmt_panics)]
671#[test]
672fn test_buffers() {
673    let map = SourceMap::new("/");
674    let mut output = AlignedVec::new();
675    match map.to_buffer(&mut output) {
676        Ok(_) => {}
677        Err(err) => panic!("{:?}", err),
678    }
679    match SourceMap::from_buffer("/", &output) {
680        Ok(map) => {
681            println!("{:?}", map)
682        }
683        Err(err) => panic!("{:?}", err),
684    }
685}