summaryrefslogtreecommitdiff
path: root/cli/npm
diff options
context:
space:
mode:
Diffstat (limited to 'cli/npm')
-rw-r--r--cli/npm/resolution.rs118
-rw-r--r--cli/npm/resolvers/common.rs3
-rw-r--r--cli/npm/resolvers/global.rs6
-rw-r--r--cli/npm/resolvers/local.rs5
-rw-r--r--cli/npm/resolvers/mod.rs44
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 {