@@ -13,7 +13,7 @@ use crate::Lockfile;
13
13
mod de;
14
14
mod id;
15
15
16
- type Map < K , V > = std:: collections:: HashMap < K , V > ;
16
+ type Map < K , V > = std:: collections:: BTreeMap < K , V > ;
17
17
18
18
#[ derive( Debug , thiserror:: Error ) ]
19
19
pub enum Error {
@@ -25,12 +25,18 @@ pub enum Error {
25
25
Print ( #[ from] biome_formatter:: PrintError ) ,
26
26
#[ error( "Turborepo cannot serialize Bun lockfiles." ) ]
27
27
NotImplemented ,
28
+ #[ error( "{ident} had two entries with differing checksums: {sha1}, {sha2}" ) ]
29
+ MismatchedShas {
30
+ ident : String ,
31
+ sha1 : String ,
32
+ sha2 : String ,
33
+ } ,
28
34
}
29
35
30
36
#[ derive( Debug ) ]
31
37
pub struct BunLockfile {
32
38
data : BunLockfileData ,
33
- key_to_entry : Map < String , String > ,
39
+ key_to_entry : HashMap < String , String > ,
34
40
}
35
41
36
42
#[ derive( Debug , Deserialize ) ]
@@ -230,9 +236,22 @@ impl FromStr for BunLockfile {
230
236
. map_err ( Error :: from) ?;
231
237
let strict_json = format. print ( ) . map_err ( Error :: from) ?;
232
238
let data: BunLockfileData = serde_json:: from_str ( strict_json. as_code ( ) ) ?;
233
- let mut key_to_entry = Map :: new ( ) ;
239
+ let mut key_to_entry = HashMap :: with_capacity ( data . packages . len ( ) ) ;
234
240
for ( path, info) in data. packages . iter ( ) {
235
- key_to_entry. insert ( info. ident . clone ( ) , path. clone ( ) ) ;
241
+ if let Some ( prev_path) = key_to_entry. insert ( info. ident . clone ( ) , path. clone ( ) ) {
242
+ let prev_info = data
243
+ . packages
244
+ . get ( & prev_path)
245
+ . expect ( "we just got this path from the packages list" ) ;
246
+ if prev_info. checksum != info. checksum {
247
+ return Err ( Error :: MismatchedShas {
248
+ ident : info. ident . clone ( ) ,
249
+ sha1 : prev_info. checksum . clone ( ) . unwrap_or_default ( ) ,
250
+ sha2 : info. checksum . clone ( ) . unwrap_or_default ( ) ,
251
+ }
252
+ . into ( ) ) ;
253
+ }
254
+ }
236
255
}
237
256
Ok ( Self { data, key_to_entry } )
238
257
}
@@ -264,6 +283,7 @@ impl PackageInfo {
264
283
#[ cfg( test) ]
265
284
mod test {
266
285
use pretty_assertions:: assert_eq;
286
+ use serde_json:: json;
267
287
use test_case:: test_case;
268
288
269
289
use super :: * ;
@@ -298,10 +318,13 @@ mod test {
298
318
"validate-npm-package-name" ,
299
319
]
300
320
. as_slice ( ) ;
321
+ // Both @turbo/gen and log-symbols depend on the same version of chalk
322
+ // log-symbols version wins out, but this is okay since they are the same exact
323
+ // version of chalk.
301
324
const TURBO_GEN_CHALK_DEPS : & [ & str ] = [
302
- "@turbo/gen /chalk/ansi-styles" ,
303
- "@turbo/gen /chalk/escape-string-regexp" ,
304
- "@turbo/gen /chalk/supports-color" ,
325
+ "log-symbols /chalk/ansi-styles" ,
326
+ "log-symbols /chalk/escape-string-regexp" ,
327
+ "log-symbols /chalk/supports-color" ,
305
328
]
306
329
. as_slice ( ) ;
307
330
const CHALK_DEPS : & [ & str ] = [ "ansi-styles" , "supports-color" ] . as_slice ( ) ;
@@ -336,4 +359,29 @@ mod test {
336
359
crate :: Package :: new
( "[email protected] " , "3.0.0+patches/[email protected] " )
337
360
) ;
338
361
}
362
+
363
+ #[ test]
364
+ fn test_failure_if_mismatched_keys ( ) {
365
+ let contents = serde_json:: to_string ( & json ! ( {
366
+ "lockfileVersion" : 0 ,
367
+ "workspaces" : {
368
+ "" : {
369
+ "name" : "test" ,
370
+ "dependencies" : {
371
+ "foo" : "^1.0.0" ,
372
+ "bar" : "^1.0.0" ,
373
+ }
374
+ }
375
+ } ,
376
+ "packages" : {
377
+ "bar" : [ "[email protected] " , { "dependencies" : { "shared" : "^1.0.0" } } , "sha512-goodbye" ] ,
378
+ "bar/shared" : [ "[email protected] " , { } , "sha512-bar" ] ,
379
+ "foo" : [ "[email protected] " , { "dependencies" : { "shared" : "^1.0.0" } } , "sha512-hello" ] ,
380
+ "foo/shared" : [ "[email protected] " , { } , "sha512-foo" ] ,
381
+ }
382
+ } ) )
383
+ . unwrap ( ) ;
384
+ let lockfile = BunLockfile :: from_str ( & contents) ;
385
+ assert ! ( lockfile. is_err( ) , "matching packages have differing shas" ) ;
386
+ }
339
387
}
0 commit comments