Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(resolver): Fix incorrect peer dependency res. from different trees #4687

Merged
merged 4 commits into from
Oct 11, 2017

Conversation

BYK
Copy link
Member

@BYK BYK commented Oct 11, 2017

Summary

Fixes #4539. Yarn was resolving peer dependencies from the closest level where the peer dependency was requested
but it was not checking if the peer dependency was in the same subtree. This was causing incorrect
peer dependency resolutions and package duplication when an unrelated subtree has a depedency
satisfying the required peer dependency at the same tree level.

Test plan

Added new install integration test that fails without the fix.

…t trees

**Summary**

Fixes #4539. Yarn was resolving peer dependencies from the closest level where the peer dependency was requested
but it was not checking if the peer dependency was in the same subtree. This was causing incorrect
peer dependency resolutions and package duplication when  an unrelated subtree has a depedency
satisfying the required peer dependency at the same tree level.

**Test plan**

Manually verified. Autmated tests coming.
@@ -446,6 +446,24 @@ export default class PackageLinker {
}
const ref = pkg._reference;
invariant(ref, 'Package reference is missing');
// TODO: We are taking the "shortest" ref tree but there may be multiple ref trees with the same length
const refTree = ref.requests.map(req => req.parentNames).sort((arr1, arr2) => arr1.length - arr2.length)[0];
const refTreeLen = refTree.length;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just use refTree.length, it looks like a premature optim (same for pkgParentsLen)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was mostly for ease of reading for myself but sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh well, these were easier on the eyes in a previous version of the code. Now they are useless as you pointed out.

@@ -446,6 +446,24 @@ export default class PackageLinker {
}
const ref = pkg._reference;
invariant(ref, 'Package reference is missing');
// TODO: We are taking the "shortest" ref tree but there may be multiple ref trees with the same length
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a problem if we choose one of those at random?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly don't know, hence the TODO.

@buildsize
Copy link

buildsize bot commented Oct 11, 2017

This change will decrease the build size from 9.93 MB to 9.93 MB, a decrease of 6.68 KB (0%)

File name Previous Size New Size Change
yarn-[version].noarch.rpm 859.11 KB 858.49 KB -631 bytes (0%)
yarn-[version].js 3.78 MB 3.78 MB -2.53 KB (0%)
yarn-legacy-[version].js 3.83 MB 3.83 MB -2.63 KB (0%)
yarn-v[version].tar.gz 864.78 KB 864.25 KB -551 bytes (0%)
yarn_[version]all.deb 653.78 KB 653.41 KB -374 bytes (0%)

const levelDistance = ref.level - peerPkgRef.level;
if (levelDistance >= 0 && levelDistance < resolvedLevelDistance) {
const levelDistance = getLevelDistance(peerPkgRef);
if (isFinite(levelDistance) && levelDistance < resolvedLevelDistance) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to not set minDistance to Infinity and just check for existence now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted that too but then I'd need to add special casing into the getLevelDistance code above due to the distance >= 0 && distance < minDistance checks. Is it too confusing right now? :(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, hmm can't think of an alternative atm. Thanks for explaining. :)

const pkgParentsLen = pkgParents.length;
const distance = refTreeLen - pkgParentsLen;

if (distance >= 0 && distance < minDistance && pkgParents.every((name, idx) => name === refTree[idx])) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be more less readable, but more efficient to use !pakgParents.some instead?

test.concurrent('install should resolve peer dependencies from same subtrees', async () => {
await runInstall({}, 'peer-dep-same-subtree', async (config): Promise<void> => {
expect(JSON.parse(await fs.readFile(`${config.cwd}/node_modules/d/node_modules/a/package.json`)).version).toEqual(
'1.1.0',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this equal 1.0.0 since d points to file:../a-1.0.0 as a dependency? and the top level version of a equal 1.1.0?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, that's why the test was failing :)

for (const req of pkgRef.requests) {
const distance = refTree.length - req.parentNames.length;

if (distance >= 0 && distance < minDistance && req.parentNames.every((name, idx) => name === refTree[idx])) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be slightly less readable but more efficient to use !some to exit early if they don't match

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both every and some do early exits so don't think it would change perf.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugh, I keep forgetting that sorry!

@BYK BYK merged commit f60269f into master Oct 11, 2017
@BYK BYK deleted the peer-dep-tree-hug branch October 11, 2017 22:48
@FDiskas FDiskas mentioned this pull request Oct 12, 2017
joaolucasl pushed a commit to joaolucasl/yarn that referenced this pull request Oct 27, 2017
yarnpkg#4687)

**Summary**

Fixes yarnpkg#4539. Yarn was resolving peer dependencies from the closest level where the peer dependency was requested
but it was not checking if the peer dependency was in the same subtree. This was causing incorrect
peer dependency resolutions and package duplication when  an unrelated subtree has a depedency
satisfying the required peer dependency at the same tree level.

**Test plan**

Added new install integration test that fails without the fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Theia builds are weirdly broken with npm 5.3.0 + yarn 1.1.0
3 participants