-
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 i
produces different filesystem results before/after a specific time (likely related to a semi-related major version bump)
#3881
Comments
I confirmed this happens in |
This is a really neat effect. Here's the fix. Explanation: We build the peer set for the eslint plugin, which has a peer dependency on something like Then, we check to see where we can place the entire peer set, and in the process, we see that placing eslint@8.0.0 in the root is no good, because it conflicts with the root dependency on eslint@7.x, and call that a conflict. If we really had to place the dependency there, we'd just go ahead and do so, and warn about the conflict when it ended up actually having to be in conflict, because it's a transitive dependency. But, because the eslint plugin is a regular dep of another dep in the tree, we say, "ok, no worries, we can just nest it to avoid the conflict". Then we go to nest all the members in the peer set. But since So it gets nested to avoid a conflict that never manifests! The solution in the gist is to only do the "does the target have an edge that conflicts with the dep we're trying to place" if there isn't already a current node in that position. If there is a current node there, we're going to do the work to see if we need to replace it anyway, so it'll still conflict when it needs to. If there isn't a node in that position, then this is the first thing we're trying to place there, and that conflict will definitely pose a problem. This change causes a few snapshots to be a bit more aggressively deduped, but that's actually desirable. Side note, relying on deduping behavior and the ability to
This will never ever work in isolated-mode, so it's worth fixing sooner rather than later, regardless. npm's reification strategy is not public API surface. We guarantee that we'll satisfy the stated dependency contracts, and in this case, that's exactly what we're doing. But the specific folder paths should be treated as 100% black box -- you'll get the version of a package you depend on if you |
Thanks for this thorough explanation @isaacs! This maps pretty well to what I imagined was happening under the hood. Re. our need for peer deps: def agree, and Additionally, I think eslint's means of loading plugins has changed with v8, and that seems to have also resolved our usage issue – we only remained broken because one of the dependencies of that config wasn't yet I think the only thing I'd like to raise is that having determinism in folder path resolution is, imo, important. Although this particular case came about due to a confluence of circumstances, I'm sure plenty of instances like this exist out there (or may soon appear). And, for better or worse, it's also not unheard of for devs to reach directly into their modules' file paths (not that I'm telling you anything you don't already know). If as of npm 7 dependency file paths can (no longer?) be treated as a deterministic outcome then I'd imagine that may erode trust in the system, no? In any event, thank you for the escalation and resolution! 💕 |
…urrent node Fix: npm/cli#3881 When placing a peer set containing a dependency that could be deduped by an older version higher up in the tree, the entire peer set would get nested unnecessarily. For example: ``` root -> (k, y@1) k -> (x) x -> PEER(y@1||2) ``` When placing `x` it would previously result in a tree with `x` nested under `k` even though it can be properly deduped to the root: ``` root +-- k@1 | +-- x@1 +-- y@1 ``` After this patch the tree will now result in `x` being properly deduped: ``` root +-- k@1 +-- x@1 +-- y@1 ``` Co-authored-by: @isaacs
…urrent node Fix: npm/cli#3881 When placing a peer set containing a dependency that could not be placed higher in the tree due to a conflict, the entire peer set would get nested even though the current node would satisfy the condition at the target. For example: ``` root -> (k, y@1) k -> (x) x -> PEER(y@1||2) ``` When placing `x` it would previously result in a tree with `x` nested under `k` even though it can be properly hoisted to the root: ``` root +-- k@1 | +-- x@1 +-- y@1 ``` After this patch the tree will now result in `x` being properly hoisted: ``` root +-- k@1 +-- x@1 +-- y@1 ``` Co-authored-by: @isaacs
…urrent node (#337) Fix: npm/cli#3881 When placing a peer set containing a dependency that could not be placed higher in the tree due to a conflict, the entire peer set would get nested even though the current node would satisfy the condition at the target. For example: ``` root -> (k, y@1) k -> (x) x -> PEER(y@1||2) ``` When placing `x` it would previously result in a tree with `x` nested under `k` even though it can be properly hoisted to the root: ``` root +-- k@1 | +-- x@1 +-- y@1 ``` After this patch the tree will now result in `x` being properly hoisted: ``` root +-- k@1 +-- x@1 +-- y@1 ``` Co-authored-by: @isaacs
Is there an existing issue for this?
Current Behavior
Currently all of our open source projects and many of our non-open source projects are unable to build because
npm
7+ is reifying different file trees based on the publish event of an unrelated module. This meant that all of our builds that were working suddenly stopped working even though no dependencies changed on our side.Here's a reduced test case repo with detailed explanation and steps to repro: https://github.com/ryanblock/npm-time-based-dependency-graph
Summary:
eslint@7
(which has many subdependencies) +@architect/eslint-config
, which has 3 subdependencies:eslint-plugin-filenames
eslint-plugin-fp
eslint-plugin-import
2021-10-09T20:46:00.000Z
are installed:eslint-plugin-$name
dependencies above are installed flatly intonode_modules/
(e.g.node_modules/eslint-plugin/filenames
)2021-10-09T20:47:00.000Z
are installed (one minute later):node_modules/@architect/eslint-config/node_modules/
(specificallyeslint-plugin-filenames
,eslint-plugin-fp
)node_modules/
eslint-plugin-filenames
(noreslint-plugin-fp
, but it fails on the first plugin) because they are both installed innode_modules/@architect/eslint-config/node_modules/
, away from the reach of Node's module resolutioneslint@8
DOES NOT appear in this dependency tree anywhere, however it was published at2021-10-09T20:46:13.874Z
Expected Behavior
No matter when
npm i
is run in a project with identical dependencies, the filesystem results should be the same.Steps To Reproduce
Steps to reproduce can be found at the following reduced test case repo: https://github.com/ryanblock/npm-time-based-dependency-graph
Environment
The text was updated successfully, but these errors were encountered: