1 use std::borrow::Cow;
2 use std::cmp::Ordering;
3 use std::fmt;
4 use std::io::{Read, Write};
5 use std::path::Path;
6 
7 use crate::builder::SourceMapBuilder;
8 use crate::decoder::{decode, decode_slice};
9 use crate::encoder::encode;
10 use crate::errors::{Error, Result};
11 use crate::hermes::SourceMapHermes;
12 use crate::sourceview::SourceView;
13 use crate::utils::find_common_prefix;
14 
15 /// Controls the `SourceMap::rewrite` behavior
16 ///
17 /// Default configuration:
18 ///
19 /// * `with_names`: true
20 /// * `with_source_contents`: true
21 /// * `load_local_source_contents`: false
22 pub struct RewriteOptions<'a> {
23     /// If enabled, names are kept in the rewritten sourcemap.
24     pub with_names: bool,
25     /// If enabled source contents are kept in the sourcemap.
26     pub with_source_contents: bool,
27     /// If enabled local source contents that are not in the
28     /// file are automatically inlined.
29     #[cfg(any(unix, windows, target_os = "redox"))]
30     pub load_local_source_contents: bool,
31     /// The base path to the used for source reference resolving
32     /// when loading local source contents is used.
33     pub base_path: Option<&'a Path>,
34     /// Optionally strips common prefixes from the sources.  If
35     /// an item in the list is set to `~` then the common prefix
36     /// of all sources is stripped.
37     pub strip_prefixes: &'a [&'a str],
38 }
39 
40 impl<'a> Default for RewriteOptions<'a> {
default() -> RewriteOptions<'a>41     fn default() -> RewriteOptions<'a> {
42         RewriteOptions {
43             with_names: true,
44             with_source_contents: true,
45             #[cfg(any(unix, windows, target_os = "redox"))]
46             load_local_source_contents: false,
47             base_path: None,
48             strip_prefixes: &[][..],
49         }
50     }
51 }
52 
53 /// Represents the result of a decode operation
54 ///
55 /// This represents either an actual sourcemap or a source map index.
56 /// Usually the two things are too distinct to provide a common
57 /// interface however for token lookup and writing back into a writer
58 /// general methods are provided.
59 pub enum DecodedMap {
60     /// Indicates a regular sourcemap
61     Regular(SourceMap),
62     /// Indicates a sourcemap index
63     Index(SourceMapIndex),
64     /// Indicates a sourcemap as generated by Metro+Hermes, as used by react-native
65     Hermes(SourceMapHermes),
66 }
67 
68 impl DecodedMap {
69     /// Alias for `decode`.
from_reader<R: Read>(rdr: R) -> Result<DecodedMap>70     pub fn from_reader<R: Read>(rdr: R) -> Result<DecodedMap> {
71         decode(rdr)
72     }
73 
74     /// Writes a decoded sourcemap to a writer.
to_writer<W: Write>(&self, w: W) -> Result<()>75     pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
76         match *self {
77             DecodedMap::Regular(ref sm) => encode(sm, w),
78             DecodedMap::Index(ref smi) => encode(smi, w),
79             DecodedMap::Hermes(ref smh) => encode(smh, w),
80         }
81     }
82 
83     /// Shortcut to look up a token on either an index or a
84     /// regular sourcemap.  This method can only be used if
85     /// the contained index actually contains embedded maps
86     /// or it will not be able to look up anything.
lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>>87     pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
88         match *self {
89             DecodedMap::Regular(ref sm) => sm.lookup_token(line, col),
90             DecodedMap::Index(ref smi) => smi.lookup_token(line, col),
91             DecodedMap::Hermes(ref smh) => smh.lookup_token(line, col),
92         }
93     }
94 }
95 
96 /// Represents a raw token
97 ///
98 /// Raw tokens are used internally to represent the sourcemap
99 /// in a memory efficient way.  If you construct sourcemaps yourself
100 /// then you need to create these objects, otherwise they are invisible
101 /// to you as a user.
102 #[derive(PartialEq, Copy, Clone, Debug)]
103 pub struct RawToken {
104     /// the destination (minified) line number
105     pub dst_line: u32,
106     /// the destination (minified) column number
107     pub dst_col: u32,
108     /// the source line number
109     pub src_line: u32,
110     /// the source line column
111     pub src_col: u32,
112     /// source identifier
113     pub src_id: u32,
114     /// name identifier (`!0` in case there is no associated name)
115     pub name_id: u32,
116 }
117 
118 /// Represents a token from a sourcemap
119 #[derive(Copy, Clone)]
120 pub struct Token<'a> {
121     raw: &'a RawToken,
122     i: &'a SourceMap,
123     idx: u32,
124 }
125 
126 impl<'a> PartialEq for Token<'a> {
eq(&self, other: &Token<'_>) -> bool127     fn eq(&self, other: &Token<'_>) -> bool {
128         self.raw == other.raw
129     }
130 }
131 
132 impl<'a> Eq for Token<'a> {}
133 
134 impl<'a> PartialOrd for Token<'a> {
partial_cmp(&self, other: &Token<'_>) -> Option<Ordering>135     fn partial_cmp(&self, other: &Token<'_>) -> Option<Ordering> {
136         Some(self.cmp(other))
137     }
138 }
139 
140 impl<'a> Ord for Token<'a> {
cmp(&self, other: &Token<'_>) -> Ordering141     fn cmp(&self, other: &Token<'_>) -> Ordering {
142         macro_rules! try_cmp {
143             ($a:expr, $b:expr) => {
144                 match $a.cmp(&$b) {
145                     Ordering::Equal => {}
146                     x => {
147                         return x;
148                     }
149                 }
150             };
151         }
152         try_cmp!(self.get_dst_line(), other.get_dst_line());
153         try_cmp!(self.get_dst_col(), other.get_dst_col());
154         try_cmp!(self.get_source(), other.get_source());
155         try_cmp!(self.get_src_line(), other.get_src_line());
156         try_cmp!(self.get_src_col(), other.get_src_col());
157         try_cmp!(self.get_name(), other.get_name());
158         Ordering::Equal
159     }
160 }
161 
162 impl<'a> Token<'a> {
163     /// get the destination (minified) line number
get_dst_line(&self) -> u32164     pub fn get_dst_line(&self) -> u32 {
165         self.raw.dst_line
166     }
167 
168     /// get the destination (minified) column number
get_dst_col(&self) -> u32169     pub fn get_dst_col(&self) -> u32 {
170         self.raw.dst_col
171     }
172 
173     /// get the destination line and column
get_dst(&self) -> (u32, u32)174     pub fn get_dst(&self) -> (u32, u32) {
175         (self.get_dst_line(), self.get_dst_col())
176     }
177 
178     /// get the source line number
get_src_line(&self) -> u32179     pub fn get_src_line(&self) -> u32 {
180         self.raw.src_line
181     }
182 
183     /// get the source column number
get_src_col(&self) -> u32184     pub fn get_src_col(&self) -> u32 {
185         self.raw.src_col
186     }
187 
188     /// get the source line and column
get_src(&self) -> (u32, u32)189     pub fn get_src(&self) -> (u32, u32) {
190         (self.get_src_line(), self.get_src_col())
191     }
192 
193     /// Return the source ID of the token
get_src_id(&self) -> u32194     pub fn get_src_id(&self) -> u32 {
195         self.raw.src_id
196     }
197 
198     /// get the source if it exists as string
get_source(&self) -> Option<&'a str>199     pub fn get_source(&self) -> Option<&'a str> {
200         if self.raw.src_id == !0 {
201             None
202         } else {
203             self.i.get_source(self.raw.src_id)
204         }
205     }
206 
207     /// Is there a source for this token?
has_source(&self) -> bool208     pub fn has_source(&self) -> bool {
209         self.raw.src_id != !0
210     }
211 
212     /// get the name if it exists as string
get_name(&self) -> Option<&'a str>213     pub fn get_name(&self) -> Option<&'a str> {
214         if self.raw.name_id == !0 {
215             None
216         } else {
217             self.i.get_name(self.raw.name_id)
218         }
219     }
220 
221     /// returns `true` if a name exists, `false` otherwise
has_name(&self) -> bool222     pub fn has_name(&self) -> bool {
223         self.get_name().is_some()
224     }
225 
226     /// Return the name ID of the token
get_name_id(&self) -> u32227     pub fn get_name_id(&self) -> u32 {
228         self.raw.name_id
229     }
230 
231     /// Converts the token into a debug tuple in the form
232     /// `(source, src_line, src_col, name)`
to_tuple(&self) -> (&'a str, u32, u32, Option<&'a str>)233     pub fn to_tuple(&self) -> (&'a str, u32, u32, Option<&'a str>) {
234         (
235             self.get_source().unwrap_or(""),
236             self.get_src_line(),
237             self.get_src_col(),
238             self.get_name(),
239         )
240     }
241 
242     /// Get the underlying raw token
get_raw_token(&self) -> RawToken243     pub fn get_raw_token(&self) -> RawToken {
244         *self.raw
245     }
246 }
247 
idx_from_token(token: &Token<'_>) -> u32248 pub fn idx_from_token(token: &Token<'_>) -> u32 {
249     token.idx
250 }
251 
sourcemap_from_token<'a>(token: &Token<'a>) -> &'a SourceMap252 pub fn sourcemap_from_token<'a>(token: &Token<'a>) -> &'a SourceMap {
253     token.i
254 }
255 
256 /// Iterates over all tokens in a sourcemap
257 pub struct TokenIter<'a> {
258     i: &'a SourceMap,
259     next_idx: u32,
260 }
261 
262 impl<'a> TokenIter<'a> {
seek(&mut self, line: u32, col: u32) -> bool263     pub fn seek(&mut self, line: u32, col: u32) -> bool {
264         let token = self.i.lookup_token(line, col);
265         match token {
266             Some(token) => {
267                 self.next_idx = token.idx + 1;
268                 true
269             }
270             None => false,
271         }
272     }
273 }
274 
275 impl<'a> Iterator for TokenIter<'a> {
276     type Item = Token<'a>;
277 
next(&mut self) -> Option<Token<'a>>278     fn next(&mut self) -> Option<Token<'a>> {
279         self.i.get_token(self.next_idx).map(|tok| {
280             self.next_idx += 1;
281             tok
282         })
283     }
284 }
285 
286 /// Iterates over all sources in a sourcemap
287 pub struct SourceIter<'a> {
288     i: &'a SourceMap,
289     next_idx: u32,
290 }
291 
292 impl<'a> Iterator for SourceIter<'a> {
293     type Item = &'a str;
294 
next(&mut self) -> Option<&'a str>295     fn next(&mut self) -> Option<&'a str> {
296         self.i.get_source(self.next_idx).map(|source| {
297             self.next_idx += 1;
298             source
299         })
300     }
301 }
302 
303 /// Iterates over all source contents in a sourcemap
304 pub struct SourceContentsIter<'a> {
305     i: &'a SourceMap,
306     next_idx: u32,
307 }
308 
309 impl<'a> Iterator for SourceContentsIter<'a> {
310     type Item = Option<&'a str>;
311 
next(&mut self) -> Option<Option<&'a str>>312     fn next(&mut self) -> Option<Option<&'a str>> {
313         if self.next_idx >= self.i.get_source_count() {
314             None
315         } else {
316             let rv = Some(self.i.get_source_contents(self.next_idx));
317             self.next_idx += 1;
318             rv
319         }
320     }
321 }
322 
323 /// Iterates over all tokens in a sourcemap
324 pub struct NameIter<'a> {
325     i: &'a SourceMap,
326     next_idx: u32,
327 }
328 
329 impl<'a> Iterator for NameIter<'a> {
330     type Item = &'a str;
331 
next(&mut self) -> Option<&'a str>332     fn next(&mut self) -> Option<&'a str> {
333         self.i.get_name(self.next_idx).map(|name| {
334             self.next_idx += 1;
335             name
336         })
337     }
338 }
339 
340 /// Iterates over all index items in a sourcemap
341 pub struct IndexIter<'a> {
342     i: &'a SourceMap,
343     next_idx: usize,
344 }
345 
346 impl<'a> Iterator for IndexIter<'a> {
347     type Item = (u32, u32, u32);
348 
next(&mut self) -> Option<(u32, u32, u32)>349     fn next(&mut self) -> Option<(u32, u32, u32)> {
350         self.i.index.get(self.next_idx).map(|idx| {
351             self.next_idx += 1;
352             *idx
353         })
354     }
355 }
356 
357 impl<'a> fmt::Debug for Token<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result358     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359         write!(f, "<Token {:#}>", self)
360     }
361 }
362 
363 impl<'a> fmt::Display for Token<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result364     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365         write!(
366             f,
367             "{}:{}:{}{}",
368             self.get_source().unwrap_or("<unknown>"),
369             self.get_src_line(),
370             self.get_src_col(),
371             self.get_name()
372                 .map(|x| format!(" name={}", x))
373                 .unwrap_or_default()
374         )?;
375         if f.alternate() {
376             write!(f, " ({}:{})", self.get_dst_line(), self.get_dst_col())?;
377         }
378         Ok(())
379     }
380 }
381 
382 /// Represents a section in a sourcemap index
383 pub struct SourceMapSection {
384     offset: (u32, u32),
385     url: Option<String>,
386     map: Option<Box<DecodedMap>>,
387 }
388 
389 /// Iterates over all sections in a sourcemap index
390 pub struct SourceMapSectionIter<'a> {
391     i: &'a SourceMapIndex,
392     next_idx: u32,
393 }
394 
395 impl<'a> Iterator for SourceMapSectionIter<'a> {
396     type Item = &'a SourceMapSection;
397 
next(&mut self) -> Option<&'a SourceMapSection>398     fn next(&mut self) -> Option<&'a SourceMapSection> {
399         self.i.get_section(self.next_idx).map(|sec| {
400             self.next_idx += 1;
401             sec
402         })
403     }
404 }
405 
406 /// Represents a sourcemap index in memory
407 pub struct SourceMapIndex {
408     file: Option<String>,
409     sections: Vec<SourceMapSection>,
410     x_facebook_offsets: Option<Vec<Option<u32>>>,
411     x_metro_module_paths: Option<Vec<String>>,
412 }
413 
414 /// Represents a sourcemap in memory
415 ///
416 /// This is always represents a regular "non-indexed" sourcemap.  Particularly
417 /// in case the `from_reader` method is used an index sourcemap will be
418 /// rejected with an error on reading.
419 #[derive(Clone, Debug)]
420 pub struct SourceMap {
421     file: Option<String>,
422     tokens: Vec<RawToken>,
423     index: Vec<(u32, u32, u32)>,
424     names: Vec<String>,
425     sources: Vec<String>,
426     sources_content: Vec<Option<SourceView<'static>>>,
427 }
428 
429 impl SourceMap {
430     /// Creates a sourcemap from a reader over a JSON stream in UTF-8
431     /// format.  Optionally a "garbage header" as defined by the
432     /// sourcemap draft specification is supported.  In case an indexed
433     /// sourcemap is encountered an error is returned.
434     ///
435     /// ```rust
436     /// use sourcemap::SourceMap;
437     /// let input: &[_] = b"{
438     ///     \"version\":3,
439     ///     \"sources\":[\"coolstuff.js\"],
440     ///     \"names\":[\"x\",\"alert\"],
441     ///     \"mappings\":\"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM\"
442     /// }";
443     /// let sm = SourceMap::from_reader(input).unwrap();
444     /// ```
445     ///
446     /// While sourcemaps objects permit some modifications, it's generally
447     /// not possible to modify tokens after they have been added.  For
448     /// creating sourcemaps from scratch or for general operations for
449     /// modifying a sourcemap have a look at the `SourceMapBuilder`.
from_reader<R: Read>(rdr: R) -> Result<SourceMap>450     pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMap> {
451         match decode(rdr)? {
452             DecodedMap::Regular(sm) => Ok(sm),
453             _ => Err(Error::IncompatibleSourceMap),
454         }
455     }
456 
457     /// Writes a sourcemap into a writer.
458     ///
459     /// Note that this operation will generate an equivalent sourcemap to the
460     /// one that was generated on load however there might be small differences
461     /// in the generated JSON and layout.  For instance `sourceRoot` will not
462     /// be set as upon parsing of the sourcemap the sources will already be
463     /// expanded.
464     ///
465     /// ```rust
466     /// # use sourcemap::SourceMap;
467     /// # let input: &[_] = b"{
468     /// #     \"version\":3,
469     /// #     \"sources\":[\"coolstuff.js\"],
470     /// #     \"names\":[\"x\",\"alert\"],
471     /// #     \"mappings\":\"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM\"
472     /// # }";
473     /// let sm = SourceMap::from_reader(input).unwrap();
474     /// let mut output : Vec<u8> = vec![];
475     /// sm.to_writer(&mut output).unwrap();
476     /// ```
to_writer<W: Write>(&self, w: W) -> Result<()>477     pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
478         encode(self, w)
479     }
480 
481     /// Creates a sourcemap from a reader over a JSON byte slice in UTF-8
482     /// format.  Optionally a "garbage header" as defined by the
483     /// sourcemap draft specification is supported.  In case an indexed
484     /// sourcemap is encountered an error is returned.
485     ///
486     /// ```rust
487     /// use sourcemap::SourceMap;
488     /// let input: &[_] = b"{
489     ///     \"version\":3,
490     ///     \"sources\":[\"coolstuff.js\"],
491     ///     \"names\":[\"x\",\"alert\"],
492     ///     \"mappings\":\"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM\"
493     /// }";
494     /// let sm = SourceMap::from_slice(input).unwrap();
495     /// ```
from_slice(slice: &[u8]) -> Result<SourceMap>496     pub fn from_slice(slice: &[u8]) -> Result<SourceMap> {
497         match decode_slice(slice)? {
498             DecodedMap::Regular(sm) => Ok(sm),
499             _ => Err(Error::IncompatibleSourceMap),
500         }
501     }
502 
503     /// Constructs a new sourcemap from raw components.
504     ///
505     /// - `file`: an optional filename of the sourcemap
506     /// - `tokens`: a list of raw tokens
507     /// - `names`: a vector of names
508     /// - `sources` a vector of source filenames
509     /// - `sources_content` optional source contents
new( file: Option<String>, tokens: Vec<RawToken>, names: Vec<String>, sources: Vec<String>, sources_content: Option<Vec<Option<String>>>, ) -> SourceMap510     pub fn new(
511         file: Option<String>,
512         tokens: Vec<RawToken>,
513         names: Vec<String>,
514         sources: Vec<String>,
515         sources_content: Option<Vec<Option<String>>>,
516     ) -> SourceMap {
517         let mut index: Vec<_> = tokens
518             .iter()
519             .enumerate()
520             .map(|(idx, token)| (token.dst_line, token.dst_col, idx as u32))
521             .collect();
522         index.sort();
523         SourceMap {
524             file,
525             tokens,
526             index,
527             names,
528             sources,
529             sources_content: sources_content
530                 .unwrap_or_default()
531                 .into_iter()
532                 .map(|opt| opt.map(SourceView::from_string))
533                 .collect(),
534         }
535     }
536 
537     /// Returns the embedded filename in case there is one.
get_file(&self) -> Option<&str>538     pub fn get_file(&self) -> Option<&str> {
539         self.file.as_ref().map(|x| &x[..])
540     }
541 
542     /// Sets a new value for the file.
set_file(&mut self, value: Option<&str>)543     pub fn set_file(&mut self, value: Option<&str>) {
544         self.file = value.map(str::to_owned);
545     }
546 
547     /// Looks up a token by its index.
get_token(&self, idx: u32) -> Option<Token<'_>>548     pub fn get_token(&self, idx: u32) -> Option<Token<'_>> {
549         self.tokens
550             .get(idx as usize)
551             .map(|raw| Token { raw, i: self, idx })
552     }
553 
554     /// Returns the number of tokens in the sourcemap.
get_token_count(&self) -> u32555     pub fn get_token_count(&self) -> u32 {
556         self.tokens.len() as u32
557     }
558 
559     /// Returns an iterator over the tokens.
tokens(&self) -> TokenIter<'_>560     pub fn tokens(&self) -> TokenIter<'_> {
561         TokenIter {
562             i: self,
563             next_idx: 0,
564         }
565     }
566 
567     /// Looks up the closest token to a given line and column.
lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>>568     pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
569         let mut low = 0;
570         let mut high = self.index.len();
571 
572         while low < high {
573             let mid = (low + high) / 2;
574             let ii = &self.index[mid as usize];
575             if (line, col) < (ii.0, ii.1) {
576                 high = mid;
577             } else {
578                 low = mid + 1;
579             }
580         }
581 
582         if low > 0 && low <= self.index.len() {
583             self.get_token(self.index[low as usize - 1].2)
584         } else {
585             None
586         }
587     }
588 
589     /// Given a location, name and minified source file resolve a minified
590     /// name to an original function name.
591     ///
592     /// This invokes some guesswork and requires access to the original minified
593     /// source.  This will not yield proper results for anonymous functions or
594     /// functions that do not have clear function names.  (For instance it's
595     /// recommended that dotted function names are not passed to this
596     /// function).
get_original_function_name<'a>( &self, line: u32, col: u32, minified_name: &str, sv: &'a SourceView<'a>, ) -> Option<&str>597     pub fn get_original_function_name<'a>(
598         &self,
599         line: u32,
600         col: u32,
601         minified_name: &str,
602         sv: &'a SourceView<'a>,
603     ) -> Option<&str> {
604         self.lookup_token(line, col)
605             .and_then(|token| sv.get_original_function_name(token, minified_name))
606     }
607 
608     /// Returns the number of sources in the sourcemap.
get_source_count(&self) -> u32609     pub fn get_source_count(&self) -> u32 {
610         self.sources.len() as u32
611     }
612 
613     /// Looks up a source for a specific index.
get_source(&self, idx: u32) -> Option<&str>614     pub fn get_source(&self, idx: u32) -> Option<&str> {
615         self.sources.get(idx as usize).map(|x| &x[..])
616     }
617 
618     /// Sets a new source value for an index.  This cannot add new
619     /// sources.
620     ///
621     /// This panics if a source is set that does not exist.
set_source(&mut self, idx: u32, value: &str)622     pub fn set_source(&mut self, idx: u32, value: &str) {
623         self.sources[idx as usize] = value.to_string();
624     }
625 
626     /// Iterates over all sources
sources(&self) -> SourceIter<'_>627     pub fn sources(&self) -> SourceIter<'_> {
628         SourceIter {
629             i: self,
630             next_idx: 0,
631         }
632     }
633 
634     /// Returns the sources content as source view.
get_source_view(&self, idx: u32) -> Option<&SourceView<'_>>635     pub fn get_source_view(&self, idx: u32) -> Option<&SourceView<'_>> {
636         self.sources_content
637             .get(idx as usize)
638             .and_then(Option::as_ref)
639     }
640 
641     /// Looks up the content for a source.
get_source_contents(&self, idx: u32) -> Option<&str>642     pub fn get_source_contents(&self, idx: u32) -> Option<&str> {
643         self.sources_content
644             .get(idx as usize)
645             .and_then(Option::as_ref)
646             .map(SourceView::source)
647     }
648 
649     /// Sets source contents for a source.
set_source_contents(&mut self, idx: u32, value: Option<&str>)650     pub fn set_source_contents(&mut self, idx: u32, value: Option<&str>) {
651         if self.sources_content.len() != self.sources.len() {
652             self.sources_content.resize(self.sources.len(), None);
653         }
654         self.sources_content[idx as usize] = value.map(|x| SourceView::from_string(x.to_string()));
655     }
656 
657     /// Iterates over all source contents
source_contents(&self) -> SourceContentsIter<'_>658     pub fn source_contents(&self) -> SourceContentsIter<'_> {
659         SourceContentsIter {
660             i: self,
661             next_idx: 0,
662         }
663     }
664 
665     /// Returns an iterator over the names.
names(&self) -> NameIter<'_>666     pub fn names(&self) -> NameIter<'_> {
667         NameIter {
668             i: self,
669             next_idx: 0,
670         }
671     }
672 
673     /// Returns the number of names in the sourcemap.
get_name_count(&self) -> u32674     pub fn get_name_count(&self) -> u32 {
675         self.names.len() as u32
676     }
677 
678     /// Returns true if there are any names in the map.
has_names(&self) -> bool679     pub fn has_names(&self) -> bool {
680         !self.names.is_empty()
681     }
682 
683     /// Looks up a name for a specific index.
get_name(&self, idx: u32) -> Option<&str>684     pub fn get_name(&self, idx: u32) -> Option<&str> {
685         self.names.get(idx as usize).map(|x| &x[..])
686     }
687 
688     /// Removes all names from the sourcemap.
remove_names(&mut self)689     pub fn remove_names(&mut self) {
690         self.names.clear();
691     }
692 
693     /// Returns the number of items in the index
get_index_size(&self) -> usize694     pub fn get_index_size(&self) -> usize {
695         self.index.len()
696     }
697 
698     /// Returns the number of items in the index
index_iter(&self) -> IndexIter<'_>699     pub fn index_iter(&self) -> IndexIter<'_> {
700         IndexIter {
701             i: self,
702             next_idx: 0,
703         }
704     }
705 
706     /// This rewrites the sourcemap according to the provided rewrite
707     /// options.
708     ///
709     /// The default behavior is to just deduplicate the sourcemap, something
710     /// that automatically takes place.  This for instance can be used to
711     /// slightly compress sourcemaps if certain data is not wanted.
712     ///
713     /// ```rust
714     /// use sourcemap::{SourceMap, RewriteOptions};
715     /// # let input: &[_] = b"{
716     /// #     \"version\":3,
717     /// #     \"sources\":[\"coolstuff.js\"],
718     /// #     \"names\":[\"x\",\"alert\"],
719     /// #     \"mappings\":\"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM\"
720     /// # }";
721     /// let sm = SourceMap::from_slice(input).unwrap();
722     /// let new_sm = sm.rewrite(&RewriteOptions {
723     ///     with_names: false,
724     ///     ..Default::default()
725     /// });
726     /// ```
rewrite(self, options: &RewriteOptions<'_>) -> Result<SourceMap>727     pub fn rewrite(self, options: &RewriteOptions<'_>) -> Result<SourceMap> {
728         let mut builder = SourceMapBuilder::new(self.get_file());
729 
730         for token in self.tokens() {
731             let raw = builder.add_token(&token, options.with_names);
732             if raw.src_id != !0
733                 && options.with_source_contents
734                 && !builder.has_source_contents(raw.src_id)
735             {
736                 builder
737                     .set_source_contents(raw.src_id, self.get_source_contents(token.get_src_id()));
738             }
739         }
740 
741         #[cfg(any(unix, windows, target_os = "redox"))]
742         {
743             if options.load_local_source_contents {
744                 builder.load_local_source_contents(options.base_path)?;
745             }
746         }
747 
748         let mut prefixes = vec![];
749         let mut need_common_prefix = false;
750         for &prefix in options.strip_prefixes.iter() {
751             if prefix == "~" {
752                 need_common_prefix = true;
753             } else {
754                 prefixes.push(prefix.to_string());
755             }
756         }
757         if need_common_prefix {
758             if let Some(prefix) = find_common_prefix(self.sources.iter().map(String::as_str)) {
759                 prefixes.push(prefix);
760             }
761         }
762         if !prefixes.is_empty() {
763             builder.strip_prefixes(&prefixes);
764         }
765 
766         Ok(builder.into_sourcemap())
767     }
768 }
769 
770 impl SourceMapIndex {
771     /// Creates a sourcemap index from a reader over a JSON stream in UTF-8
772     /// format.  Optionally a "garbage header" as defined by the
773     /// sourcemap draft specification is supported.  In case a regular
774     /// sourcemap is encountered an error is returned.
from_reader<R: Read>(rdr: R) -> Result<SourceMapIndex>775     pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMapIndex> {
776         match decode(rdr)? {
777             DecodedMap::Index(smi) => Ok(smi),
778             _ => Err(Error::IncompatibleSourceMap),
779         }
780     }
781 
782     /// Writes a sourcemap index into a writer.
to_writer<W: Write>(&self, w: W) -> Result<()>783     pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
784         encode(self, w)
785     }
786 
787     /// Creates a sourcemap index from a reader over a JSON byte slice in UTF-8
788     /// format.  Optionally a "garbage header" as defined by the
789     /// sourcemap draft specification is supported.  In case a regular
790     /// sourcemap is encountered an error is returned.
from_slice(slice: &[u8]) -> Result<SourceMapIndex>791     pub fn from_slice(slice: &[u8]) -> Result<SourceMapIndex> {
792         match decode_slice(slice)? {
793             DecodedMap::Index(smi) => Ok(smi),
794             _ => Err(Error::IncompatibleSourceMap),
795         }
796     }
797 
798     /// Constructs a new sourcemap index from raw components.
799     ///
800     /// - `file`: an optional filename of the index
801     /// - `sections`: a vector of source map index sections
new(file: Option<String>, sections: Vec<SourceMapSection>) -> SourceMapIndex802     pub fn new(file: Option<String>, sections: Vec<SourceMapSection>) -> SourceMapIndex {
803         SourceMapIndex {
804             file,
805             sections,
806             x_facebook_offsets: None,
807             x_metro_module_paths: None,
808         }
809     }
810 
811     /// Constructs a new sourcemap index from raw components including the
812     /// facebook RAM bundle extensions.
813     ///
814     /// - `file`: an optional filename of the index
815     /// - `sections`: a vector of source map index sections
816     /// - `x_facebook_offsets`: a vector of facebook offsets
817     /// - `x_metro_module_paths`: a vector of metro module paths
new_ram_bundle_compatible( file: Option<String>, sections: Vec<SourceMapSection>, x_facebook_offsets: Option<Vec<Option<u32>>>, x_metro_module_paths: Option<Vec<String>>, ) -> SourceMapIndex818     pub fn new_ram_bundle_compatible(
819         file: Option<String>,
820         sections: Vec<SourceMapSection>,
821         x_facebook_offsets: Option<Vec<Option<u32>>>,
822         x_metro_module_paths: Option<Vec<String>>,
823     ) -> SourceMapIndex {
824         SourceMapIndex {
825             file,
826             sections,
827             x_facebook_offsets,
828             x_metro_module_paths,
829         }
830     }
831 
832     /// Returns the embedded filename in case there is one.
get_file(&self) -> Option<&str>833     pub fn get_file(&self) -> Option<&str> {
834         self.file.as_ref().map(|x| &x[..])
835     }
836 
837     /// Sets a new value for the file.
set_file(&mut self, value: Option<&str>)838     pub fn set_file(&mut self, value: Option<&str>) {
839         self.file = value.map(str::to_owned);
840     }
841 
842     /// Returns the number of sections in this index
get_section_count(&self) -> u32843     pub fn get_section_count(&self) -> u32 {
844         self.sections.len() as u32
845     }
846 
847     /// Looks up a single section and returns it
get_section(&self, idx: u32) -> Option<&SourceMapSection>848     pub fn get_section(&self, idx: u32) -> Option<&SourceMapSection> {
849         self.sections.get(idx as usize)
850     }
851 
852     /// Looks up a single section and returns it as a mutable ref
get_section_mut(&mut self, idx: u32) -> Option<&mut SourceMapSection>853     pub fn get_section_mut(&mut self, idx: u32) -> Option<&mut SourceMapSection> {
854         self.sections.get_mut(idx as usize)
855     }
856 
857     /// Iterates over all sections
sections(&self) -> SourceMapSectionIter<'_>858     pub fn sections(&self) -> SourceMapSectionIter<'_> {
859         SourceMapSectionIter {
860             i: self,
861             next_idx: 0,
862         }
863     }
864 
865     /// Looks up the closest token to a given line and column.
866     ///
867     /// This requires that the referenced sourcemaps are actually loaded.
868     /// If a sourcemap is encountered that is not embedded but just
869     /// externally referenced it is silently skipped.
lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>>870     pub fn lookup_token(&self, line: u32, col: u32) -> Option<Token<'_>> {
871         for section in self.sections() {
872             let (off_line, off_col) = section.get_offset();
873             println!("off_line: {}, off_col: {}", off_line, off_col);
874             if off_line < line || off_col < col {
875                 continue;
876             }
877             if let Some(map) = section.get_sourcemap() {
878                 if let Some(tok) = map.lookup_token(line - off_line, col - off_col) {
879                     return Some(tok);
880                 }
881             }
882         }
883         None
884     }
885 
886     /// Flattens an indexed sourcemap into a regular one.  This requires
887     /// that all referenced sourcemaps are attached.
flatten(&self) -> Result<SourceMap>888     pub fn flatten(&self) -> Result<SourceMap> {
889         let mut builder = SourceMapBuilder::new(self.get_file());
890 
891         for section in self.sections() {
892             let (off_line, off_col) = section.get_offset();
893             let map = match section.get_sourcemap() {
894                 Some(map) => match map {
895                     DecodedMap::Regular(sm) => Cow::Borrowed(sm),
896                     DecodedMap::Index(idx) => Cow::Owned(idx.flatten()?),
897                     DecodedMap::Hermes(smh) => Cow::Borrowed(&smh.sm),
898                 },
899                 None => {
900                     return Err(Error::CannotFlatten(format!(
901                         "Section has an unresolved \
902                          sourcemap: {}",
903                         section.get_url().unwrap_or("<unknown url>")
904                     )));
905                 }
906             };
907 
908             for token in map.tokens() {
909                 let raw = builder.add(
910                     token.get_dst_line() + off_line,
911                     token.get_dst_col() + off_col,
912                     token.get_src_line(),
913                     token.get_src_col(),
914                     token.get_source(),
915                     token.get_name(),
916                 );
917                 if token.get_source().is_some() && !builder.has_source_contents(raw.src_id) {
918                     builder.set_source_contents(
919                         raw.src_id,
920                         map.get_source_contents(token.get_src_id()),
921                     );
922                 }
923             }
924         }
925 
926         Ok(builder.into_sourcemap())
927     }
928 
929     /// Flattens an indexed sourcemap into a regular one and automatically
930     /// rewrites it.  This is more useful than plain flattening as this will
931     /// cause the sourcemap to be properly deduplicated.
flatten_and_rewrite(self, options: &RewriteOptions<'_>) -> Result<SourceMap>932     pub fn flatten_and_rewrite(self, options: &RewriteOptions<'_>) -> Result<SourceMap> {
933         self.flatten()?.rewrite(options)
934     }
935 
936     /// Returns `true` if this sourcemap is for a RAM bundle.
is_for_ram_bundle(&self) -> bool937     pub fn is_for_ram_bundle(&self) -> bool {
938         self.x_facebook_offsets.is_some() && self.x_metro_module_paths.is_some()
939     }
940 
941     /// Returns embeded x-facebook-offset values.
x_facebook_offsets(&self) -> Option<&[Option<u32>]>942     pub fn x_facebook_offsets(&self) -> Option<&[Option<u32>]> {
943         self.x_facebook_offsets.as_ref().map(|x| &x[..])
944     }
945 
946     /// Returns embedded metro module paths.
x_metro_module_paths(&self) -> Option<&[String]>947     pub fn x_metro_module_paths(&self) -> Option<&[String]> {
948         self.x_metro_module_paths.as_ref().map(|x| &x[..])
949     }
950 }
951 
952 impl SourceMapSection {
953     /// Create a new sourcemap index section
954     ///
955     /// - `offset`: offset as line and column
956     /// - `url`: optional URL of where the sourcemap is located
957     /// - `map`: an optional already resolved internal sourcemap
new( offset: (u32, u32), url: Option<String>, map: Option<DecodedMap>, ) -> SourceMapSection958     pub fn new(
959         offset: (u32, u32),
960         url: Option<String>,
961         map: Option<DecodedMap>,
962     ) -> SourceMapSection {
963         SourceMapSection {
964             offset,
965             url,
966             map: map.map(Box::new),
967         }
968     }
969 
970     /// Returns the offset line
get_offset_line(&self) -> u32971     pub fn get_offset_line(&self) -> u32 {
972         self.offset.0
973     }
974 
975     /// Returns the offset column
get_offset_col(&self) -> u32976     pub fn get_offset_col(&self) -> u32 {
977         self.offset.1
978     }
979 
980     /// Returns the offset as tuple
get_offset(&self) -> (u32, u32)981     pub fn get_offset(&self) -> (u32, u32) {
982         self.offset
983     }
984 
985     /// Returns the URL of the referenced map if available
get_url(&self) -> Option<&str>986     pub fn get_url(&self) -> Option<&str> {
987         self.url.as_ref().map(|x| &**x)
988     }
989 
990     /// Updates the URL for this section.
set_url(&mut self, value: Option<&str>)991     pub fn set_url(&mut self, value: Option<&str>) {
992         self.url = value.map(str::to_owned);
993     }
994 
995     /// Returns a reference to the embedded sourcemap if available
get_sourcemap(&self) -> Option<&DecodedMap>996     pub fn get_sourcemap(&self) -> Option<&DecodedMap> {
997         self.map.as_ref().map(Box::as_ref)
998     }
999 
1000     /// Returns a reference to the embedded sourcemap if available
get_sourcemap_mut(&mut self) -> Option<&mut DecodedMap>1001     pub fn get_sourcemap_mut(&mut self) -> Option<&mut DecodedMap> {
1002         self.map.as_mut().map(Box::as_mut)
1003     }
1004 
1005     /// Replaces the embedded sourcemap
set_sourcemap(&mut self, sm: Option<DecodedMap>)1006     pub fn set_sourcemap(&mut self, sm: Option<DecodedMap>) {
1007         self.map = sm.map(Box::new);
1008     }
1009 }
1010