@@ -103,6 +103,7 @@ impl<TSys: LocalHttpCacheSys> LocalLspHttpCache<TSys> {
103103 // 2. We need to verify the checksums for JSR https specifiers match what
104104 // is found in the package's manifest.
105105 allow_global_to_local : GlobalToLocalCopy :: Disallow ,
106+ jsr_registry_url : None , // only used when GlobalToLocalCopy::Allow
106107 } ,
107108 }
108109 }
@@ -250,13 +251,15 @@ pub struct LocalHttpCache<TSys: LocalHttpCacheSys> {
250251 manifest : LocalCacheManifest < TSys > ,
251252 global_cache : GlobalHttpCacheRc < TSys > ,
252253 allow_global_to_local : GlobalToLocalCopy ,
254+ jsr_registry_url : Option < Url > ,
253255}
254256
255257impl < TSys : LocalHttpCacheSys > LocalHttpCache < TSys > {
256258 pub fn new (
257259 path : PathBuf ,
258260 global_cache : GlobalHttpCacheRc < TSys > ,
259261 allow_global_to_local : GlobalToLocalCopy ,
262+ jsr_registry_url : Url ,
260263 ) -> Self {
261264 #[ cfg( not( feature = "wasm" ) ) ]
262265 assert ! ( path. is_absolute( ) ) ;
@@ -269,6 +272,7 @@ impl<TSys: LocalHttpCacheSys> LocalHttpCache<TSys> {
269272 manifest,
270273 global_cache,
271274 allow_global_to_local,
275+ jsr_registry_url : Some ( jsr_registry_url) ,
272276 }
273277 }
274278
@@ -332,6 +336,54 @@ impl<TSys: LocalHttpCacheSys> LocalHttpCache<TSys> {
332336 Ok ( None )
333337 }
334338 }
339+
340+ fn transform_content_on_copy_to_local (
341+ & self ,
342+ url : & Url ,
343+ content : Cow < ' static , [ u8 ] > ,
344+ ) -> Cow < ' static , [ u8 ] > {
345+ let Some ( jsr_url) = & self . jsr_registry_url else {
346+ return content;
347+ } ;
348+ if is_jsr_version_metadata_url ( url, jsr_url) {
349+ if let Some ( data) = transform_jsr_version_metadata ( & content) {
350+ return Cow :: Owned ( data) ;
351+ }
352+ }
353+ content
354+ }
355+ }
356+
357+ fn is_jsr_version_metadata_url ( url : & Url , jsr_url : & Url ) -> bool {
358+ // example url: https://jsr.io/@david/dax/0.43.0_meta.json
359+ let Some ( suffix) = url. as_str ( ) . strip_prefix ( jsr_url. as_str ( ) ) else {
360+ return false ;
361+ } ;
362+ let Some ( suffix) = suffix. strip_prefix ( '@' ) else {
363+ return false ;
364+ } ;
365+ let Some ( prefix) = suffix. strip_suffix ( "_meta.json" ) else {
366+ return false ;
367+ } ;
368+ prefix. chars ( ) . filter ( |c| * c == '/' ) . count ( ) == 2
369+ }
370+
371+ fn transform_jsr_version_metadata ( content : & [ u8 ] ) -> Option < Vec < u8 > > {
372+ let mut json_data =
373+ serde_json:: from_slice :: < serde_json:: Value > ( content) . ok ( ) ?;
374+ let obj = json_data. as_object_mut ( ) ?;
375+ let keys_to_remove = obj
376+ . keys ( )
377+ . filter ( |k| k. starts_with ( "moduleGraph" ) )
378+ . cloned ( )
379+ . collect :: < Vec < _ > > ( ) ;
380+ if keys_to_remove. is_empty ( ) {
381+ return None ;
382+ }
383+ for key in keys_to_remove {
384+ obj. remove ( & key) ;
385+ }
386+ serde_json:: to_vec ( & json_data) . ok ( )
335387}
336388
337389impl < TSys : LocalHttpCacheSys > HttpCache for LocalHttpCache < TSys > {
@@ -434,13 +486,15 @@ impl<TSys: LocalHttpCacheSys> HttpCache for LocalHttpCache<TSys> {
434486 let maybe_global_cache_file =
435487 self . global_cache . get ( & global_key, maybe_checksum) ?;
436488 if let Some ( file) = maybe_global_cache_file {
489+ let content = self
490+ . transform_content_on_copy_to_local ( & key. url , file. content ) ;
437491 atomic_write_file_with_retries (
438492 self . env ( ) ,
439493 & local_file_path,
440- & file . content ,
494+ & content,
441495 CACHE_PERM ,
442496 ) ?;
443- file . content
497+ content
444498 } else {
445499 return Ok ( None ) ;
446500 }
@@ -1054,6 +1108,7 @@ mod test {
10541108 local_temp. clone ( ) ,
10551109 global_cache. clone ( ) ,
10561110 GlobalToLocalCopy :: Allow ,
1111+ Url :: parse ( "https://jsr.io/" ) . unwrap ( ) ,
10571112 ) ) ;
10581113 Self {
10591114 global_cache,
@@ -1216,4 +1271,60 @@ mod test {
12161271 Ok ( None )
12171272 ) ) ;
12181273 }
1274+
1275+ #[ test]
1276+ fn test_copy_version_metadata_file ( ) {
1277+ let test_caches = TestCaches :: new ( ) ;
1278+ let metadata_url =
1279+ Url :: parse ( "https://jsr.io/@david/dax/1.2.3_meta.json" ) . unwrap ( ) ;
1280+ let data =
1281+ r#"{ "moduleGraph2": "testing", "checksums": { "test": "test" } }"# ;
1282+ test_caches
1283+ . global_cache
1284+ . set ( & metadata_url, Default :: default ( ) , data. as_bytes ( ) )
1285+ . unwrap ( ) ;
1286+ let key = test_caches
1287+ . local_cache
1288+ . cache_item_key ( & metadata_url)
1289+ . unwrap ( ) ;
1290+ let final_data = test_caches. local_cache . get ( & key, None ) . unwrap ( ) . unwrap ( ) ;
1291+ assert_eq ! (
1292+ String :: from_utf8( final_data. content. to_vec( ) ) . unwrap( ) ,
1293+ // had the moduleGraph2 property stripped
1294+ r#"{"checksums":{"test":"test"}}"#
1295+ ) ;
1296+ }
1297+
1298+ #[ test]
1299+ fn test_is_jsr_version_metadata_url ( ) {
1300+ let cases = [
1301+ ( "https://jsr.io/@test/test/1.2.3_meta.json" , true ) ,
1302+ ( "https://jsr.io/@test/test/test/1.2.3_meta.json" , false ) ,
1303+ ( "https://jsr.io/@test/test/meta.json" , false ) ,
1304+ ( "https://jsr.io/test/test/1.2.3_meta.json" , false ) ,
1305+ ( "https://jsr.com/@test/test/1.2.3_meta.json" , false ) ,
1306+ ] ;
1307+ let jsr_url = Url :: parse ( "https://jsr.io/" ) . unwrap ( ) ;
1308+ for ( url, expected) in cases {
1309+ let value =
1310+ is_jsr_version_metadata_url ( & Url :: parse ( url) . unwrap ( ) , & jsr_url) ;
1311+ assert_eq ! ( value, expected) ;
1312+ }
1313+ }
1314+
1315+ #[ test]
1316+ fn test_transform_jsr_version_metadata ( ) {
1317+ let cases = [
1318+ (
1319+ r#"{ "moduleGraph1": "data", "moduleGraph2": "data", "moduleGraph3": "data", "other": "data" }"# ,
1320+ Some ( r#"{"other":"data"}"# ) ,
1321+ ) ,
1322+ ( r#"{ "other": "data" }"# , None ) ,
1323+ ] ;
1324+
1325+ for ( input, expected) in cases {
1326+ let output = transform_jsr_version_metadata ( input. as_bytes ( ) ) ;
1327+ assert_eq ! ( output. as_deref( ) , expected. map( |e| e. as_bytes( ) ) )
1328+ }
1329+ }
12191330}
0 commit comments