Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support PNPM v6+ Lockfile #325

Merged
merged 8 commits into from
Apr 12, 2023
14 changes: 14 additions & 0 deletions pkg/lockfile/fixtures/pnpm/one-package-v6-lockfile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
lockfileVersion: '6.0'

dependencies:
acorn:
specifier: 8.7.0
version: 8.7.0

packages:

/[email protected]:
dbtedman marked this conversation as resolved.
Show resolved Hide resolved
resolution: {integrity: sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: false
13 changes: 13 additions & 0 deletions pkg/lockfile/fixtures/pnpm/scoped-packages-v6-lockfile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
lockfileVersion: '6.0'

dependencies:
'@typescript-eslint/types':
specifier: ^5.0.0
version: 5.57.1

packages:

/@typescript-eslint/[email protected]:
resolution: {integrity: sha512-bSs4LOgyV3bJ08F5RDqO2KXqg3WAdwHCu06zOqcQ6vqbTJizyBhuh1o1ImC69X4bV2g1OJxbH71PJqiO7Y1RuA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: false
40 changes: 40 additions & 0 deletions pkg/lockfile/parse-pnpm-lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"regexp"
"strconv"
"strings"

"gopkg.in/yaml.v3"
Expand All @@ -27,6 +28,30 @@ type PnpmLockfile struct {
Packages map[string]PnpmLockPackage `yaml:"packages,omitempty"`
}

type pnpmLockfileV6 struct {
Version string `yaml:"lockfileVersion"`
oliverchang marked this conversation as resolved.
Show resolved Hide resolved
Packages map[string]PnpmLockPackage `yaml:"packages,omitempty"`
}

func (l *PnpmLockfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
var lockfileV6 pnpmLockfileV6

if err := unmarshal(&lockfileV6); err != nil {
return err
}

parsedVersion, err := strconv.ParseFloat(lockfileV6.Version, 64)

if err != nil {
return err
}

l.Version = parsedVersion
l.Packages = lockfileV6.Packages

return nil
}

const PnpmEcosystem = NpmEcosystem

func startsWithNumber(str string) bool {
Expand Down Expand Up @@ -64,6 +89,10 @@ func extractPnpmPackageNameAndVersion(dependencyPath string) (string, string) {
version = parts[0]
}

if version == "" {
name, version = parseNameAtVersion(name)
}

if version == "" || !startsWithNumber(version) {
return "", ""
}
Expand All @@ -77,6 +106,17 @@ func extractPnpmPackageNameAndVersion(dependencyPath string) (string, string) {
return name, version
}

func parseNameAtVersion(value string) (name string, version string) {
// look for pattern "name@version", where name is allowed to contain zero or more "@"
matches := regexp.MustCompile(`^(.+)@([\d.]+)$`).FindStringSubmatch(value)

if len(matches) != 3 {
return name, ""
}

return matches[1], matches[2]
}

func parsePnpmLock(lockfile PnpmLockfile) []PackageDetails {
packages := make([]PackageDetails, 0, len(lockfile.Packages))

Expand Down
38 changes: 38 additions & 0 deletions pkg/lockfile/parse-pnpm-lock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ func TestParsePnpmLock_OnePackage(t *testing.T) {
})
}

func TestParsePnpmLock_OnePackageV6Lockfile(t *testing.T) {
t.Parallel()

packages, err := lockfile.ParsePnpmLock("fixtures/pnpm/one-package-v6-lockfile.yaml")

if err != nil {
t.Errorf("Got unexpected error: %v", err)
}

expectPackages(t, packages, []lockfile.PackageDetails{
{
Name: "acorn",
Version: "8.7.0",
Ecosystem: lockfile.PnpmEcosystem,
CompareAs: lockfile.PnpmEcosystem,
},
})
}

func TestParsePnpmLock_OnePackageDev(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -92,6 +111,25 @@ func TestParsePnpmLock_ScopedPackages(t *testing.T) {
})
}

func TestParsePnpmLock_ScopedPackagesV6Lockfile(t *testing.T) {
t.Parallel()

packages, err := lockfile.ParsePnpmLock("fixtures/pnpm/scoped-packages-v6-lockfile.yaml")

if err != nil {
t.Errorf("Got unexpected error: %v", err)
}

expectPackages(t, packages, []lockfile.PackageDetails{
{
Name: "@typescript-eslint/types",
Version: "5.57.1",
Ecosystem: lockfile.PnpmEcosystem,
CompareAs: lockfile.PnpmEcosystem,
},
})
}

func TestParsePnpmLock_PeerDependencies(t *testing.T) {
t.Parallel()

Expand Down