-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
[BUG] npm can install an invalid tree with transitive optional peer deps #4859
Comments
(cc @lukekarrys, per your comment in #4664 (comment).) |
I think I'm also hitting this on 8.9.0 and it's causing a Dockerfile build to fail, though it's not restricted to peerDeps - all my deps are failing to install with |
created a Gist of my package.json, package-lock.json, and log file: https://gist.github.com/bnb/f05ed6601c7997c0453d062ac7c23a64 I've nuked node_modules and run |
@bnb I don't think yours is the same issue. They share the same error message (" I don't believe that your case is a bug, though the error message could be improved. You generally need to run The error message should probably make clear that |
@billyjanitsch Thanks for the thorough report & repro! I was able to reproduce (on Funny enough, I was able to run How to unblock yourself right now:
The issue is as you explained it; the direct dep gets hoisted the first time we reify but it conflicts with the optional peer. Running We'll definitely look into where to solve this early next week. |
Thanks @darcyclarke. I mistakenly assumed that this didn't occur on <8.6.0 because I was using the |
All good, we'll get a fix in soon. |
Updated lock-file to fix npm/cli#4859.
Updated lock-file to fix npm/cli#4859. Updated integration-tests to webpack 5 to fix webpack/webpack#14532.
Updated lock-file to fix npm/cli#4859. Updated integration-tests to webpack 5 to fix webpack/webpack#14532. Added `mode` to webpack-integration-tests to avoid the warning `The 'mode' option has not been set...`.
* Updated lock-file to fix npm/cli#4859. * Updated integration-tests to webpack 5 to fix webpack/webpack#14532. * Added `mode` to webpack-integration-tests to avoid the warning `The 'mode' option has not been set...`. * Replaced outdated `grunt-bg-shell`-package to get rid of coffee-script warnings
* Updated lock-file to fix npm/cli#4859. * Updated integration-tests to webpack 5 to fix webpack/webpack#14532. * Added `mode` to webpack-integration-tests to avoid the warning `The 'mode' option has not been set...`. * Replaced outdated `grunt-bg-shell`-package to get rid of coffee-script warnings
Thanks! That worked! |
hi there folks, linked above is a pull request that addresses this problem. it was unfortunately not as simple to fix as we had hoped. the reason for the strange output is that we don't actually install optional peer dependencies at all today unless you have them declared as your own dependency too. since your example doesn't do this, we end up installing the fix is changing this behavior such that a missing optional peer will attempt to install. if it fails, we prune it from the tree. if it's in conflict with another type of node at the same location, the optional peer takes the lowest precedent and may become invalid. |
@nlf IMO, that doesn't seem like the right way to fix this bug.
This is intentional, according to the RFC (npm/rfcs#224) and the implementation commit (npm/arborist@e13ba3b). I don't understand why you'd want to change this behavior. The ecosystem has widely embraced optional peer dependencies specifically as a way to avoid auto-installation, and the RFC explicitly states this as a goal. The proposed change would leave npm without any way to specify a version constraint without auto-installing the associated package. I understand from the PR that you're planning to delay the change until the next major npm release, but why make that change at all? |
this is a proposed fix, not a concrete solution. the problem as it stands is that we either place a node or we do not. if we do not place a node in the tree, there is absolutely no means today for us to ensure that another node doesn't end up in that location. this is a limitation of our current code base that we're aware of and have long term plans to address. the second problem is the inconsistency in the meaning of "optional". as you pointed out there is an rfc suggesting that we not attempt to automatically install an optional peer dependency, but that rfc made no mention whatsoever regarding a regular optional dependency (i.e. one listed in personally, i'd like to see us not install any optional dependencies by default. if we can make the argument that an optional peer should only be installed when directly requested, then we should be able to make the same argument about any other optional dependency. between that and a very significant refactoring to consider certain tree locations to be off limits we can get pretty far, but there will always be corner cases where we end up doing something the user may not expect. as an example, let's say i have a project defined like so: {
"name": "my-project",
"version": "1.0.0",
"dependencies": {
"a": "^1.0.0",
"b": "^1.0.0"
}
}
{
"name": "a",
"version": "1.0.0",
"peerDependencies": {
"c": "^1.0.0"
},
"peerDependenciesMeta": {
"c": {
"optional": true
},
}
} while {
"name": "b",
"version": "1.0.0",
"peerDependencies": {
"c": "^2.0.0"
},
"peerDependenciesMeta": {
"c": {
"optional": true
}
}
} if a user runs the invalid dependency is an optional one so throwing an error doesn't feel right because you didn't ask for that optional dependency, however it is physically impossible for us to install the 3 requested dependencies without rendering the 4th unrequested one invalid. so what's the right behavior? force the user to add an override saying that if it's not clear, what i'm getting at here is that there are an overwhelming number of corner cases out there and we cannot solve for all of them, especially when it comes to peer dependencies. even if we went back to not installing any peer dependencies at all, there's still the problem that the definitions for peers existing in your tree means you're still likely to end up with an invalid node somewhere. i hear what you're saying, and i agree that reversing a decision made in a past rfc can be a bit alarming (though i would also note that there is absolutely nothing saying we can't or won't reverse a past decision in the future). i just want to assure you that it's not something we take lightly and will not be making a change like this without planning and clear reasoning. my ultimate end goal is to start developing a formal specification for how npm decides what modules to install and where to install them. it's going to be a long road, but we will get there. |
I think the difference is that an optional dependency is something the end user shouldn't have to know about or specify - so it makes sense to install it by default. However, an optional peer dep IS something the end user has to know about and specify - so it makes sense not to install it by default. In other words, even many breaking changes from now, I don't think these two optional categories should be "consistent" with each other in terms of auto-installation. |
so a user is expected to know about an optional peer but not about an optional prod dependency? that doesn't really make sense to me. i can concede that the two don't need to behave the same way, but if that's the case then we need to refactor arborist to stop referring to them both the same way. the |
Yes, a peer (optional or not) is a part of the API that a user always has to know about - adding one, or raising the version threshold on one, is always a breaking change. I have no opinion about the arborist representation. |
* Updated lock-file to fix npm/cli#4859. * Updated integration-tests to webpack 5 to fix webpack/webpack#14532. * Added `mode` to webpack-integration-tests to avoid the warning `The 'mode' option has not been set...`. * Replaced outdated `grunt-bg-shell`-package to get rid of coffee-script warnings
* Updated lock-file to fix npm/cli#4859. * Updated integration-tests to webpack 5 to fix webpack/webpack#14532. * Added `mode` to webpack-integration-tests to avoid the warning `The 'mode' option has not been set...`. * Replaced outdated `grunt-bg-shell`-package to get rid of coffee-script warnings
Hi all!
more info: vitejs/vite#18728 |
Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
Given the following packages:
Running
npm install
inapp
completes successfully, but results in the following invalid tree (npm ls
):Basically,
b
's version ofc
gets incorrectly hoisted, resulting in an incompatible peer dep fora
.npm ci
also refuses to install the tree thatnpm install
just generated:Related to #4664. That issue was closed but there does seem to be an npm bug here.
Expected Behavior
I haven't thought through the details carefully, but it seems that
b
's dependency onc
shouldn't be hoisted if it would result in an invalid optional peer dep.Steps To Reproduce
This reproduces in npm 8.6.0 through 8.9.0.
npm install
.npm ci
ornpm ls
; both exit with an error.@pmmmwh/react-refresh-webpack-plugin@0.5.5
optionally peer-depends ontype-fest@>=0.17.0 <3.0.0
.ow
directly depends ontype-fest@^0.11.0
. The latter gets incorrectly hoisted.Environment
; n/a
The text was updated successfully, but these errors were encountered: