diff --git a/crates/oxc_sourcemap/src/concat_sourcemap_builder.rs b/crates/oxc_sourcemap/src/concat_sourcemap_builder.rs index 520cadfe4..cd73ac406 100644 --- a/crates/oxc_sourcemap/src/concat_sourcemap_builder.rs +++ b/crates/oxc_sourcemap/src/concat_sourcemap_builder.rs @@ -78,6 +78,7 @@ impl ConcatSourceMapBuilder { SourceMap::new( None, self.names, + None, self.sources, Some(self.source_contents), self.tokens, @@ -91,6 +92,7 @@ fn test_concat_sourcemap_builder() { let sm1 = SourceMap::new( None, vec!["foo".into(), "foo2".into()], + None, vec!["foo.js".into()], None, vec![Token::new(1, 1, 1, 1, Some(0), Some(0))], @@ -99,6 +101,7 @@ fn test_concat_sourcemap_builder() { let sm2 = SourceMap::new( None, vec!["bar".into()], + None, vec!["bar.js".into()], None, vec![Token::new(1, 1, 1, 1, Some(0), Some(0))], @@ -112,6 +115,7 @@ fn test_concat_sourcemap_builder() { let sm = SourceMap::new( None, vec!["foo".into(), "foo2".into(), "bar".into()], + None, vec!["foo.js".into(), "bar.js".into()], None, vec![Token::new(1, 1, 1, 1, Some(0), Some(0)), Token::new(3, 1, 1, 1, Some(1), Some(2))], diff --git a/crates/oxc_sourcemap/src/decode.rs b/crates/oxc_sourcemap/src/decode.rs index 86e1f7660..70fe0c442 100644 --- a/crates/oxc_sourcemap/src/decode.rs +++ b/crates/oxc_sourcemap/src/decode.rs @@ -6,10 +6,17 @@ use crate::{SourceMap, Token}; #[derive(serde::Deserialize)] #[serde(rename_all = "camelCase")] struct JSONSourceMap { + // An optional name of the generated code that this source map is associated with. file: Option, + // A string with the encoded mapping data. mappings: Option, + // An optional source root, useful for relocating source files on a server or removing repeated values in the “sources” entry. This value is prepended to the individual entries in the “source” field. + source_root: Option, + // A list of original sources used by the “mappings” entry. sources: Option>>, + // An optional list of source content, useful when the “source” can’t be hosted. The contents are listed in the same order as the sources in line 5. “null” may be used if some original sources should be retrieved by name. sources_content: Option>>, + // A list of symbol names used by the “mappings” entry. names: Option>, } @@ -18,6 +25,7 @@ pub fn decode(value: &str) -> Result { let file = json.file.map(Into::into); let names = json.names.map(|v| v.into_iter().map(Into::into).collect::>()).unwrap_or_default(); + let source_root = json.source_root.map(Into::into); let sources = json .sources .map(|v| v.into_iter().map(Option::unwrap_or_default).map(Into::into).collect::>()) @@ -26,7 +34,7 @@ pub fn decode(value: &str) -> Result { .sources_content .map(|v| v.into_iter().map(Option::unwrap_or_default).map(Into::into).collect::>()); let tokens = decode_mapping(&json.mappings.unwrap_or_default(), names.len(), sources.len())?; - Ok(SourceMap::new(file, names, sources, source_contents, tokens, None)) + Ok(SourceMap::new(file, names, source_root, sources, source_contents, tokens, None)) } #[allow(clippy::cast_possible_truncation)] @@ -140,6 +148,7 @@ fn test_decode_sourcemap() { "mappings": "AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM" }"#; let sm = SourceMap::from_json_string(input).unwrap(); + assert_eq!(sm.get_source_root(), Some("x")); let mut iter = sm.get_source_view_tokens().filter(|token| token.get_name_id().is_some()); assert_eq!(iter.next().unwrap().to_tuple(), (Some("coolstuff.js"), 0, 4, Some("x"))); assert_eq!(iter.next().unwrap().to_tuple(), (Some("coolstuff.js"), 1, 4, Some("x"))); diff --git a/crates/oxc_sourcemap/src/encode.rs b/crates/oxc_sourcemap/src/encode.rs index 201bbd3b5..8feaad889 100644 --- a/crates/oxc_sourcemap/src/encode.rs +++ b/crates/oxc_sourcemap/src/encode.rs @@ -16,6 +16,11 @@ pub fn encode(sourcemap: &SourceMap) -> Result { buf.push_str(file); buf.push_str("\","); } + if let Some(source_root) = sourcemap.get_source_root() { + buf.push_str("\"sourceRoot\":\""); + buf.push_str(source_root); + buf.push_str("\","); + } buf.push_str("\"names\":["); let names = sourcemap .names @@ -144,6 +149,7 @@ fn test_encode() { let input = r#"{ "version": 3, "sources": ["coolstuff.js"], + "sourceRoot": "x", "names": ["x","alert"], "mappings": "AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM" }"#; @@ -161,6 +167,7 @@ fn test_encode_escape_string() { let sm = SourceMap::new( None, vec!["\0".into()], + None, vec!["\0".into()], Some(vec!["\0".into()]), vec![], diff --git a/crates/oxc_sourcemap/src/sourcemap.rs b/crates/oxc_sourcemap/src/sourcemap.rs index c82994018..9e9797b04 100644 --- a/crates/oxc_sourcemap/src/sourcemap.rs +++ b/crates/oxc_sourcemap/src/sourcemap.rs @@ -11,6 +11,7 @@ use std::sync::Arc; pub struct SourceMap { pub(crate) file: Option>, pub(crate) names: Vec>, + pub(crate) source_root: Option, pub(crate) sources: Vec>, pub(crate) source_contents: Option>>, pub(crate) tokens: Vec, @@ -22,12 +23,13 @@ impl SourceMap { pub fn new( file: Option>, names: Vec>, + source_root: Option, sources: Vec>, source_contents: Option>>, tokens: Vec, token_chunks: Option>, ) -> Self { - Self { file, names, sources, source_contents, tokens, token_chunks } + Self { file, names, source_root, sources, source_contents, tokens, token_chunks } } /// Convert `SourceMap` to vlq sourcemap string. @@ -63,6 +65,10 @@ impl SourceMap { self.file = Some(file.into()); } + pub fn get_source_root(&self) -> Option<&str> { + self.source_root.as_deref() + } + pub fn get_names(&self) -> impl Iterator { self.names.iter().map(AsRef::as_ref) } @@ -224,6 +230,7 @@ fn test_sourcemap_source_view_token() { let sm = SourceMap::new( None, vec!["foo".into()], + None, vec!["foo.js".into()], None, vec![Token::new(1, 1, 1, 1, Some(0), Some(0))], diff --git a/crates/oxc_sourcemap/src/sourcemap_builder.rs b/crates/oxc_sourcemap/src/sourcemap_builder.rs index 3147a6ceb..2d77166c6 100644 --- a/crates/oxc_sourcemap/src/sourcemap_builder.rs +++ b/crates/oxc_sourcemap/src/sourcemap_builder.rs @@ -70,6 +70,7 @@ impl SourceMapBuilder { SourceMap::new( self.file, self.names, + None, self.sources, Some(self.source_contents), self.tokens,