Skip to content

Commit 63e218c

Browse files
authored
feat: support source root (#83)
* feat: support source root * feat: new `get_source` function for obtaining the combined source path * chore: default source_root to None * fix: should not compose source_root to source when RawSourceMap to SourceMap * chore: rust fmt
1 parent a440626 commit 63e218c

File tree

3 files changed

+104
-33
lines changed

3 files changed

+104
-33
lines changed

src/helpers.rs

+29-4
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,15 @@ pub fn stream_chunks_of_source_map(
595595
}
596596
}
597597

598+
fn get_source(source_map: &SourceMap, source: &str) -> String {
599+
let source_root = source_map.source_root();
600+
match source_root {
601+
Some(root) if root.ends_with('/') => format!("{}{}", root, source),
602+
Some(root) => format!("{}/{}", root, source),
603+
None => source.to_string(),
604+
}
605+
}
606+
598607
fn stream_chunks_of_source_map_final(
599608
source: &str,
600609
source_map: &SourceMap,
@@ -607,7 +616,11 @@ fn stream_chunks_of_source_map_final(
607616
return result;
608617
}
609618
for (i, source) in source_map.sources().iter().enumerate() {
610-
on_source(i as u32, source, source_map.get_source_content(i))
619+
on_source(
620+
i as u32,
621+
&get_source(source_map, source),
622+
source_map.get_source_content(i),
623+
)
611624
}
612625
for (i, name) in source_map.names().iter().enumerate() {
613626
on_name(i as u32, name);
@@ -665,7 +678,11 @@ fn stream_chunks_of_source_map_full(
665678
};
666679
}
667680
for (i, source) in source_map.sources().iter().enumerate() {
668-
on_source(i as u32, source, source_map.get_source_content(i))
681+
on_source(
682+
i as u32,
683+
&get_source(source_map, source),
684+
source_map.get_source_content(i),
685+
)
669686
}
670687
for (i, name) in source_map.names().iter().enumerate() {
671688
on_name(i as u32, name);
@@ -807,7 +824,11 @@ fn stream_chunks_of_source_map_lines_final(
807824
};
808825
}
809826
for (i, source) in source_map.sources().iter().enumerate() {
810-
on_source(i as u32, source, source_map.get_source_content(i))
827+
on_source(
828+
i as u32,
829+
&get_source(source_map, source),
830+
source_map.get_source_content(i),
831+
)
811832
}
812833
let final_line = if result.generated_column == 0 {
813834
result.generated_line - 1
@@ -858,7 +879,11 @@ fn stream_chunks_of_source_map_lines_full(
858879
};
859880
}
860881
for (i, source) in source_map.sources().iter().enumerate() {
861-
on_source(i as u32, source, source_map.get_source_content(i))
882+
on_source(
883+
i as u32,
884+
&get_source(source_map, source),
885+
source_map.get_source_content(i),
886+
)
862887
}
863888
let mut current_generated_line = 1;
864889
let mut on_mapping = |mapping: &Mapping| {

src/source.rs

+22-28
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ pub struct SourceMap {
178178
sources: Vec<Cow<'static, str>>,
179179
sources_content: Vec<Cow<'static, str>>,
180180
names: Vec<Cow<'static, str>>,
181+
source_root: Option<String>,
181182
}
182183

183184
impl SourceMap {
@@ -195,6 +196,7 @@ impl SourceMap {
195196
sources,
196197
sources_content,
197198
names,
199+
source_root: None,
198200
}
199201
}
200202

@@ -286,6 +288,16 @@ impl SourceMap {
286288
) -> Option<&mut Cow<'static, str>> {
287289
self.names.get_mut(index)
288290
}
291+
292+
/// Get the source_root field in [SourceMap].
293+
pub fn source_root(&self) -> Option<&str> {
294+
self.source_root.as_deref()
295+
}
296+
297+
/// Set the source_root field in [SourceMap].
298+
pub fn set_source_root(&mut self, source_root: Option<String>) {
299+
self.source_root = source_root;
300+
}
289301
}
290302

291303
#[derive(Debug, Default, Serialize, Deserialize)]
@@ -364,32 +376,13 @@ impl TryFrom<RawSourceMap> for SourceMap {
364376
type Error = crate::Error;
365377

366378
fn try_from(raw: RawSourceMap) -> Result<Self> {
367-
let sources = raw.sources.unwrap_or_default();
368-
let sources = match raw.source_root {
369-
Some(ref source_root) if !source_root.is_empty() => {
370-
let source_root = source_root.trim_end_matches('/');
371-
sources
372-
.into_iter()
373-
.map(|x| {
374-
let x = x.unwrap_or_default();
375-
let is_valid = !x.is_empty()
376-
&& (x.starts_with('/')
377-
|| x.starts_with("http:")
378-
|| x.starts_with("https:"));
379-
if is_valid {
380-
x
381-
} else {
382-
format!("{source_root}/{x}").into()
383-
}
384-
})
385-
.collect()
386-
}
387-
_ => sources
388-
.into_iter()
389-
.map(Option::unwrap_or_default)
390-
.map(Cow::from)
391-
.collect(),
392-
};
379+
let sources = raw
380+
.sources
381+
.unwrap_or_default()
382+
.into_iter()
383+
.map(Option::unwrap_or_default)
384+
.map(Cow::from)
385+
.collect();
393386
let sources_content = raw
394387
.sources_content
395388
.unwrap_or_default()
@@ -410,6 +403,7 @@ impl TryFrom<RawSourceMap> for SourceMap {
410403
sources,
411404
sources_content,
412405
names,
406+
source_root: raw.source_root,
413407
})
414408
}
415409
}
@@ -430,7 +424,7 @@ impl From<SourceMap> for RawSourceMap {
430424
.map(|s| (!s.is_empty()).then_some(s))
431425
.collect(),
432426
),
433-
source_root: None,
427+
source_root: map.source_root,
434428
sources_content: sources_content
435429
.clone()
436430
.any(|s| s.is_some())
@@ -547,7 +541,7 @@ mod tests {
547541
RawSource::from("g").boxed().hash(&mut state);
548542
(&RawSource::from("h") as &dyn Source).hash(&mut state);
549543
ReplaceSource::new(RawSource::from("i").boxed()).hash(&mut state);
550-
assert_eq!(format!("{:x}", state.finish()), "fb814b430ddd31e0");
544+
assert_eq!(format!("{:x}", state.finish()), "8163b42b7cb1d8f0");
551545
}
552546

553547
#[test]

src/source_map_source.rs

+53-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ mod tests {
261261

262262
let mut hasher = twox_hash::XxHash64::default();
263263
sms1.hash(&mut hasher);
264-
assert_eq!(format!("{:x}", hasher.finish()), "60180dd058e3cdd7");
264+
assert_eq!(format!("{:x}", hasher.finish()), "d136621583d4618c");
265265
}
266266

267267
#[test]
@@ -578,4 +578,56 @@ mod tests {
578578
let map = source.map(&MapOptions::new(false)).unwrap();
579579
assert_eq!(map.mappings(), ";;;AAAA");
580580
}
581+
582+
#[test]
583+
fn source_root_is_correctly_applied_to_mappings() {
584+
let inner_source_code = "Hello World\nis a test string\n";
585+
let inner_source = ConcatSource::new([
586+
OriginalSource::new(inner_source_code, "hello-world.txt").boxed(),
587+
OriginalSource::new("Translate: ", "header.txt").boxed(),
588+
RawSource::from("Other text").boxed(),
589+
]);
590+
let source_r_code =
591+
"Translated: Hallo Welt\nist ein test Text\nAnderer Text";
592+
let source_r_map = SourceMap::from_json(
593+
r#"{
594+
"version": 3,
595+
"sources": [ "text" ],
596+
"names": [ "Hello", "World", "nope" ],
597+
"mappings": "YAAAA,K,CAAMC;AACNC,O,MAAU;AACC,O,CAAM",
598+
"file": "translated.txt",
599+
"sourcesContent": [ "Hello World\nis a test string\n" ]
600+
}"#,
601+
)
602+
.unwrap();
603+
let inner_source_map =
604+
inner_source.map(&MapOptions::default()).map(|mut map| {
605+
map.set_source_root(Some("/path/to/folder/".to_string()));
606+
map
607+
});
608+
let sms = SourceMapSource::new(SourceMapSourceOptions {
609+
value: source_r_code,
610+
name: "text",
611+
source_map: source_r_map.clone(),
612+
original_source: Some(inner_source.source().to_string()),
613+
inner_source_map,
614+
remove_original_source: false,
615+
});
616+
assert_eq!(
617+
sms.map(&MapOptions::default()).unwrap(),
618+
SourceMap::from_json(
619+
r#"{
620+
"mappings": "YAAAA,K,CAAMC;AACN,O,MAAU;ACCC,O,CAAM",
621+
"names": ["Hello", "World"],
622+
"sources": ["/path/to/folder/hello-world.txt", "text"],
623+
"sourcesContent": [
624+
"Hello World\nis a test string\n",
625+
"Hello World\nis a test string\nTranslate: Other text"
626+
],
627+
"version": 3
628+
}"#
629+
)
630+
.unwrap(),
631+
);
632+
}
581633
}

0 commit comments

Comments
 (0)