feat(sourcemap): Add support for sourcemap debug IDs (#6221)

The sourcemap [`debugId`
proposal](https://github.com/tc39/source-map/blob/main/proposals/debug-id.md)
adds globally unique build or debug IDs to source maps and generated
code, making build artifacts self-identifying.

Support for debug IDs was added to
[`rust-sourcemap`](https://github.com/getsentry/rust-sourcemap/pull/66)
in 2023 and Sentry have made use of this to aid in matching up source
and sourcemap files without having to worry about path mismatches or
release versions.

I want to add debug ID support to Rolldown but it uses `oxc::sourcemap`
so it looks like I need to start here first!
This commit is contained in:
Tim Fish 2024-10-07 07:54:08 +02:00 committed by GitHub
parent 9e62396803
commit f6e42b6d85
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 22 additions and 1 deletions

View file

@ -23,6 +23,8 @@ pub struct JSONSourceMap {
pub sources_content: Option<Vec<Option<String>>>,
/// A list of symbol names used by the “mappings” entry.
pub names: Vec<String>,
/// An optional field containing the debugId for this sourcemap.
pub debug_id: Option<String>,
}
pub fn decode(json: JSONSourceMap) -> Result<SourceMap> {
@ -38,6 +40,7 @@ pub fn decode(json: JSONSourceMap) -> Result<SourceMap> {
tokens,
token_chunks: None,
x_google_ignore_list: None,
debug_id: json.debug_id,
})
}

View file

@ -21,6 +21,7 @@ pub fn encode(sourcemap: &SourceMap) -> JSONSourceMap {
.as_ref()
.map(|x| x.iter().map(ToString::to_string).map(Some).collect()),
names: sourcemap.names.iter().map(ToString::to_string).collect(),
debug_id: sourcemap.get_debug_id().map(ToString::to_string),
}
}
@ -76,6 +77,12 @@ pub fn encode_to_string(sourcemap: &SourceMap) -> String {
contents.push("],\"mappings\":\"".into());
contents.push(serialize_sourcemap_mappings(sourcemap).into());
if let Some(debug_id) = sourcemap.get_debug_id() {
contents.push("\",\"debugId\":\"".into());
contents.push(debug_id.into());
}
contents.push("\"}".into());
// Check we calculated number of segments required correctly
@ -401,9 +408,10 @@ fn test_encode_escape_string() {
None,
);
sm.set_x_google_ignore_list(vec![0]);
sm.set_debug_id("56431d54-c0a6-451d-8ea2-ba5de5d8ca2e");
assert_eq!(
sm.to_json_string(),
r#"{"version":3,"names":["name_length_greater_than_16_\u0000"],"sources":["\u0000"],"sourcesContent":["emoji-👀-\u0000"],"x_google_ignoreList":[0],"mappings":""}"#
r#"{"version":3,"names":["name_length_greater_than_16_\u0000"],"sources":["\u0000"],"sourcesContent":["emoji-👀-\u0000"],"x_google_ignoreList":[0],"mappings":"","debugId":"56431d54-c0a6-451d-8ea2-ba5de5d8ca2e"}"#
);
}

View file

@ -21,6 +21,7 @@ pub struct SourceMap {
/// The `x_google_ignoreList` field refers to the `sources` array, and lists the indices of all the known third-party sources in that source map.
/// When parsing the source map, developer tools can use this to determine sections of the code that the browser loads and runs that could be automatically ignore-listed.
pub(crate) x_google_ignore_list: Option<Vec<u32>>,
pub(crate) debug_id: Option<String>,
}
#[allow(clippy::cast_possible_truncation)]
@ -43,6 +44,7 @@ impl SourceMap {
tokens,
token_chunks,
x_google_ignore_list: None,
debug_id: None,
}
}
@ -95,6 +97,14 @@ impl SourceMap {
self.x_google_ignore_list = Some(x_google_ignore_list);
}
pub fn set_debug_id(&mut self, debug_id: &str) {
self.debug_id = Some(debug_id.into());
}
pub fn get_debug_id(&self) -> Option<&str> {
self.debug_id.as_deref()
}
pub fn get_names(&self) -> impl Iterator<Item = &str> {
self.names.iter().map(AsRef::as_ref)
}