Skip to main content

turbopack_core/
debug_id.rs

1use turbo_rcstr::RcStr;
2use turbo_tasks_fs::rope::Rope;
3use turbo_tasks_hash::Xxh3Hash128Hasher;
4
5/// Generate a deterministic debug ID from content using hash-based UUID generation
6///
7/// This follows the TC39 debug ID proposal by generating UUIDs that are deterministic
8/// based on the content, ensuring reproducible builds while maintaining uniqueness.
9/// Uses xxHash3-128 for fast, stable, and collision-resistant hashing.
10pub fn generate_debug_id(content: &Rope) -> RcStr {
11    let mut hasher = Xxh3Hash128Hasher::new();
12    hasher.write_value(content.content_hash());
13    let hash = hasher.finish();
14    uuid::Uuid::from_u128(hash)
15        .as_hyphenated()
16        .to_string()
17        .into()
18}
19
20#[cfg(test)]
21mod tests {
22    use super::*;
23
24    #[test]
25    fn test_generate_debug_id_deterministic() {
26        // Create test content
27        let content = Rope::from("console.log('Hello World');");
28
29        // Generate debug ID twice
30        let id1 = generate_debug_id(&content);
31        let id2 = generate_debug_id(&content);
32
33        // Should be identical (deterministic)
34        assert_eq!(id1, id2);
35
36        // Should be valid UUID format (8-4-4-4-12)
37        assert_eq!(id1.len(), 36);
38        assert!(id1.contains('-'));
39    }
40
41    #[test]
42    fn test_generate_debug_id_different_content() {
43        // Create two different pieces of content
44        let content1 = Rope::from("console.log('Hello');");
45        let content2 = Rope::from("console.log('World');");
46
47        // Generate debug IDs
48        let id1 = generate_debug_id(&content1);
49        let id2 = generate_debug_id(&content2);
50
51        // Should be different
52        assert_ne!(id1, id2);
53    }
54
55    #[test]
56    fn test_debug_id_format() {
57        let content = Rope::from("test content");
58        let debug_id = generate_debug_id(&content);
59
60        // Verify UUID format: 8-4-4-4-12 characters
61        let parts: Vec<&str> = debug_id.split('-').collect();
62        assert_eq!(parts.len(), 5);
63        assert_eq!(parts[0].len(), 8);
64        assert_eq!(parts[1].len(), 4);
65        assert_eq!(parts[2].len(), 4);
66        assert_eq!(parts[3].len(), 4);
67        assert_eq!(parts[4].len(), 12);
68
69        // Should be lowercase
70        assert_eq!(debug_id, debug_id.to_lowercase());
71    }
72}