-
Notifications
You must be signed in to change notification settings - Fork 2
/
npm.go
92 lines (77 loc) · 2.73 KB
/
npm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package pkgmanager
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"github.com/khulnasoft-lab/package-analysis/internal/utils"
"github.com/khulnasoft-lab/package-analysis/pkg/api/pkgecosystem"
)
// npmPackageJSON represents relevant JSON data from the NPM registry response
// when package information is requested.
// See https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md
type npmPackageJSON struct {
DistTags struct {
Latest string `json:"latest"`
} `json:"dist-tags"`
}
// npmVersionJSON represents relevant JSON data from the NPM registry response
// when package version information is requested.
// See https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md
type npmVersionJSON struct {
Dist struct {
Tarball string `json:"tarball"`
} `json:"dist"`
}
func getNPMLatest(pkg string) (string, error) {
resp, err := http.Get(fmt.Sprintf("https://registry.npmjs.org/%s", pkg))
if err != nil {
return "", err
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
var details npmPackageJSON
err = decoder.Decode(&details)
if err != nil {
return "", err
}
return details.DistTags.Latest, nil
}
/*
getNPMArchiveFilename generates a filename for a package archive to be downloaded from NPM.
It is generated by replacing any '/' characters in the package name with '-' (ref [1]).
Unlike in [1], the leading '@' is not stripped as '@' characters are allowed in filenames.
The cleaned package name is then concatenated with "-", the version string and ".tgz".
[1] https://github.com/npm/cli/blob/8ecbcb9a54b95541f35ebce55d60e4a1feac82c6/lib/commands/pack.js#L64
*/
func getNPMArchiveFilename(pkgName, version, _ string) string {
cleanedName := strings.ReplaceAll(pkgName, "/", "-")
return fmt.Sprintf("%s-%s.tgz", cleanedName, version)
}
func getNPMArchiveURL(pkgName, version string) (string, error) {
resp, err := http.Get(fmt.Sprintf("https://registry.npmjs.org/%s/%s", pkgName, version))
if err != nil {
return "", err
}
defer resp.Body.Close()
responseBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading HTTP response: %w", err)
}
responseString := string(responseBytes)
decoder := json.NewDecoder(strings.NewReader(responseString))
var packageInfo npmVersionJSON
if err := decoder.Decode(&packageInfo); err != nil {
// invalid version, non-existent package, etc. Details in responseString
return "", fmt.Errorf("%w. NPM response: %s", err, responseString)
}
return packageInfo.Dist.Tarball, nil
}
var npmPkgManager = PkgManager{
ecosystem: pkgecosystem.NPM,
latestVersion: getNPMLatest,
archiveURL: getNPMArchiveURL,
archiveFilename: getNPMArchiveFilename,
extractArchive: utils.ExtractTarGzFile,
}