Skip to content

Commit 3ffe4ef

Browse files
committed
feat(pnpm/v5): merge package contexts by name and version
1 parent 2d93189 commit 3ffe4ef

File tree

2 files changed

+129
-22
lines changed

2 files changed

+129
-22
lines changed

module/pnpm/v5/v5.go

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,38 @@ func (p *Pkg) adjustByPath(path string) {
8383
if p.Name != "" && p.Version != "" {
8484
return
8585
}
86-
var underscore = strings.LastIndex(path, "_")
87-
if underscore > -1 {
88-
path = path[:underscore]
89-
}
90-
var i = strings.LastIndex(path, "/")
91-
if i == -1 {
86+
name, version, ok := parseNameVersionFromPackagePath(path)
87+
if !ok {
9288
return
9389
}
9490
if p.Name == "" {
95-
p.Name = strings.Trim(path[:i], "/")
91+
p.Name = name
9692
}
9793
if p.Version == "" {
98-
p.Version = strings.Trim(path[i:], "/")
94+
p.Version = version
95+
}
96+
}
97+
98+
func parseNameVersionFromPackagePath(path string) (name, version string, ok bool) {
99+
trimmed := strings.Trim(path, "/")
100+
if trimmed == "" {
101+
return "", "", false
102+
}
103+
slash := strings.LastIndex(trimmed, "/")
104+
if slash <= 0 || slash == len(trimmed)-1 {
105+
return "", "", false
106+
}
107+
108+
name = trimmed[:slash]
109+
versionWithSuffix := trimmed[slash+1:]
110+
underscore := strings.Index(versionWithSuffix, "_")
111+
if underscore >= 0 {
112+
versionWithSuffix = versionWithSuffix[:underscore]
113+
}
114+
if name == "" || versionWithSuffix == "" {
115+
return "", "", false
99116
}
117+
return name, versionWithSuffix, true
100118
}
101119

102120
func (l *Lockfile) buildIndexes() {
@@ -111,34 +129,51 @@ func (l *Lockfile) buildIndexes() {
111129
pkg.adjustByPath(path)
112130
}
113131
l.pkgIndexes = make(map[[2]string]*Pkg, len(l.Packages))
114-
indexPaths := make(map[[2]string]string, len(l.Packages))
115132
for _, path := range paths {
116133
pkg := l.Packages[path]
117134
var name, version = pkg.Name, pkg.Version
118135
if name == "" {
119136
continue
120137
}
121138
key := [2]string{name, version}
122-
prevPath, exists := indexPaths[key]
123-
if !exists || preferPkgPath(prevPath, path, name, version) {
124-
indexPaths[key] = path
125-
l.pkgIndexes[key] = pkg
139+
if merged, ok := l.pkgIndexes[key]; ok {
140+
mergePkg(merged, pkg)
141+
} else {
142+
l.pkgIndexes[key] = clonePkg(pkg)
126143
}
127144
}
128145
}
129146

130-
func preferPkgPath(currentPath, candidatePath, name, version string) bool {
131-
currentCanonical := isCanonicalPath(currentPath, name, version)
132-
candidateCanonical := isCanonicalPath(candidatePath, name, version)
133-
if currentCanonical != candidateCanonical {
134-
return candidateCanonical
147+
func clonePkg(pkg *Pkg) *Pkg {
148+
out := &Pkg{
149+
Name: pkg.Name,
150+
Version: pkg.Version,
151+
Dev: pkg.Dev,
135152
}
136-
return candidatePath < currentPath
153+
if len(pkg.Dependencies) == 0 {
154+
return out
155+
}
156+
out.Dependencies = make(map[string]string, len(pkg.Dependencies))
157+
for n, v := range pkg.Dependencies {
158+
out.Dependencies[n] = v
159+
}
160+
return out
137161
}
138162

139-
func isCanonicalPath(path, name, version string) bool {
140-
canonical := "/" + name + "/" + version
141-
return path == canonical || strings.TrimPrefix(path, "/") == strings.TrimPrefix(canonical, "/")
163+
func mergePkg(merged, pkg *Pkg) {
164+
// If any context is non-dev, keep the merged node as non-dev.
165+
merged.Dev = merged.Dev && pkg.Dev
166+
if len(pkg.Dependencies) == 0 {
167+
return
168+
}
169+
if merged.Dependencies == nil {
170+
merged.Dependencies = make(map[string]string, len(pkg.Dependencies))
171+
}
172+
for n, v := range pkg.Dependencies {
173+
if _, exists := merged.Dependencies[n]; !exists {
174+
merged.Dependencies[n] = v
175+
}
176+
}
142177
}
143178

144179
func (l *Lockfile) findPkg(name, version string) (p *Pkg) {

module/pnpm/v5/v5_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,75 @@ func TestBuildDepTree_AllTestdata(t *testing.T) {
9292
})
9393
}
9494
}
95+
96+
func TestParseNameVersionFromPackagePath(t *testing.T) {
97+
testCases := []struct {
98+
path string
99+
name string
100+
version string
101+
ok bool
102+
}{
103+
{
104+
path: "/@babel/helper-compilation-targets/7.21.4_@babel+core@7.12.10",
105+
name: "@babel/helper-compilation-targets",
106+
version: "7.21.4",
107+
ok: true,
108+
},
109+
{
110+
path: "/foo/1.2.3_a@1.0.0_b@2.0.0",
111+
name: "foo",
112+
version: "1.2.3",
113+
ok: true,
114+
},
115+
{
116+
path: "/@types/babel__core/7.20.1",
117+
name: "@types/babel__core",
118+
version: "7.20.1",
119+
ok: true,
120+
},
121+
{
122+
path: "/bad",
123+
ok: false,
124+
},
125+
}
126+
127+
for _, tc := range testCases {
128+
t.Run(tc.path, func(t *testing.T) {
129+
name, version, ok := parseNameVersionFromPackagePath(tc.path)
130+
assert.Equal(t, tc.ok, ok)
131+
assert.Equal(t, tc.name, name)
132+
assert.Equal(t, tc.version, version)
133+
})
134+
}
135+
}
136+
137+
func TestBuildIndexes_MergeMultipleContexts(t *testing.T) {
138+
l := &Lockfile{
139+
Packages: map[string]*Pkg{
140+
"/foo/1.2.3_peer-a@1.0.0": {
141+
Dependencies: map[string]string{
142+
"a": "1.0.0",
143+
},
144+
Dev: true,
145+
},
146+
"/foo/1.2.3_peer-b@2.0.0": {
147+
Dependencies: map[string]string{
148+
"b": "2.0.0",
149+
},
150+
Dev: false,
151+
},
152+
},
153+
}
154+
155+
l.buildIndexes()
156+
157+
pkg := l.pkgIndexes[[2]string{"foo", "1.2.3"}]
158+
assert.NotNil(t, pkg)
159+
assert.Equal(t, "foo", pkg.Name)
160+
assert.Equal(t, "1.2.3", pkg.Version)
161+
assert.Equal(t, map[string]string{
162+
"a": "1.0.0",
163+
"b": "2.0.0",
164+
}, pkg.Dependencies)
165+
assert.False(t, pkg.Dev)
166+
}

0 commit comments

Comments
 (0)