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