summaryrefslogtreecommitdiff
path: root/cli/npm/resolvers
diff options
context:
space:
mode:
Diffstat (limited to 'cli/npm/resolvers')
-rw-r--r--cli/npm/resolvers/common.rs7
-rw-r--r--cli/npm/resolvers/global.rs60
-rw-r--r--cli/npm/resolvers/local.rs58
-rw-r--r--cli/npm/resolvers/mod.rs83
4 files changed, 179 insertions, 29 deletions
diff --git a/cli/npm/resolvers/common.rs b/cli/npm/resolvers/common.rs
index b91deae9e..4981d5613 100644
--- a/cli/npm/resolvers/common.rs
+++ b/cli/npm/resolvers/common.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+use std::collections::HashSet;
use std::io::ErrorKind;
use std::path::Path;
use std::path::PathBuf;
@@ -26,6 +27,7 @@ pub trait InnerNpmPackageResolver: Send + Sync {
&self,
name: &str,
referrer: &ModuleSpecifier,
+ conditions: &[&str],
) -> Result<PathBuf, AnyError>;
fn resolve_package_folder_from_specifier(
@@ -40,6 +42,11 @@ pub trait InnerNpmPackageResolver: Send + Sync {
packages: Vec<NpmPackageReq>,
) -> BoxFuture<'static, Result<(), AnyError>>;
+ fn set_package_reqs(
+ &self,
+ packages: HashSet<NpmPackageReq>,
+ ) -> BoxFuture<'static, Result<(), AnyError>>;
+
fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError>;
fn snapshot(&self) -> NpmResolutionSnapshot;
diff --git a/cli/npm/resolvers/global.rs b/cli/npm/resolvers/global.rs
index c1b6818fd..8eafc19f4 100644
--- a/cli/npm/resolvers/global.rs
+++ b/cli/npm/resolvers/global.rs
@@ -2,6 +2,7 @@
//! Code for global npm cache resolution.
+use std::collections::HashSet;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
@@ -11,6 +12,8 @@ use deno_core::error::AnyError;
use deno_core::futures::future::BoxFuture;
use deno_core::futures::FutureExt;
use deno_core::url::Url;
+use deno_runtime::deno_node::PackageJson;
+use deno_runtime::deno_node::TYPES_CONDITIONS;
use crate::npm::resolution::NpmResolution;
use crate::npm::resolution::NpmResolutionSnapshot;
@@ -65,14 +68,35 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
&self,
name: &str,
referrer: &ModuleSpecifier,
+ conditions: &[&str],
) -> Result<PathBuf, AnyError> {
let referrer_pkg_id = self
.cache
.resolve_package_id_from_specifier(referrer, &self.registry_url)?;
- let pkg = self
+ let pkg_result = self
.resolution
- .resolve_package_from_package(name, &referrer_pkg_id)?;
- Ok(self.package_folder(&pkg.id))
+ .resolve_package_from_package(name, &referrer_pkg_id);
+ if conditions == TYPES_CONDITIONS && !name.starts_with("@types/") {
+ // When doing types resolution, the package must contain a "types"
+ // entry, or else it will then search for a @types package
+ if let Ok(pkg) = pkg_result {
+ let package_folder = self.package_folder(&pkg.id);
+ let package_json = PackageJson::load_skip_read_permission(
+ package_folder.join("package.json"),
+ )?;
+ if package_json.types.is_some() {
+ return Ok(package_folder);
+ }
+ }
+
+ let name = format!("@types/{}", name);
+ let pkg = self
+ .resolution
+ .resolve_package_from_package(&name, &referrer_pkg_id)?;
+ Ok(self.package_folder(&pkg.id))
+ } else {
+ Ok(self.package_folder(&pkg_result?.id))
+ }
}
fn resolve_package_folder_from_specifier(
@@ -96,12 +120,19 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
let resolver = self.clone();
async move {
resolver.resolution.add_package_reqs(packages).await?;
- cache_packages(
- resolver.resolution.all_packages(),
- &resolver.cache,
- &resolver.registry_url,
- )
- .await
+ cache_packages_in_resolver(&resolver).await
+ }
+ .boxed()
+ }
+
+ fn set_package_reqs(
+ &self,
+ packages: HashSet<NpmPackageReq>,
+ ) -> BoxFuture<'static, Result<(), AnyError>> {
+ let resolver = self.clone();
+ async move {
+ resolver.resolution.set_package_reqs(packages).await?;
+ cache_packages_in_resolver(&resolver).await
}
.boxed()
}
@@ -115,3 +146,14 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
self.resolution.snapshot()
}
}
+
+async fn cache_packages_in_resolver(
+ resolver: &GlobalNpmPackageResolver,
+) -> Result<(), AnyError> {
+ cache_packages(
+ resolver.resolution.all_packages(),
+ &resolver.cache,
+ &resolver.registry_url,
+ )
+ .await
+}
diff --git a/cli/npm/resolvers/local.rs b/cli/npm/resolvers/local.rs
index cd79320b7..b51593d4c 100644
--- a/cli/npm/resolvers/local.rs
+++ b/cli/npm/resolvers/local.rs
@@ -17,6 +17,8 @@ use deno_core::futures::future::BoxFuture;
use deno_core::futures::FutureExt;
use deno_core::url::Url;
use deno_runtime::deno_core::futures;
+use deno_runtime::deno_node::PackageJson;
+use deno_runtime::deno_node::TYPES_CONDITIONS;
use tokio::task::JoinHandle;
use crate::fs_util;
@@ -124,6 +126,7 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
&self,
name: &str,
referrer: &ModuleSpecifier,
+ conditions: &[&str],
) -> Result<PathBuf, AnyError> {
let local_path = self.resolve_folder_for_specifier(referrer)?;
let package_root_path = self.resolve_package_root(&local_path);
@@ -132,8 +135,28 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
current_folder = get_next_node_modules_ancestor(current_folder);
let sub_dir = join_package_name(current_folder, name);
if sub_dir.is_dir() {
- return Ok(sub_dir);
+ // if doing types resolution, only resolve the package if it specifies a types property
+ if conditions == TYPES_CONDITIONS && !name.starts_with("@types/") {
+ let package_json = PackageJson::load_skip_read_permission(
+ sub_dir.join("package.json"),
+ )?;
+ if package_json.types.is_some() {
+ return Ok(sub_dir);
+ }
+ } else {
+ return Ok(sub_dir);
+ }
}
+
+ // if doing type resolution, check for the existance of a @types package
+ if conditions == TYPES_CONDITIONS && !name.starts_with("@types/") {
+ let sub_dir =
+ join_package_name(current_folder, &format!("@types/{}", name));
+ if sub_dir.is_dir() {
+ return Ok(sub_dir);
+ }
+ }
+
if current_folder == self.root_node_modules_path {
bail!(
"could not find package '{}' from referrer '{}'.",
@@ -164,15 +187,20 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
let resolver = self.clone();
async move {
resolver.resolution.add_package_reqs(packages).await?;
+ sync_resolver_with_fs(&resolver).await?;
+ Ok(())
+ }
+ .boxed()
+ }
- sync_resolution_with_fs(
- &resolver.resolution.snapshot(),
- &resolver.cache,
- &resolver.registry_url,
- &resolver.root_node_modules_path,
- )
- .await?;
-
+ fn set_package_reqs(
+ &self,
+ packages: HashSet<NpmPackageReq>,
+ ) -> BoxFuture<'static, Result<(), AnyError>> {
+ let resolver = self.clone();
+ async move {
+ resolver.resolution.set_package_reqs(packages).await?;
+ sync_resolver_with_fs(&resolver).await?;
Ok(())
}
.boxed()
@@ -187,6 +215,18 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
}
}
+async fn sync_resolver_with_fs(
+ resolver: &LocalNpmPackageResolver,
+) -> Result<(), AnyError> {
+ sync_resolution_with_fs(
+ &resolver.resolution.snapshot(),
+ &resolver.cache,
+ &resolver.registry_url,
+ &resolver.root_node_modules_path,
+ )
+ .await
+}
+
/// Creates a pnpm style folder structure.
async fn sync_resolution_with_fs(
snapshot: &NpmResolutionSnapshot,
diff --git a/cli/npm/resolvers/mod.rs b/cli/npm/resolvers/mod.rs
index d290a5569..5498bbf75 100644
--- a/cli/npm/resolvers/mod.rs
+++ b/cli/npm/resolvers/mod.rs
@@ -15,6 +15,7 @@ use global::GlobalNpmPackageResolver;
use once_cell::sync::Lazy;
use serde::Deserialize;
use serde::Serialize;
+use std::collections::HashSet;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
@@ -23,10 +24,10 @@ use crate::fs_util;
use self::common::InnerNpmPackageResolver;
use self::local::LocalNpmPackageResolver;
-use super::resolution::NpmResolutionSnapshot;
use super::NpmCache;
use super::NpmPackageReq;
use super::NpmRegistryApi;
+use super::NpmResolutionSnapshot;
const RESOLUTION_STATE_ENV_VAR_NAME: &str =
"DENO_DONT_USE_INTERNAL_NODE_COMPAT_STATE";
@@ -67,6 +68,19 @@ pub struct NpmPackageResolver {
no_npm: bool,
inner: Arc<dyn InnerNpmPackageResolver>,
local_node_modules_path: Option<PathBuf>,
+ api: NpmRegistryApi,
+ cache: NpmCache,
+}
+
+impl std::fmt::Debug for NpmPackageResolver {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("NpmPackageResolver")
+ .field("unstable", &self.unstable)
+ .field("no_npm", &self.no_npm)
+ .field("inner", &"<omitted>")
+ .field("local_node_modules_path", &self.local_node_modules_path)
+ .finish()
+ }
}
impl NpmPackageResolver {
@@ -77,30 +91,53 @@ impl NpmPackageResolver {
no_npm: bool,
local_node_modules_path: Option<PathBuf>,
) -> Self {
+ Self::new_with_maybe_snapshot(
+ cache,
+ api,
+ unstable,
+ no_npm,
+ local_node_modules_path,
+ None,
+ )
+ }
+
+ fn new_with_maybe_snapshot(
+ cache: NpmCache,
+ api: NpmRegistryApi,
+ unstable: bool,
+ no_npm: bool,
+ local_node_modules_path: Option<PathBuf>,
+ initial_snapshot: Option<NpmResolutionSnapshot>,
+ ) -> Self {
let process_npm_state = NpmProcessState::take();
let local_node_modules_path = local_node_modules_path.or_else(|| {
process_npm_state
.as_ref()
.and_then(|s| s.local_node_modules_path.as_ref().map(PathBuf::from))
});
- let maybe_snapshot = process_npm_state.map(|s| s.snapshot);
+ let maybe_snapshot =
+ initial_snapshot.or_else(|| process_npm_state.map(|s| s.snapshot));
let inner: Arc<dyn InnerNpmPackageResolver> = match &local_node_modules_path
{
Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new(
- cache,
- api,
+ cache.clone(),
+ api.clone(),
node_modules_folder.clone(),
maybe_snapshot,
)),
- None => {
- Arc::new(GlobalNpmPackageResolver::new(cache, api, maybe_snapshot))
- }
+ None => Arc::new(GlobalNpmPackageResolver::new(
+ cache.clone(),
+ api.clone(),
+ maybe_snapshot,
+ )),
};
Self {
unstable,
no_npm,
inner,
local_node_modules_path,
+ api,
+ cache,
}
}
@@ -122,10 +159,11 @@ impl NpmPackageResolver {
&self,
name: &str,
referrer: &ModuleSpecifier,
+ conditions: &[&str],
) -> Result<PathBuf, AnyError> {
let path = self
.inner
- .resolve_package_folder_from_package(name, referrer)?;
+ .resolve_package_folder_from_package(name, referrer, conditions)?;
log::debug!("Resolved {} from {} to {}", name, referrer, path.display());
Ok(path)
}
@@ -156,12 +194,14 @@ impl NpmPackageResolver {
self.inner.has_packages()
}
- /// Adds a package requirement to the resolver and ensures everything is setup.
+ /// Adds package requirements to the resolver and ensures everything is setup.
pub async fn add_package_reqs(
&self,
packages: Vec<NpmPackageReq>,
) -> Result<(), AnyError> {
- assert!(!packages.is_empty());
+ if packages.is_empty() {
+ return Ok(());
+ }
if !self.unstable {
bail!(
@@ -187,6 +227,14 @@ impl NpmPackageResolver {
self.inner.add_package_reqs(packages).await
}
+ /// Sets package requirements to the resolver, removing old requirements and adding new ones.
+ pub async fn set_package_reqs(
+ &self,
+ packages: HashSet<NpmPackageReq>,
+ ) -> Result<(), AnyError> {
+ self.inner.set_package_reqs(packages).await
+ }
+
// If the main module should be treated as being in an npm package.
// This is triggered via a secret environment variable which is used
// for functionality like child_process.fork. Users should NOT depend
@@ -206,6 +254,18 @@ impl NpmPackageResolver {
})
.unwrap()
}
+
+ /// Gets a new resolver with a new snapshotted state.
+ pub fn snapshotted(&self) -> Self {
+ Self::new_with_maybe_snapshot(
+ self.cache.clone(),
+ self.api.clone(),
+ self.unstable,
+ self.no_npm,
+ self.local_node_modules_path.clone(),
+ Some(self.inner.snapshot()),
+ )
+ }
}
impl RequireNpmResolver for NpmPackageResolver {
@@ -213,9 +273,10 @@ impl RequireNpmResolver for NpmPackageResolver {
&self,
specifier: &str,
referrer: &std::path::Path,
+ conditions: &[&str],
) -> Result<PathBuf, AnyError> {
let referrer = path_to_specifier(referrer)?;
- self.resolve_package_folder_from_package(specifier, &referrer)
+ self.resolve_package_folder_from_package(specifier, &referrer, conditions)
}
fn resolve_package_folder_from_path(