From df7ab0d4147e42b138b0233dab403d7a5d160f5c Mon Sep 17 00:00:00 2001 From: Shahar Shalev Date: Tue, 13 Aug 2024 11:56:42 +0300 Subject: [PATCH] feat: is dev for packages installed under npm and pnpm --- crates/turborepo-lib/src/hash/mod.rs | 11 +++- crates/turborepo-lockfiles/src/berry/mod.rs | 33 +++++++--- crates/turborepo-lockfiles/src/bun/mod.rs | 1 + crates/turborepo-lockfiles/src/lib.rs | 8 ++- crates/turborepo-lockfiles/src/npm.rs | 26 ++++++-- crates/turborepo-lockfiles/src/pnpm/data.rs | 67 ++++++++++++++++++--- crates/turborepo-lockfiles/src/yarn1/mod.rs | 1 + 7 files changed, 121 insertions(+), 26 deletions(-) diff --git a/crates/turborepo-lib/src/hash/mod.rs b/crates/turborepo-lib/src/hash/mod.rs index 74fd46ccbd224..371281af73166 100644 --- a/crates/turborepo-lib/src/hash/mod.rs +++ b/crates/turborepo-lib/src/hash/mod.rs @@ -120,7 +120,9 @@ impl From for Builder { { let mut packages_builder = builder.reborrow().init_packages(packages.len() as u32); - for (i, turborepo_lockfiles::Package { key, version }) in packages.iter().enumerate() { + for (i, turborepo_lockfiles::Package { key, version, .. }) in + packages.iter().enumerate() + { let mut package = packages_builder.reborrow().get(i as u32); package.set_key(key); package.set_version(version); @@ -441,24 +443,30 @@ mod test { #[test_case(vec![Package { key: "key".to_string(), version: "version".to_string(), + is_dev: false, }], "1b266409f3ae154e" ; "non-empty")] #[test_case(vec![Package { key: "key".to_string(), version: "".to_string(), + is_dev: false, }], "bde280722f61644a" ; "empty version")] #[test_case(vec![Package { key: "key".to_string(), version: "version".to_string(), + is_dev: false, }, Package { key: "zey".to_string(), version: "version".to_string(), + is_dev: false, }], "6c0185544234b6dc" ; "multiple in-order")] #[test_case(vec![Package { key: "zey".to_string(), version: "version".to_string(), + is_dev: false, }, Package { key: "key".to_string(), version: "version".to_string(), + is_dev: false, }], "26a67c9beeb0d16f" ; "care about order")] fn lock_file_packages(vec: Vec, expected: &str) { let packages = LockFilePackages(vec); @@ -470,6 +478,7 @@ mod test { let packages = (0..100).map(|i| Package { key: format!("key{}", i), version: format!("version{}", i), + is_dev: false, }); lock_file_packages(packages.collect(), "4fd770c37194168e"); diff --git a/crates/turborepo-lockfiles/src/berry/mod.rs b/crates/turborepo-lockfiles/src/berry/mod.rs index ae6a886159cfa..e5fdc41702e52 100644 --- a/crates/turborepo-lockfiles/src/berry/mod.rs +++ b/crates/turborepo-lockfiles/src/berry/mod.rs @@ -455,6 +455,7 @@ impl Lockfile for BerryLockfile { Ok(Some(crate::Package { key: locator.to_string(), version: package.version.clone(), + is_dev: false, })) } @@ -641,7 +642,8 @@ mod test { .unwrap(), Some(Package { key: "js-tokens@npm:4.0.0".into(), - version: "4.0.0".into() + version: "4.0.0".into(), + is_dev: false, }), ); assert_eq!( @@ -650,7 +652,8 @@ mod test { .unwrap(), Some(Package { key: "js-tokens@npm:4.0.0".into(), - version: "4.0.0".into() + version: "4.0.0".into(), + is_dev: false, }), ); assert_eq!( @@ -659,7 +662,8 @@ mod test { .unwrap(), Some(Package { key: "eslint-config-custom@workspace:packages/eslint-config-custom".into(), - version: "0.0.0-use.local".into() + version: "0.0.0-use.local".into(), + is_dev: false, }), ); assert_eq!( @@ -790,7 +794,8 @@ mod test { assert!(closure.contains(&Package { key: "lodash@npm:4.17.21".into(), - version: "4.17.21".into() + version: "4.17.21".into(), + is_dev: false, })); } @@ -818,7 +823,8 @@ mod test { pkg, Package { key: "debug@npm:1.0.0".into(), - version: "1.0.0".into() + version: "1.0.0".into(), + is_dev: false, } ); } @@ -862,7 +868,8 @@ mod test { pkg, Package { key: "ms@npm:0.6.0".into(), - version: "0.6.0".into() + version: "0.6.0".into(), + is_dev: false, } ); } @@ -898,11 +905,13 @@ mod test { assert!(closure.contains(&Package { key: "ajv@npm:8.11.2".into(), - version: "8.11.2".into() + version: "8.11.2".into(), + is_dev: false, })); assert!(closure.contains(&Package { key: "uri-js@npm:4.4.1".into(), - version: "4.4.1".into() + version: "4.4.1".into(), + is_dev: false, })); } @@ -927,6 +936,7 @@ mod test { let expected = Package { key: "react@npm:18.1.0".into(), version: "18.1.0".into(), + is_dev: false, }; assert_eq!(actual, expected,); @@ -1061,6 +1071,7 @@ mod test { crate::Package { key: expected_key.into(), version: "3.0.1".into(), + is_dev: false, } ); @@ -1083,11 +1094,13 @@ mod test { vec![ crate::Package { key: expected_key.into(), - version: "3.0.1".into() + version: "3.0.1".into(), + is_dev: false, }, crate::Package { key: "is-number@npm:6.0.0".into(), - version: "6.0.0".into() + version: "6.0.0".into(), + is_dev: false, } ] .into_iter() diff --git a/crates/turborepo-lockfiles/src/bun/mod.rs b/crates/turborepo-lockfiles/src/bun/mod.rs index 363d443fe3705..2f2f985ba8502 100644 --- a/crates/turborepo-lockfiles/src/bun/mod.rs +++ b/crates/turborepo-lockfiles/src/bun/mod.rs @@ -68,6 +68,7 @@ impl Lockfile for BunLockfile { return Ok(Some(crate::Package { key, version: entry.version.clone(), + is_dev: false, })); } } diff --git a/crates/turborepo-lockfiles/src/lib.rs b/crates/turborepo-lockfiles/src/lib.rs index c8fbb9d3d2f45..d7ce4536db4ba 100644 --- a/crates/turborepo-lockfiles/src/lib.rs +++ b/crates/turborepo-lockfiles/src/lib.rs @@ -29,6 +29,7 @@ pub use yarn1::{yarn_subgraph, Yarn1Lockfile}; pub struct Package { pub key: String, pub version: String, + pub is_dev: bool, } // This trait will only be used when migrating the Go lockfile implementations @@ -156,6 +157,11 @@ impl Package { pub fn new(key: impl Into, version: impl Into) -> Self { let key = key.into(); let version = version.into(); - Self { key, version } + let is_dev: bool = false; + Self { + key, + version, + is_dev, + } } } diff --git a/crates/turborepo-lockfiles/src/npm.rs b/crates/turborepo-lockfiles/src/npm.rs index 2422a190e8ff8..d3aa482714159 100644 --- a/crates/turborepo-lockfiles/src/npm.rs +++ b/crates/turborepo-lockfiles/src/npm.rs @@ -30,6 +30,7 @@ pub struct NpmLockfile { struct NpmPackage { version: Option, resolved: Option, + dev: Option, #[serde(default)] dependencies: Map, #[serde(default)] @@ -72,7 +73,12 @@ impl Lockfile for NpmLockfile { .filter_map(|key| { self.packages.get(&key).map(|pkg| { let version = pkg.version.clone().unwrap_or_default(); - Ok(Package { key, version }) + let is_dev: bool = pkg.dev.clone().unwrap_or(false); + Ok(Package { + key, + version, + is_dev, + }) }) }) .next() @@ -305,28 +311,37 @@ mod test { fn test_resolve_package() -> Result<(), Error> { let lockfile = NpmLockfile::load(include_bytes!("../fixtures/npm-lock.json"))?; let tests = [ - ("", "turbo", "node_modules/turbo", "1.5.5"), + ("", "turbo", "node_modules/turbo", "1.5.5", true), ( "apps/web", "lodash", "apps/web/node_modules/lodash", "4.17.21", + false, + ), + ( + "apps/docs", + "lodash", + "node_modules/lodash", + "3.10.1", + false, ), - ("apps/docs", "lodash", "node_modules/lodash", "3.10.1"), ( "apps/docs", "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping", "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping", "0.3.2", + true, ), ]; - for (workspace, name, key, version) in &tests { + for (workspace, name, key, version, is_dev) in &tests { let pkg = lockfile.resolve_package(workspace, name, "")?; assert!(pkg.is_some()); let pkg = pkg.unwrap(); assert_eq!(pkg.key, *key); assert_eq!(pkg.version, *version); + assert_eq!(pkg.is_dev, *is_dev) } Ok(()) @@ -449,7 +464,8 @@ mod test { )?; assert!(closures.get("packages/a").unwrap().contains(&Package { key: "node_modules/eslint-plugin-turbo".into(), - version: "1.9.3".into() + version: "1.9.3".into(), + is_dev: false, })); assert!(closures.get("packages/b").unwrap().is_empty()); assert!(closures.get("packages/c").unwrap().is_empty()); diff --git a/crates/turborepo-lockfiles/src/pnpm/data.rs b/crates/turborepo-lockfiles/src/pnpm/data.rs index dd83afb632711..98a14abce328f 100644 --- a/crates/turborepo-lockfiles/src/pnpm/data.rs +++ b/crates/turborepo-lockfiles/src/pnpm/data.rs @@ -106,6 +106,9 @@ pub struct PackageSnapshot { #[serde(flatten)] snapshot: PackageSnapshotV7, + #[serde(skip_serializing_if = "Option::is_none")] + dev: Option, + #[serde(skip_serializing_if = "Option::is_none")] patched: Option, @@ -200,6 +203,12 @@ impl PnpmLockfile { pkg.version.as_deref() } + fn package_is_dev(&self, key: &str) -> Option { + let pkgs = self.packages.as_ref()?; + let pkg = pkgs.get(key)?; + pkg.dev.clone() + } + fn get_workspace(&self, workspace_path: &str) -> Result<&ProjectSnapshot, crate::Error> { let key = match workspace_path { // For pnpm, the root is named "." @@ -374,9 +383,16 @@ impl crate::Lockfile for PnpmLockfile { // Check if version is a key if self.has_package(version) { let extracted_version = self.extract_version(version)?; + let is_dev = self + .packages + .as_ref() + .map_or(None, |map| map.get(version)) + .and_then(|entry| entry.dev) + .unwrap_or(false); return Ok(Some(crate::Package { key: version.into(), version: extracted_version.into(), + is_dev: is_dev, })); } @@ -391,7 +407,12 @@ impl crate::Lockfile for PnpmLockfile { .package_version(&key) .unwrap_or(resolved_version) .to_owned(); - Ok(Some(crate::Package { key, version })) + let is_dev: bool = self.package_is_dev(&key).unwrap_or(false).to_owned(); + Ok(Some(crate::Package { + key, + version, + is_dev, + })) } else if self.has_package(resolved_version) { let version = self.package_version(resolved_version).map_or_else( || { @@ -400,9 +421,14 @@ impl crate::Lockfile for PnpmLockfile { }, |version| Ok(version.to_string()), )?; + let is_dev = self + .package_is_dev(resolved_version) + .unwrap_or(false) + .to_owned(); Ok(Some(crate::Package { key: resolved_version.to_string(), version, + is_dev: is_dev, })) } else { Ok(None) @@ -784,19 +810,33 @@ mod tests { Ok(Some(crate::Package { key: "github.com/peerigon/dashboard-icons/ce27ef933144e09cef3911025f3649040a8571b6".into(), version: "1.0.0".into(), + is_dev: false, })) ; "git package" )] #[test_case( - PNPM_ABSOLUTE, - "packages/a", - "child", - "/@scope/child/1.0.0", + PNPM7, + "apps/docs", + "@types/node", + "^17.0.12", Ok(Some(crate::Package { - key: "/@scope/child/1.0.0".into(), - version: "1.0.0".into(), + key: "/@types/node/17.0.45".into(), + version: "17.0.45".into(), + is_dev: true, + })) + ; "dev dependency pnpm7" + )] + #[test_case( + PNPM6, + ".", + "@pnpm/make-dedicated-lockfile", + "^0.3.19", + Ok(Some(crate::Package { + key: "/@pnpm/make-dedicated-lockfile/0.3.19".into(), + version: "0.3.19".into(), + is_dev: true, })) - ; "absolute package" + ; "dev dependency pnpm6" )] #[test_case( PNPM_ABSOLUTE_V6, @@ -806,6 +846,7 @@ mod tests { Ok(Some(crate::Package { key: "/@scope/child@1.0.0".into(), version: "1.0.0".into(), + is_dev: false, })) ; "v6 absolute package" )] @@ -817,6 +858,7 @@ mod tests { Ok(Some(crate::Package { key: "/next@13.0.4(react-dom@18.2.0)(react@18.2.0)".into(), version: "13.0.4(react-dom@18.2.0)(react@18.2.0)".into(), + is_dev: false, })) ; "v6 peer package" )] @@ -828,6 +870,7 @@ mod tests { Ok(Some(crate::Package { key: "/ci-info/3.7.1".into(), version: "3.7.1".into(), + is_dev: false, })) ; "top level override" )] @@ -839,6 +882,7 @@ mod tests { Ok(Some(crate::Package { key: "/hardhat-deploy-ethers/0.3.0-beta.13_yab2ug5tvye2kp6e24l5x3z7uy".into(), version: "0.3.0-beta.13_yab2ug5tvye2kp6e24l5x3z7uy".into(), + is_dev: false, })) ; "pnpm override" )] @@ -850,6 +894,7 @@ mod tests { Ok(Some(crate::Package { key: "is-negative@https://codeload.github.com/kevva/is-negative/tar.gz/1d7e288222b53a0cab90a331f1865220ec29560c".into(), version: "2.1.0".into(), + is_dev: false, })) ; "v7 git" )] @@ -861,6 +906,7 @@ mod tests { Ok(Some(crate::Package { key: "ajv-keywords@5.1.0(ajv@8.12.0)".into(), version: "5.1.0(ajv@8.12.0)".into(), + is_dev: false, })) ; "v7 peer" )] @@ -872,6 +918,7 @@ mod tests { Ok(Some(crate::Package { key: "ajv-keywords@5.1.0(ajv@8.11.0)".into(), version: "5.1.0(ajv@8.11.0)".into(), + is_dev: false, })) ; "v7 peer 2" )] @@ -883,6 +930,7 @@ mod tests { Ok(Some(crate::Package { key: "turbo@1.13.3-canary.1".into(), version: "1.13.3-canary.1".into(), + is_dev: false, })) ; "v9" )] @@ -1102,7 +1150,8 @@ c: is_even, Package { key: "is-even@1.0.0".into(), - version: "1.0.0".into() + version: "1.0.0".into(), + is_dev: false, } ); let is_even_deps = lockfile.all_dependencies(&is_even.key).unwrap().unwrap(); diff --git a/crates/turborepo-lockfiles/src/yarn1/mod.rs b/crates/turborepo-lockfiles/src/yarn1/mod.rs index f93f1c6d837c4..5c715c6e5b197 100644 --- a/crates/turborepo-lockfiles/src/yarn1/mod.rs +++ b/crates/turborepo-lockfiles/src/yarn1/mod.rs @@ -67,6 +67,7 @@ impl Lockfile for Yarn1Lockfile { return Ok(Some(crate::Package { key, version: entry.version.clone(), + is_dev: false, })); } }