Skip to content

Commit 4020ccd

Browse files
mxmulBYK
authored andcommitted
Fix: unbound transitive dependencies should not conflict with top level dependency (#4488)
**Summary** Fixes #3780, and makes the failing test from #3779 passing. As a final step of package resolution, for each dependency we check whether any version satisfies all resolved version ranges. **Test plan** Fixes an existing (failing) test: "unbound transitive dependencies should not conflict with top level dependency"
1 parent 107ebf1 commit 4020ccd

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

__tests__/commands/install/integration.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,8 +1049,7 @@ test.concurrent('transitive file: dependencies should work', (): Promise<void> =
10491049
});
10501050
});
10511051

1052-
// Unskip once https://github.com/yarnpkg/yarn/issues/3778 is resolved
1053-
test.skip('unbound transitive dependencies should not conflict with top level dependency', async () => {
1052+
test('unbound transitive dependencies should not conflict with top level dependency', async () => {
10541053
await runInstall({flat: true}, 'install-conflicts', async config => {
10551054
expect((await fs.readJson(path.join(config.cwd, 'node_modules', 'left-pad', 'package.json'))).version).toEqual(
10561055
'1.0.0',

src/package-resolver.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,14 @@ export default class PackageResolver {
228228

229229
getAllInfoForPackageName(name: string): Array<Manifest> {
230230
const patterns = this.patternsByPackage[name] || [];
231+
return this.getAllInfoForPatterns(patterns);
232+
}
233+
234+
/**
235+
* Retrieve all the package info stored for a list of patterns.
236+
*/
237+
238+
getAllInfoForPatterns(patterns: string[]): Array<Manifest> {
231239
const infos = [];
232240
const seen = new Set();
233241

@@ -284,6 +292,13 @@ export default class PackageResolver {
284292

285293
collapseAllVersionsOfPackage(name: string, version: string): string {
286294
const patterns = this.dedupePatterns(this.patternsByPackage[name]);
295+
return this.collapsePackageVersions(name, version, patterns);
296+
}
297+
298+
/**
299+
* Make all given patterns resolve to version.
300+
*/
301+
collapsePackageVersions(name: string, version: string, patterns: string[]): string {
287302
const human = `${name}@${version}`;
288303

289304
// get manifest that matches the version we're collapsing too
@@ -530,10 +545,37 @@ export default class PackageResolver {
530545
this.resolveToResolution(req);
531546
}
532547

548+
for (const dep of deps) {
549+
const name = normalizePattern(dep.pattern).name;
550+
this.optimizeResolutions(name);
551+
}
552+
533553
activity.end();
534554
this.activity = null;
535555
}
536556

557+
// for a given package, see if a single manifest can satisfy all ranges
558+
optimizeResolutions(name: string) {
559+
const patterns: Array<string> = this.dedupePatterns(this.patternsByPackage[name] || []);
560+
561+
// don't optimize things that already have a lockfile entry:
562+
// https://github.com/yarnpkg/yarn/issues/79
563+
const collapsablePatterns = patterns.filter(pattern => {
564+
const remote = this.patterns[pattern]._remote;
565+
return !this.lockfile.getLocked(pattern) && (!remote || remote.type !== 'workspace');
566+
});
567+
if (collapsablePatterns.length < 2) {
568+
return;
569+
}
570+
571+
const availableVersions = this.getAllInfoForPatterns(collapsablePatterns).map(manifest => manifest.version);
572+
const combinedRange = collapsablePatterns.map(pattern => normalizePattern(pattern).range).join(' ');
573+
const singleVersion = semver.maxSatisfying(availableVersions, combinedRange);
574+
if (singleVersion) {
575+
this.collapsePackageVersions(name, singleVersion, collapsablePatterns);
576+
}
577+
}
578+
537579
/**
538580
* Called by the package requester for packages that this resolver already had
539581
* a matching version for. Delay the resolve, because better matches can still be

0 commit comments

Comments
 (0)