Skip to content

Commit f419eb5

Browse files
committed
Accept peer-instantiated Yarn store entries
Yarn names a registry package instantiated for its peers by its virtual locator (react-dom-virtual-685e277730), not npm-<version>, so the relocated-store check refused it as linked source. Accept <ident>-virtual-<10 hex> beside <ident>-npm-<version>-<10 hex>. Workspaces never get a store entry under the pnpm linker, so this admits no first-party source. Refs #626 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SoWUfwR22TaCBa9tCuJGpU
1 parent 3ac1213 commit f419eb5

2 files changed

Lines changed: 23 additions & 6 deletions

File tree

‎crates/socket-patch-core/src/patch/shared_store.rs‎

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
240240
fn 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.
289293
fn 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

300307
fn 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
}

‎docs/ecosystems.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ into the root `node_modules/.pnpm`, are patched as usual. So are links into
179179
Yarn's pnpm-linker store relocated outside `node_modules`, but only for an
180180
active Yarn pnpm install (a `yarn.lock`, and `nodeLinker: pnpm` with
181181
`pnpmStoreFolder` in the nearest `.yarnrc.yml`), only to a registry entry's
182-
`<store>/<name>-npm-<version>-<hash>/package` directory, and only when that
182+
`<store>/<name>-npm-<version>-<hash>/package` (or, for one installed for
183+
its peers, `<store>/<name>-virtual-<hash>/package`) directory, and only when that
183184
store does not contain the project.
184185

185186
Every command that looks for installed npm copies walks these same trees, not

0 commit comments

Comments
 (0)