diff --git a/CHANGELOG.md b/CHANGELOG.md index bc60e58c6..d95f1f489 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased +### Fixed + +- `pnpm` version 5 parser including metadata in package versions + ## 7.1.4 - 2024-11-07 ### Fixed diff --git a/lockfile/src/javascript.rs b/lockfile/src/javascript.rs index dad779239..5ff5d23d9 100644 --- a/lockfile/src/javascript.rs +++ b/lockfile/src/javascript.rs @@ -344,9 +344,9 @@ impl PnpmLock { /// /// This parses the combined name and version used as an index for the /// `packages` map. - fn parse_key(mut key: &str, version: PnpmVersion) -> anyhow::Result<(String, String)> { + fn parse_key(mut key: &str, pnpm_version: PnpmVersion) -> anyhow::Result<(String, String)> { // Strip prefix from `version < 9` lockfiles. - if version < PnpmVersion::V9 { + if pnpm_version < PnpmVersion::V9 { key = key .strip_prefix('/') .ok_or_else(|| anyhow!("Dependency '{key}' is missing '/' prefix"))?; @@ -356,16 +356,23 @@ impl PnpmLock { let name = key.split_once('(').map(|(name, _)| name).unwrap_or(key); // Get version separator based on PNPM version. - let version_separator = match version { + let version_separator = match pnpm_version { PnpmVersion::V6 | PnpmVersion::V9 => '@', PnpmVersion::V5 => '/', }; // Separate name and version. - match name.rsplit_once(version_separator) { - Some((name, version)) => Ok((name.into(), version.into())), + let (name, mut version) = match name.rsplit_once(version_separator) { + Some((name, version)) => Ok((name, version)), None => Err(anyhow!("Dependency '{name}' is missing a version")), + }?; + + // Remove dependency information from V5 versions. + if pnpm_version == PnpmVersion::V5 { + version = version.split_once('_').map_or(version, |(version, _)| version); } + + Ok((name.into(), version.into())) } /// Parse a first-party registry package. @@ -682,7 +689,7 @@ mod tests { fn pnpm() { let pkgs = Pnpm.parse(include_str!("../../tests/fixtures/pnpm-lock.yaml")).unwrap(); - assert_eq!(pkgs.len(), 65); + assert_eq!(pkgs.len(), 66); let expected_pkgs = [ Package { @@ -725,6 +732,11 @@ mod tests { version: PackageVersion::Path(Some("projects/workspace_member".into())), package_type: PackageType::Npm, }, + Package { + name: "@emotion/use-insertion-effect-with-fallbacks".into(), + version: PackageVersion::FirstParty("1.1.0".into()), + package_type: PackageType::Npm, + }, ]; for expected_pkg in expected_pkgs { @@ -774,10 +786,28 @@ mod tests { fn pnpm_v5() { let pkgs = Pnpm.parse(include_str!("../../tests/fixtures/pnpm-lock-v5.yaml")).unwrap(); - assert_eq!(pkgs, vec![Package { - name: "lodash".into(), - version: PackageVersion::FirstParty("4.17.21".into()), - package_type: PackageType::Npm, - }]); + assert_eq!(pkgs.len(), 3); + + let expected_pkgs = [ + Package { + name: "use-sync-external-store".into(), + version: PackageVersion::FirstParty("1.2.2".into()), + package_type: PackageType::Npm, + }, + Package { + name: "@types/eslint__js".into(), + version: PackageVersion::FirstParty("8.42.3".into()), + package_type: PackageType::Npm, + }, + Package { + name: "lodash".into(), + version: PackageVersion::FirstParty("4.17.21".into()), + package_type: PackageType::Npm, + }, + ]; + + for expected_pkg in expected_pkgs { + assert!(pkgs.contains(&expected_pkg), "missing package {expected_pkg:?}"); + } } } diff --git a/tests/fixtures/pnpm-lock-v5.yaml b/tests/fixtures/pnpm-lock-v5.yaml index 196818617..b9290545e 100644 --- a/tests/fixtures/pnpm-lock-v5.yaml +++ b/tests/fixtures/pnpm-lock-v5.yaml @@ -6,5 +6,18 @@ packages: dev: false resolution: integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + + /use-sync-external-store/1.2.2_react@18.3.1: + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.3.1 + + /@types/eslint__js/8.42.3: + resolution: {integrity: sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==} + dependencies: + '@types/eslint': 9.6.1 + dev: true specifiers: lodash: ^4.17.21 diff --git a/tests/fixtures/pnpm-lock.yaml b/tests/fixtures/pnpm-lock.yaml index a8b2ee503..a1fce01f0 100644 --- a/tests/fixtures/pnpm-lock.yaml +++ b/tests/fixtures/pnpm-lock.yaml @@ -483,3 +483,11 @@ packages: resolution: {directory: projects/workspace_member, type: directory} name: workspace_member dev: false + + /@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.3.1): + resolution: {integrity: sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.3.1 + dev: false