internal/deps/npm.go

d775997ead04022093817ab513287b5886065247
gitbay/internal/deps/npm.go history · blame · raw

67 lines · 1779 bytes

 1package deps
 2
 3import (
 4	"encoding/json"
 5	"strings"
 6)
 7
 8// parseNPM takes the direct dependency names from package.json and their
 9// installed versions from package-lock.json where one exists. The lockfile
10// is the honest source: package.json records ranges, and a range that
11// already admits the newest release is not something to report.
12func parseNPM(read ReadFile) []Dep {
13	raw, err := read("package.json")
14	if err != nil {
15		return nil
16	}
17	var pkg struct {
18		Dependencies    map[string]string `json:"dependencies"`
19		DevDependencies map[string]string `json:"devDependencies"`
20	}
21	if json.Unmarshal(raw, &pkg) != nil {
22		return nil
23	}
24	locked := npmLock(read)
25	var out []Dep
26	for _, set := range []map[string]string{pkg.Dependencies, pkg.DevDependencies} {
27		for name, spec := range set {
28			current := locked[name]
29			if current == "" {
30				current = pin(spec)
31			}
32			if current != "" {
33				out = append(out, Dep{Ecosystem: EcoNPM, Name: name, Current: current})
34			}
35		}
36	}
37	return out
38}
39
40// npmLock reads installed versions out of a v2 or v3 lockfile, keyed by
41// package name. Nested entries (a transitive copy under another package's
42// node_modules) are ignored: only the top-level install is the direct one.
43func npmLock(read ReadFile) map[string]string {
44	raw, err := read("package-lock.json")
45	if err != nil {
46		return nil
47	}
48	var lock struct {
49		Packages map[string]struct {
50			Version string `json:"version"`
51		} `json:"packages"`
52	}
53	if json.Unmarshal(raw, &lock) != nil {
54		return nil
55	}
56	out := map[string]string{}
57	for path, entry := range lock.Packages {
58		name, ok := strings.CutPrefix(path, "node_modules/")
59		if !ok || strings.Contains(name, "node_modules/") {
60			continue
61		}
62		if v := pin(entry.Version); v != "" {
63			out[name] = v
64		}
65	}
66	return out
67}