@@ -235,8 +235,8 @@ fn linked_source_of(pkg_path: &Path, real: PathBuf) -> Option<SharedStore> {
235235/// sets it, resolved against that file's directory as Yarn does, and does
236236/// not contain the project (`pnpmStoreFolder: .`);
237237/// * `real` is exactly `<store>/<entry>/package`, where `<entry>` is Yarn's
238- /// slug of a registry locator, `<ident>-npm-<version>-<10 hex>` : the
239- /// layout Yarn gives a hard (installed) package, never a workspace.
238+ /// slug of a registry locator ([`is_yarn_registry_slug`]) : the layout
239+ /// Yarn gives a hard (installed) package, never a workspace.
240240fn in_yarn_pnpm_store ( node_modules : & Path , real : & Path ) -> bool {
241241 let Some ( project) = node_modules. parent ( ) else {
242242 return false ;
@@ -284,17 +284,24 @@ fn in_yarn_pnpm_store(node_modules: &Path, real: &Path) -> bool {
284284 && parts. next ( ) . is_none ( )
285285}
286286
287- /// `left-pad-npm-1.3.0-0123456789`, `@types-node-npm-20.1.0-abcdef0123`:
288- /// Yarn's slug of an `npm:` locator, ending in ten hex digits of its hash.
287+ /// Yarn's slug of a registry package's store entry, ending in ten hex
288+ /// digits of its locator hash: `left-pad-npm-1.3.0-0123456789` and
289+ /// `@types-node-npm-20.1.0-abcdef0123` for an `npm:` locator, or
290+ /// `react-dom-virtual-685e277730` for one instantiated for its peers (a
291+ /// `virtual:` locator). Workspaces never get a store entry: the pnpm linker
292+ /// links them to their source directly.
289293fn is_yarn_registry_slug ( entry : & str ) -> bool {
290294 let Some ( ( head, hash) ) = entry. rsplit_once ( '-' ) else {
291295 return false ;
292296 } ;
293297 hash. len ( ) == 10
294298 && hash. bytes ( ) . all ( |b| b. is_ascii_hexdigit ( ) )
295- && head
299+ && ( head
296300 . split_once ( "-npm-" )
297301 . is_some_and ( |( ident, version) | !ident. is_empty ( ) && !version. is_empty ( ) )
302+ || head
303+ . strip_suffix ( "-virtual" )
304+ . is_some_and ( |ident| !ident. is_empty ( ) ) )
298305}
299306
300307fn is_node_modules ( dir : & Path ) -> bool {
@@ -578,6 +585,10 @@ mod tests {
578585 let entry = store. join ( "left-pad-npm-1.3.0-0123456789" ) . join ( "package" ) ;
579586 std:: fs:: create_dir_all ( & entry) . unwrap ( ) ;
580587 symlink ( & entry, nm. join ( "left-pad" ) ) . unwrap ( ) ;
588+ // A registry package instantiated for its peers (`virtual:`).
589+ let virtual_entry = store. join ( "react-dom-virtual-685e277730" ) . join ( "package" ) ;
590+ std:: fs:: create_dir_all ( & virtual_entry) . unwrap ( ) ;
591+ symlink ( & virtual_entry, nm. join ( "react-dom" ) ) . unwrap ( ) ;
581592 // A workspace member linked beside it stays refused.
582593 let member = root. join ( "packages" ) . join ( "a" ) ;
583594 std:: fs:: create_dir_all ( & member) . unwrap ( ) ;
@@ -603,6 +614,7 @@ mod tests {
603614 // With a yarn.lock, an ancestor's settings resolve against its dir.
604615 std:: fs:: write ( root. join ( "yarn.lock" ) , "" ) . unwrap ( ) ;
605616 assert_eq ! ( shared_store_of( & nm. join( "left-pad" ) ) . await , None ) ;
617+ assert_eq ! ( shared_store_of( & nm. join( "react-dom" ) ) . await , None ) ;
606618 // The project's own `.yarnrc.yml` wins over the ancestor's.
607619 std:: fs:: write (
608620 root. join ( ".yarnrc.yml" ) ,
@@ -686,6 +698,8 @@ mod tests {
686698 for ok in [
687699 "left-pad-npm-1.3.0-0123456789" ,
688700 "@types-node-npm-20.1.0-abcdef0123" ,
701+ "react-dom-virtual-685e277730" ,
702+ "@emotion-react-virtual-0123456789" ,
689703 ] {
690704 assert ! ( is_yarn_registry_slug( ok) , "{ok}" ) ;
691705 }
@@ -697,6 +711,8 @@ mod tests {
697711 "left-pad-npm-1.3.0-0123456789a" ,
698712 "b-workspace-packages-b-0123456789" ,
699713 "-npm-1.0.0-0123456789" ,
714+ "-virtual-0123456789" ,
715+ "react-dom-virtual-x" ,
700716 ] {
701717 assert ! ( !is_yarn_registry_slug( bad) , "{bad}" ) ;
702718 }
0 commit comments