Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ pub struct ResolverGeneric<Fs> {
options: ResolveOptions,
cache: Arc<Cache<Fs>>,
#[cfg(feature = "yarn_pnp")]
pnp_manifest: Arc<arc_swap::ArcSwapOption<pnp::Manifest>>,
pnp_manifest: Arc<arc_swap::ArcSwapOption<(PathBuf, pnp::Manifest)>>,
/// Paths that have been searched and confirmed to have no `.pnp.cjs` reachable by filesystem walk.
#[cfg(feature = "yarn_pnp")]
pnp_no_manifest_cache: Arc<DashSet<CachedPath>>,
Expand Down Expand Up @@ -957,7 +957,7 @@ impl<Fs: FileSystem + Send + Sync> ResolverGeneric<Fs> {

#[cfg(feature = "yarn_pnp")]
#[cfg_attr(feature = "enable_instrument", tracing::instrument(level=tracing::Level::DEBUG, skip_all, fields(path = %cached_path.path().to_string_lossy())))]
fn find_pnp_manifest(&self, cached_path: &CachedPath) -> Option<Arc<pnp::Manifest>> {
fn find_pnp_manifest(&self, cached_path: &CachedPath) -> Option<Arc<(PathBuf, pnp::Manifest)>> {
// 1. Already have a manifest → return it (covers global cache paths too)
if let Some(manifest) = self.pnp_manifest.load_full() {
return Some(manifest);
Expand Down Expand Up @@ -986,7 +986,7 @@ impl<Fs: FileSystem + Send + Sync> ResolverGeneric<Fs> {
tracing::debug!("use manifest path: {:?}", manifest_path);

let manifest = pnp::load_pnp_manifest(&manifest_path).ok()?;
let manifest = Arc::new(manifest);
let manifest = Arc::new((manifest_path, manifest));

let previous = self
.pnp_manifest
Expand All @@ -1007,13 +1007,15 @@ impl<Fs: FileSystem + Send + Sync> ResolverGeneric<Fs> {
specifier: &str,
ctx: &mut Ctx,
) -> Result<Option<CachedPath>, ResolveError> {
let Some(manifest) = self.find_pnp_manifest(cached_path) else {
let Some(pnp_data) = self.find_pnp_manifest(cached_path) else {
return Ok(None);
};
let (manifest_path, manifest) = pnp_data.as_ref();
ctx.add_file_dependency(manifest_path);

let mut path = cached_path.to_path_buf();
path.push("");
let resolution = pnp::resolve_to_unqualified_via_manifest(&manifest, specifier, &path);
let resolution = pnp::resolve_to_unqualified_via_manifest(manifest, specifier, &path);

tracing::debug!("pnp resolve unqualified as: {:?}", resolution);

Expand Down
24 changes: 23 additions & 1 deletion src/tests/pnp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! enhanced_resolve's test <https://github.com/webpack/enhanced-resolve/blob/main/test/pnp.test.js>
//! cannot be ported over because it uses mocks on `pnpApi` provided by the runtime.

use crate::{path::PathUtil, ResolveError::NotFound, ResolveOptions, Resolver};
use crate::{path::PathUtil, ResolveContext, ResolveError::NotFound, ResolveOptions, Resolver};

#[tokio::test]
async fn pnp1() {
Expand Down Expand Up @@ -78,6 +78,28 @@ async fn pnp1() {
);
}

#[tokio::test]
async fn pnp_file_dependencies() {
let fixture = super::fixture_root().join("pnp");

let resolver = Resolver::new(ResolveOptions {
extensions: vec![".js".into()],
condition_names: vec!["import".into()],
..ResolveOptions::default()
});

let mut ctx = ResolveContext::default();
let result = resolver
.resolve_with_context(&fixture, "is-even", &mut ctx)
.await;
assert!(result.is_ok());
assert!(
ctx.file_dependencies.contains(&fixture.join(".pnp.cjs")),
".pnp.cjs should be in file_dependencies, got: {:?}",
ctx.file_dependencies
);
}

// https://github.com/webpack/enhanced-resolve/blob/main/lib/PnpPlugin.js#L118
// `fullySpecified` should be disabled for bare specifiers without subpath,
// so that the `main` field value (e.g. "index" without extension) can be resolved.
Expand Down
Loading