summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--cli/npm/registry.rs21
-rw-r--r--cli/npm/resolution.rs75
-rw-r--r--cli/npm/semver/mod.rs4
-rw-r--r--test_util/Cargo.toml1
-rw-r--r--test_util/src/npm.rs11
6 files changed, 108 insertions, 5 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e2ca9d58e..9e85b8065 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4627,6 +4627,7 @@ dependencies = [
"reqwest",
"ring",
"rustls-pemfile 1.0.0",
+ "semver 1.0.13",
"serde",
"serde_json",
"tar",
diff --git a/cli/npm/registry.rs b/cli/npm/registry.rs
index c70741b01..1fb4b2e0a 100644
--- a/cli/npm/registry.rs
+++ b/cli/npm/registry.rs
@@ -31,6 +31,8 @@ use super::semver::NpmVersionReq;
pub struct NpmPackageInfo {
pub name: String,
pub versions: HashMap<String, NpmPackageVersionInfo>,
+ #[serde(rename = "dist-tags")]
+ pub dist_tags: HashMap<String, String>,
}
pub struct NpmDependencyEntry {
@@ -39,7 +41,7 @@ pub struct NpmDependencyEntry {
pub version_req: NpmVersionReq,
}
-#[derive(Debug, Deserialize, Serialize, Clone)]
+#[derive(Debug, Default, Deserialize, Serialize, Clone)]
pub struct NpmPackageVersionInfo {
pub version: String,
pub dist: NpmPackageVersionDistInfo,
@@ -89,7 +91,7 @@ impl NpmPackageVersionInfo {
}
}
-#[derive(Debug, Deserialize, Serialize, Clone)]
+#[derive(Debug, Default, Deserialize, Serialize, Clone)]
pub struct NpmPackageVersionDistInfo {
/// URL to the tarball.
pub tarball: String,
@@ -231,7 +233,20 @@ impl NpmRegistryApi {
Err(err) if err.kind() == ErrorKind::NotFound => return Ok(None),
Err(err) => return Err(err.into()),
};
- Ok(serde_json::from_str(&file_text)?)
+ match serde_json::from_str(&file_text) {
+ Ok(package_info) => Ok(Some(package_info)),
+ Err(err) => {
+ // This scenario might mean we need to load more data from the
+ // npm registry than before. So, just debug log while in debug
+ // rather than panic.
+ log::debug!(
+ "error deserializing registry.json for '{}'. Reloading. {:?}",
+ name,
+ err
+ );
+ Ok(None)
+ }
+ }
}
fn save_package_info_to_file_cache(
diff --git a/cli/npm/resolution.rs b/cli/npm/resolution.rs
index 4f46bbed4..8fac92716 100644
--- a/cli/npm/resolution.rs
+++ b/cli/npm/resolution.rs
@@ -19,9 +19,10 @@ use super::semver::NpmVersion;
use super::semver::SpecifierVersionReq;
/// The version matcher used for npm schemed urls is more strict than
-/// the one used by npm packages.
+/// the one used by npm packages and so we represent either via a trait.
pub trait NpmVersionMatcher {
fn matches(&self, version: &NpmVersion) -> bool;
+ fn is_latest(&self) -> bool;
fn version_text(&self) -> String;
}
@@ -112,6 +113,10 @@ impl NpmVersionMatcher for NpmPackageReq {
}
}
+ fn is_latest(&self) -> bool {
+ self.version_req.is_none()
+ }
+
fn version_text(&self) -> String {
self
.version_req
@@ -484,6 +489,25 @@ fn get_resolved_package_version_and_info(
parent: Option<&NpmPackageId>,
) -> Result<VersionAndInfo, AnyError> {
let mut maybe_best_version: Option<VersionAndInfo> = None;
+ if version_matcher.is_latest() {
+ if let Some(version) = info.dist_tags.get("latest") {
+ match info.versions.get(version) {
+ Some(info) => {
+ return Ok(VersionAndInfo {
+ version: NpmVersion::parse(version)?,
+ info: info.clone(),
+ });
+ }
+ None => {
+ bail!(
+ "Could not find version '{}' referenced in dist-tag 'latest'.",
+ version,
+ )
+ }
+ }
+ }
+ }
+
for (_, version_info) in info.versions.into_iter() {
let version = NpmVersion::parse(&version_info.version)?;
if version_matcher.matches(&version) {
@@ -651,4 +675,53 @@ mod tests {
assert_eq!(name_without_path("@foo/bar/baz"), "@foo/bar");
assert_eq!(name_without_path("@hello"), "@hello");
}
+
+ #[test]
+ fn test_get_resolved_package_version_and_info() {
+ // dist tag where version doesn't exist
+ let package_ref = NpmPackageReference::from_str("npm:test").unwrap();
+ let result = get_resolved_package_version_and_info(
+ "test",
+ &package_ref.req,
+ NpmPackageInfo {
+ name: "test".to_string(),
+ versions: HashMap::new(),
+ dist_tags: HashMap::from([(
+ "latest".to_string(),
+ "1.0.0-alpha".to_string(),
+ )]),
+ },
+ None,
+ );
+ assert_eq!(
+ result.err().unwrap().to_string(),
+ "Could not find version '1.0.0-alpha' referenced in dist-tag 'latest'."
+ );
+
+ // dist tag where version is a pre-release
+ let package_ref = NpmPackageReference::from_str("npm:test").unwrap();
+ let result = get_resolved_package_version_and_info(
+ "test",
+ &package_ref.req,
+ NpmPackageInfo {
+ name: "test".to_string(),
+ versions: HashMap::from([
+ ("0.1.0".to_string(), NpmPackageVersionInfo::default()),
+ (
+ "1.0.0-alpha".to_string(),
+ NpmPackageVersionInfo {
+ version: "0.1.0-alpha".to_string(),
+ ..Default::default()
+ },
+ ),
+ ]),
+ dist_tags: HashMap::from([(
+ "latest".to_string(),
+ "1.0.0-alpha".to_string(),
+ )]),
+ },
+ None,
+ );
+ assert_eq!(result.unwrap().version.to_string(), "1.0.0-alpha");
+ }
}
diff --git a/cli/npm/semver/mod.rs b/cli/npm/semver/mod.rs
index 10a87a13f..53f0f199f 100644
--- a/cli/npm/semver/mod.rs
+++ b/cli/npm/semver/mod.rs
@@ -174,6 +174,10 @@ impl NpmVersionMatcher for NpmVersionReq {
self.satisfies(version)
}
+ fn is_latest(&self) -> bool {
+ false
+ }
+
fn version_text(&self) -> String {
self.raw_text.clone()
}
diff --git a/test_util/Cargo.toml b/test_util/Cargo.toml
index 8d9da66e2..7fa309dac 100644
--- a/test_util/Cargo.toml
+++ b/test_util/Cargo.toml
@@ -28,6 +28,7 @@ regex = "1.6.0"
reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli", "socks"] }
ring = "0.16.20"
rustls-pemfile = "1.0.0"
+semver = "1.0.13"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"
tar = "0.4.38"
diff --git a/test_util/src/npm.rs b/test_util/src/npm.rs
index fad59e3b8..6e393d835 100644
--- a/test_util/src/npm.rs
+++ b/test_util/src/npm.rs
@@ -71,6 +71,7 @@ fn get_npm_package(package_name: &str) -> Result<Option<CustomNpmPackage>> {
// read all the package's versions
let mut tarballs = HashMap::new();
let mut versions = serde_json::Map::new();
+ let mut latest_version = semver::Version::parse("0.0.0").unwrap();
for entry in fs::read_dir(&package_folder)? {
let entry = entry?;
let file_type = entry.file_type()?;
@@ -134,13 +135,21 @@ fn get_npm_package(package_name: &str) -> Result<Option<CustomNpmPackage>> {
let mut version_info: serde_json::Map<String, serde_json::Value> =
serde_json::from_str(&package_json_text)?;
version_info.insert("dist".to_string(), dist.into());
- versions.insert(version, version_info.into());
+ versions.insert(version.clone(), version_info.into());
+ let version = semver::Version::parse(&version)?;
+ if version.cmp(&latest_version).is_gt() {
+ latest_version = version;
+ }
}
+ let mut dist_tags = serde_json::Map::new();
+ dist_tags.insert("latest".to_string(), latest_version.to_string().into());
+
// create the registry file for this package
let mut registry_file = serde_json::Map::new();
registry_file.insert("name".to_string(), package_name.to_string().into());
registry_file.insert("versions".to_string(), versions.into());
+ registry_file.insert("dist-tags".to_string(), dist_tags.into());
Ok(Some(CustomNpmPackage {
registry_file: serde_json::to_string(&registry_file).unwrap(),
tarballs,