swc_sourcemap/
detector.rs1use std::{
2 io::{BufRead, BufReader, Read},
3 str,
4};
5
6use url::Url;
7
8use crate::{
9 decoder::{decode_data_url, strip_junk_header, StripHeaderReader},
10 errors::Result,
11 jsontypes::MinimalRawSourceMap,
12 types::DecodedMap,
13};
14
15#[derive(PartialEq, Eq, Debug)]
17pub enum SourceMapRef {
18 Ref(String),
20 LegacyRef(String),
22}
23
24fn resolve_url(ref_url: &str, minified_url: &Url) -> Option<Url> {
25 minified_url.join(ref_url).ok()
26}
27
28impl SourceMapRef {
29 pub fn get_url(&self) -> &str {
31 match *self {
32 SourceMapRef::Ref(ref u) => u.as_str(),
33 SourceMapRef::LegacyRef(ref u) => u.as_str(),
34 }
35 }
36
37 pub fn resolve(&self, minified_url: &str) -> Option<String> {
42 let url = self.get_url();
43 if url.starts_with("data:") {
44 return None;
45 }
46 resolve_url(url, &Url::parse(minified_url).ok()?).map(|x| x.to_string())
47 }
48
49 #[cfg(any(unix, windows, target_os = "redox"))]
53 pub fn resolve_path(&self, minified_path: &std::path::Path) -> Option<std::path::PathBuf> {
54 let url = self.get_url();
55 if url.starts_with("data:") {
56 return None;
57 }
58 resolve_url(url, &Url::from_file_path(minified_path).ok()?)
59 .and_then(|x| x.to_file_path().ok())
60 }
61
62 pub fn get_embedded_sourcemap(&self) -> Result<Option<DecodedMap>> {
64 let url = self.get_url();
65 if url.starts_with("data:") {
66 Ok(Some(decode_data_url(url)?))
67 } else {
68 Ok(None)
69 }
70 }
71}
72
73pub fn locate_sourcemap_reference<R: Read>(rdr: R) -> Result<Option<SourceMapRef>> {
78 for line in BufReader::new(rdr).lines() {
79 let line = line?;
80 if line.starts_with("//# sourceMappingURL=") || line.starts_with("//@ sourceMappingURL=") {
81 let url = str::from_utf8(&line.as_bytes()[21..])?.trim().to_owned();
82 if line.starts_with("//@") {
83 return Ok(Some(SourceMapRef::LegacyRef(url)));
84 } else {
85 return Ok(Some(SourceMapRef::Ref(url)));
86 }
87 }
88 }
89 Ok(None)
90}
91
92pub fn locate_sourcemap_reference_slice(slice: &[u8]) -> Result<Option<SourceMapRef>> {
97 locate_sourcemap_reference(slice)
98}
99
100fn is_sourcemap_common(rsm: MinimalRawSourceMap) -> bool {
101 (rsm.version.is_some() || rsm.file.is_some())
102 && ((rsm.sources.is_some()
103 || rsm.source_root.is_some()
104 || rsm.sources_content.is_some()
105 || rsm.names.is_some())
106 && rsm.mappings.is_some())
107 || rsm.sections.is_some()
108}
109
110fn is_sourcemap_impl<R: Read>(rdr: R) -> Result<bool> {
111 let mut rdr = StripHeaderReader::new(rdr);
112 let mut rdr = BufReader::new(&mut rdr);
113 let rsm: MinimalRawSourceMap = serde_json::from_reader(&mut rdr)?;
114 Ok(is_sourcemap_common(rsm))
115}
116
117fn is_sourcemap_slice_impl(slice: &[u8]) -> Result<bool> {
118 let content = strip_junk_header(slice)?;
119 let rsm: MinimalRawSourceMap = serde_json::from_slice(content)?;
120 Ok(is_sourcemap_common(rsm))
121}
122
123pub fn is_sourcemap<R: Read>(rdr: R) -> bool {
125 is_sourcemap_impl(rdr).unwrap_or(false)
126}
127
128pub fn is_sourcemap_slice(slice: &[u8]) -> bool {
130 is_sourcemap_slice_impl(slice).unwrap_or(false)
131}