11package lockfile
22
33import (
4+ "errors"
45 "fmt"
56 "os"
67 "strconv"
@@ -10,6 +11,8 @@ import (
1011 "gopkg.in/yaml.v3"
1112)
1213
14+ var errInvalidPackagePath = errors .New ("invalid package path" )
15+
1316type PnpmLockPackageResolution struct {
1417 Tarball string `yaml:"tarball"`
1518 Commit string `yaml:"commit"`
@@ -62,12 +65,12 @@ func startsWithNumber(str string) bool {
6265
6366// extractPnpmPackageNameAndVersion parses a dependency path, attempting to
6467// extract the name and version of the package it represents
65- func extractPnpmPackageNameAndVersion (dependencyPath string , lockfileVersion float64 ) (string , string ) {
68+ func extractPnpmPackageNameAndVersion (dependencyPath string , lockfileVersion float64 ) (string , string , error ) {
6669 // file dependencies must always have a name property to be installed,
6770 // and their dependency path never has the version encoded, so we can
6871 // skip trying to extract either from their dependency path
6972 if strings .HasPrefix (dependencyPath , "file:" ) {
70- return "" , ""
73+ return "" , "" , nil
7174 }
7275
7376 // v9.0 specifies the dependencies as <package>@<version> rather than as a path
@@ -81,10 +84,15 @@ func extractPnpmPackageNameAndVersion(dependencyPath string, lockfileVersion flo
8184 name = "@" + name
8285 }
8386
84- return name , version
87+ return name , version , nil
8588 }
8689
8790 parts := strings .Split (dependencyPath , "/" )
91+
92+ if len (parts ) == 1 {
93+ return "" , "" , errInvalidPackagePath
94+ }
95+
8896 var name string
8997
9098 parts = parts [1 :]
@@ -108,14 +116,14 @@ func extractPnpmPackageNameAndVersion(dependencyPath string, lockfileVersion flo
108116 }
109117
110118 if version == "" || ! startsWithNumber (version ) {
111- return "" , ""
119+ return "" , "" , nil
112120 }
113121
114122 // peer dependencies in v5 lockfiles are attached to the end of the version
115123 // with an "_", so we always want the first element if an "_" is present
116124 version , _ , _ = strings .Cut (version , "_" )
117125
118- return name , version
126+ return name , version , nil
119127}
120128
121129func parseNameAtVersion (value string ) (name string , version string ) {
@@ -129,11 +137,15 @@ func parseNameAtVersion(value string) (name string, version string) {
129137 return matches [1 ], matches [2 ]
130138}
131139
132- func parsePnpmLock (lockfile PnpmLockfile ) []PackageDetails {
140+ func parsePnpmLock (lockfile PnpmLockfile ) ( []PackageDetails , error ) {
133141 packages := make ([]PackageDetails , 0 , len (lockfile .Packages ))
134142
135143 for s , pkg := range lockfile .Packages {
136- name , version := extractPnpmPackageNameAndVersion (s , lockfile .Version )
144+ name , version , err := extractPnpmPackageNameAndVersion (s , lockfile .Version )
145+
146+ if err != nil {
147+ return nil , err
148+ }
137149
138150 // "name" is only present if it's not in the dependency path and takes
139151 // priority over whatever name we think we've extracted (if any)
@@ -171,7 +183,7 @@ func parsePnpmLock(lockfile PnpmLockfile) []PackageDetails {
171183 })
172184 }
173185
174- return packages
186+ return packages , nil
175187}
176188
177189func ParsePnpmLock (pathToLockfile string ) ([]PackageDetails , error ) {
@@ -194,5 +206,11 @@ func ParsePnpmLock(pathToLockfile string) ([]PackageDetails, error) {
194206 parsedLockfile = & PnpmLockfile {}
195207 }
196208
197- return parsePnpmLock (* parsedLockfile ), nil
209+ packageDetails , err := parsePnpmLock (* parsedLockfile )
210+
211+ if err != nil {
212+ return []PackageDetails {}, fmt .Errorf ("could not parse %s: %w" , pathToLockfile , err )
213+ }
214+
215+ return packageDetails , nil
198216}
0 commit comments