@@ -1946,6 +1946,66 @@ mod tests {
19461946 dir
19471947 }
19481948
1949+ /// #1462: node's lookup from an installed copy finds a bundled,
1950+ /// hoisted or pnpm-sibling dependency; an optional one may be absent,
1951+ /// and a copy outside `node_modules` or an unsafe name is no evidence.
1952+ #[ tokio:: test]
1953+ async fn unresolved_npm_dependency_follows_node_lookup ( ) {
1954+ let tmp = tempfile:: tempdir ( ) . unwrap ( ) ;
1955+ let root = tmp. path ( ) ;
1956+ let put = |rel : & str , text : & str | {
1957+ let path = root. join ( rel) ;
1958+ std:: fs:: create_dir_all ( path. parent ( ) . unwrap ( ) ) . unwrap ( ) ;
1959+ std:: fs:: write ( path, text) . unwrap ( ) ;
1960+ } ;
1961+ let manifest = r#"{"name":"lp","dependencies":{"a":"1","@s/b":"1","opt":"1",
1962+ "../../escape":"1"},"optionalDependencies":{"opt":"1"}}"# ;
1963+
1964+ // Hoisted layout: `a` and `@s/b` at the root; `@s/b` missing first.
1965+ put ( "node_modules/lp/package.json" , manifest) ;
1966+ put ( "node_modules/a/package.json" , "{}" ) ;
1967+ let hoisted = root. join ( "node_modules/lp" ) ;
1968+ assert_eq ! (
1969+ unresolved_npm_dependency( & hoisted) . await . as_deref( ) ,
1970+ Some ( "@s/b" )
1971+ ) ;
1972+ // Bundled inside the package itself.
1973+ put ( "node_modules/lp/node_modules/@s/b/package.json" , "{}" ) ;
1974+ assert_eq ! ( unresolved_npm_dependency( & hoisted) . await , None ) ;
1975+
1976+ // pnpm's isolated store: dependencies are siblings of the package.
1977+ let store = "node_modules/.pnpm/lp@1.0.0/node_modules" ;
1978+ put ( & format ! ( "{store}/lp/package.json" ) , manifest) ;
1979+ put ( & format ! ( "{store}/a/package.json" ) , "{}" ) ;
1980+ let isolated = root. join ( store) . join ( "lp" ) ;
1981+ assert_eq ! (
1982+ unresolved_npm_dependency( & isolated) . await . as_deref( ) ,
1983+ Some ( "@s/b" )
1984+ ) ;
1985+ put ( & format ! ( "{store}/@s/b/package.json" ) , "{}" ) ;
1986+ assert_eq ! ( unresolved_npm_dependency( & isolated) . await , None ) ;
1987+
1988+ // Not in a node_modules tree: never evidence.
1989+ put ( "unplugged/lp/package.json" , manifest) ;
1990+ assert_eq ! (
1991+ unresolved_npm_dependency( & root. join( "unplugged/lp" ) ) . await ,
1992+ None
1993+ ) ;
1994+
1995+ for ( name, plain) in [
1996+ ( "a" , true ) ,
1997+ ( "@s/b" , true ) ,
1998+ ( "@/b" , false ) ,
1999+ ( "s/b" , false ) ,
2000+ ( "../x" , false ) ,
2001+ ( "@s/.." , false ) ,
2002+ ( "a\\ b" , false ) ,
2003+ ( "" , false ) ,
2004+ ] {
2005+ assert_eq ! ( plain_npm_name( name) , plain, "{name}" ) ;
2006+ }
2007+ }
2008+
19492009 fn hosted_ctx ( purl : & str , copies : HostedCopies ) -> VendorContext {
19502010 VendorContext {
19512011 hosted : [ ( purl. to_string ( ) , copies) ] . into_iter ( ) . collect ( ) ,
0 commit comments