diff options
Diffstat (limited to 'cli/npm')
-rw-r--r-- | cli/npm/resolution.rs | 118 | ||||
-rw-r--r-- | cli/npm/resolvers/common.rs | 3 | ||||
-rw-r--r-- | cli/npm/resolvers/global.rs | 6 | ||||
-rw-r--r-- | cli/npm/resolvers/local.rs | 5 | ||||
-rw-r--r-- | cli/npm/resolvers/mod.rs | 44 |
5 files changed, 175 insertions, 1 deletions
diff --git a/cli/npm/resolution.rs b/cli/npm/resolution.rs index 8656d8a3c..7cd4df124 100644 --- a/cli/npm/resolution.rs +++ b/cli/npm/resolution.rs @@ -11,9 +11,13 @@ use deno_core::anyhow::Context; use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::futures; +use deno_core::parking_lot::Mutex; use deno_core::parking_lot::RwLock; use serde::Deserialize; use serde::Serialize; +use std::sync::Arc; + +use crate::lockfile::Lockfile; use super::cache::should_sync_download; use super::registry::NpmPackageInfo; @@ -172,6 +176,24 @@ impl NpmPackageId { None } } + + pub fn serialize_for_lock_file(&self) -> String { + format!("{}@{}", self.name, self.version) + } + + pub fn deserialize_from_lock_file(id: &str) -> Result<Self, AnyError> { + let reference = NpmPackageReference::from_str(&format!("npm:{}", id)) + .with_context(|| { + format!("Unable to deserialize npm package reference: {}", id) + })?; + let version = + NpmVersion::parse(&reference.req.version_req.unwrap().to_string()) + .unwrap(); + Ok(Self { + name: reference.req.name, + version, + }) + } } impl std::fmt::Display for NpmPackageId { @@ -345,6 +367,88 @@ impl NpmResolutionSnapshot { } maybe_best_version.cloned() } + + pub async fn from_lockfile( + lockfile: Arc<Mutex<Lockfile>>, + api: &NpmRegistryApi, + ) -> Result<Self, AnyError> { + let mut package_reqs = HashMap::new(); + let mut packages_by_name: HashMap<String, Vec<NpmVersion>> = HashMap::new(); + let mut packages = HashMap::new(); + + { + let lockfile = lockfile.lock(); + + for (key, value) in &lockfile.content.npm.specifiers { + let reference = NpmPackageReference::from_str(&format!("npm:{}", key)) + .with_context(|| format!("Unable to parse npm specifier: {}", key))?; + let package_id = NpmPackageId::deserialize_from_lock_file(value)?; + package_reqs.insert(reference.req, package_id.version.clone()); + } + + for (key, value) in &lockfile.content.npm.packages { + let package_id = NpmPackageId::deserialize_from_lock_file(key)?; + let mut dependencies = HashMap::default(); + + for (name, specifier) in &value.dependencies { + dependencies.insert( + name.to_string(), + NpmPackageId::deserialize_from_lock_file(specifier)?, + ); + } + + for (name, id) in &dependencies { + packages_by_name + .entry(name.to_string()) + .or_default() + .push(id.version.clone()); + } + + let package = NpmResolutionPackage { + id: package_id.clone(), + // temporary dummy value + dist: NpmPackageVersionDistInfo { + tarball: "foobar".to_string(), + shasum: "foobar".to_string(), + integrity: Some("foobar".to_string()), + }, + dependencies, + }; + + packages.insert(package_id.clone(), package); + } + } + + let mut unresolved_tasks = Vec::with_capacity(packages_by_name.len()); + + for package_id in packages.keys() { + let package_id = package_id.clone(); + let api = api.clone(); + unresolved_tasks.push(tokio::task::spawn(async move { + let info = api.package_info(&package_id.name).await?; + Result::<_, AnyError>::Ok((package_id, info)) + })); + } + for result in futures::future::join_all(unresolved_tasks).await { + let (package_id, info) = result??; + + let version_and_info = get_resolved_package_version_and_info( + &package_id.name, + &NpmVersionReq::parse(&package_id.version.to_string()).unwrap(), + info, + None, + )?; + + let package = packages.get_mut(&package_id).unwrap(); + package.dist = version_and_info.info.dist; + } + + Ok(Self { + package_reqs, + packages_by_name, + packages, + }) + } } pub struct NpmResolution { @@ -628,6 +732,20 @@ impl NpmResolution { pub fn snapshot(&self) -> NpmResolutionSnapshot { self.snapshot.read().clone() } + + pub fn lock( + &self, + lockfile: &mut Lockfile, + snapshot: &NpmResolutionSnapshot, + ) -> Result<(), AnyError> { + for (package_req, version) in snapshot.package_reqs.iter() { + lockfile.insert_npm_specifier(package_req, version.to_string()); + } + for package in self.all_packages() { + lockfile.check_or_insert_npm_package(&package)?; + } + Ok(()) + } } #[derive(Clone)] diff --git a/cli/npm/resolvers/common.rs b/cli/npm/resolvers/common.rs index 4981d5613..e114f3f8a 100644 --- a/cli/npm/resolvers/common.rs +++ b/cli/npm/resolvers/common.rs @@ -11,6 +11,7 @@ use deno_core::futures; use deno_core::futures::future::BoxFuture; use deno_core::url::Url; +use crate::lockfile::Lockfile; use crate::npm::cache::should_sync_download; use crate::npm::resolution::NpmResolutionSnapshot; use crate::npm::NpmCache; @@ -50,6 +51,8 @@ pub trait InnerNpmPackageResolver: Send + Sync { fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError>; fn snapshot(&self) -> NpmResolutionSnapshot; + + fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError>; } /// Caches all the packages in parallel. diff --git a/cli/npm/resolvers/global.rs b/cli/npm/resolvers/global.rs index 8eafc19f4..996f55c2d 100644 --- a/cli/npm/resolvers/global.rs +++ b/cli/npm/resolvers/global.rs @@ -15,6 +15,7 @@ use deno_core::url::Url; use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::TYPES_CONDITIONS; +use crate::lockfile::Lockfile; use crate::npm::resolution::NpmResolution; use crate::npm::resolution::NpmResolutionSnapshot; use crate::npm::resolvers::common::cache_packages; @@ -145,6 +146,11 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver { fn snapshot(&self) -> NpmResolutionSnapshot { self.resolution.snapshot() } + + fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> { + let snapshot = self.resolution.snapshot(); + self.resolution.lock(lockfile, &snapshot) + } } async fn cache_packages_in_resolver( diff --git a/cli/npm/resolvers/local.rs b/cli/npm/resolvers/local.rs index b51593d4c..6c4c4ef6c 100644 --- a/cli/npm/resolvers/local.rs +++ b/cli/npm/resolvers/local.rs @@ -22,6 +22,7 @@ use deno_runtime::deno_node::TYPES_CONDITIONS; use tokio::task::JoinHandle; use crate::fs_util; +use crate::lockfile::Lockfile; use crate::npm::cache::should_sync_download; use crate::npm::resolution::NpmResolution; use crate::npm::resolution::NpmResolutionSnapshot; @@ -213,6 +214,10 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver { fn snapshot(&self) -> NpmResolutionSnapshot { self.resolution.snapshot() } + + fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> { + self.resolution.lock(lockfile, &self.snapshot()) + } } async fn sync_resolver_with_fs( diff --git a/cli/npm/resolvers/mod.rs b/cli/npm/resolvers/mod.rs index 5498bbf75..3d55170ac 100644 --- a/cli/npm/resolvers/mod.rs +++ b/cli/npm/resolvers/mod.rs @@ -8,6 +8,7 @@ use deno_ast::ModuleSpecifier; use deno_core::anyhow::bail; use deno_core::error::custom_error; use deno_core::error::AnyError; +use deno_core::parking_lot::Mutex; use deno_core::serde_json; use deno_runtime::deno_node::PathClean; use deno_runtime::deno_node::RequireNpmResolver; @@ -21,6 +22,7 @@ use std::path::PathBuf; use std::sync::Arc; use crate::fs_util; +use crate::lockfile::Lockfile; use self::common::InnerNpmPackageResolver; use self::local::LocalNpmPackageResolver; @@ -70,6 +72,7 @@ pub struct NpmPackageResolver { local_node_modules_path: Option<PathBuf>, api: NpmRegistryApi, cache: NpmCache, + maybe_lockfile: Option<Arc<Mutex<Lockfile>>>, } impl std::fmt::Debug for NpmPackageResolver { @@ -101,6 +104,32 @@ impl NpmPackageResolver { ) } + /// This function will replace current resolver with a new one built from a + /// snapshot created out of the lockfile. + pub async fn add_lockfile( + &mut self, + lockfile: Arc<Mutex<Lockfile>>, + ) -> Result<(), AnyError> { + let snapshot = + NpmResolutionSnapshot::from_lockfile(lockfile.clone(), &self.api).await?; + self.maybe_lockfile = Some(lockfile); + if let Some(node_modules_folder) = &self.local_node_modules_path { + self.inner = Arc::new(LocalNpmPackageResolver::new( + self.cache.clone(), + self.api.clone(), + node_modules_folder.clone(), + Some(snapshot), + )); + } else { + self.inner = Arc::new(GlobalNpmPackageResolver::new( + self.cache.clone(), + self.api.clone(), + Some(snapshot), + )); + } + Ok(()) + } + fn new_with_maybe_snapshot( cache: NpmCache, api: NpmRegistryApi, @@ -138,6 +167,7 @@ impl NpmPackageResolver { local_node_modules_path, api, cache, + maybe_lockfile: None, } } @@ -224,7 +254,15 @@ impl NpmPackageResolver { )); } - self.inner.add_package_reqs(packages).await + self.inner.add_package_reqs(packages).await?; + + // If there's a lock file, update it with all discovered npm packages + if let Some(lockfile_mutex) = &self.maybe_lockfile { + let mut lockfile = lockfile_mutex.lock(); + self.lock(&mut lockfile)?; + } + + Ok(()) } /// Sets package requirements to the resolver, removing old requirements and adding new ones. @@ -266,6 +304,10 @@ impl NpmPackageResolver { Some(self.inner.snapshot()), ) } + + pub fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> { + self.inner.lock(lockfile) + } } impl RequireNpmResolver for NpmPackageResolver { |