From 59edac770dd45ec0d6b61bfecb1c5f3bf954b413 Mon Sep 17 00:00:00 2001 From: ha1fstack <61955474+ha1fstack@users.noreply.github.com> Date: Sun, 22 Feb 2026 23:52:37 +0900 Subject: [PATCH 1/2] test(pnp): mimic enhanced-resolve exports-field PnP cases --- fixtures/pnp/package.json | 1 + fixtures/pnp/shared-name-mismatch/dist/a.js | 1 + .../pnp/shared-name-mismatch/package.json | 8 ++++ fixtures/pnp/yarn.lock | 7 ++++ src/tests/pnp.rs | 40 +++++++++++++++++++ 5 files changed, 57 insertions(+) create mode 100644 fixtures/pnp/shared-name-mismatch/dist/a.js create mode 100644 fixtures/pnp/shared-name-mismatch/package.json diff --git a/fixtures/pnp/package.json b/fixtures/pnp/package.json index c0d356e7..aea78e8d 100644 --- a/fixtures/pnp/package.json +++ b/fixtures/pnp/package.json @@ -2,6 +2,7 @@ "name": "pnp", "packageManager": "yarn@4.3.1", "dependencies": { + "@user/m1": "link:./shared-name-mismatch", "extend": "3.0.2", "is-even": "^1.0.0", "is-odd": "^3.0.1", diff --git a/fixtures/pnp/shared-name-mismatch/dist/a.js b/fixtures/pnp/shared-name-mismatch/dist/a.js new file mode 100644 index 00000000..33535fc6 --- /dev/null +++ b/fixtures/pnp/shared-name-mismatch/dist/a.js @@ -0,0 +1 @@ +module.exports = "feature"; diff --git a/fixtures/pnp/shared-name-mismatch/package.json b/fixtures/pnp/shared-name-mismatch/package.json new file mode 100644 index 00000000..0bec0c81 --- /dev/null +++ b/fixtures/pnp/shared-name-mismatch/package.json @@ -0,0 +1,8 @@ +{ + "name": "shared-package", + "version": "1.0.0", + "exports": { + ".": "./dist/a.js", + "./x": "./dist/a.js" + } +} diff --git a/fixtures/pnp/yarn.lock b/fixtures/pnp/yarn.lock index 5e6eca0a..9f9a9dfd 100644 --- a/fixtures/pnp/yarn.lock +++ b/fixtures/pnp/yarn.lock @@ -5,6 +5,12 @@ __metadata: version: 8 cacheKey: 10c0 +"@user/m1@link:./shared-name-mismatch::locator=pnp%40workspace%3A.": + version: 0.0.0-use.local + resolution: "@user/m1@link:./shared-name-mismatch::locator=pnp%40workspace%3A." + languageName: node + linkType: soft + "extend@npm:3.0.2": version: 3.0.2 resolution: "extend@npm:3.0.2" @@ -88,6 +94,7 @@ __metadata: version: 0.0.0-use.local resolution: "pnp@workspace:." dependencies: + "@user/m1": "link:./shared-name-mismatch" extend: "npm:3.0.2" is-even: "npm:^1.0.0" is-odd: "npm:^3.0.1" diff --git a/src/tests/pnp.rs b/src/tests/pnp.rs index cdcd548c..49728e61 100644 --- a/src/tests/pnp.rs +++ b/src/tests/pnp.rs @@ -303,3 +303,43 @@ async fn resolve_pnp_transitive_dep_from_global_cache() { "Expected isarray in global cache, got: {isarray}" ); } + +#[tokio::test] +async fn resolve_pnp_should_handle_exports_field_when_using_pnp() { + // Mimics enhanced-resolve: "should handle the exports field when using PnP" + let fixture = super::fixture_root().join("pnp"); + + let resolver = Resolver::new(ResolveOptions { + extensions: vec![".js".into()], + condition_names: vec!["import".into()], + ..ResolveOptions::default() + }); + + assert_eq!( + resolver + .resolve(&fixture, "@user/m1") + .await + .map(|r| r.full_path()), + Ok(fixture.join("shared-name-mismatch/dist/a.js")) + ); +} + +#[tokio::test] +async fn resolve_pnp_should_handle_exports_field_when_using_pnp_with_sub_path() { + // Mimics enhanced-resolve: "should handle the exports field when using PnP (with sub path)" + let fixture = super::fixture_root().join("pnp"); + + let resolver = Resolver::new(ResolveOptions { + extensions: vec![".js".into()], + condition_names: vec!["import".into()], + ..ResolveOptions::default() + }); + + assert_eq!( + resolver + .resolve(&fixture, "@user/m1/x") + .await + .map(|r| r.full_path()), + Ok(fixture.join("shared-name-mismatch/dist/a.js")) + ); +} From b682ef62f27709dfe009c5937a93b39d07980444 Mon Sep 17 00:00:00 2001 From: ha1fstack <61955474+ha1fstack@users.noreply.github.com> Date: Sun, 22 Feb 2026 23:52:37 +0900 Subject: [PATCH 2/2] fix(pnp): resolve exports from pnp subpath --- src/lib.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 55eafb1d..d52d1b9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1022,21 +1022,26 @@ impl ResolverGeneric { match resolution { Ok(pnp::Resolution::Resolved(path, subpath)) => { let cached_path = self.cache.value(&path); - - let export_resolution = self.load_package_self(&cached_path, specifier, ctx).await?; - // can be found in pnp cached folder + let normalized_subpath = subpath + .as_deref() + .map(|subpath| subpath.trim_start_matches('/')) + .filter(|subpath| !subpath.is_empty()); + + // Mirror enhanced-resolve's PnP pipeline by resolving exports from the + // request-derived subpath instead of PACKAGE_SELF name matching. + let exports_subpath = + normalized_subpath.map_or_else(String::new, |subpath| format!("/{subpath}")); + let export_resolution = self + .load_package_exports(specifier, &exports_subpath, &cached_path, ctx) + .await?; if export_resolution.is_some() { return Ok(export_resolution); } - let is_bare = subpath.is_none(); - - let inner_request = subpath.map_or_else( - || ".".to_string(), - |mut p| { - p.insert_str(0, "./"); - p - }, - ); + + let is_bare = normalized_subpath.is_none(); + + let inner_request = normalized_subpath + .map_or_else(|| ".".to_string(), |subpath| format!("./{subpath}")); let mut inner_options = self.options().clone(); if is_bare {