-
Notifications
You must be signed in to change notification settings - Fork 332
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: Do not upgrade if peerDependencies are not met #1421
Conversation
The CI here fails as well I will investigate soon. Regarding the fix - this works according to my tests but it is very complicated. Would appreciate you're review. overview:: Current process:
Suggested:
|
Hi @raineorshine this is ready for initial review. It is not a straightforward fix and I would appreciate your input |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution. I did my best to review, but it is admittedly complicated, as you pointed out. The unit test helps a lot though, so thanks for that. If there are any other test cases you want to add, that would help make this more robust.
In general, the code deserves some more thorough commenting. Otherwise it's going to be challenging to come back to this code in the future if any issues arise, or if the functionality needs to be extended.
src/lib/getIgnoredUpgrades.ts
Outdated
.filter( | ||
([peer, peerSpec]) => | ||
upgradedPackagesWithPeerRestriction[peer] && | ||
!satisfies(upgradedPackagesWithPeerRestriction[peer], peerSpec), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I am mistaken, you can filter by current[peer] && !intersects(current[peer], peerSpec)
, where intersects
is from the semver package. This avoids the use of upgradedPackagesWithPeerRestriction
to extract the lower bound of the version range.
Not sure if current
is sufficient, or if you need to check { ...current, ...upgraded }
.
(Note: Make sure to import the named import intersects
rather than the default import in order to get the benefits of tree-shaking. You can see this done with satisfies
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm using the same logic as in:
npm-check-updates/src/lib/runLocal.ts
Lines 183 to 192 in 0f17df2
if (options.peer) { | |
options.peerDependencies = await getPeerDependenciesFromRegistry( | |
Object.fromEntries( | |
Object.entries(current).map(([packageName, versionSpec]) => { | |
return [packageName, nodeSemver.minVersion(versionSpec)?.version ?? versionSpec] | |
}), | |
), | |
options, | |
) | |
} |
I don't think intersect is the right way to go as it is not sufficient to make sure that peer dependency is met. For example, if package P1 spec is ^1.0.0
and P2 has P1 as peer with spec ^1.2.0
, the 2 ranges will intersect but it will not mean that the peer dependency is met...
current
is not sufficient, as we need to check against the versions that are currently upgraded.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if the P1 spec is ^1.0.0
(not 1.0.0
) then npm will install 1.2.0
or later and the peer dependency will be met, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That will not be the case if your lcokfile was not updated and P1 was not upgraded due to a filter. But if you think that will be better I will make the change, looking forward to your opinion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
npm-check-updates is generally just concerned with package.json versions, and assumes that the user will run npm install
and manage the lockfile through npm. The lockfile is basically always out of date after running ncu -u
, so I don't see that as a problem. It would be a problem if npm-check-updates upgraded a version range to something that was incompatible though. If you think that's the case, please let me know. I didn't notice that the existing code uses the same minVersion
trick to get the bottom of the range.
I'm having a hard time picturing the situation you described with a filter on P1. If you think that's a problem, we should add a unit test that covers that. It is admittedly difficult to reason about this abstractly, at least for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im not sure I can write a test for it. I changed to intresect in both places where I used minVersion
@raineorshine I pushed all the fixes, looking forward to your next review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting closer. Thanks for your effort!
src/lib/upgradePackageDefinitions.ts
Outdated
return [packageName, minVersion(versionSpec)?.version ?? versionSpec] | ||
}), | ||
) | ||
const filteredUpgradedPeerDependencies = { ...upgradedPeerDependencies } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a little unnecessary complexity in checkIfInPeerViolation
due to filteredUpgradedPeerDependencies
. Let me try to explain.
The pickBy
below is used to filter down filteredUpgradedDependencies
to only those that satisfy the peerDependencies. At the same time, it needs to filter down upgradedPeerDependencies
by exactly the same dependencies. To do this, the current implementation introduces a mutable variable filteredUpgradedPeerDependencies
which it modifies to keep in alignment with the pickBy accumulator, i.e. filteredUpgradedPeerDependencies
. This requires the developer to observe the delete operation and then reason about the pickBy behavior to understand the relationship between filteredUpgradedPeerDependencies
and filteredUpgradedDependenciesAfterPeers
.
Instead, it would be cleaner to change the pickBy
to a reduce
and filter down filteredUpgradedDependencies
and upgradedPeerDependencies
at the same time. This avoids the mutable variable and shows both collections being filtered down at the same time through the accumulator.
Let me know if this makes sense :).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I can't see how you can filter down both at the same time. Their keys are overlapping and not contained in one another and as such there is no single set of keys I can reduce on. If you can post a snippet showing how, I would appreciate it.
That being said, I did change it a bit to be cleaner without deleting keys, let me know what you think
@raineorshine I pushed all the fixes, looking forward to your next review |
Thanks! I have some work responsibilities right now, but I'll review as soon as I can. |
Thanks I appreciate it |
Hi @raineorshine just a ping to remind you in case you forgot :-) |
src/lib/upgradePackageDefinitions.ts
Outdated
const violated = violatedDependencies.size > 0 | ||
let filteredUpgradedPeerDependencies = upgradedPeerDependencies | ||
if (violated) { | ||
filteredUpgradedPeerDependencies = pickBy(upgradedPeerDependencies, (spec, dep) => !violatedDependencies.has(dep)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is !violatedDependencies.has(dep)
the same as !filteredUpgradedDependencies[dep] || !filteredUpgradedDependenciesAfterPeers[dep]
?
If so, we should be able to get rid of violatedDependencies
. If you don't see it, let me know and I'll take a stab at it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not quite but I changed it
} | ||
let rerunResult: UpgradePackageDefinitionsResult | ||
let runIndex = 0 | ||
do { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm struggling to understand this do-while loop. Something about base case being both when runIndex is 0 and 1, and the continual reassignment of rerunResult
makes it challenging to read.
Any ideas how to make this logic more obvious?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave it another shot
Thanks, I'll take a look soon. Would you mind rebasing on |
of course already on it |
Done |
Sorry about the delay! I've had work responsibilities and have not found time to review this yet. |
6d7d9f6
to
9e813c0
Compare
@raineorshine how are you? Can we expect this to be reviewed soon? It will help my team a lot to have that feature in production |
Yes. I've been at a conference this week, but I will try to get to it soon. |
… are not currently met raineorshine#1418
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Thanks for your patience.
Will release tonight or tomorrow.
fix: #1418